Go

一、基础

1、类型

基础类型

Go 自带基础类型有如下:

类型 长度 默认值 说明
bool 1 false
byte 1 0 uint8 别名
int, uint 4, 8 0 默认整数类型,不同平台使用不同长度32或64位
int8, uint8 1 0 -128127, 0255
int16, uint16 2 0 -3276832767, 065535
int32, uint32 4 0 -21亿21亿, 042亿
int64, uint64 8 0
float32 4 0.0
float64 8 0.0 默认浮点类型
complex64 8
complex128 16
rune 4 0 int32 别名
uintptr 4, 8 0 存储指针的 uint
string “” 默认空字符串而不是 NULL
array
struct
function nil
interface nil
map nil 引用类型
slice nil 引用类型
channel nil 引用类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// strconv 包用来在不同类型间转换:
strconv.ParseInt()
strconv.ParseFloat()

// Go 中没有隐式转换,所有的类型转换都必须显示转换,注意如果要转成指针、通道、方法时类型也要加上括号
a := 10
b = byte(a)
c = (*int)(a)
(<-chan int)(a)
(func())(a)

// 未指定类型赋值时并不一定总是由赋值决定类型,还可能由其后的操作决定
var v = 20 // 未指定类型,按理说应该被推断为 int 类型
var a byte = 10
b = v + a // 但被用来和 byte 类型一起计算,所以 v 被推断为 byte 类型


// 多变量赋值,注意:先计算出右边的所有值才会执行赋值
x, y := 1, 2
x, y = y+3, x+2 // out: x = 5, y = 3

// Go 中没有 enum,通过一组自增常量实现,iota 自增类型默认为 int
const (
a = iota // 0
b // 1
c = 100 // 100 打断了 iota,必须显示恢复不会默认恢复
d // 100 没有使用 iota 时,未指定时默认与上一行类型、值相同
e = iota // 4 显示恢复 iota,而且恢复后是按照行序递增
f // 5
)

// iota 默认类型是 int,可以自定义类型
type color byte
const (
black color = iota
red
blue
)

// 常量和变量的区别:
// 变量在运行期分配内存地址,运行中可以获取变量内存地址
// 常量经过编译器预处理之后直接使用值,在运行期不存在这个常量了所以不能取到常量内存地址
var x = 0x100
const y = 0x200
&x // ok
&y // error


// Go 中有两个函数用来分配内存:new、make
// 对于非引用类型可以使用 new 来分配需要的内存并分配零值,最后返回指针
new(int)
new(float32)
// 对于三种引用类型:slice、map、channel 则必须使用 make 来分配内存并返回该类型(注意不是返回指针)
make([]int, 0, 10)
make(map[string]int)

自定义类型

Go 中自定义类型有二种方式:

  • 基于现有的基础类型创建,也就是别名。别名只能继承基础类型的操作符,其他方法等都不能继承。
  • 自定义结构体、方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 别名,别名只能继承基础类型的操作符,其他方法等都不能继承
type flags byte

// 结构体,注意:结构体的 tag 也是类型的一部分,tag 不同的 struct 肯定不是一个类型
type person struct {
name string `name`
}

type (
user struct {
name string `name`
age int `age`
}

admin struct {
user
level int
}

// 方法
event func(string) bool
)

2、表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Go 中自增自减只能后置不能前置,而且必须放在单独的一行语句中而不能放在表达式中
a := 1
++a // error: ++ 不能前置
a++ // ok
if (a++) > 1 { // error:不能用于表达式中
}

// 如果两个指针指向同一个地址或都为 nil 时,两个指针相等

// 对于数组、切片、字典、结构体的字面量初始化时,每行必须以逗号或花括号结尾
type data struct {
x int
y string
}
var a data = data{1, "abc"} // ok
var b data = data{
1,
"abc", // 这里的逗号是必须的,必须以逗号或花括号结尾
}

// Go 中没有类似如下的三目条件运算符
a > b ? a : b

// if switch 这两个条件语句都支持初始化语句
// 初始化了 err 然后判断
if err := check(s); err != nil {

}
// 初始化了 x 然后判断
switch x := 5; x {
case 5, 6: // 多个值时表示或者
x += 10
default
x += 100
}

// switch 不会自动贯穿,需要手动 fallthrough 贯穿,使用 fallthrough 之后按照源码顺序直接执行下一个 case 而不会在判断是否满足条件
switch x := 5; x {
case 5:
x += 10
fallthrough // 贯穿
case 6:
x += 20 // 按照源码顺序执行这里,并在这里停止
default:
x += 30
}

// Go 中的循环只有 for 一种方式,for-range 可以用于字符串、数组、数组指针、切片、字典、通道
data := [3]int{"a", "b", "c"}
for i, v := range data {

}

// 仅迭代不取值
for range data {

}

// for-range 遍历有个注意点:遍历时会复制一次遍历对象,对于数组、切片等类型可能表现不同
data := [3]int{10, 20, 30}
for i, x := range data {
if i == 0 {
data[0] = 100
data[1] = 200
data[2] = 300
}
fmt.Printf("x = %d, data: %d\n", x, data[i])
}

// out:
// x = 10, data: 100
// x = 20, data: 200
// x = 30, data: 300

data := [3]int{10, 20, 30}
for i, x := range data[:] {
if i == 0 {
data[0] = 100
data[1] = 200
data[2] = 300
}
fmt.Printf("x = %d, data: %d\n", x, data[i])
}

// out:
// x = 10, data: 100
// x = 200, data: 200
// x = 300, data: 300

3、函数

Go 中函数有一些限制:不支持默认参数、不支持函数重载。

函数在 Go 中是第一类对象,具有相同参数和返回类型的函数是同一类型。

1
2
3
4
5
6
// 以下三个方法是同一类型
func func1(str string) int {}
func func2(str string) int {}
var f = func(str string) int {

}

二、GO 商城

1、RabbitMQ

RabbitMQ 主要使用场景:流量削峰、异步处理、应用解耦。

RabbitMQ 安装依赖 Erlang

三、Go 容器微服务