1. go简述
1.1 为什么要创造一门编程语言
- C/C++ 的发展速度无法跟上计算机发展的脚步,十多年来也没有出现一门与时代相符的主流系统编程语言,因此人们需要一门新的系统编程语言来弥补这个空缺,尤其是在计算机信息时代。
- 相比计算机性能的提升,软件开发领域不被认为发展得足够快或者比硬件发展得更加成功(有许多项目均以失败告终),同时应用程序的体积始终在不断地扩大,这就迫切地需要一门具备更高层次概念的低级语言来突破现状。
- 在 Go 语言出现之前,开发者们总是面临非常艰难的抉择,究竟是使用执行速度快但是编译速度并不理想的语言(如:C++),还是使用编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言呢?显然,Go 语言在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。
1.2 Go语言的发展目标
- Go 语言的主要目标是将静态语言的安全性和高效性(C++)与动态语言的易开发性(python)进行有机结合,达到完美平衡,从而使编程变得更加有乐趣,而不是在艰难抉择中痛苦前行。
因此,Go 语言是一门类型安全和内存安全的编程语言。虽然 Go 语言中仍有指针的存在,但并不允许进行指针运算。
- Go 语言的另一个目标是对于网络通信、并发和并行编程的极佳支持,从而更好地利用大量的分布式和多核的计算机,这一点对于谷歌内部的使用来说就非常重要了。设计者通过** goroutine** 这种轻量级线程的概念来实现这个目标,然后通过** channel **来实现各个 goroutine 之间的通信。他们实现了分段栈增长和 goroutine 在线程基础上多路复用技术的自动化。
这个特性显然是 Go 语言最强有力的部分,不仅支持了日益重要的多核与多处理器计算机,也弥补了现存编程语言在这方面所存在的不足。
- Go 语言中另一个非常重要的特性就是它的构建速度(编译和链接到机器代码的速度),一般情况下构建一个程序的时间只需要数百毫秒到几秒。作为大量使用
C++
来构建基础设施的谷歌来说,无疑从根本上摆脱了 C++ 在构建速度上非常不理想的噩梦。这不仅极大地提升了开发者的生产力,同时也使得软件开发过程中的代码测试环节更加紧凑,而不必浪费大量的时间在等待程序的构建上。
依赖管理是现今软件开发的一个重要组成部分,但是 C 语言中“头文件”的概念却导致越来越多因为依赖关系而使得构建一个大型的项目需要长达几个小时的时间。人们越来越需要一门具有严格的、简洁的依赖关系分析系统从而能够快速编译的编程语言。这正是 Go 语言采用包模型的根本原因,这个模型通过严格的依赖关系检查机制来加快程序构建的速度,提供了非常好的可量测性。
整个 Go 语言标准库的编译时间一般都在** 20 **秒以内,其它的常规项目也只需要半秒钟的时间来完成编译工作。这种闪电般的编译速度甚至比编译 C 语言或者 Fortran 更加快,使得编译这一环节不再成为在软件开发中困扰开发人员的问题。在这之前,动态语言将快速编译作为自身的一大亮点,像C++
那样的静态语言一般都有非常漫长的编译和链接工作。而同样作为静态语言的 Go 语言,通过自身优良的构建机制,成功地去除了这个弊端,使得程序的构建过程变得微不足道,拥有了像脚本语言和动态语言那样的高效开发的能力。
另外,Go 语言在执行速度方面也可以与 C/C++ 相提并论。
由于内存问题(通常称为内存泄漏)长期以来一直伴随着 C++ 的开发者们,Go 语言的设计者们认为内存管理不应该是开发人员所需要考虑的问题。因此尽管 Go 语言像其它静态语言一样执行本地代码,但它依旧运行在某种意义上的虚拟机,以此来实现高效快速的垃圾回收(使用了一个简单的标记-清除算法)。
尽管垃圾回收并不容易实现,但考虑这将是未来并发应用程序发展的一个重要组成部分,Go 语言的设计者们还是完成了这项艰难的任务。
Go 语言还能够在运行时进行反射相关的操作。
使用 go install 能够很轻松地对第三方包进行部署。
此外,Go 语言还支持调用由 C 语言编写的海量库文件(第 3.9 节),从而能够将过去开发的软件进行快速迁移。
1.3 语言的特性
Go 语言从本质上(程序和结构方面)来实现并发编程。
因为 Go 语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口 (interface) 的概念来实现多态性。Go 语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说这是一门混合型的语言。
Go 语言使用静态类型,所以它是类型安全的一门语言,加上通过构建到本地代码,程序的执行速度也非常快。
作为强类型语言,隐式的类型转换是不被允许的,记住一条原则:让所有的东西都是显式的。
Go 语言其实也有一些动态语言的特性(通过关键字 var),所以它对那些逃离 Java 和 .Net 世界而使用 Python、Ruby、PHP 和 JavaScript 的开发者们也具有很大的吸引力。
Go 语言支持交叉编译,比如说你可以在运行 Linux 系统的计算机上开发运行 Windows 下运行的应用程序。这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go 语言做到了真正的国际化!
1.4 相关特性的缺失
许多能够在大多数面向对象语言中使用的特性 Go 语言都没有支持,但其中的一部分可能会在未来被支持。
- 为了简化设计,不支持函数重载和操作符重载
- 为了避免在 C/C++ 开发中的一些 Bug 和混乱,不支持隐式转换
- Go 语言通过另一种途径实现面向对象设计(第 10-11 章)来放弃类和类型的继承
- 尽管在接口的使用方面(第 11 章)可以实现类似变体类型的功能,但本身不支持变体类型
- 不支持动态加载代码
- 不支持动态链接库
- 不支持泛型
- 通过 recover() 和 panic() 来替代异常机制(第 13.2-13.3 节)
- 不支持静态变量
1.5 Linux上安装go
- 首先进入go官网下载指定的已编译好的源码包(ps:这里我的电脑是x86-64架构所以选择了`go1.22.1.linux-amd64.tar.gz,将其下载下来
1
wget https://golang.google.cn/dl/go1.22.1.linux-amd64.tar.gz
- 解压缩到指定路径
1
tar -C /usr/lib/go/ -xvf go1.22.1.linux-amd64.tar.gz
建立软连接(可选)
1
ln -s /usr/lib/go/bin/go /usr/bin/go
- 之后我们需要能够让Linux能够找到
go
这个编译器,所以需要配置~/.bashrc
文件1
2
3export GOROOT=/usr/lib/go
export GOPATH=$PATH:$GOROOT/bin
export PATH=$PATH:/usr/bin 有时候我们需要下载能够支持go快速开发的各种工具,但时时会有网络连接问题导致无法访问Google的服务器。因此,可以设置代理
1
2
3
4
5go env -w GOPROXY=https://goproxy.cn,direct
//设置完成后,可以通过以下命令验证
go env | grep GOPROXY
或者
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
1.6 安装插件&工具
- 1.
gotests
是一个非常有用的Go测试自动化工具,它可以自动生成基于表格驱动的测试代码,提高Go语言测试效率,支持为函数、结构体批量生成测试,节省编写测试的时间。1
go get -u github.com/cweill/gotests/gotests@latest
- 2.
gopls
:Go 官方语言服务器,提供代码补全、定义跳转、重构等核心功能,是 VSCode Go 扩展的 “大脑”。1
go install golang.org/x/tools/gopls@latest
- 3.
go-outline
:生成代码大纲,在 VSCode 的 “大纲” 面板中显示函数、变量、结构体等,方便快速定位代码。1
go install github.com/ramya-rao-a/go-outline@latest
- 4.
godef
:快速跳转到变量 / 函数的定义处,配合 VSCode 的 “转到定义”(F12)使用。1
go install github.com/rogpeppe/godef@latest
- 5.
goimports
:自动格式化代码,并管理 import 语句(添加缺失的包、删除未使用的包),比原生 gofmt 更强大,是 settings.json 中推荐的格式化工具。1
go install golang.org/x/tools/cmd/goimports@latest
- 6.
delve
:Go 官方调试器,与 VSCode 集成后支持断点调试、变量监视、调用栈查看等功能,是排查代码问题的必备工具。1
go install github.com/go-delve/delve/cmd/dlv@latest
验证是否安装成功: 1
2
3
4
5
6# 验证安装 gotests -h # 或检查路径 which gotests
# 检查版本 gopls version # 检查是否在PATH中 which gopls
# 检查可执行文件 go-outline -h # 或直接检查安装路径 ls $(go env GOPATH)/bin/go-outline
# 检查安装 godef -h # 测试功能(需要在一个Go项目中) godef -f main.go -o 10 # 10代表行号
# 检查安装 goimports -h # 测试格式化功能 echo 'package main\nimport "fmt"\nfunc main() { fmt.Println("hello") }' | goimports
# 检查版本 dlv version # 或检查帮助 dlv -h
如果上面出现command not found
,则路径未配置进环境变量,因为上述命令安装的位置默认在~/go/bin
目录下,需要如下配置: 1
2
3
4
5
# 查看当前值GOPATH值是否是~/go(即/home/[你的用户名]/go)
go env GOPATH
export PATH=$PATH:$(go env GOPATH)/bin
1.7 gotest的使用
基本用法:
1
2
3go test [包路径] # 运行当前目录下所有测试
go test -v # 显示详细输出
go test -run TestFuncName # 运行特定测试函数覆盖率测试:
1
2go test -cover # 输出覆盖率统计
go test -coverprofile=cover.out && go tool cover -html=cover.out # 生成HTML覆盖率报告基准测试:需编写BenchmarkXxx函数,通过-bench参数运行
1
go test -bench=. -benchmem
1.8 delve的使用(可使用vscode配置直接调用dlv调试)
- 点击调试按钮,创建一个
launch.json
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"version": "0.2.0",
"configurations": [
{
"name": "gotest", //项目名称
"type": "go", //调试器类型,固定为go
"request": "launch", //launch(启动程序)或 attach(附加进程)
"mode": "debug", //auto/debug/test/exec/remote
"program": "hello.go", //入口文件,{workspaceFolder}为项目根
"env": {}, //环境变量,键值对形式{"KEY": "value"}
"args": [], //命令行参数,如["--port=8080"]
"showLog": true
},
]
}
1.9 goimports的使用
1 | goimports -w . # 格式化当前目录所有文件并保存 |
与gofmt区别:在代码格式化基础上增加了import管理功能
1.10 go-outline的使用
- 功能:生成Go代码的结构化大纲(函数/方法/变量等符号列表),主要用于IDE的代码导航 5
- 典型应用场景:
- VS Code等编辑器通过该工具实现"Go to Symbol"功能
- 生成项目文档的API结构概览
- 输出示例:
1
2
3[{"label":"main","kind":"package","children":[
{"label":"foo","kind":"function","pos":"main.go:5"}
]}]
1.11 项目的模块管理
go项目模块管理依赖go init mod、go mod tidy、go clean
- go mod init 为你的 Go 项目创建一个身份标识(go.mod 文件),告诉世界:"这个项目叫什么?依赖谁?" 会创建一个 go.mod 文件,内容类似:
1
2# 在项目文件夹中执行(比如项目叫 "hello")
go mod init github.com/你的名字/hello1
2module github.com/你的名字/hello
go 1.21 # 你当前使用的Go版本 - go mod tidy —— 项目的"管家":自动帮你做两件事:
- 添加依赖:检查代码中用到了哪些第三方库,自动下载并记录到 go.mod。
- 清理垃圾:移除代码中未使用的依赖,保持项目干净。
所以,你刚下载别人的项目时(先 go mod tidy 再运行),你修改代码后(比如新增/删除了某个库的引用) 1
go mod tidy
- go clean —— 项目的"大扫除":清理临时文件和缓存,解决一些奇怪的问题(比如依赖版本冲突)。
1
2go clean -modcache # 彻底清空所有下载的依赖(下次运行会重新下载)
go clean -testcache # 清理测试缓存(比如测试用例改了但结果没变时用)
什么时候用:项目突然报错,但明明没改代码,磁盘空间不足时