tips: 总结知识点一定要自己动手,这样知识点才能牢记!
比如开源社区有雨痕大佬的 Go 语言笔记,但是光看几个用法,而不进行代码的编写,代码能力提升不明显,能看懂,但是做不到,形成一种眼高手低的习惯。更何况,以后的工作本来就是写需求的代码,所以,各种基础知识点,快速过完!
1、保留字与预定义字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 break continue func interface select case defer go map struct chan else goto package fallthrough const switch if range type default for import return var true false iota nil int int16 int32 int64 uint uint16 uint32 uint64 float32 float64 complex64 complex128 bool byte rune string error make len cap new append copy close delete complex real imag panic recover
2、Go 中的一些常用函数 1 2 3 4 5 6 7 8 9 10 11 12 append -- 用来追加元素到数组、slice中,返回修改后的数组、sliceclose -- 主要用来关闭 channel delete -- 从map 中删除 key 对应的 valuepanic -- 停止常规的 goroutine (panic 和recover :用来做错误处理) recover -- 允许程序定义goroutine的panic 动作real -- 返回complex 的实部 (complex 、real imag 用于创建和操作复数) imag -- 返回complex 的虚部 make -- 用来分配内存,返回 Type 本身(只能应用于 slice, map , channel ) new -- 用来分配内存,主要用来分配值类型,如 int 、struct 返回指向 Type 的指针 cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map ) copy -- 用于复制和连接 slice,返回复制的数目 len -- 来求长度,比如 string 、array、slice、map 、channel,返回长度
3、Go 中的 Array 数组
答:Go 语言中的数组与其他语言的数组不一样 1、Go 语言中的数组是值类型,赋值和传参会复制整个数组,而不是指针
2、Go 语言中的数组长度必须是常量,而且是类型的组成成分,[2]int,[3]int 是不同类型
3、Go 语言中的数组能支持 “==” “!=” 操作,因为内存被初初始化过。
4、指针数组 [n]T //表示由 n 个 T 指针构成
5、数组指针 *[n]T //表示由一个指针指向这个数组,可以视为一维数组来看
6、array 数组不建议拷贝,如需要拷贝使用 slice 更好,或者数组指针
4、Go 中的 slice 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct Slice { byte * array uintgo len uintgo cap } package mainimport "fmt" func main () { s1 := []int {1 , 2 , 3 } s2 := []int {4 , 5 } s1 = append (s1, s2) fmt.Println(s1) } s1 = append (s1, s2...)
5、Go 中的面向对象 面向对象的三大特征:多态、继承、封装 Go 语言只支持封装行为,没有继承与多态,也没有 class 关键字但是能做到 struct 中嵌入 struct类型
6、Go 方法 方法:方法是绑定对象实例的,隐式将实例作为第一形参
1、只能为当前包内命名类型定义方法
2、参数 receiver 随意命名,如果未使用此名称,可省略
3、参数 receiver 类型可以为 T、*T,但是基类T不能是指针与接口
4、不支持方法重载,receiver 为参数签名的组成成分
5、可用实例 value 或 pointer 调用全部方法,编译器自动转换
6、通常使用简单工厂模式返回对象实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type Queue struct { elements []interface {} }func NewQueue () { return &Queue{ make ([]interface {}, 10 ) } }func (*Queue) push(e interface {}) error { panic ("not implemented" ) }func (this *Queue) length() int { return len (this.elements) }
6、Go 接口 接口是方法的集合,由于不支持重载,所以每个方法命名唯一
1、接口命名以 er 结尾,结构体
2、接口不实现方法
3、接口无数据字段
4、接口内可以嵌入其他接口
5、类型可实现多接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type Stringer interface { String() string }type Pointer interface { Stringer Print() }type User struct { id int name string }func (this *User) String() string { return fmt.Sprintf("User %d, %s" , this.id, this.name) }func (this *User) Print() { return fmt.Println(this.String()) }
7、Goroutine Goroutine 为 go 协程,仅仅需要在函数调用语句前添加 go 关键字即可调用goroutine,并且创建并发执行单元。goroutine 的设计具有轻量级!
1 2 3 4 5 6 go func () { fmt.Println("Hello World" ) }() runtime.Goexit() 此处学一下使用即可,具体 CSP 的模式与 GMP 的模型下一篇博客继续学习
8、Channel 引用类型 channel 为 CSP 的模式的具体实现,使用 channel 用于多个 goroutine 的通信,确保并发安全,尽量使用 channel 来代替锁进行同步。 channel 默认为同步的模式,需要发送和接受配对,否则一直处于被阻塞的状态,直到两边都处于活跃状态然后才被唤醒。 以下为同步模式
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 package mainimport "fmt" func main () { data := make (chan int ) exit := make (chan bool ) go func () { for d := range data { fmt.Println(d) } fmt.Println("recv over." ) exit <- true }() data <- 1 data <- 2 data <- 3 close (data) fmt.Println("send over." ) <-exit }
异步方式:通过判断缓冲区来决定是否阻塞。 如果缓冲区满,发送阻塞;缓冲区为空,接受阻塞。 通常情况下,异步的 channel 可以减少排队阻塞,具有更高的效率。 但是应该考虑使用指针规避大对象的拷贝,将大对象分治为小对象,如多个元素打包、减小缓冲区大小等。
以下为异步模式
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 package mainimport "fmt" func main () { data := make (chan int , 3 ) exit := make (chan bool ) data <- 1 data <- 2 data <- 3 go func () { for d := range data { fmt.Println(d) } exit <- true }() data <- 4 data <- 5 close (data) <-exit } 其中缓冲区是内部属性,非类型构成要素,内置函数 len 返回未被读取的缓冲元素数量, cap 返回缓冲区⼤⼩。 channel 是第一类对象,可传参 (内部实现为指针) 或者作为结构成员。
Go 语言还内建了 close()
函数来关闭一个 channel 但是存在以下几种情况:
1、读写 nil Channel 会永远阻塞,关闭 nil Channel 会导致 panic
2、关闭一个已关闭的 Channel 会导致 panic
3、向已经关闭的 Channel 发送数据会导致 panic
4、向已经关闭的 Channel 读取数据不会导致 panic,但读取的值为 Channel 传递的数据类型的零值,可以通过接受语句第二个返回值来检查 Channel 是否关闭且排空:
1 2 3 4 5 v, ok := <- chif !ok { ... }
9、Go 中的 select 用法 select 是Go中的一个控制结构,类似于用于通信的 switch 语句,其用于处理异步的 I/O 操作。。其有以下的几个特征
1 2 3 4 5 6 7 8 9 1 、每个case 都必须是一个通信,所有 channel 表达式都会被求值。 3 、所有被发送的表达式都会被求值 4 、如果任意某个通信可以进行,它就执行;其他被忽略。 5 、如果有多个case 都可以运行,select 会随机 (看源码) 公平地选出一个执行。其他不会执行。 6 、如果有default 子句,则执行该语句。如果没有default 字句,select 将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
select 会监听每个 case 中 channel 的读写操作,但是每次只能运行一个channel 进行读或写
10、初始化函数 初始化函数 init():go语言中init
函数用于包(package)
的初始化,该函数是go语言的一个重要特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 1 init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等 2 每个包可以拥有多个init函数 3 包的每个源文件也可以拥有多个init函数 4 同一个包中多个init函数的执行顺序go 语言没有明确的定义(说明) 5 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序 6 init函数不能被其他函数调用,而是在main函数执行之前,自动被调用 main 对比 init 函数,main函数只能用于main包中,且只能定义一个。
11、go mod 包管理 go mod 有以下几种命令
1 2 3 4 5 6 初始化模块 go mod init <项目模块名称> 依赖关系处理 ,根据go .mod文件 go mod tidy 将依赖包复制到项目下的 vendor目录。 go mod vendor 如果包被屏蔽(墙),可以使用这个命令,随后使用go build -mod=vendor编译 显示依赖关系 go list -m all 显示详细依赖关系 go list -m -json all 下载依赖 go mod download [path@version] [path@version]是非必写的
12、go debug
go run -race 可以查看有无相应的锁竞争一块
go tool complie -S [filename] 查看相应的汇编代码
Linux 下使用 top/htop 指令查看 CPU、Memory、VIRT 等信息,其中关于内存泄漏的相应的问题,主要查看的是 VIRT 是否出现不断增长的一种情况。
13、go 内存逃逸 内存逃逸的主要概念是:编译时 go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆 。
一般内存逃逸都是编译器进行分析的,go 语言的设计者不希望我们过多的研究这个方面的内容,所以一般不用管!
————END 如果本博客存在误解的问题,恳请大家指出!—————-