内建方法
即不需要引用包, 可以直接使用
1. make
- 创建 slice, map, chan 三种引用类型
- 返回引用类型, 即类型本身
// 创建 slice, 切片相当于变长的数组, 切片底层是一个数组, 需设置长度和容量, 默认容量等于长度
mySlice := make([]string, 3) // 开辟内存空间, 设置size, 指定长度
// 赋值
mySlice[0] = "dog"
mySlice[1] = "cat"
mySlice[2] = "pig"
// 超出大小, 索引越界, panic: runtime error: index out of range [3] with length 3
mySlice[3] = "tiger" // 此时应该使用append方法进行追加, 系统会自动扩容
fmt.Println(mySlice) // [dog cat pig]
// 创建 map, 键值, 定义键的类型和值的类型
myMap := make(map[int]string) // 可以省略size, 会为空映射分配足够的空间来容纳指定数量的元素
// 赋值
myMap[10] = "dog"
myMap[100] = "cat"
fmt.Println(myMap) // map[10:dog 100:cat]
// 创建 chan, 可以设置管道缓冲区大小
myChan := make(chan int) // size不设置代表没有缓存区, 没有缓存区时, 需要有另外的协程进行接收, 否则写入会阻塞, 设置后代表有缓存区, 有缓冲区时即使没有接收者也可以写入, 但超出大小后也会阻塞
myChan <- 1 // 写入阻塞, fatal error: all goroutines are asleep - deadlock!
2. new
- 创建 int, float, string, bool, array, struct 等基本数据值类型, 默认值为对应类型的零值
- 返回对应类型的指针地址
// 创建str
myStr := new(string)
// 创建int
myInt := new(int)
// 创建bool
myBool := new(bool)
// 创建struct
myStruct := new(struct {
name string
age int
})
fmt.Printf("type: %T, value: %v\n", myStr, \*myStr) // type: \*string, value:
fmt.Printf("type: %T, value: %v\n", myInt, \*myInt) // type: \*int, value: 0
fmt.Printf("type: %T, value: %v\n", myBool, \*myBool) // type: \*bool, value: false
fmt.Printf("type: %T, value: %v\n", myStruct, \*myStruct) // type: \*struct { name string; age int }, value: { 0}
2. append, delete, copy
- 操作 slice, append, copy
- 操作 map, delete
// append
myArray := [3]int{1, 2, 3}
mySlice := myArray[:]
fmt.Printf("slice: %v, len: %d, cap: %d\n", mySlice, len(mySlice), cap(mySlice))
// slice: [1 2 3], len: 3, cap: 3
// 扩容
mySlice = append(mySlice, 100)
fmt.Printf("slice: %v, len: %d, cap: %d\n", mySlice, len(mySlice), cap(mySlice))
// slice: [1 2 3 100], len: 4, cap: 6
// 创建切片时, 最好提前预估好长度, 避免自动扩容, 申请内存去影响部分性能
// copy
sliceDst := make([]string, 2)
sliceDst[0] = "dst-1"
sliceDst[1] = "dst-2"
sliceSrc := make([]string, 2)
sliceSrc[0] = "src-1"
sliceSrc[1] = "src-2"
copy(sliceDst, sliceSrc)
fmt.Println(sliceDst) // [src-1 src-2]
// copy
sliceDst := make([]string, 2)
sliceDst[0] = "dst-1"
sliceDst[1] = "dst-2"
sliceSrc := make([]string, 3)
sliceSrc[0] = "src-1"
sliceSrc[1] = "src-2"
sliceSrc[2] = "src-3"
copy(sliceDst, sliceSrc)
// 目标源切片的容量不变, 其值被拷贝源切片的值, 全部覆盖
fmt.Println(sliceDst) // [src-1 src-2]
// copy
sliceDst := make([]string, 3)
sliceDst[0] = "dst-1"
sliceDst[1] = "dst-2"
sliceDst[2] = "dst-3"
sliceSrc := make([]string, 2)
sliceSrc[0] = "src-1"
sliceSrc[1] = "src-2"
copy(sliceDst, sliceSrc)
// 目标源切片的容量不变, 其值被拷贝源切片的值, 覆盖一部分, 其他值保持不变
fmt.Println(sliceDst) // [src-1 src-2 dst-3]
// delete
myMap := make(map[int]string)
myMap[1] = "dog"
myMap[2] = "cat"
delete(myMap, 1)
delete(myMap, 100) // 删除不存在的键, 不会报错, 只是不操作任何内容
fmt.Println(myMap) // map[2:cat]
3. panic, recover
- panic 抛出异常, 致命错误, 不允许程序继续运行
- recover 捕获异常, 程序不再报错, 继续运行
defer func() {
msg := recover() // 捕获信息
// 返回是空接口类型, 类型断言
switch msg.(type) {
case string: // panic("error")
fmt.Println("string error: ", msg)
case error: // panic(errors.New("error"))
fmt.Println("error: ", msg)
default: // panic(500)
fmt.Println("unknown error: ", msg)
}
}()
panic("panic err") // 遇到致命错误panic, 不再报错并退出程序, 而是继续向下执行
4. len, cap, close
- len 支持 string, array, slice, map, chan 类型
- cap 支持 slice, array, chan 类型
- -close 仅支持 chan 类型
chan 是协程与协程之间的数据通道, 用来实现协程间的通信, 比较占资源, 使用完之后要关闭
// 创建管道
myChan := make(chan int, 1)
defer close(myChan) // 一般在项目中, 创建管道后, 再 defer 关闭
// 写入值
myChan <- 1
// 关闭通道后, 不可再往管道写入值
close(myChan)
myChan <- 2 // panic: send on closed channel
结构体
若干种类型字段的集合, 它是一整块内存空间
1. 定义结构体
- 定义struct
- 面向对象特性: 封装
- 初始化
// struct
type animal struct {
id int
name string
age int
}
// 先声明
var dog animal
// 再赋值
dog.id = 1
dog.name = "bai"
dog.age = 2
// 声明并赋值
cat := animal{
id: 2,
name: "hei",
age: 1,
}
// new, 返回指针地址
pig := new(animal)
pig.id = 3
pig.name = "hui"
pig.age = 5
fmt.Println(dog, cat, pig) // {1 bai 2} {2 hei 1} &{3 hui 5}
2. 属性及方法定义
- 作用域, 首字母大写表示公共的, 包外可调用, 首字母小写表示私有的, 包内可见, 包外无法调用
type animal struct {
// 结构体属性
id int
name string
age int
}
func (a \*animal) run() {
fmt.Println("running...", a.name)
}
// new
dog := new(animal)
dog.id = 1
dog.name = "la"
dog.age = 2
// 调用方法
dog.run() // running... la
3. 组合
- 面向对象特性: 继承
- 组合实现 ( 结构体嵌套 )
type biology struct {
birthday time.Time
}
type animal struct {
// 结构体属性
id int
name string
age int
// 结构体嵌套
biology
}
func (a \*animal) run() {
fmt.Println("running...", a.name)
}
func (b \*biology) birth() {
fmt.Println("today is my birthday")
}
// new
dog := new(animal)
dog.id = 1
dog.name = "la"
dog.age = 2
dog.birthday = time.Now().Local()
// 调用方法
dog.run() // running... la
// 可以直接使用嵌套结构体的方法
dog.birth() // today is my birthday
接口
接口可以这样理解, 公共方法组合起来, 以封装特定功能的一个集合, 抽象出接口的目的就是让一些类去实现它, 接口本质上是指针类型
- 面向对象特性: 多态 ( 接口的多种不同实现方式 )
- 接口定义变量
// 定义接口, 实现接口中所有的方法, 即实现了这个接口
type behavior interface {
speak() string
move()
}
type person struct {
name string
}
type dog struct {
name string
}
// 隐性实现接口
func (p \*person) speak() string {
return "hello"
}
func (p \*person) move() {
fmt.Println(p.name, " move...")
}
// 隐性实现接口
func (d \*dog) speak() string {
return "~~~"
}
func (d \*dog) move() {
fmt.Println(d.name, " move...")
}
// 接口类型作为函数参数
func do(b behavior) {
fmt.Println(b.speak())
b.move()
}
// 实现了接口的结构体对象, 就可以存储在接口变量中
var i behavior
long := new(person) // new, 返回指针类型
long.name = "long"
// 赋值给接口变量
i = long
i.move()
dog := new(dog)
dog.name = "dog"
i = dog
fmt.Println(i.speak())
// 实现了接口的结构体, 就可以作为函数do的参数
do(long)
do(dog)
并发
- 并发实现: 协程 ( 用户态维护, 比线程更小, 需要的内存也更少, 比线程更易用, 而且更高效更轻量 )
- 通过管道实现多协程间的通信
- 通过锁机制实现多协程间的同步
1. 协程
- 启动协程
- 多核CPU设置
// 获取当前主机CPU逻辑核心数
CPUNum := runtime.NumCPU()
// 设置启动的最大CPU个数
runtime.GOMAXPROCS(CPUNum - 1)
// 利用go关键字开启goroutine, 并行
go func() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}()
// 此时如果不加睡眠时间, 终端任何打印结果都没有, 因为主函数为主goroutine, 启动其他子goroutine时, 还没来得及打印结果输出, 主函数程序就执行完了, 即主goroutine结束后, 子goroutine也会一起结束退出
time.Sleep(time.Second \* 1)
2. 协程通信
- 管道 channel 在协程之间接收和发送数据, 进行数据交互
- 多管道之间通过 select 调度, select 一般与 for 配置使用
myChan := make(chan int, 5)
overChan := make(chan bool, 1)
go func() {
// 发送数据到管道
for i := 0; i < 5; i++ {
myChan <- i
time.Sleep(1 \* time.Second)
}
overChan <- true
}()
go func() {
// 循环从管道中取值
for {
// select 监听多个管道
select {
case num := <-myChan:
fmt.Println(num)
case <-overChan:
fmt.Println("over")
break
}
}
}()
time.Sleep(10 \* time.Second)
3. 协程同步
系统工具 sync.WaitGroup
- Add(delta int) 添加协程记录
- Done() 移除协程记录
- Wait() 同步等待所有记录的协程全部结束
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
// 添加记录
wg.Add(1)
go func() {
// 移除记录
defer wg.Done()
fmt.Println("hello")
time.Sleep(time.Second \* 1)
}()
}
// 等待
wg.Wait()
指针
1. 基本使用
- 定义指针变量
- 为指针变量赋值
- 访问指针变量中指向地址的值
// 定义一个int类型的指针变量, 未赋值为nil, 不支持指针运算
var ptr \*int
num := 1
// 取指针地址, 即变量num的内存地址
ptr = &num
// 根据指针地址, 取其指向的值
myNum := \*ptr
fmt.Println(ptr, myNum) // 0xc00000a2e0 1
2. 指针数组与数组指针
// 定义指针数组
var pointArr [2]\*int
age, money := 20, 100
pointArr[0] = &age
pointArr[1] = &money
fmt.Println(pointArr) // [0xc00000a2e0 0xc00000a2e8]
// 数组指针
var ptr \*[2]int
myArr := [2]int{1, 2}
ptr = &myArr
fmt.Println(ptr) // &[1 2]
JSON
1. 序列化
- 结构体序列化及 Tag
- Map 序列化
// 序列化, Tag标签类似于别名, 它既可以满足字段被包外使用, 又可以对外提供指定的别名
type user struct {
Name string `json:"name"`