Go 语言入门指南 | 豆包MarsCode AI 刷题

91 阅读5分钟

Go基础语法

Go特点

  1. 高性能, 高并发
  2. 丰富的标准库
  3. 完善的工具链
  4. 静态编译
  5. 快速编译
  6. 跨平台
  7. 垃圾回收

变量

与js类似,通过var,const声明

声明变量方式

var 变量名 = 值自动推导变量类型

var 变量名 变量类型 = 值规定类型

变量名 := 值

常量

将var改成const

常量没有确定的类型, 会根据使用的上下文自动确定类型

if else

与其他语言类似

for

// 死循环
for {
	fmt.Println("123")
}
// 经典循环
for j := 7; j < 9; j++ {  
    fmt.Println(j)  
}

continue, break 与c一样 for后不加条件是指死循环

switch case

不需要加break, 不会继续往下执行

// 不加变量直接写结构分支
t := time.Now()  
switch {  
case t.Hour() < 12:  
    fmt.Println("It's before noon")  
default:  
    fmt.Println("It's after noon")  
}

数组

定义方式

  1. var a [5]int
  2. b := [5]int{1, 2, 3, 4, 5}
  3. var twoD [2][3]int

切片

切片不同于数组, 是一个可变长度的数组 用make创建切片

func main() {  
  
    s := make([]string, 3)  
    s[0] = "a"  
    s[1] = "b"  
    s[2] = "c"  
    s = append(s, "d") 
    // 第[l~r)个元素
    fmt.Println(s[2:5]) // [c d e]  
}

map

类似于java的hashmap

  1. 创建和初始化 map

    • m := make(map[string]int):创建一个空的 map,键为字符串类型,值为整数类型。
    • m["one"] = 1m["two"] = 2:向 map 中添加键值对。
  2. 打印 map

    • fmt.Println(m):输出整个 map 的内容,结果为 map[one:1 two:2]
  3. 获取 map 的长度

    • fmt.Println(len(m)):输出 map 的长度,结果为 2
  4. 获取指定键的值

    • fmt.Println(m["one"]):获取键为 "one" 的值,结果为 1
    • fmt.Println(m["unknow"]):尝试获取不存在的键 "unknow" 的值,结果为 0(默认值)。
  5. 检查键是否存在

    • r, ok := m["unknow"]:使用多重赋值检查键是否存在。r 是键对应的值,ok 是一个布尔值,表示键是否存在。
    • fmt.Println(r, ok):输出 0 false,表示键 "unknow" 不存在。
  6. 删除指定键的值

    • delete(m, "one"):从 map 中删除键为 "one" 的条目。
  7. 使用字面量创建 map

    • m2 := map[string]int{"one": 1, "two": 2}:使用字面量创建并初始化 map。
    • var m3 = map[string]int{"one": 1, "two": 2}:使用 var 关键字和字面量创建并初始化 map。
    • fmt.Println(m2, m3):输出两个 map 的内容,结果为 map[one:1 two:2] map[one:1 two:2]

range

  1. 定义和遍历切片

    • nums := []int{2, 3, 4}:定义一个包含三个整数的切片。
    • sum := 0:初始化一个变量 sum 用于存储切片元素的总和。
    • for i, num := range nums:使用 range 关键字遍历切片,i 是索引,num 是当前元素的值。
      • sum += num:将当前元素的值累加到 sum 中。
      • if num == 2:如果当前元素的值为 2,则打印其索引和值。
    • fmt.Println(sum):输出切片元素的总和,结果为 9
  2. 定义和遍历 Map

    • m := map[string]string{"a": "A", "b": "B"}:定义一个字符串到字符串的 map。
    • for k, v := range m:使用 range 关键字遍历 map,k 是键,v 是值。
      • fmt.Println(k, v):打印每个键值对,输出顺序可能不固定,例如 b B; a A
    • for k := range m:使用 range 关键字遍历 map,只获取键。
      • fmt.Println("key", k):打印每个键,输出顺序可能不固定,例如 key a; key b

