Go语言从入门到实战(二)
一、变量与常量
1.1 变量
我们先来看一下Go语言当中的标准变量声明方法:
1 | var name string = "tom" |
其中,var是声明变量的关键字,而name就是我们的变量名。需要特别注意的是,作为类型的string被放在了变量名的后面。这样设计主要是为了引导程序员使用类型自动推导,从而省略变量类型:
1 | // 使用类型推导 |
拓展:这种类型后置的设计在现代编程语言当中已经非常常见了,如
Scala、Kotlin、Swift、Rust等
而当我们需要一次性声明多个变量的时候,则可以使用代码块来做:
1 | var ( |
当我们的变量是局部变量的时候,我们还可以使用简短声明的方法来声明变量:
1 | func main() { |
在这里,我们有几个注意点需要强调:
- 简短声明仅可使用于局部,全局变量必须使用
var关键字声明 - 局部变量一经声明必须被使用,否则将会编译错误;如果需要丢弃该变量,则使用下划线
_命名变量 - 全局变量是
包级别的变量,而不是以单个文件作为作用域;全局变量可以声明但不使用 - 局部变量如果是不同类型(包括int32与int64也不是相同类型),无论如何都不能直接运算,而需要显式类型转换
1.2 常量
Go语言的常量使用const关键字进行声明:
1 | const name string = "tom" |
同样,变量类型是可以省略的:
1 | const name = "tom" |
当我们需要一次性声明多个常量的时候,同样可以使用代码块来做:
1 | const ( |
关于常量,我们也有几点需要强调的:
- 常量声明必须使用关键字的方式,不能使用简短声明
- 常量可以声明后不使用
- 常量如果是以省略变量类型的方式声明的,
整型和浮点型变量之间可以直接运算
1.3 基于常量的枚举类型
Go并不提供专门的枚举类型关键字,如其他语言当中常见的enum。而是通过常量的关键字const + iota的方式,实现枚举
我们先来看一段示例:
1 | func main() { |
输出
1 | a = 9, b = 1, c = 2, d = 10, e = 4, f = abc, g = abc, h = 7, i = 0 |
通过上述例子,可以总结为:
iota不管是否被使用,在同一个const代码块内,都会从0开始不断自增;如果遇到新的const,则重制为0- 如果
枚举项没有声明表达式,则继承上一个枚举项的表达式
二、Go语言的数据类型
2.1 布尔与字符串类型
| 类型 | 描述 |
|---|---|
| bool | 布尔类型(true、flase) |
| string | 字符串 |
2.2 无符号整数类型
| 类型 | 描述 |
|---|---|
| uint | 长度由系统架构类型决定 |
| uint8 | 长度为8位,即1字节(0~255) |
| uint16 | 长度为16位,即2字节(0 到 65535) |
| uint32 | 长度为32位,即4字节(0 到 4294967295) |
| uint64 | 长度为64位,即8字节(0 到 18446744073709551615) |
| uintptr | 长度为8位,即1字节(0~255) |
2.3 有符号整数类型
| 类型 | 描述 |
|---|---|
| int | 长度由系统架构类型决定 |
| int8 | 长度为8位,即1字节(-128 到 127) |
| int16 | 长度为16位,即2字节(-32768 到 32767) |
| int32 | 长度为32位,即4字节(-2147483648 到 2147483647) |
| int64 | 长度为64位,即8字节(-9223372036854775808 到 9223372036854775807) |
注意:虽然int类型和uint类型长度没有指定,但是不代表它们可以像Python这种语言一样不限制int的大小。例如在64位操作系统上,int和uint的最大值就是int64和uint64的最大值
我们也可以在代码中轻松获得每个数值类型的最大最小值,例如:
1 | func main() { |
那如果我们需要操作的数字的大小,int64已经无法满足了,要怎么办呢?
我们可以使用math/big包下的函数进行处理:
1 | func main() { |
关于这一点,此处就不再多说了,毕竟这不是目前的重点。如果需要更多内容,可以直接查找相关API文档
2.4 浮点数类型
| 类型 | 描述 |
|---|---|
| float32 | 长度为4个字节,IEEE-754 32位浮点型数 |
| float64 | 长度为8个字节,IEEE-754 64位浮点型数 |
不同于整型当中的int类型,浮点数类型要么是float32要么就是float64,不存在float这个类型
在使用简短声明的时候,编译器会根据当前计算机的架构自动进行推断,从而决定使用float32还是float64
2.4.1 浮点数与字符串类型的转换
浮点数转换为字符串:
1 | func main() { |
字符串转换为浮点数:
1 | func main() { |
2.5 复数类型
| 类型 | 描述 |
|---|---|
| complex64 | 32 位复数(实部与虚部分别32位) |
| complex128 | 64 位实数和虚数(实部与虚部分别64位) |
2.6 别名类型
2.6.1 byte和rune
| 类型 | 描述 |
|---|---|
| byte | 字节类型,uint8类型的别名 |
| rune | 字符类型,int32类型的别名 |
因为Go语言的rune和其他语言的char还是有较大差异,所以我们在这里针对rune类型,多做一些介绍
我们先看一下Go语言源码中,对于rune类型的注释:
1 | // rune is an alias for int32 and is equivalent to int32 in all ways. It is |
因此,rune其实并不是一个独立的类型,它只是int32类型的另一个称呼罢了。所以,rune类型可以和int32类型直接做计算,而不需要任何转换。
如果我们直接将rune类型的值打印出来,那么它就是一个数字:
1 | func main() { |
对于rune类型的类型转换,下面给出两个示例:
例1:
将rune类型转换为字符串:
1 | func main() { |
例2:
将rune类型所代表的数字,转换为字符串:
这个问题其实可以等价为,将整型转换为字符串。在其他语言当中,如Python,我们是直接使用str()函数进行转换的,但是因为Go语言的rune类型的存在,如果直接使用string()函数,就会得到该数字对应的字符,而不是数字本身的字符串格式。这显然不是我们想要的。
正确的做法应为:
1 | func main() { |
2.6.2 类型定义与类型别名
这两个概念我们需要重点区分:类型定义和类型别名
什么是类型定义?举个例子:
1 | type myInt int |
在上面的例子当中,myInt这个类型就是我们新定义的。那为什么说是新定义的呢?因为,它和int类型其实并不是一个类型。他只是与int类型有共同的实现、共同的能力罢了。
我们知道Go是一个强类型语言,且不存在类型隐式转换。只要不是同一个类型,就不能直接运算,如下面的代码是不能编译成功的:
1 | type myInt int |
但是如果我们直接与字面量运算,还是没有问题的:
1 | type myInt int |
我们可以通过使用类型定义这个特性来拓展原有类型的能力:
1 | type myInt int |
而类型别名像我们上述提到type和rune时介绍的一样,并不是一个新的类型,只是叫法不同。这个特性是在Go1.9版本时引入的。
不同于类型定义,我们无法对别名类型的能力进行拓展,因为本来就是同一种类型
三、条件与循环语句
3.1 if条件语句
示例:
1 | func main() { |
注意:Go语言的if语句,其条件不需要放在括号中
3.2 switch语句
3.2.1 基础switch
示例:
1 | func main() { |
输出:
1 | 10以内的奇数 |
这里应该能看出Go和其他主流编程语言的诸多不同,我们这里一一列举:
Go语言的switch语句可以用来匹配任何类型,包括结构体- 每个
case都可以匹配多个值 - 每个
case都默认自带break,如果需要实现switch穿透,则在需要穿透的case结尾加上fallthrough
除此之外,switch还可以作为if-else if结构的简写版本:
1 | func main() { |
3.2.2 type switch
type switch主要用于对变量类型进行判断的场景,使用方法如下:
1 | func main() { |
这里需要说明的是,type switch只能对interface{}类型使用,interface{}类型是一个接口,Go当中所有类型都实现了该接口,所以type switch其实就是判断一个interface{}的实现到底是什么类型
提到了type switch,就不能不提type assertion。type assertion是一个类型断言,他能帮助我们先校验类型,后执行操作。
如下面这个s就可以顺利作为参数传递给println()
1 | func main() { |
3.3 循环语句
Go只有for循,但是存在多种不同的使用方式。比如其他语言当中的while循环,在Go语言中也是通过for来实现
3.3.1 经典for循环
示例:
1 | func main() { |
其中注意点和if是一样的,即不需要使用括号来包围条件
3.3.2 for循环变体
Go中既然没有while循环,那么怎么实现while循环的效果呢?其实只要把for当作while就可以了:
1 | func main() { |
for还可以轻松实现无限循环,相比其他语言的while(true)更加简洁:
1 | func main() { |
3.3.3 for…range
当我们需要遍历一个集合或者字符串的时候,使用经典for循环显然是太啰嗦了,我们可以使用for...range语句来简化语法,但是要注意,在某些场景下,for...range不仅仅是帮助我们简化了语法,还多做了一些人性化的事情,后面会详细讲到的。
经典for循环遍历数组:
1 |