入门06_教程biancheng

Go语言基本语法:c.biancheng.net/golang/syntax/

简介

基本语法

Go语言的基本类型

有:
bool
string
int、int8、int16、int32、int64
uint、uint8、uint16、uint32、uint64、uintptr
byte // uint8 的别名
rune // int32 的别名 代表一个 Unicode 码
float32、float64
complex64、complex128

标准格式
批量格式
简短格式

简短模式(short variable declaration)有以下限制:
定义变量,同时显式初始化。
不能提供数据类型。
只能用在函数内部。

当一个变量被声明之后,系统自动赋予它该类型的零值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil 等。所有的内存在 Go 中都是经过初始化的。

Go语言变量的初始化

var hp int = 100
var hp = 100
var 的变量声明还有一种更为精简的写法,例如:
hp := 100
编译器会自动根据右值类型推断出左值的对应类型。
注意:由于使用了:=,而不是赋值的=,因此推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。

Go语言多个变量同时赋值

Go语言匿名变量(没有名字的变量)

Go语言变量的作用域

根据变量定义位置的不同,可以分为以下三个类型:
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
全局变量
全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。

Go语言整型(整数类型)

Go语言提供了两种精度的浮点数 float32 和 float64,它们的算术规范由 IEEE754 浮点数国际标准定义,该浮点数规范被所有现代的 CPU 支持。

这些浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 math 包中找到:
常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38;
常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308;
float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。

一个 float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度,通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。

Go语言复数

Go语言变量逃逸分析

变量逃逸(Escape Analysis)——自动决定变量分配方式,提高运行效率

Go语言变量的生命周期

变量的生命周期与变量的作用域有着不可分割的联系:
全局变量:它的生命周期和整个程序的运行周期是一致的;
局部变量:它的生命周期则是动态的,从创建这个变量的声明语句开始,到这个变量不再被引用为止;
形式参数和函数返回值:它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁。

Go语言常量和const关键字

常量的值必须是能够在编译时就能够确定的,可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。
iota 常量生成器
无类型常量
编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算,可以认为至少有 256bit 的运算精度。
通过延迟明确常量的具体类型,不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。

【示例 2】math.Pi 无类型的浮点数常量,可以直接用于任意需要浮点数或复数的地方:
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi
如果 math.Pi 被确定为特定类型,比如 float64,那么结果精度可能会不一样

Go语言type关键字(类型别名)

区分类型别名与类型定义
类型定义和类型别名的区别
类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
//类型定义
type NewInt int

//类型别名
type MyInt = int

func main() {
var a NewInt
var b MyInt

fmt.Printf("type of a:%T\n", a) //type of a:main.NewInt
fmt.Printf("type of b:%T\n", b) //type of b:int
}

结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是int。MyInt类型只会在代码中存在,编译完成时并不会有MyInt类型

非本地类型不能定义方法
编译器提示:不能在一个非本地的类型 time.Duration 上定义新方法,非本地类型指的就是 time.Duration 不是在 main 包中定义的,而是在 time 包中定义的,与 main 包不在同一个包中,因此不能为不在一个包中的类型定义方法。

解决这个问题有下面两种方法:
将第 8 行修改为 type MyDuration time.Duration,也就是将 MyDuration 从别名改为类型;
将 MyDuration 的别名定义放在 time 包中。
Go语言注释的定义及使用(godoc工具提取注释内容)

godoc 工具

godoc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档,也可以作为一个提供在线文档浏览的 web 服务器,Go语言官网(https://golang.google.cn/)就是通过这种形式实现的。

Go语言关键字与标识符简述

Go语言运算符的优先级

Go语言strconv包:字符串和数值类型的相互转换

string 与 int 类型之间的转换
Itoa():整型转字符串
Atoi():字符串转整型
Parse 系列函数
包括 ParseBool()、ParseFloat()、ParseInt()、ParseUint()。
Format 系列函数
将给定类型数据格式化为字符串类型的功能,其中包括 FormatBool()、FormatInt()、FormatUint()、FormatFloat()。
Append 系列函数
Append 系列函数用于将指定类型转换成字符串后追加到一个切片中,其中包含 AppendBool()、AppendFloat()、AppendInt()、AppendUint()。

容器

Go语言数组详解

1
2
3
4
5
var q [3]int = [3]int{1, 2, 3}  
q := [...]int{1, 2, 3}
数组的长度是数组类型的一个组成部分,因此 [3]int 和 [4]int 是两种不同的数组类型,数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定。
q := [3]int{1, 2, 3}
q = [4]int{1, 2, 3, 4} // 编译错误:无法将 [4]int 赋给 [3]int

比较两个数组是否相等
遍历数组——访问每一个数组元素

1
2
3
4
5
6
var array [4][2]int  
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化数组中指定的元素
array = [4][2]int{1: {0: 20}, 3: {1: 41}}

【示例 4】使用索引为多维数组赋值

1
2
// 将 array1 的索引为 1 的维度复制到一个同类型的新数组里  
var array3 [2]int = array1[1]

Go语言切片详解

切片使用中无法对切片内部的地址和大小进行手动调整,因此切片比指针更安全、强大。

1
2
3
4
// 声明字符串切片  
var strList []string
动态地创建一个切片,可以使用 make() 内建函数,格式如下:
make( []Type, size, cap )

size 指的是为这个类型分配多少个元素,cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。
使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。

Go语言append()为切片添加元素

1
2
3
4
5
6
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包  
切片长度 len 并不等于切片的容量 cap。
a = append([]int{0}, a...) // 在开头添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片
var a []int
a = append(a[:i], append([]int{x}, a[i:]...)...) // 在第i个位置插入x

Go语言copy():切片复制(切片拷贝)

Go语言从切片中删除元素

1
2
3
4
5
a = a[N:] // 删除开头N个元素  
从中间位置删除
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:len(a)-N] // 删除尾部N个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素

