基础
1. 变量初始化
go中使用var声明的变量会自动初始化,如下是等价的
wg := sync.WaitGroup{}
var wg sync.WaitGroup
2. array
- 数组:是同一种数据类型的固定长度的序列。
- 数组定义:
var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。 - 长度是数组类型的一部分,因此,
var a[5] int和var a[10]int是不同的类型。 - 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
- 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
- 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
- 支持
"=="、"!="操作符,因为内存总是被初始化过的。 - 指针数组
[n]*T,数组指针*[n]T。
func TestArray(t *testing.T) {
array1 := [5]int{}
// go中array赋值是值拷贝类型,改变array1数据不会影响array2
array2 := array1
array1[0] = 1
// [1 0 0 0 0]
fmt.Println(array1)
// [0 0 0 0 0]
fmt.Println(array2)
// array2 == array3 : true
// go中数组比较是值比较
array3 := [5]int{}
fmt.Printf("array2 == array3 : %t", array2 == array3)
}
2.1. 数组初始化
func TestArrayInit(t *testing.T) {
// 一维数组
a := [3]int{1, 2} // 未初始化元素值为 0。
b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素类型。
{"user2", 20}, // 别忘了最后一行的逗号。
}
// [1 2 0] [1 2 3 4] [0 0 100 0 200] [{user1 10} {user2 20}]
fmt.Println(a, b, c, d)
// 二维数组
e := [2][3]int{{1, 2, 3}, {4, 5, 6}}
f := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."
//f := [...][...]int{{1, 1}, {2, 2}, {3, 3}}
g := [2][3]int{}
// [[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]] [[0 0 0] [0 0 0]] [[0 0 0] [0 0 2]]
h := [2][3]int{1: {2: 2}}
fmt.Println(e, f, g, h)
}
3. slice
3.1. 创建切片
func TestCreate(t *testing.T) {
//1.声明切片
var s1 []int
if s1 == nil {
fmt.Println("是空")
} else {
fmt.Println("不是空")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
// [] [] []
fmt.Println(s1, s2, s3)
// 4.初始化并赋值,第一个参数代表是int类型切片,第二个参数代表初始有1个元素,第二个参数代表初始长度为2;通过预先分配容量的做法,可以在一定程度上减少内存重新分配的次数,从而提高性能
//make([]type, len, cap)
var s4 []int = make([]int, 1, 2)
// [0]
fmt.Println(s4)
s5 := []int{1, 2, 3}
// [1 2 3]
fmt.Println(s5)
// 5.从数组切片
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// 前包后不包
s6 = arr[1:4]
// [2 3 4]
fmt.Println(s6)
}
3.2. 切片初始化
func TestInit(t *testing.T) {
arr := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice1 := arr[0:10] // 左闭右开,全部元素
slice2 := arr[:8]
slice3 := arr[1:]
slice4 := arr[:]
slice5 := arr[:len(arr)-1] //去掉切片的最后一个元素
// [9 8 7 6 5 4 3 2 1 0] [9 8 7 6 5 4 3 2] [8 7 6 5 4 3 2 1 0] [9 8 7 6 5 4 3 2 1 0] [9 8 7 6 5 4 3 2 1]
fmt.Println(slice1, slice2, slice3, slice4, slice5)
}
3.3. len和cap
func TestLenCap(t *testing.T) {
slice1 := make([]int, 1, 2)
print(len(slice1)) // 1
print(cap(slice1)) // 2
}
3.4. append追加
mySlice := make([]int, 3)
newSlice := append(mySlice, 4)
mySlice[0] = 1
var newSlice2 = make([]int, 4)
copy(mySlice, newSlice2)
newSlice[0] = 2
fmt.Println(mySlice) // [0 0 0]
fmt.Println(newSlice) // [2 0 0 4]
fmt.Println(newSlice2) // [0 0 0 0]
4. Map
// 线程不安全
map[KeyType]ValueType
// map嵌套
map[string]map[string]int
// 线程安全
var myMap sync.Map
myMap.Store("a", 1)
myMap.Store(2, "b")
value, _ := myMap.Load("a")
fmt.Println(value) // 1
value, _ = myMap.Load(2)
fmt.Println(value) // b
5. 常量
// go 枚举
const (
spring = 0
summer = 1
autumn = 2
winter = 3
)
// constant block
const (
one = 1
valueStillOne // value为1, 在常量块中,如果某个常量的值被省略,Go编译器会自动将这个常量的值设置为与上一个显式赋值的常量相同的值
two = 1
)
iota,特殊常量,可以认为是一个可以被编译器修改的常量。iota在const关键字出现时将被重置为 0(const 内部的第一行之前),const中每新增一行常量声明将使iota计数一次(iota 可理解为 const 语句块中的行索引)。
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 独立值,iota += 1
e // “ha” iota += 1
f = 100 // iota +=1
g // 100 iota +=1
h = iota // 7, 恢复计数
i // i = iota, 为8
)
const (
j = 1 << iota
k = 3 << iota
l // 3<<iota, iota为3,最后值12
m // 3<<iota, iota为4,最后值24
)
6. switch
- case自带break,不会默认继续执行
- 可以使用fallthrough强制执行后一个case的代码(如果后一个是default也会匹配)
- case可以有多个条件
- switch空相当于switch,可以用于表示if..else if..else
[var x interface{} = 10
switch x.(type) {
case nil:
fmt.Printf(" x 的类型 :nil")
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型")
default:
fmt.Printf("未知型")
}](<func main() {
var x interface{} = 10
//写法一:
switch i := x.(type) { // 带初始化语句
case nil:
fmt.Printf(" x 的类型 :%T\n", i)
case int:
fmt.Printf("x 是 int 型\n")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型")
default:
fmt.Printf("未知型")
}
//写法二
var j = 0
switch j {
case 0:
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
//写法三
var k = 0
switch k {
case 0:
println("fallthrough")
fallthrough
/*
Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
而如果switch没有表达式,它会匹配true。
Go里面switch默认相当于每个case最后带有break,
匹配成功后不会自动向下执行其他case,而是跳出整个switch,
但是可以使用fallthrough强制执行后面的case代码。
*/
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
//写法三
var m = 0
switch m {
case 0, 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
//写法四
var n = 0
switch { //省略条件表达式,可当 if...else if...else
case n %3E 0 && n %3C 10:
fmt.Println("i > 0 and i < 10")
case n > 10 && n < 20:
fmt.Println("i > 10 and i < 20")
default:
fmt.Println("def")
}
}>)
7. for循环
// 循环有三种写法
func main() {
// for {} 替代 while(true) {}
for {
fmt.Println("loop in for")
break
}
// for condition {}
count := 10
for count >= 0 {
fmt.Printf("loop, value %d\n", count)
count -= 1
}
// for init; condition; post {}
for i := 0; i < 10; i++ {
fmt.Printf("loop, value %d\n", i)
}
}
for还可以用于迭迭代器,for range,可以对slice/map/数组/字符串/channel等
| 1st value | 2nd value | ||
|---|---|---|---|
| string | index | s[index] | unicode, rune |
| array/slice | index | s[index] | |
| map | key | m[key] | |
| channel | element |
s := "hello world"
//for _, value : range s 不需要index
//for index : range s 不需要char
for index, value := range s {
fmt.Printf("index %d, value %s\n", index, string(value))
}
for index := range s {
fmt.Printf("index %d", index)
}
arr := [3]int{1, 2, 3}
for index, value := range arr {
fmt.Printf("index %d, value %d\n", index, value)
}
slice := arr[:2]
for index, value := range slice {
fmt.Printf("index %d, value %d\n", index, value)
}
myMap := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
for key, value := range myMap {
fmt.Printf("key %s, value %d", key, value)
}
myChan := make(chan int, 10)
for i := 10; i > 0; i-- {
myChan <- i
}
for value := range myChan {
fmt.Printf("value %d", value)
}
close(myChan)
8. 切片
9. Struct
格式定义
StructType = "struct" "{" { [FieldDecl](https://go.dev/ref/spec<span class="tag">#FieldDecl</span>) ";" } "}" .
FieldDecl = ([IdentifierList](https://go.dev/ref/spec<span class="tag">#IdentifierList</span>) [Type](https://go.dev/ref/spec<span class="tag">#Type</span>) | [EmbeddedField](https://go.dev/ref/spec<span class="tag">#EmbeddedField</span>)) [ [Tag](https://go.dev/ref/spec<span class="tag">#Tag</span>) ] .
EmbeddedField = [ "*" ] [TypeName](https://go.dev/ref/spec<span class="tag">#TypeName</span>) [ [TypeArgs](https://go.dev/ref/spec<span class="tag">#TypeArgs</span>) ] .
Tag = [string_lit](https://go.dev/ref/spec<span class="tag">#string_lit</span>) .
9.1. structTag
struct属性定义的最后可以添加Tag,Tag格式如下:
// `key1:"value1" key2:"value2"`
type Book struct {
name string `tag1:"value1" tag2:"value2"`
price int
}
使用ini作为tag的key,配置文件中的配置项key作为value,可以使用ini.MapTo()将配置映射到结构体中。