1 GO语言简介 1.1 什么是GO语言 Go语言是谷歌2009发布的第二款开源编程语言,它是一门编译型语言,它结合了解释型语言的游刃有余,动态类型语言的开发效率,以及静态类型语言的安全性。go语言支持并发、垃圾回收、快速编译(同时支持交叉编译)、内存安全(没有指针)、丰富的内置类型、函数返回多值、错误处理、匿名函数和闭包、类型和接口、运行时反射、安全的并行机制、编译时代码生成(通过反射)、语言交互性(C语言调用go编译的动态库,go调用C语言编译的动态库)、自动垃圾回收、更丰富的标准库等特性。go语言目前常用在Web 开发、分布式系统、云平台、网络服务、系统工具开发、自动化运维、云计算、云存储、云网络、网络安全、游戏开发等领域。常见的由go语言开发的产品有Docker、Kubernetes等。
1.2 安装GO语言 参照官方指南 安装go语言,安装完成后,执行go version查看go语言版本。如果执行成功,则安装成功。
1.3 第一个GO语言程序 创建一个hello.go文件,内容如下:
1 2 3 4 5 6 7 package mainimport "fmt" func main () { fmt.Println("Hello, World!" ) }
执行go run hello.go,输出Hello, World!。其中package main表示这是一个可独立执行的程序,import "fmt"表示引入了一个包,fmt包实现了格式化IO(输入/输出)的函数。 func main()是程序开始执行的函数,main函数是每一个可执行程序所必须包含的,main函数只能由package main声明一次。fmt.Println可以将字符串输出到控制台,并在最后自动增加换行字符\n。
2 变量声明 2.1 变量类型 go语言的基本类型有bool、string、int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、uintptr、byte、rune、float32、float64、complex64、complex128。其中int、uint、uintptr在32位系统上是32位,在64位系统上是64位。byte是uint8的别名,rune是int32的别名。complex64和complex128分别是32位和64位大小的复数类型。
2.2 四种声明方式 1 2 3 4 var name type var name type = value var name = value name := value
2.3 多变量声明 1 2 3 4 var name1, name2 type var name1, name2 type = value1, value2 var name1, name2 = value1, value2 name1, name2 := value1, value2
2.4 匿名变量 匿名变量用_表示,匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。
1 2 3 4 5 6 7 8 9 10 func foo () (int , string ) { return 10 , "bar" }func main () { x, _ := foo() _, y := foo() fmt.Println("x=" , x) fmt.Println("y=" , y) }
2.5 常量 常量是一个简单值的标识符,在程序运行时,不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
1 2 const name = valueconst name type = value
iota是 go 语言的常量计数器,只能在常量的表达式中使用。iota在const关键字出现时将被重置为0,const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。使用iota能简化定义,在定义枚举时很有用。
1 2 3 4 5 6 7 8 9 10 11 const ( a = iota b c d = "ha" e f = 100 g h = iota i )
3 函数 3.1 函数定义 GO语言的函数与C语言的函数定义类似,但是GO语言支持多返回值。
1 2 3 4 5 func funcName (input1 type1, input2 type2) (output1 type1, output2 type2) { return value1, value2 }
3.2 函数调用 1 2 3 4 func main () { value1, value2 := funcName(param1, param2) }
3.3 函数返回值 go语言支持多返回值,函数定义时需要定义返回值类型,函数返回时需要返回相应数量、类型的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func sum1 (a int , b int ) (int , int ) { return a + b, a - b }func sum2 (a int , b int ) (x int , y int ) { x = a + b y = a - b return }func main () { x, y := sum1(1 , 2 ) fmt.Println("x=" , x) fmt.Println("y=" , y) x1, y1 = sum2(1 , 2 ) fmt.Println("x=" , x1) fmt.Println("y=" , y1) }
3.4 函数作为参数 1 2 3 4 5 6 7 8 9 10 11 12 func sum (a int , b int ) int { return a + b }func calc (a int , b int , op func (int , int ) int ) int { return op(a, b) }func main () { x := calc(1 , 2 , sum) fmt.Println("x=" , x) }
3.5 函数作为返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func do (s string ) func (int , int ) int { switch s { case "+" : return func (a int , b int ) int { return a + b } case "-" : return func (a int , b int ) int { return a - b } default : return nil } }func main () { x := do("+" ) y := x(1 , 2 ) fmt.Println("y=" , y) }
3.6 闭包 闭包是引用了外部变量的函数,被引用的变量和函数一同存在,即使函数已经离开了声明所在的环境,但是引用的变量仍然存在,闭包使得这些变量的生命周期延长了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func adder () func (int ) int { var x int return func (d int ) int { x += d return x } }func main () { x := adder() y := x(1 ) fmt.Println("y=" , y) y = x(2 ) fmt.Println("y=" , y) }
3.7 defer defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。
1 2 3 4 func main () { defer fmt.Println("world" ) fmt.Println("hello" ) }
3.8 panic panic用于报告一个严重错误,通常发生在不可恢复的错误,如数组越界、空指针引用等,这些运行时错误会引起panic异常。panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数调用,之后程序崩溃并输出日志信息。panic可以直接调用panic函数,也可以由运行时错误触发。
1 2 3 4 5 6 7 8 func main () { panic ("a problem" ) _, err := os.Create("/tmp/file" ) if err != nil { panic (err) } }
3.9 recover recover用于终止错误处理流程,recover只能在defer修饰的函数中使用,用于取得panic调用中传递过来的错误值,如果是正常执行,调用recover会返回nil,并且没有其它效果,如果当前的goroutine陷入panic状态,调用recover可以捕获到panic的输入值,并且恢复正常执行。
1 2 3 4 5 6 7 8 func main () { defer func () { if err := recover (); err != nil { fmt.Println(err) } }() panic ("a problem" ) }
4 流程控制 4.1 if 1 2 3 4 5 6 7 if condition { } else if condition { } else { }
4.2 switch 1 2 3 4 5 6 7 8 switch condition {case condition: case condition: default : }
4.3 for 1 2 3 for init; condition; post { }
4.4 for range 1 2 3 for key, value := range oldMap { newMap[key] = value }
4.5 break 1 2 3 4 5 6 for i := 0 ; i < 10 ; i++ { if i > 5 { break } fmt.Println("i=" , i) }
4.6 continue 1 2 3 4 5 6 for i := 0 ; i < 10 ; i++ { if i == 5 { continue } fmt.Println("i=" , i) }
4.7 goto 1 2 3 4 5 6 7 8 9 func main () { i := 0 Here: fmt.Println("i=" , i) i++ if i < 10 { goto Here } }
5 数组 5.1 数组定义 1 var variable_name [SIZE] variable_type
5.2 数组初始化 1 var balance = [5 ]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
5.3 数组访问 1 var salary float32 = balance[9 ]
5.4 数组长度
5.5 数组遍历 1 2 3 for i := 0 ; i < len (balance); i++ { fmt.Println(balance[i]) }
5.6 多维数组 1 var threedim [5 ][10 ][4 ]int
三种导入方式 1. import . “fmt” 在导入包后,调用包中函数时可以省略包名。
1 2 3 4 5 6 7 package mainimport . "fmt" func main () { Println("hello world" ) }
2. import _ “fmt” 导入包,但是不使用包中函数,而是调用了该包中的init函数。
1 2 3 4 5 6 7 package mainimport _ "fmt" func main () { }
3. import “fmt” 导入包,调用包中函数时,需要带上包名。
1 2 3 4 5 6 7 package mainimport "fmt" func main () { fmt.Println("hello world" ) }