Go 语言的程序元素
Go 语言包含了五种程序符号,分别是标识符(identifier)、关键字 (keyword)、运算符(operator)、分隔符(delimiter) 与字面量(literal)。这五种程序元素组合成各种表达式和语句。
- 标识符
标识符是用于标识变量、函数或任何用户自定义项目的名称(程序实体)
标识符可分为两类,一类是 用户定义的标识符 ,作为程序实体存在;另一类是 预定义标识符 ,它们在 Go 语言中的源代码被声明(如数据类型)。
还有一个特殊的标识符,叫 空标识符 ,记作 “_”,一般用在变量声明与包的导入中。例如,在 Go 语言程序中,若存在一个没有使用的变量 a,那么编译时就会报错,因为 Go 语言要求存在的程序实体必须被使用,这时就可以使用这样一句 _=a 的代码,编译器就会认为已经使用了变量 a(实际上并没有使用),也就不会报错。
在大型的 Go 语言开发项目中,空标识符的运用很普遍,提高了代码的灵活性和可读性。
-
关键字
Go 语言作为一门静态强类型的编译语言,它的关键字一共只有 25 个(均为小写)分别是:
- 包导入与声明:import、package
- 程序实体声明与定义:var、type、func、interface、map、struct、chan、const
- 流程控制:if、continute、for、return、go、case、goto、switch、select、else、break、default、defer、fallthrough、range
- 字面量
字面量是表示值的一种标记法(其实我觉得这个概念生涩难懂)举个例子很容易就理解是什么了。
var str string = "dog" //str 为变量,dog 为字面量
const b := 10 // b 为变量,10 为字面量
-
分隔符
//fmt.Println(“b”)使用括号和标点符分隔而成,包含了 6 个标记
fmt
.
Println
(
“b”
)
程序代码都是通过语句来实现结构化的,但 **Go 语言的语句结束不需要开发者手动写上分号,编译器会在每一行语句的末尾加上分号,这一过程是自动完成的 **。- 运算符
运算符是用于执行运算或逻辑操作的符号,**Go 语言不支持运算符重载 **。在 Go 语言中,一元运算符的优先级高于二元运算符;如果一个表达式中出现相同优先级的运算符,就根据 ** 从左到右的顺序依次操作 **。需要特别注意的是,在 Go 语言中,**++ 和 - - 是语句,不是表达式,没有运算符优先级一说 **。- 注释
Go 语言的注释风格和 C 语言一样,分为单行注释和多行注释
/*
- 测试:使用 http 包实现服务
- 启动:bee run
- 访问:curl “http://127.0.0.1:8888/v1/demo”
*/
func main() {
// 初始化一个 http.Server
server := &http.Server{}
// 初始化 handler 并赋值给 server.Handler
server.Handler = DemoHandle{}
// 绑定地址
server.Addr = ":8888"
// 启动一个 http 服务
server.ListenAndServe()
}
## 常量
在 Go 语言中,常量是编译期间已知,且在执行过程中不会改变的固定值;在 Go 语言程序中,常量使用 const 关键字定义
// 指定常量
const H = 1.287456875623903
// 指定常量的类型,float64 类型默认保留小数点后 15 位
const H float64 = 1.28745687562390300000
//bool 值
const Return := true
// 甚至可以使用中文
const 完满主任 = 100
- 数字表示
Go 语言的常量可以是十进制、八进制或十六进制常数
1. 前缀 0x 或 0X 的是十六进制
2. 前缀 0 的是八进制
3. 十进制没有前缀
4. 整数可以有一个后缀,是字母 U 和 L 的组合,分别用于表示 unsigned 和 long;后缀字母大小写和顺序都是任意的,但不能重复。
0123 // 八进制,合法
0x3a // 十六进制,合法
0xFeeL // 合法
30l //long,合法
40u // 无符号整型,合法
078 // 后缀 8 不是八进制的数,非法
032UU // 后缀不能重复,非法
## 转义字符
与其他程序语言一样,Go 语言使用反斜杠 "\" 表示转义符号,例如换行符 "\n" 或制表符 "\t"
func main() {
fmt.Println("Hello\nGo") // 换行
fmt.Println("Hello\tGo") // 水平
}
以上程序实体的输出结果为:
$ Hello
$ Go
$ Hello Go
## 赋值
func main() {
a := 20
c := 200
c = a
// 以下 c 的值依次被使用
fmt.Println("赋值操作,把 a 的值赋给 c,c 的值为:", c) //20
c += a
fmt.Println("相加和赋值运算符,c 的值为:", c) //40
c -= a
fmt.Println("相减和赋值运算符,c 的值为:", c) //20
c /= a
fmt.Println("相除和赋值运算符,c 的值为:", c) //1
c *= a
fmt.Println("相乘和赋值运算符,c 的值为:", c) //20
c <<= 2
fmt.Println("左移和赋值运算符,c 的值为:", c) //80,正数左移 1 位相当于乘 2(左边的二进制丢弃,右边补 0)c >>= 2
fmt.Println("右移和赋值运算符,c 的值为", c) //40,正数右移 1 位相当于除 2 (正数左补 0,负数左补 1)
c &= 2
fmt.Println("按位与赋值运算符,c 的值为:", c) //0
c ^= 2
fmt.Println("按位异或运算符,c 的值为:", c) //2
c |= 2
fmt.Println("按位或和赋值运算符,c 的值为:", c) //2
}
Go 语言也支持并行赋值的写法:
func main() {
// 使用并行赋值的方法
const (
a, b, c int = 1, 2, 3
d, e, f int = 4, 5, 6
)
// 打印常量 a,b,c,d,e,f 的值
fmt.Println("分别是:", a, b, c, d, e, f)
}
## 枚举
在 C /C++,Java 等高级语言中,常常会用到枚举类型表示状态;在 Go 语言中却没有枚举类型,iota 是 Go 语言的常量计数器,只能在常量的表达式中使用,因此可以借助 iota 实现枚举。
const (
a = iota // 使用 iota 关键字实现枚举,每次 const 出现时,都会让 iota 的值初始化为 0
b //b = 1
c //c = 2
d, e, f = iota, iota, iota //d, e, f = 3, 3, 3
g = iota // g = 4
h = "h" // h = "h"
i // i = 6
j = iota // j = 7
)
const z = iota // iota 初始化为 0,因此 z = 0
func main() {
// 分别打印:0 1 2 3 3 3 4 h h 7 0
fmt.Println(a, b, c, d, e, f, g, h, i, j, z)
}
## 变量
- 声明变量的标准形式
// 定义一个变量 a 为 int 型
var a int
// 定义一个变量 b 为 bool 型
var b bool
// 并行定义类型
var (
a, b int
c bool
str string
)
- 局部变量声明
声明变量有两种写法,一种是像上面使用 =,另一种是使用赋值运算符号 := 代替。
a := 1 // 等价于 a = 1,此处可以省略声明类型,因为编译器会自动推导
Go 语言官方推荐使用这种方式声明变量,但有一点必须要注意,** 这种声明方式只能被用于函数体内,不可以用于全局变量的声明与赋值。**
- 匿名变量
全局变量自然是全局可用,局部变量只能在相应代码块内使用,除此之外,局部变量还有一个特点就是,定义了后必须要使用(全局变量允许声明但可以不使用)否则编译器会报错。但在使用强类型编程语言时,经常会在调用函数时,返回多个值,有时候我们只需要其中的一个值,不需要其他的值,就可以使用匿名变量来解决这个问题。匿名变量可以通过使用空标识符 "_" 来实现。
func GetClass() (ageNum int, stuTeacher string, isGirl bool) {// 获取班级信息
return 78, "完满主任", false // 多个返回值
}
func main() {
_, stuTeacher, _ := GetClass() // 选取完满主任的名字,其他信息使用匿名变量屏蔽
fmt.Println(stuTeacher) // 打印
}
- init 函数初始化
在 Go 语言中,除了可以在全局声明中初始化实体,还可以借助 init 函数初始化,这个函数比较特殊,** 它会在包完成初始化后自动执行,执行优先级比 main 函数更高,并且不能手动调用 init 函数。** 每一个源代码文件有且只有一个 init 函数,初始化过程会根据包的依赖关系按 ** 顺序单线程执行 **。