Go语言range关键字:循环迭代切片

Go语言多维切片简述

1
2
3
4
// 声明一个二维整型切片并赋值  
slice := [][]int{{10}, {100, 200}}
// 为第一个切片追加值为 20 的元素
slice[0] = append(slice[0], 20)

Go语言map(Go语言映射)

如只遍历值,可以使用下面的形式:
for _, v := range scene {
将不需要的键使用_改为匿名变量形式。

只遍历键时,使用下面的形式:
for k := range scene {
无须将值改为匿名变量形式,忽略值即可。
注意:遍历输出元素的顺序与填充顺序无关,不能期望 map 在遍历时返回某种期望顺序的结果。

Go语言map元素的删除和清空

delete(map, 键)
清空 map 的唯一办法就是重新 make 一个新的 map,不用担心垃圾回收的效率,Go语言中的并行垃圾回收效率比写一个清空函数要高效的多。
基于map的多键值索引,可以提高查询效率。尤其是在一些对查询时效要求较高的场景,map的多键值索引是一个很好的选择。

Go语言sync.Map(在并发环境中使用的map)

1
2
3
4
5
6
 var scene sync.Map  
scene.Store("egypt", 200)
scene.Load("london")
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{}) bool {

sync.Map 没有提供获取 map 数量的方法,替代方法是在获取 sync.Map 时遍历自行计算数量,sync.Map 为了保证并发安全有一些性能损失,因此在非并发情况下,使用 map 相比使用 sync.Map 会有更好的性能。

Go语言nil:空值/零值

nil 标识符是不能比较的
nil 不是关键字或保留字
nil 没有默认类型
不同类型 nil 的指针是一样的
不同类型的 nil 是不能比较的
两个相同类型的 nil 值也可能无法比较
nil 是 map、slice、pointer、channel、func、interface 的零值
不同类型的 nil 值占用的内存大小可能是不一样的
具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。

Go语言make和new关键字的区别及实现原理

Go语言中的 new 和 make 主要区别如下:
make 只能用来分配及初始化类型为 slice、map、chan 的数据。new 可以分配任意类型的数据;
new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type;
new 分配的空间被清零。make 分配空间后,会进行初始化;

流程控制

Go语言if else(分支结构)

if 还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,代码如下:

1
2
3
4
if err := Connect(); err != nil {  
fmt.Println(err)
return
}

Connect 是一个带有返回值的函数,err:=Connect() 是一个语句,执行 Connect 后,将错误保存到 err 变量中。

err != nil 才是 if 的判断表达式,当 err 不为空时,打印错误并返回。

这种写法可以将返回值与判断放在一行进行处理,而且返回值的作用范围被限制在 if、else 语句组合中。

Go语言for(循环结构)

提供了一个更高级的 break,可以选择中断哪一个循环
初始语句可以被忽略,但是初始语句之后的分号必须要写,代码如下:

1
2
3
4
step := 2  
for ; step > 0; step-- {
fmt.Println(step)
}

无限循环
上面的代码还可以改写为更美观的写法,代码如下:

1
2
3
4
5
6
7
var i int  
for {
if i > 10 {
break
}
i++
}

只有一个循环条件的循环
在上面代码的基础上进一步简化代码,将 if 判断整合到 for 中,变为下面的代码:

1
2
3
4
var i int  
for i <= 10 {
i++
}

上面这段代码其实类似于其他编程语言中的 while,在 while 后添加一个条件表达式,满足条件表达式时持续循环,否则结束循环。
for 中的结束语句——每次循环结束时执行的语句
在结束每次循环前执行的语句,如果循环被 break、goto、return、panic 等语句强制退出,结束语句不会被执行。

Go语言for range(键值循环)

1
2
3
for key, val := range coll {  
...
}

需要要注意的是,val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质

1) 一分支多值
2) 分支表达式
跨越 case 的 fallthrough——兼容C语言的 case 设计
在Go语言中 case 是一个独立的代码块,执行完毕后不会像C语言那样紧接着执行下一个 case,但是为了兼容一些移植代码,依然加入了 fallthrough 关键字来实现这一功能
新编写的代码,不建议使用 fallthrough。