注意事项

  • range 关键字在遍历切片时返回索引和值,在遍历 map 时返回键和值。
  • 遍历 map 的顺序是不确定的,每次运行程序时输出的顺序可能会不同。

多线程

协程

协程: 用户态, 轻量级线程, 栈KB级别 协程: 内核态, 线程跑多个协程, 栈MB级别 使用go关键字来开启一个协程

通道

提倡通过通信共享内存, 而不是通过共享内存实现通信 make(chan 元素类型,[缓冲大小]

  • 无缓冲通道(同步通道) make(chan int)
  • 有缓冲通道 make(chan int,2)
func CalSquare() {  
    src := make(chan int)  
    dest := make(chan int, 1)  
    go func() {  
       defer close(src)  
       for i := 0; i < 10; i++ {  
          src <- i  
       }  
    }()  
    go func() {  
       defer close(dest)  
       for i := range src {  
          dest <- i * i  
       }  
    }()  
    for i := range dest {  
       time.Sleep(3000000000)  
       println(i)  
    }  
}

并发安全 Lock

lock sync.Mutex 对于共享内存来说, 有可能出现并发安全问题, 所以要对其加锁

WaitGroup

使用time.sleep时并不能确定应当让线程暂停的时间, 从而实现线程同步

Add(delta int)计数器+delta
Done()计数器-1
Wait()阻塞知道计数器=0
计数器: 开启协程+1, 执行结束-1; 主协程阻塞知道计数器为0
func CalSquare() {  
    var wg sync.WaitGroup  
    for i := 0; i < 5; i++ {  
       go func(j int) {  
          defer wg.Done()  
          println(j)  
       }(i)  
    }  
    wg.Wait()  
}

依赖管理

GOPATH->GO VENDOR -> GO Module

环境变量GOPATH

| |-- bin 项目编译的二进制文件 |-- pkg 项目编译的中间产物, 加速编译 |-- src 项目源码

  • 项目源码直接依赖src下的代码
  • go get 下载最新版本的包到src目录下 弊端
  • 无法实现package的多版本控制

GO VENDOR

解决无法实现package的多版本控制的问题 | |-- Readme.md |-- dao |-- handler |-- mian.go |-- service |-- vendor

  • 项目目录下增加vendor文件, 所有依赖包副本形式放在$ProjectRoot/vendor
  • 依赖寻址方式: vendor -> GOPATH 通过每个项目引入一份依赖的副本, 解决了多个项目需要同一个package依赖冲突问题 弊端
  • 无法控制依赖的版本
  • 更新项目可能出现依赖冲突, 导致编译出错

GO Module

  • 通过go.mod文件管理依赖包版本
  • 通过go get/go mod 指令工具管理依赖包 类比maven
module github.com/wangkechun/go-by-example   依赖管理基本单元
  
go 1.18  原生库
  
require (  单元依赖  
	example/lib1 v1.0.2
    )

version

语义化版本 ${MAJOR(大版本)}${MINOR(新增函数/功能)}${PATCH(修改bug)} V1.3.0 V2.3.0 基于commit的伪版本 vx.0.0-yyyymmddhhmmss-${12位hash码前缀}

  • 主版本2+模块会在模块路径增加/vN 后缀
  • 对于没用go.mod文件并且主版本2+的依赖, 会+incompatible go在遇到依赖了同一个模块的不同版本时, 会选择依赖最低的兼容版本

go get

go get example.org/pkg +

@update默认
@none删除依赖
@v1.2.2tag版本,语义版本
@23dfdd5特定的commit
@master分支的最新commit

go mod

go mod +

init初始化, 创建go.mod
download下载模块到本地缓存
tidy增加需要的依赖, 删除不需要的依赖

总结与思考

协程

用户态协程:轻量级线程,栈大小为KB级别。

内核态协程:运行在内核态的线程上,栈大小为MB级别。

Go中的协程:使用go关键字启动一个新的协程。

通道

通信方式:提倡通过通信共享内存,而不是通过共享内存实现通信。

通道创建:

无缓冲通道:make(chan int)

有缓冲通道:make(chan int, 2)