目录
变量的声明
Golang常用容器
defer
有趣的多态
结构体标签和reflect 反射
Golang最强的协程
channel
go可能造成的内存泄露
变量的声明
方法 1:有类型,有var,不赋值 在Golang中默认值为0
方法 2:无类型,有var,赋值
方法 3:无类型,无var,赋值
多变量声明
多变量声明,无类型无var
如下
package main
import (
"fmt"
)
func main() {
//var 用于声明变量
var a int // 方法 1:有类型,有var,不赋值 在Golang中默认值为0
fmt.Println("a=", a)
var b = 0 //方法 2:无类型,有var,赋值
fmt.Println("b=", b)
c := 0 //方法 3:无类型,无var,赋值
fmt.Println("c=", c)
//多变量声明
var d, e int = 1, 2 //
fmt.Println("d=", d, "e=", e)
//多变量声明,无类型无var
f, g := "string", 3
fmt.Println("f=", f, "e=", g)
}
常量
定义后无法改变
Golang常用容器
静态数组(slice)
package main
import "fmt"
func main() {
//数组,静态数组
var arr [5]int
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
fmt.Println(arr) // 输出: [1 2 3 4 5]
fmt.Println(len(arr)) // 输出: 5
}
动态数组(array)
跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容
package main
import "fmt"
func main() {
//跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice) // 输出: [1 2 3 4 5]
fmt.Println(len(slice)) // 输出: 5
}
自动扩容演示
slice两倍扩容
package main
import "fmt"
func main() {
//跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice) // 输出: [1 2 3 4 5]
fmt.Println(len(slice)) // 输出: 5
fmt.Println(cap(slice)) //当前容量为5
slice = append(slice, 6)
fmt.Println(len(slice)) // 输出: 数据数量=6
fmt.Println(cap(slice)) // 输出: 10 slice两倍扩容
}
字符串(string)
package main
import "fmt"
func main() {
str := "Hello, World!"
fmt.Println(str) // 输出: Hello, World!
fmt.Println(len(str)) // 输出: 13
fmt.Printf("%s", str[0]) // 输出: H
}
映射(map)
package main
import "fmt"
func main() {
m := make(map[int]string)
m[1] = "apple"
m[2] = "banana"
m[3] = "orange"
for i := 1; i < 4; i++ {
fmt.Printf("m=%s\n", m[i])
}
}
defer
类似c++中的析构函数
defer作用:
释放占用的资源
捕捉处理异常
输出日志
func main() {
defer func() {
fmt.Println("析构函数")
}()
fmt.Println("main start")
//===
//业务
//===
fmt.Println("main off")
}
当然Golang中有资源自动回收,不用自己析构,当然有特殊的例子下面讲了协程再说
有趣的多态
import "fmt"
// 两个类
type Student struct {
age int
}
type Programmer struct {
age int
}
// 同一调用
func whatJob(p Person) {
p.job()
}
func growUp(p Person) {
p.growUp()
}
// 一个万能接口
type Person interface { //intetface 万能变量
job()
growUp()
}
// Student 函数方法
func (p Student) job() {
fmt.Println("I am a student.")
return
}
func (p *Student) growUp() {
p.age += 1
return
}
// Programmer 函数方法
func (p Programmer) job() {
fmt.Println("I am a programmer.")
return
}
func (p *Programmer) growUp() {
p.age += 10
return
}
func main() {
qcrao := Student{age: 18}
whatJob(&qcrao)
growUp(&qcrao)
fmt.Println(qcrao)
stefno := Programmer{age: 100}
whatJob(&stefno)
growUp(&stefno) //同一个函数,调用结果不同
fmt.Println(stefno)
}
结构体标签和reflect 反射
import (
"fmt"
"reflect"
)
type resume struct {
Name string `json:"电影" doc:"喜剧之王"`
}
func findDoc(stru interface{}) map[string]string {
t := reflect.TypeOf(stru).Elem() //reflect 反射 逆推类型
doc := make(map[string]string)
for i := 0; i < t.NumField(); i++ {
doc[t.Field(i).Tag.Get("json")] = t.Field(i).Tag.Get("doc")
}
return doc
}
func main() {
var stru resume
doc := findDoc(&stru)
fmt.Printf("电影字段=%s\n", doc["电影"])
}
Golang最强的协程
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("我是协程")
}()
fmt.Println("我是主协程")
time.Sleep(1 * time.Second) // 等待一秒钟,让协程有足够的时间执行
}
身为一个cpp学者,当看见Golang写协程那么方便,异常兴奋
那么协程用什么与主协程通信呢?
channel
管道(channel)接收和发送数据都是阻塞的
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int) //创建管道(channel),而且接收和发送数据都是阻塞的
go func() {
fmt.Println("我是协程")
c <- 666 //向管道内写入666
}()
a := <-c //管道类读出
fmt.Println("我是主协程")
fmt.Println("a=", a)
time.Sleep(1 * time.Second) // 等待一秒钟,让协程有足够的时间执行
}
go可能造成的内存泄露
func main() {
ch := func() <-chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
ch <- i
}
} ()
return ch
}()
for v := range ch {
fmt.Println(v)
if v == 5 {
break
}
}
}
使用 valgrind 运行 程序,发现 协程内存并没有回收
问题:
上面的程序中后台Goroutine向管道输入自然数序列,main函数中输出序列。
但是当break跳出for循环的时候,后台Goroutine就处于无法被回收的状态了。
因为 Goroutine还在向管道中写数据,主协程已经退出
改正:
使用context
package main
import (
"context"
"fmt"
)
func main() {
//WithCancel(ctx Context, cancel CancelFunc)=(名 Context,处理函数 CancelFunc)
ctx, cancel := context.WithCancel(context.Background()) //context.Background() 处理 Goroutine
ch := func(ctx context.Context) <-chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
select {
case <-ctx.Done():
return
case ch <- i:
}
}
}()
return ch
}(ctx)
for v := range ch {
fmt.Println(v)
if v == 5 {
cancel()
break
}
}
}
当main函数在break跳出循环时,通过调用 context 来通知后台Goroutine退出,
这样就避免了Goroutine的泄漏。