文章目录
- 变量声明方式
- [常量 const 与 iota](#常量 const 与 iota)
- 函数的多返回值
- [init 函数和 import 导包](#init 函数和 import 导包)
-
- [import 匿名和别名导包](#import 匿名和别名导包)
- 指针
- [defer 语句调用顺序](#defer 语句调用顺序)
- 数组与动态数组
-
- [1. 数组](#1. 数组)
- [2. 动态数组(切片)](#2. 动态数组(切片))
- [3. slice 切片的声明方式](#3. slice 切片的声明方式)
- [4. 切片的追加与截取](#4. 切片的追加与截取)
- map
-
- [1. map 声明方式](#1. map 声明方式)
- [2. map 的常用方法](#2. map 的常用方法)
- [struct 的使用](#struct 的使用)

变量声明方式
go
package main
import "fmt"
// 全局变量
var aa = "hello"
var bb string = "hello"
// 错误声明方式
// cc := "hello"
func main() {
// 方法一:创建一个变量,赋默认值
var a int
fmt.Println(a)
// 方法二:创建一个变量,并赋值
var b int = 10
fmt.Println(b)
var c int = 100
fmt.Println(c)
fmt.Printf("type of c %T\n", c)
// 方法三:初始化时,省去数据类型,通过赋值自动匹配类型
var d = 100
fmt.Println(c)
fmt.Printf("type of d %T\n", d)
var e = "hello"
fmt.Println(e)
fmt.Printf("type of e %T\n", e)
// 方法四: (常用)省去 var 关键字,直接自动匹配
f := 100
fmt.Println(f)
fmt.Printf("type of f %T\n", f)
g := "hello"
fmt.Println(g)
fmt.Printf("type of g %T\n", g)
h := 1.25
fmt.Println(h)
fmt.Printf("type of h %T\n", h)
fmt.Println("================================================")
// 全局变量
fmt.Println("aa = ", aa , " bb = ", bb)
fmt.Println("================================================")
// 声明多个变量
var i, j int = 10, 20
fmt.Println("i = ", i, " j = ", j)
var s, t = 100, "world"
fmt.Println("s = ", s, " t = ", t)
// (常用)多行多变量声明
var (
x int = 88
flag bool = true
)
fmt.Println("x = ", x, " flag = ", flag)
}
- 局部变量声明:
(1)自动赋默认值:var a int
(2)初始化赋值:var b int = 100
(3)省去数据类型,自动匹配数据类型:var c = "hello"
(4)省去 var 关键字:d := 100 - 全局变量声明:不支持方法四(不支持省去 var 关键字的写法)
- 多变量声明:
(1)单行声明:var i, j int = 10, 20,var k t = 100, "hello"
(2)多行声明:
go
var (
x int = 88
flag bool = true
)
常量 const 与 iota
go
package main
import "fmt"
// const用于枚举
const (
BEIJING = 0
SHANGHAI = 1
CHENGDU = 2
)
const (
SHENGZHENG = iota // 0
CHONGQING = iota // 1
XIANGGANG = iota // 2
)
const (
a, b = iota, iota + 1 // iota = 0, a = 0, b = 1
c, d = iota + 1, iota + 2 // iota = 1, c = 2, d = 3
e, f = iota + 2, iota + 3 // iota = 2, e = 4, f = 5
g, h = iota * 2, iota * 3 // iota = 3, g = 6, h = 9
i, j = iota * 3, iota * 4 // iota = 4, i = 12, j = 16
)
func main() {
// 常量
const x int = 10
fmt.Println("x = ", x)
// x = 100 报错,常量不能被修改
fmt.Println("================================================")
// 枚举值
fmt.Println("BEIJING = ", BEIJING)
fmt.Println("SHANGHAI = ", SHANGHAI)
fmt.Println("CHENGDU = ", CHENGDU)
fmt.Println("================================================")
// iota 枚举值
fmt.Println("SHENGZHENG = ", SHENGZHENG)
fmt.Println("CHONGQING = ", CHONGQING)
fmt.Println("XIANGGANG = ", XIANGGANG)
fmt.Println("================================================")
fmt.Println("a = ", a, " b = ", b)
fmt.Println("c = ", c, " d = ", d)
fmt.Println("e = ", e, " f = ", f)
fmt.Println("g = ", g, " h = ", h)
fmt.Println("i = ", i, " j = ", j)
// iota 只能用在const中进行累加效果,配合 const 使用
// var t int = iota
}
注意:const 通常用于枚举,iota 是与 const 配合使用表示枚举类型。
函数的多返回值
go
package main
import "fmt"
// 写法一:多个返回值,匿名返回值
func foo1(a string, b int) (int, int) {
fmt.Println("foo1=======================================")
fmt.Println("a = ", a, " b = ", b)
return 100, 200
}
// 写法二:多个返回值,命名返回值
func foo2(a string, b int) (x int, y int) {
fmt.Println("foo2=======================================")
fmt.Println("a = ", a, " b = ", b)
x = 1000
y = 2000
return
}
// 写法三:多个返回值数据类型相同,省略多余的返回值类型
func foo3(a string, b int) (x, y int) {
fmt.Println("foo3=======================================")
fmt.Println("a = ", a, " b = ", b)
x = 88
y = 99
return
}
func main() {
c, d := foo1("foo1", 100)
fmt.Println("c = ", c, " d = ", d)
e, f := foo2("foo2", 200)
fmt.Println("e = ", e, " f = ", f)
g, h := foo3("foo3", 300)
fmt.Println("g = ", g, " h = ", h)
}
init 函数和 import 导包

import 匿名和别名导包
go
import (
_ "GoLearn/5-init/lib1" // 匿名导包
// mylib2 "GoLearn/5-init/lib2" // 别名导包
. "GoLearn/5-init/lib2"
)
func main() {
//lib1.Lib1Test()
//lib2.Lib2Test()
//mylib2.Lib2Test()
Lib2Test()
}

- 匿名导包:
_ lib1给 lib1 包起一个别名,匿名,无法使用 lib1 包的方法,但是可以执行 lib1 内部的 init 方法。 - 别名导包:
mylib2 lib2给 lib2 包起一个别名 mylib2,使用 mylib2.func() 调用 lib2 包的方法。 . lib2将 lib2 包中的方法全部导入到当前本包的全部作用中,直接调用方法执行(不建议,可能与本包内的方法冲突)。
指针
go
package main
import "fmt"
func swap(a *int, b *int) {
var temp = *a
*a = *b
*b = temp
}
func main() {
var a int = 10
var b int = 20
// 交换 a 和 b
swap(&a, &b)
fmt.Println("a = ", a, " b = ", b)
}
defer 语句调用顺序
go
package main
import "fmt"
func foo1() {
fmt.Println("A")
}
func foo2() {
fmt.Println("B")
}
func foo3() {
fmt.Println("C")
}
func main() {
defer foo1()
defer foo2()
defer foo3()
}

defer 执行顺序:通过栈的方式,依次压入栈,再依次出栈执行

注意:defer 和 return 哪个先执行,哪个后执行?
go
package main
import "fmt"
func deferFoo() int {
fmt.Println("deferFoo()")
return 0
}
func returnFoo() int {
fmt.Println("returnFoo()")
return 0
}
func deferAndReturn() int {
defer deferFoo()
return returnFoo()
}
func main() {
deferAndReturn()
}

return 在 defer 前先执行。defer 出栈是在方法的 } 后,也就是方法已经完全结束后,才出栈执行。
数组与动态数组
1. 数组
go
package main
import "fmt"
// 固定长度数组在传参时,严格匹配数组类型
func foo(myArray [3]int) {
for _, value := range myArray {
fmt.Println(value)
}
myArray[0] = 11
}
func main() {
// 固定长度的数组
var myArray1 [10]int
myArray2 := [10]int{1, 2, 3, 4}
myArray3 := [3]int{88, 99, 100}
// 遍历数组方式一
for i := 0; i < len(myArray1); i++ {
fmt.Println(myArray1[i])
}
// 遍历数组方式二
for index, value := range myArray2 {
fmt.Println("index = ", index, " value = ", value)
}
// 查看数组类型
fmt.Printf("myArray1 types = %T\n", myArray1)
fmt.Printf("myArray2 types = %T\n", myArray2)
fmt.Printf("myArray3 types = %T\n", myArray3)
foo(myArray3)
fmt.Println("===========================================")
for _, value := range myArray3 {
fmt.Println(value)
}
}

可以发现,固定长度的数组在传参时,是值传递
2. 动态数组(切片)
go
package main
import "fmt"
func foo(myArray []int) {
// 引用传递
for _, value := range myArray {
fmt.Println(value)
}
myArray[0] = 100
}
func main() {
myArray := []int{1, 2, 3, 4, 5} // 动态数组, 切片 slice
fmt.Printf("myArray type = %T\n", myArray)
foo(myArray)
fmt.Println("===========================================")
for _, value := range myArray {
fmt.Println(value)
}
}

动态数组传参上是引用传递,不同元素长度的动态数组的形参一致。
3. slice 切片的声明方式
go
package main
import "fmt"
// 声明切片 slice 的方式
func main() {
// 方式一:声明 slice1 是一个切片,并赋初始值为 1, 2, 3, 长度为3
slice1 := []int{1, 2, 3}
// 方式二:声明 slice2 是一个切片,但并没有给 slice2 分配空间
var slice2 []int
//slice2 = make([]int, 3) // 分配3的内存,初始化值为 0
//slice2[0] = 100
// 方式三:声明 slice3 是一个切片,分配3个内存,初始化值为 0
var slice3 = make([]int, 3)
// 方式四:声明 slice4 是一个切片,分配3个内存,初始化值为 0,通过 := 推导出 slice4 是一个切片
slice4 := make([]int, 3)
fmt.Printf("len = %d slice1 = %v\n", len(slice1), slice1)
fmt.Printf("len = %d slice2 = %v\n", len(slice2), slice2)
fmt.Printf("len = %d slice3 = %v\n", len(slice3), slice3)
fmt.Printf("len = %d slice4 = %v\n", len(slice4), slice4)
// 判断 slice 是否为空
if slice2 == nil {
fmt.Println("slice is nil")
} else {
fmt.Println("slice is not nil")
}
}
4. 切片的追加与截取
切片的追加:
go
package main
import "fmt"
func main() {
// 创建一个长度为3,容量为5的切片,默认值为[0, 0, 0]
var slice1 = make([]int, 3, 5)
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
// 向切片中追加一个元素 1
slice1 = append(slice1, 1)
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
// 向切片中追加一个元素 2
slice1 = append(slice1, 2)
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
// 此时容量已满,再次向切片中追加元素 3,容量会自动扩容为原来的2倍
slice1 = append(slice1, 3)
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
}
切片的截取和拷贝:
go
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3}
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
// 截取 slice1 的第0个元素到第1个元素,作为 slice2 的引用
slice2 := slice1[0:2]
fmt.Printf("len = %d, cap = %d, slice2 = %v\n", len(slice2), cap(slice2), slice2)
// 修改 slice2 的值,slice1 的值会改变,因为是引用传递,指向的同一个位置
slice2[0] = 100
fmt.Printf("len = %d, cap = %d, slice1 = %v\n", len(slice1), cap(slice1), slice1)
fmt.Printf("len = %d, cap = %d, slice2 = %v\n", len(slice2), cap(slice2), slice2)
// copy 将底层数组的值拷贝到新的数组中
slice3 := make([]int, 3)
// 将 slice1 的值拷贝到 slice3
copy(slice3, slice1)
fmt.Printf("len = %d, cap = %d, slice3 = %v\n", len(slice3), cap(slice3), slice3)
}

map
1. map 声明方式
java
package main
import "fmt"
// map 声明方式
func main() {
// ======> 方式一:声明 map1 是一个 map 类型,key 为 string,value 为 string
var map1 map[string]string
if (map1 == nil) {
fmt.Println("map1 是一个空 map")
}
// 使用 map 前,为其分配空间
map1 = make(map[string]string, 10)
map1["one"] = "111"
map1["two"] = "222"
map1["three"] = "333"
fmt.Println(map1)
fmt.Println("=======================================================")
// ======> 方式二:
map2 := make(map[string]string)
map2["one"] = "111"
map2["two"] = "222"
map2["three"] = "333"
fmt.Println(map2)
fmt.Println("=======================================================")
// ======> 方式三:
map3 := map[int]string {
1: "one",
2: "two",
3: "three",
}
fmt.Println(map3)
fmt.Println("=======================================================")
}
2. map 的常用方法
java
package main
import "fmt"
func printCity(cityMap map[string]string) {
for key, value := range cityMap {
fmt.Println(key, ":", value)
}
}
func addCity(cityMap map[string]string, key string, value string) {
cityMap[key] = value
}
func main() {
cityMap := make(map[string]string)
// 添加元素
cityMap["China"] = "北京"
cityMap["Japan"] = "东京"
cityMap["USA"] = "华盛顿"
// 遍历
// for key, value := range cityMap {
// fmt.Println(key, ":", value)
// }
printCity(cityMap)
// 修改
cityMap["China"] = "上海"
fmt.Println(cityMap)
// 删除
delete(cityMap, "China")
fmt.Println(cityMap)
fmt.Println("=======================================================")
addCity(cityMap, "China", "成都") // map 是引用传递
fmt.Println(cityMap)
}

struct 的使用
java
package main
import "fmt"
type Book struct {
title string
author string
}
func changeBook(book Book) {
// 传递 book 的副本
book.title = "Go 语言教程"
}
func changeBook2(book *Book) {
// 传递指针
book.title = "Go 语言教程"
}
func main() {
var book1 Book
book1.title = "Go 语言"
book1.author = "Go 语言中文网"
fmt.Printf("%v\n", book1)
changeBook(book1)
fmt.Printf("%v\n", book1)
changeBook2(&book1)
fmt.Printf("%v\n", book1)
}
