GO语言入门

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 main

import "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语言的基本类型有boolstringintint8int16int32int64uintuint8uint16uint32uint64uintptrbyterunefloat32float64complex64complex128。其中intuintuintptr在32位系统上是32位,在64位系统上是64位。byteuint8的别名,runeint32的别名。complex64complex128分别是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 = value
const name type = value

iota是 go 语言的常量计数器,只能在常量的表达式中使用。iotaconst关键字出现时将被重置为0const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。使用iota能简化定义,在定义枚举时很有用。

1
2
3
4
5
6
7
8
9
10
11
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 独立值,iota += 1
e // "ha" iota += 1
f = 100 // iota +=1
g // 100 iota +=1
h = iota // 7,恢复计数
i // 8
)

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 {
// do something
} else if condition {
// do something
} else {
// do something
}

4.2 switch

1
2
3
4
5
6
7
8
switch condition {
case condition:
// do something
case condition:
// do something
default:
// do something
}

4.3 for

1
2
3
for init; condition; post {
// do something
}

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 数组长度

1
len(balance)

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 main

import . "fmt"

func main() {
Println("hello world")
}

2. import _ “fmt”

导入包,但是不使用包中函数,而是调用了该包中的init函数。

1
2
3
4
5
6
7
package main

import _ "fmt"

func main() {
// 调用了fmt包中的init函数
}

3. import “fmt”

导入包,调用包中函数时,需要带上包名。

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("hello world")
}

GO语言入门
https://wenzhaoabc.github.io/go/go-starter/
作者
wenzhaoabc
发布于
2023年11月5日
许可协议