Go语言goto语句——跳转到指定的标签

使用 goto 退出多层循环
使用 goto 集中处理错误

Go语言break(跳出循环)

Go语言continue(继续下一次循环)

Go语言字符串的链式处理——操作与数据分离的设计技巧

函数

Go语言函数类型实现接口——把函数作为接口来调用

Go语言闭包(Closure)——引用了外部变量的匿名函数

Go语言可变参数(变参函数)

func myfunc(args …int)
任意类型的可变参数
func Printf(format string, args …interface{})
用 interface{} 传递任意类型数据是Go语言的惯例用法,使用 interface{} 仍然是类型安全的,
遍历可变参数列表——获取每一个参数的值
获得可变参数类型——获得每一个参数的类型
当可变参数为 interface{} 类型时,可以传入任何类型的值,此时,如果需要获得变量的类型,可以通过 switch 获得变量的类型

Go语言defer(延迟执行语句)

多个延迟执行语句的处理顺序
当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出)
代码的延迟顺序与最终的执行顺序是反向的。
延迟调用是在 defer 所在函数结束时进行,函数结束可以是正常返回时,也可以是发生宕机时。
使用延迟执行语句在函数退出时释放资源

Go语言递归函数

Go语言处理运行时错误

Go语言宕机(panic)——程序终止运行

在宕机时触发延迟执行语句
宕机前,defer 语句会被优先执行,由于第 7 行的 defer 后执行,因此会在宕机前,这个 defer 会优先处理,随后才是第 6 行的 defer 对应的语句,这个特性可以用来在宕机发生前进行宕机信息处理。

Go语言计算函数执行时间

1
2
start := time.Now() // 获取当前时间  
elapsed := time.Since(start)

Go语言Test功能测试函数详解

测试函数的名称要以Test或Benchmark开头,后面可以跟任意字母组成的字符串,但第一个字母必须大写,例如 TestAbc(),一个测试用例文件中可以包含多个测试函数;
单元测试则以(t *testing.T)作为参数,性能测试以(t *testing.B)做为参数;
测试用例文件使用go test命令来执行,源码中不需要 main() 函数作为入口,所有以_test.go结尾的源码文件内以Test开头的函数都会自动执行。
单元(功能)测试
性能(压力)测试
覆盖率测试

结构体

Go语言实例化结构体——为结构体分配内存并初始化

基本的实例化形式:var ins T
创建指针类型的结构体:ins := new(T)
取结构体的地址实例化:ins := &T{}

Go语言初始化结构体的成员变量

使用“键值对”初始化结构体
使用多个值的列表初始化结构体
初始化匿名结构体

1
2
3
4
5
6
7
8
9
10
11
ins := struct {  
// 匿名结构体字段定义
字段1 字段类型1
字段2 字段类型2

}{
// 字段值初始化
初始化字段1: 字段1的值,
初始化字段2: 字段2的值,

}

Go语言构造函数

Go语言方法和接收器

http://c.biancheng.net/uploads/allimg/180815/1-1PQ5135I3337.jpg
接收器——方法作用的目标

1) 理解指针类型的接收器
2) 理解非指针类型的接收器
3) 指针和非指针接收器的使用
在计算机中,小对象由于值复制时的速度较快,所以适合使用非指针接收器,大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。

Go语言为任意类型添加方法

Go语言使用事件系统实现事件的响应和处理

Go语言类型内嵌和结构体内嵌

结构内嵌特性
Go语言的结构体内嵌有如下特性。

1) 内嵌的结构体可以直接访问其成员变量
2) 内嵌结构体的字段名是它的类型名

接口

Go语言空接口类型(interface{})

将值保存到空接口
从空接口获取值
空接口的值比较

1) 类型不同的空接口间的比较结果不相同
2) 不能比较空接口中的动态值
类型的可比较性
类 型 说 明
map 宕机错误,不可比较
切片([]T) 宕机错误,不可比较
通道(channel) 可比较,必须由同一个 make 生成,也就是同一个通道才会是 true,否则为 false
数组([容量]T) 可比较,编译期知道两个数组是否一致
结构体 可比较,可以逐个比较结构体的值
函数 可比较

包(package)

并发

反射

文件处理

编译与工具

go入门系列
入门02_IDE安装
入门03_工具链
入门04_入门demo和基本类型
入门05_go升级版本
入门06_教程biancheng
入门08_教程编程时光
入门09_Go语言高级编程
入门10_包导入
入门11_方法接口和嵌入类型
入门12_数组和切片

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×