Go 语言开发环境搭建
安装 Go
访问 https://golang.google.cn/dl/ 或者 https://studygolang.com/dl 下载对应系统的 Go 版本进行安装。默认情况下,Go 会安装到 /usr/local/go(macOS 和 Linux)或 C:\Go(Windows)目录下。
配置环境变量
在 macOS 或 Linux 中,可以编辑~/.bash_profile
、~/.bashrc
、~/.zshrc
、或/etc/profile
文件。在 Windows 中,可以访问系统属性 -> 高级 -> 环境变量,然后添加一个新的环境变量。
添加以下行,将 Go 的二进制文件路径(即安装目录中的 bin 文件夹)添加到PATH
环境变量中:
export PATH=$PATH:/usr/local/go/bin
保存并关闭文件,然后重新打开终端或命令提示符,以使更改生效。
source /etc/profile
ln -s /usr/local/go/bin/go /usr/sbin/go
运行以下命令来验证 Go 是否成功安装:
go version
Go 基础语法
变量定义
使用关键字var
定义变量,需要指定变量的名称和类型:
var age int
var name string
在定义变量时可以同时进行初始化赋值:
var age int = 18
var name string = "Clei"
可以使用多个变量一起定义:
var a, b, c int
var x, y, z string
可以使用括号集中定义变量:
var (
a int
b int
c int
)
使用简短声明:
定义变量,编译器会自动推导变量的类型:
age := 18
name := "Clei"
a, b, c := 1, bool, "hello"
简短声明的方式只能在函数内部使用
Go 语言定义的变量必须被使用,否则会报错。如果只想要定义一个变量而不使用,可以使用下划线_
来忽略。
_ = age
内建变量类型
- 整数类型
- 有符号整数类型:int8、int16、int32、int64、int。
- 无符号整数类型:uint8、uint16、uint32、uint64、uint。
- 浮点数类型
- float32:单精度浮点数。
- float64:双精度浮点数。
- 布尔类型
- bool:只能取值为
true
或false
。
- 字符串类型
- string:由一系列字符组成的文本。
- 字节类型
- byte:与
uint8
等价,用于表示ASCII
码表中的一个字符。
- 符文类型
- rune:与
int32
等价,用于表示Unicode
字符(UTF-8 编码)。
- 复数类型
- complex64:由两个
float32
类型组成的复数。 - complex128:由两个
float64
类型组成的复数。
- 指针类型
- *T:表示类型为
T
的指针,用于存储变量的内存地址
常量与枚举
常量使用关键字const
进行定义和声明。
常量一旦被定义,其值不可修改。
const pi = 3.14159
枚举使用关键字iota
进行定义,它用于简化定义连续的枚举值。
在const
块中,iota
的初始值为 0,每次出现时会自动递增。
const (
Monday = iota + 1 // 从1开始递增
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
iota
只在常量的定义中才有效,并且每个const
块都会将iota
重置为 0。
如果有多个const
块,则每个块中的iota
是独立计数的。
条件语句
if
语句:
if condition {
...
} else if condition {
...
} else {
...
}
switch
语句
switch expression {
case value1:
...
case value2:
...
default:
...
}
switch
后可以不跟上表达式,而将表达式放在case
后面
switch {
case num > 0:
fmt.Println("大于0")
case num < 0:
fmt.Println("小于0")
default:
fmt.Println("等于0")
}
switch
会自动break
,除非使用fallthrough
循环
for
循环
for 初始语句; 条件表达式; 后置语句 {
// 循环体
}
for range
循环
for index, value := range collection {
...
}
无限循环
for {
...
}
for
的语句可以只写条件表达式
for num <= 5 {
sum += num
num++
}
函数
函数的定义:
func 函数名(参数列表) (返回值列表) {
// 函数体
}
参数与返回值:
参数:函数可以有零个或多个参数,每个参数都有一个类型。参数之间用逗号分隔。 返回值:函数可以有零个或多个返回值,每个返回值也有一个类型。如果函数有多个返回值,需要使用括号将它们括起来。
func swap(a, b int) (int, int) {
return b, a
}
func main() {
x, y := 3, 4
x, y = swap(x, y)
fmt.Println(x, y)
}
函数命名的返回值可以作为函数的局部变量使用。
func divide(a, b int) (quotient, remainder int) {
quotient = a / b
remainder = a % b
return
}
func main() {
q, r := divide(7, 3)
fmt.Println(q, r)
}
变参函数: Go 语言支持变参函数,使用…表示。
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
func main() {
total := sum(1, 2, 3, 4, 5)
fmt.Println(total)
}
指针
通过在变量前加上*来定义指针类型的变量
var ptr *int
使用&
操作符可以获取变量的内存地址
x := 5
ptr := &x
使用*
操作符可以获取指针指向的值
x := 5
ptr := &x
fmt.Println(*ptr)
修改指针指向的值
x := 5
ptr := &x
*ptr = 10
fmt.Println(x)
可以递指针作为函数的参数,以便在函数内部修改指针指向的变量
func changeValue(ptr *int) {
*ptr = 20
}
func main() {
x := 5
changeValue(&x)
fmt.Println(x)
}
数组
数组是一种固定长度且类型相同的数据结构,用于存储一系列的元素。 数组的长度在创建时就确定,并且在整个生命周期中保持不变。
var numbers [5]int
数组是值类型,当将一个数组赋值给另一个数组或将数组作为函数参数传递时,会进行值的拷贝。意味着对一个数组的修改不会影响到其他数组。
切片
切片是 Go 语言中一种动态数组,它可以根据需要自动调整大小。 它由指向底层数组的指针、长度和容量组成。
// 创建切片
slice := make([]int, 0, 5)
slice := []int{1, 2, 3, 4, 5}
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:3]
// 添加元素
slice = append(slice, 1)
slice = append(slice, 2, 3, 4)
// 修改元素
slice[0] = 10
// 删除元素
slice = append(slice[:2], slice[3:]...) // 删除第3个元素,go没有内置的delete函数
Map
Map 是 Go 语言中一种无序的键值对集合,也称为字典。它可以用来存储和查找任意类型的数据。
// 创建map
m := make(map[string]int)
// 创建map并初始化
m := map[string]int{
"apple": 1,
"banana": 2,
}
// 添加或修改键值对
m["orange"] = 3
// 获取值并判断键是否存在
value, ok := m["apple"]
if ok {
fmt.Println("Value:", value)
}
// 删除键值对
delete(m, "banana")
// 计算map的长度
len(m)
结构体和方法
结构体是 Go 语言中一种自定义的数据类型,可以将不同类型的字段组合在一起。 方法是与结构体关联的函数,用于操作结构体的数据。
// 定义结构体
type Person struct {
Name string
Age int
}
// 定义结构体方法
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s and I'm %d years old.\n", p.Name, p.Age)
}
// 创建结构体实例并调用方法
person := Person{"John", 25}
person.Greet()
包和封装
Go 语言中的包用于组织和管理代码,它可以将相关的函数、变量、类型、常量等放在一起,形成一个独立的模块。 包可以提高代码的可读性、可维护性和复用性。
// 定义包
package example
// 使用包
import example
封装是一种将代码隐藏在包内部的机制,只允许包外部访问被明确公开的部分。 Go 语言中实现封装的方式是通过标识符(函数、变量、类型、常量等)的首字母大小写来控制其可见性。
// 定义公开函数
func PublicFunction() {
// ...
}
// 定义私有函数,只能在包内部访问
func privateFunction() {
// ...
}
扩展已有类型
- 使用别名
使用别名对已有类型进行扩展
//用别名"queue"扩展原类型 []int,为其增加push(), pop(), isEmpty() 方法
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
- 使用组合
在一个结构体中声明一个有名的字段,这个字段的类型也可以是另一个结构体、接口或者基本类型
// 原有类型
type person struct {
name string
age int
}
// 定义一个新的结构体,通过组合Person类型的字段
type student struct {
person Person // 有名的字段,类型为person
grade int
}
// 定义一个新的结构体,通过组合Person类型的字段
type teacher struct {
person person // 有名的字段,类型为person
salary int
}
- 使用内嵌
把已有类型作为新类型的一个字段来实现扩展 利用内嵌的语法糖,可以直接访问已有类型的成员变量和方法 也可以覆盖已有类型的方法,实现自定义的逻辑
// 原有类型
type Person struct {
Name string
Age int
}
func (p Person) Print() {
fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
}
//通过使用组合的方式对原有类型进行扩展,Person组合到Student中,在Student中增加了新字段和方法
type Student struct {
Person
ID string
Scores []int
}
//覆盖Person结构体的Print方法,在Student结构体中增加了学号和成绩的信息
func (s Student) Print() {
fmt.Printf("Name: %s, Age: %d, ID: %s, Scores: %v\n", s.Name, s.Age, s.ID, s.Scores)
}
内嵌和组合的区别主要有以下几点:
• 内嵌可以实现类似于继承和多态的效果,可以直接访问内嵌字段的成员变量和方法,而不需要显式地指定内嵌字段的名称。组合则需要通过字段名称来访问其成员变量和方法。
• 内嵌可以实现方法集的覆盖,即如果内嵌类型和外层类型都定义了同名的方法,则外层类型的方法会覆盖内嵌类型的方法。组合则不会发生方法覆盖,如果两个类型都定义了同名的方法,则需要通过字段名称来区分调用哪个方法。
• 内嵌可以实现接口的嵌套,即一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。组合则不能实现接口的嵌套,只能通过字段名称来访问其内部接口。
Go 依赖管理
自从 Go 1.11 版本开始,Go Modules 成为 Go 的默认依赖管理方式
初始化模块
在项目根目录下,打开终端执行以下命令来初始化 Go 模块。
go mod init <module-name>
添加依赖项
通过导入其他包来添加项目的依赖项。每当在代码中导入一个新包时,Go 会自动检测到并记录这个依赖项。
import "github.com/example/package"
下载依赖项
需要下载和安装依赖项时,只需运行以下命令。
go mod download
或
go mod tidy
Go 会查找 go.mod 文件并根据其中记录的依赖项信息进行下载。
更新依赖项
运行以下命令将更新项目的依赖项到最新版本。
go get -u
如果需要下载指定版本的依赖项,可以加上对应的版本号。
- 在导入语句中指定版本:
import "github.com/example/package@v1.2.3"
- 使用
go get
命令加上版本号
go get github.com/example/package@v1.2.3
需要注意的是,如果指定了特定的版本号,那么后续运行go get -u
命令时,该依赖项将不会自动升级到更新的版本。如果需要升级指定版本的依赖项,需要手动更新对应导入语句中的版本号或重新运行go get
命令来下载新的版本。