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 |