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

内建变量类型

  1. 整数类型
  • 有符号整数类型:int8、int16、int32、int64、int。
  • 无符号整数类型:uint8、uint16、uint32、uint64、uint。
  1. 浮点数类型
  • float32:单精度浮点数。
  • float64:双精度浮点数。
  1. 布尔类型
  • bool:只能取值为truefalse
  1. 字符串类型
  • string:由一系列字符组成的文本。
  1. 字节类型
  • byte:与uint8等价,用于表示ASCII码表中的一个字符。
  1. 符文类型
  • rune:与int32等价,用于表示Unicode字符(UTF-8 编码)。
  1. 复数类型
  • complex64:由两个float32类型组成的复数。
  • complex128:由两个float64类型组成的复数。
  1. 指针类型
  • *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是独立计数的。

条件语句

  1. if语句:
if condition {
    ...
} else if condition {
    ...
} else {
    ...
}
  1. 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() {
    // ...
}

扩展已有类型

  1. 使用别名

使用别名对已有类型进行扩展

//用别名"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
}
  1. 使用组合

在一个结构体中声明一个有名的字段,这个字段的类型也可以是另一个结构体、接口或者基本类型

// 原有类型
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
}
  1. 使用内嵌

把已有类型作为新类型的一个字段来实现扩展 利用内嵌的语法糖,可以直接访问已有类型的成员变量和方法 也可以覆盖已有类型的方法,实现自定义的逻辑

// 原有类型
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

如果需要下载指定版本的依赖项,可以加上对应的版本号。

  1. 在导入语句中指定版本:
import "github.com/example/package@v1.2.3"
  1. 使用go get命令加上版本号
go get github.com/example/package@v1.2.3

需要注意的是,如果指定了特定的版本号,那么后续运行go get -u命令时,该依赖项将不会自动升级到更新的版本。如果需要升级指定版本的依赖项,需要手动更新对应导入语句中的版本号或重新运行go get命令来下载新的版本。