文章目录
Go语言
1. Go语言简介
1.1 来历和发展目标
-
以C为原型。
-
静态语言的安全性和高效性与动态语言的易开发性进行有机结合。(C+Python)
-
用更少的代码,更短的编译时间,创建运行更快的程序,享受更多的乐趣(吉祥物是一个囊地鼠(Gopher)表示发展迅速)
1.2 特点
-
静态编译
-
强类型语言
-
函数具有多返回值
-
自带GC
-
简单的思想,没有继承、多态、类等
-
丰富的库和详细的文档
-
语法层支持并发,和拥有同步并发的channel类型
…
1.3 语言的用途
-
应用于服务器的系统编程语言
-
Go 语言同时也是一门可以用于实现一般目标的语言,例如对于文本的处理,前端展现等
…
2. 安装
2.1 Go环境变量
-
Go开发环境依赖于一些操作系统环境变量
- ** G O R O O T ∗ ∗ 表 示 G o 在 你 的 电 脑 上 的 安 装 位 置 , 它 的 值 一 般 都 是 ‘ GOROOT** 表示 Go 在你的电脑上的安装位置,它的值一般都是 ` GOROOT∗∗表示Go在你的电脑上的安装位置,它的值一般都是‘HOME/go`,也可以安装在别的地方。
- $GOARCH 表示目标机器的处理器架构
- $GOOS 表示目标机器的操作系统
- ** G O B I N ∗ ∗ 表 示 编 译 器 和 链 接 器 的 安 装 位 置 , 默 认 是 ‘ GOBIN** 表示编译器和链接器的安装位置,默认是 ` GOBIN∗∗表示编译器和链接器的安装位置,默认是‘GOROOT/bin`,
2.2 在Windows安装
- 下载Go的安装包 下载地址
- 安装后,配置环境变量
$GOPATH
即可使用
2.3 Goland配置
2.3.1 项目文件
-
- **bin存放编译后的可执行文件,** - **pkg存放编译后的包文件** - **scr 存放项目源文件(包和包中的源文件)**
- 一般,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
- 对于src目录,存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。
3. Go程序的一般结构
package main // 声明本文件的包名
import "fmt" // 导入包
//导入fmt库(用于输出)
func main() { // {必须放在函数头一行
fmt.Println("Hello,World!")
}
- 程序的代码通过语句来实现结构化。每个语句不需要像 C 家族中的其它语言一样以分号
;
结尾,因为这些工作都将由 Go 编译器自动完成。
4. 文件名、关键字与标识符
4.1 文件名
-
.go
-
文件名一般小写
-
用
_
对多个部分进行分隔scanner_test.go
4.2 标识符
-
Go区分大小写
-
无效标识符
- 1ab (以数字开头)
- case (Go语言的关键字)
- a+b (运算符是不允许的)
-
_
特殊标识符- 空白标识符
- 任何类型都可以赋值给它
- 任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用
4.3 关键字
- 25个关键字和保留字
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
5. 数据类型
5.1 基本类型
-
数字类型
- 整型:
int8, int16, int, int32, int64
; - 无符号整型:
uint8, uint16, uint, uint32, uint64
- 浮点型:
float32, float64
- 整型:
-
布尔类型:
bool
-
字符串类型:
string
-
数组类型:
[]int
-
切片类型:
make([]int, 10)
-
map类型:
make(map[string] string)
-
支持复数
-
Go输出可以使用格式化输出,利用
%d,%s,%p,%v
等
6. 常量与变量
6.1 常量
- 常量使用关键字
const
定义,存储不会改变的数据。 - 存储在常量中的数据类型只可以是布尔型,数字型(整数型,浮点型,复数)和字符串型
- 常量的定义格式:
const identifier [type] = value
例如:const Pi = 3.14
(类型可以省略)
6.2 变量
-
一般形式:
var identifier type
-
分解写法
var ( a int b bool str string ) // // 函数内部的写法 := func f1(){ := a := 1 b := "eswinESWIN" }
- 声明后的默认值:
int
为0,float
为0.1,bool
为false,string
为空字符串,指针为nil
.
- 声明后的默认值:
-
变量命名遵循驼峰规则
7. 控制结构
Go语言具有以下控制结构:
-
if-else 结构
-
switch 结构
-
for (range) 结构
-
select 结构 (用于channel的选择)
-
if
语句用法和C完全一样if x>0{ return y }else{ return x }
- if 可以包含一个初始化语句(如:给一个变量赋值)。这种写法具有固定的格式(在初始化语句后方必须加上分号)
if x:=6;x>0{ return y }else{ return x }
-
switch
结构switch varA { case val1: ... case val2: ... default: ... }
-
varA的类型必须 和 val1 、val2相同,例如 varA 是数字, 那么 val1,val2不能是bool值,或者结果为bool的表达式 varA默认true
-
可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:
case val1, val2, val3
。 -
不需要
break
来结束 -
程序不会自动地执行下一个分支的代码,若想继续执行后续分支的代码,可以使用
fallthrough
关键字。
-
-
for
结构- Go中重复执行只有for 结构
- 基于计算器的迭代
for 初始化语句; 条件语句; 修饰语句 {}
2. 基于条件判断的迭代:for 条件语句{}
。
-
for range
结构for ix, val := range coll {}
-
break
和continue
(和C一样)- break语句的作用结果是跳过整个代码块,执行后续的代码。
- 关键字continue忽略剩余的循环体而进入下一次循环的过程。
8. 函数
8.1 函数介绍
-
Go是编译型语言,对函数的编写顺序没有要求,一般
main
函数在前 -
函数一般形式
func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
-
函数可以利用
return
0个或者多个返回值,并且可以返回函数。- 这种多返回值一般用于判断某个函数是否执行成功(true/false)或与其它返回值一同返回错误消息。
-
函数调用
pack1.Function(arg1,arg2,...,argn)
Function
是pack1
包里面的一个函数,括号里面是实参,实参将会被复制给形参。
-
Go默认使用按值传递来传递参数,也就是传递参数的副本。
- 若想修改实参的值,需要传递实参的地址。(
&
+ 变量名)(引用传递)
- 若想修改实参的值,需要传递实参的地址。(
9. defer
- 关键字defer 允许我们推迟到函数返回之前(或任意位置执行
return
语句之后),才执行某个语句或函数
func a() {
i := 0
defer fmt.Println(i) // 先得到i=0的值,最后在return之后输出
i++
fmt.Println(i)
return
}
// 函数会输出 1
// 0
- 当有多个defer 行为被注册时,它们会以逆序执行(后进先出)
func a() {
i := 0
defer fmt.Println(i) a
i++
fmt.Println(i)
defer fmt.Println(i) b
return
}
// 函数会输出 1 fmt.Println(i)
// 1 defer fmt.Println(i) b
// 0 defer fmt.Println(i) a
func f() {
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}
//输出:4 3 2 1 0。
- 关键字defer的用法类似于面向对象编程语言Java的
finally
语句块,它一般用于释放某些已分配的资源。(关闭文件流,解锁资源,关闭数据库连接)
10. 匿名函数(闭包)
- 匿名函数 (没有函数名)
f1 := func(x, y int) int { return x + y }
将函数赋值给变量f1
- 通过变量来对函数进行调用
f1(3,4)
- 匿名函数可以被赋值给变量并作为值使用。
- 通过变量来对函数进行调用
func(x, y int) int { return x + y } (3, 4)
- 直接对匿名函数进行调用
11. 数组与切片
11.1 数组
-
长度固定、元素类型一致的序列
- 数组为值类型,在传参的时候会发生整个数组拷贝(并不是数组第一个元素的地址拷贝)。
- 长度为0的数组不占用内存空间
- 元素的数目,长度或数组的大小必须是固定的,并且在声明该数组时就给出。(编译时需要根据数组长度以便分配内存)
-
定义和初始化
func main() { var a [3]int //基本定义 // ... 指定数组长度为最大索引+1 var b = [...]int{1, 2, 3}//顺序初始化 var c = [...]int{0:1,1:3}//索引初始化(key-value) var d = [...]int{0:1,1:3,10:12,13,14}//混合初始化,既使用顺序初始化又使用索引初始化 ) }
-
数组的地址可以通过数组名获取:
&a
func main() { var a = [...]int{1, 2, 3} // a 是一个数组 var b = &a // b 是指向数组的指针 fmt.Println(a[0], a[1]) // 打印数组的前2个元素 fmt.Println(b[0], b[1]) // 通过数组指针访问数组元素的方式和数组类似 }
-
11.1.1 将数组传递给函数
-
直接传递数组
func sum(a [5]int) int { //形参是数组 s := 0 for i := 0; i < len(a); i++ { s += a[i] } return s } func main() { var arr = [5]int{0, 1, 2, 3, 4} sum(arr) // 传递数组 }
- 把一个大数组传递给函数会消耗很多内存,有两种方法可以避免这种现象
- 传递数组的指针
- 使用数组的切片
- 把一个大数组传递给函数会消耗很多内存,有两种方法可以避免这种现象
-
传引用
func sum(a *[5]int) int { s := 0 for i := 0; i < len(a); i++ { s += a[i] } return s } func main() { var arr = [5]int{0, 1, 2, 3, 4} sum(&arr)// 传递数组的地址 }
-
传切片
func sum(a []int) int { // 形参为切片 s := 0 for i := 0; i < len(a); i++ { s += a[i] } return s } func main() { // 1. 将数组转成切片进行传递 var arr = [5]int{0, 1, 2, 3, 4} sum(arr[:]) // 2. 直接定义切片进行传递 var arr = []int{0,1,2,3,4} sum(arr) }
11.2 切片
-
概念
- 切片(slice)是对数组一个连续片段的引用(该数组称为相关数组,通常是匿名的。)
- 这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集
- 切片是一个引用类型(类似于C中的数组类型)
- 切片是一个长度可变的数组
cap()
函数可以测量切片最长可以达到多少(切片的容量):- 等于切片的长度+ 数组除切片以外的长度(前面的不算,只能往后扩容)。
- 如果s是一个切片,
cap(s)
就是从s[0]
到数组末尾的数组长度,切片的长度永远不会超过它的容量。0 <= len(s) <= cap(s)
- 切片(slice)是对数组一个连续片段的引用(该数组称为相关数组,通常是匿名的。)
-
优点
- 切片是引用,因此不需要额外的内存
-
声明格式
var identifier []type
(不需要说明长度)。
-
初始化格式
-
var slice1 []type = arr1[start:end]
。-
改变start和end的值可以得到不同的切片
-
其他形式
slice1 = &arr1
-
-
-
切片在内存中的组织方式:
-
3 个域的结构体:指向相关数组的指针,切片长度以及切片容量。
-
下图给出了一个长度为2, 容量为4的切片y
y[0] = 3
且y[1] = 5
。- 切片
y[0:4]
由 元素 3,5,7 和 11 组成。
-
11.2.1 切片的复制和追加
-
复制:
copy()
函数,返回复制的个数(int
) -
追加:
append()
函数(常用作扩容)package main import "fmt" func main() { slFrom := []int{1, 2, 3} slTo := make([]int, 10)// 常用作生成切片 // make([]type,len,caps) // caps默认len,可以省略 n := copy(slTo, slFrom) fmt.Println(slTo) fmt.Printf("Copied %d elements\n", n) // n == 3 sl3 := []int{1, 2, 3} sl3 = append(sl3, 4, 5, 6) fmt.Println(sl3) }
-
append([]T, X...T) []T
其中append方法将0个或多个具有相同s的元素追加到切片后面并且返回新的切片; -
追加的元素必须和原切片的元素同类型
- 如果s的容量足够,增加后的切片和原切片指向同一地址(一个相关数组)。
- 如果s的容量不足以存储新增元素,append会分配新的切片保证存储(不同数组)。
package main
import "fmt"
func main() {
sl3 := make([]int, 10, 15)// 最大容量15
fmt.Printf("%p\n", sl3)
sl3 = append(sl3, 4, 5, 6)// 没超过15
fmt.Println(sl3)
fmt.Printf("%p\n", sl3)
}
// 输出
//0xc00001c100
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc00001c100
package main
import "fmt"
func main() {
sl3 := make([]int, 10, 12) // 最大容量12
fmt.Printf("%p\n", sl3)
sl3 = append(sl3, 4, 5, 6) // 超过12
fmt.Println(sl3)
fmt.Printf("%p\n", sl3) // 指向不同的地址
}
// 输出
//0xc00001e0c0
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc000112000
)。
package main
import "fmt"
func main() {
sl3 := make([]int, 10, 15)// 最大容量15
fmt.Printf("%p\n", sl3)
sl3 = append(sl3, 4, 5, 6)// 没超过15
fmt.Println(sl3)
fmt.Printf("%p\n", sl3)
}
// 输出
//0xc00001c100
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc00001c100
package main
import "fmt"
func main() {
sl3 := make([]int, 10, 12) // 最大容量12
fmt.Printf("%p\n", sl3)
sl3 = append(sl3, 4, 5, 6) // 超过12
fmt.Println(sl3)
fmt.Printf("%p\n", sl3) // 指向不同的地址
}
// 输出
//0xc00001e0c0
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc000112000