- Golang 官方文档: golang.google.cn
- C 语言中文网: Golang 简介
Golang 简介
Go 语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是 兼具 Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性。
Go 语言有时候被描述为 C 类似语言,或者是 21 世纪的C语言。
Go 从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。
因为Go语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口(interface)的概念来实现多态性。
Go 语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说 Go 语言是一门混合型的语言。
Golang 安装与环境配置(MacOS)
从 Downloads 页面下载 Go 安装程序;
打开下载的安装包文件,按照提示安装 Go,该软件包将 Go 发行版安装到
/usr/local/go
。安装完成后,在安装目录下将生成一些目录和文件,如下图所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19% ll /usr/local/go
total 392
-rw-r--r-- 1 root wheel 56057 6 2 00:44 AUTHORS
-rw-r--r-- 1 root wheel 1339 6 2 00:44 CONTRIBUTING.md
-rw-r--r-- 1 root wheel 111408 6 2 00:44 CONTRIBUTORS
-rw-r--r-- 1 root wheel 1479 6 2 00:44 LICENSE
-rw-r--r-- 1 root wheel 1303 6 2 00:44 PATENTS
-rw-r--r-- 1 root wheel 1475 6 2 00:44 README.md
-rw-r--r-- 1 root wheel 397 6 2 00:44 SECURITY.md
-rw-r--r-- 1 root wheel 8 6 2 00:44 VERSION
drwxr-xr-x 24 root wheel 768 6 2 00:48 api
drwxr-xr-x 4 root wheel 128 6 2 01:30 bin
-rw-r--r-- 1 root wheel 52 6 2 00:44 codereview.cfg
drwxr-xr-x 7 root wheel 224 6 2 00:48 doc
drwxr-xr-x 3 root wheel 96 6 2 00:48 lib
drwxr-xr-x 14 root wheel 448 6 2 00:48 misc
drwxr-xr-x 6 root wheel 192 6 2 00:51 pkg
drwxr-xr-x 69 root wheel 2208 6 2 00:49 src
drwxr-xr-x 354 root wheel 11328 6 2 00:49 test这个目录的结构遵守
GOPATH
规则,目录中各个文件夹的含义如下表所示。目录名 说明 api 每个版本的 api 变更差异 bin go 源码包编译出的编译器(go)、文档工具(godoc)、格式化工具(gofmt) doc 英文版的 Go 文档 lib 引用的一些库文件 misc 杂项用途的文件,例如 Android 平台的编译,git 的提交钩子等 pkg 对应系统平台(Windows,Linux,MacOS 等)编译好的中间文件 src 标准库的源码 test 测试用例 配置 GOPATH 环境变量,开始写 go 项目代码之前,需要我们先配置好环境变量。
1
2
3
4
5cat >> ~/.zshrc <<EOF
export GOPATH="\$HOME/go"
export GOROOT="/usr/local/go"
export PATH=\$PATH:\$GOROOT:\$GOBIN
EOF验证安装信息
1
2% go version
go version go1.18.3 darwin/amd64
Golang 常用的标准库
在 Go 语言的安装文件里包含了一些可以直接使用的包,即标准库。Go语言的标准库(通常被称为语言自带的电池),提供了清晰的构建模块和公共接口,包含 I/O 操作、文本处理、图像、密码学、网络和分布式应用程序等,并支持许多标准化的文件格式和编解码协议。
- Go 语言的标准库以包的方式提供支持,下表列出了 Go 语言标准库中常见的包及其功能。
标准库包名 功能 bufio 带缓冲的 I/O 操作 bytes 实现字节操作 container 封装堆,列表和环形列表等容器 crypto 加密算法 databases 数据库驱动和接口 debug 各种调试文件格式访问及调试功能 encoding 常见算法如 JSON,XML,Base64 等 flag 命令行解析 fmt 格式化操作 go Go 语言的词法,语法树,类型等。可通过这个包进行代码信息提取和修改 html HTML 转义及模板系统 image 常见图形格式的访问及生成 io 实现 I/O 原始访问接口及访问封装 math 数学库 net 网络库,支持 socket,HTTP,邮件,RPC,SMTP 等 os 操作系统平台不依赖平台操作封装 path 兼容各操作系统的路径操作实用函数 plugin Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载 reflect 语言反射支持。可以动态获取代码中的类型信息,获取和修改变量的值 regexp 正则表达式封装 runtime 运行时接口 sort 排序接口 strings 字符串转换,解析及实用函数 time 时间接口 text 文本模板及 Token 词法器
Golang 集成开发环境(IDE)
Goland
Goland 是由 JetBrains 公司开发的一个新的商业 IDE,旨在为 Go 开发者提供的一个符合人体工程学的新的商业 IDE。
Goland 整合了 IntelliJ 平台(一个用于 java 语言开发的集成环境,也可用于其他开发语言),提供了针对Go语言的编码辅助和工具集成。
LiteIDE
LiteIDE是一款专门针对 Go 开发的集成开发环境,在编辑、编译和运行 Go 程序和项目方面都有非常好的支持。同时还包括了对源代码的抽象语法树视图和一些内置工具(此开发环境由国人 vfc 大叔开发)。
LiteIDE 是一款非常好用的轻量级 Go 集成开发环境(基于 QT、Kate 和 SciTE),包含了跨平台开发及其它必要的特性,对代码编写、自动补全和运行调试都有极佳的支持。它采用了 Go 项目的概念来对项目文件进行浏览和管理,它还支持在各个 Go 开发环境之间随意切换以及交叉编译的功能。同时,它具备了抽象语法树视图的功能,可以清楚地纵览项目中的常量、变量、函数、不同类型以及他们的属性和方法。
Sublime Text
一个革命性的跨平台(Linux、Mac OS X、Windows)文本编辑器,它支持编写非常多的编程语言代码。对于 Go 而言,它有一个插件叫做 GoSublime 来支持代码补全和代码模版。
GoClipse
是一款 Eclipse IDE 的插件,拥有非常多的特性以及通过 GoCode 来实现代码补全功能。其依附于著名的 Eclipse 这个大型开发环境,虽然需要安装 JVM 运行环境,但却可以很容易地享有 Eclipse 本身所具有的诸多功能。这是一个非常好的编辑器,完善的代码补全、抽象语法树视图、项目管理和程序调试功能。
Visual Studio Code(简称VS Code)
是一款由微软公司开发的,能运行在 Mac OS X、Windows 和 Linux 上的跨平台开源代码编辑器。VS Code 使用 JSON 格式的配置文件进行所有功能和特性的配置,同时它还可以通过扩展程序为编辑器实现编程语言高亮、参数提示、编译、调试、文档生成等各种功能。
Golang 工程结构
一般的编程语言往往对工程(项目)的目录结构是没有什么规定的,但是 Go 语言却在这方面做了相关规定,本节我们就来聊聊 Go 语言在工程结构方面的有关知识。
前面讲搭建 Go 语言开发环境时提到的环境变量 GOPATH
,项目的构建主要是靠它来实现的。这么说吧,如果想要构建一个项目,就需要将这个项目的目录添加到 GOPATH
中,多个项目之间可以使用 ;
分隔。
如果不配置
GOPATH
,即使处于同一目录,代码之间也无法通过绝对路径相互调用。
目录结构
一个Go语言项目的目录一般包含以下三个子目录:
- src 目录:放置项目和库的源文件;
- pkg 目录:放置编译后生成的包/库的归档文件;
- bin 目录:放置编译后生成的可执行文件。
src目录
用于以包(package)的形式组织并存放 Go 源文件,这里的包与 src 下的每个子目录是一一对应。例如,若一个源文件被声明属于 log 包,那么它就应当保存在 src/log 目录中。
并不是说 src 目录下不能存放 Go 源文件,一般在测试或演示的时候也可以把 Go 源文件直接放在 src 目录下,但是这么做的话就只能声明该源文件属于 main 包了。正常开发中还是建议大家把 Go 源文件放入特定的目录中。
包是Go语言管理代码的重要机制,其作用类似于Java中的 package 和 C/C++ 的头文件。Go 源文件中第一段有效代码必须是package <包名>
的形式,如package hello
。另外需要注意的是,Go 语言会把通过
go get
命令获取到的库源文件下载到 src 目录下对应的文件夹当中。pkg目录
用于存放通过go install
命令安装某个包后的归档文件。归档文件是指那些名称以.a
结尾的文件。
该目录与GOROOT
目录(也就是Go语言的安装目录)下的 pkg 目录功能类似,区别在于这里的 pkg 目录专门用来存放项目代码的归档文件。
编译和安装项目代码的过程一般会以代码包为单位进行,比如 log 包被编译安装后,将生成一个名为log.a
的归档文件,并存放在当前项目的 pkg 目录下。bin目录
与 pkg 目录类似,在通过go install
命令完成安装后,保存由 Go 命令源文件生成的可执行文件。在类 Unix 操作系统下,这个可执行文件的名称与命令源文件的文件名相同。而在 Windows 操作系统下,这个可执行文件的名称则是命令源文件的文件名加.exe
后缀。
源文件
上面我们提到了 命令源文件 和 库源文件,它们到底是什么呢?
命令源文件:如果一个 Go 源文件被声明属于 main 包,并且该文件中包含 main 函数,则它就是命令源码文件。
命令源文件属于程序的入口,可以通过 Go 语言的go run
命令运行或者通过go build
命令生成可执行文件。库源文件:库源文件则是指存在于某个包中的普通源文件,并且库源文件中不包含 main 函数。
不管是命令源文件还是库源文件,在同一个目录下的所有源文件,其所属包的名称必须一致的。
Golang 依赖管理
最初的时候 Go 语言所依赖的所有的第三方包都放在 GOPATH
目录下面,这就导致了同一个包只能保存一个版本的代码,如果不同的项目依赖同一个第三方的包的不同版本,应该怎么解决呢?
godep(新版本以废弃)
godep 是一个 Go 语言官方提供的通过 vender
模式来管理第三方依赖的工具,类似的还有由社区维护的准官方包管理工具 dep
。
Golang 从 1.5 版本开始开始引入 vendor 模式,如果项目目录下有 vendor 目录,那么 Go 语言编译器会优先使用 vendor 内的包进行编译、测试等。
我们可以通过go get 命令来获取 godep 工具。
1
go get github.com/tools/godep
命令执行成功后会将 godep 工具的源码下载到
GOPATH
的 src 目录下对应的文件夹中,同时还会在GOPATH
的 bin 目录下生成一个名为 godep.exe 的可执行文件为了方便使用 godep 工具,我们需要将存放 godep.exe 文件的目录添加到环境变量 PATH 中。
godep 工具的基本命令
命令 作用 godep save 将依赖包的信息保存到 Godeps.json 文件中 godep go 使用保存的依赖项运行 go 工具 godep get 下载并安装指定的包 godep path 打印依赖的 GOPATH 路径 godep restore 在 GOPATH 中拉取依赖的版本 godep update 更新选定的包或 go 版本 godep diff 显示当前和以前保存的依赖项集之间的差异 godep version 查看版本信息 使用
godep help [命令名称]
可以查看命令的帮助信息
go module
go module 是 Go 语言从 1.11 版本之后官方推出的版本管理工具,并且从 Go1.13 版本开始,go module 成为了Go语言默认的依赖管理工具。
GO111MODULE
在 Go 语言 1.12 版本之前,要启用 go module 工具首先要设置环境变量 GO111MODULE
,不过在 Go 语言 1.13 及以后的版本则不再需要设置环境变量。通过 GO111MODULE
可以开启或关闭 go module 工具。
- GO111MODULE=off : 禁用 go module,编译时会从 GOPATH 和 vendor 文件夹中查找包。
- GO111MODULE=on : 启用 go module,编译时会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。
- GO111MODULE=auto(默认值),当项目在 GOPATH/src 目录之外,并且项目根目录有 go.mod 文件时,开启 go module。
Windows 下开启 GO111MODULE 的命令为
1
set GO111MODULE=on 或者 set GO111MODULE=auto
MacOS 或者 Linux 下开启 GO111MODULE 的命令为
1
export GO111MODULE=on 或者 export GO111MODULE=auto
在开启
GO111MODULE
之后就可以使用 go module 工具了,也就是说在以后的开发中就没有必要在GOPATH
中创建项目了,并且还能够很好的管理项目依赖的第三方包信息。使用 go module 的
go mod init
命令后会在当前目录下生成一个go. mod
文件,并且在编译/运行当前目录下代码或者使用 go get 命令的时候会在当前目录下生成一个go.sun
文件。- go.mod 文件记录了项目所有的依赖信息,其结构大致如下:
1
2
3
4
5
6
7
8module main.go
go 1.13
require (
github.com/astaxie/beego v1.12.0
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
)其中,module 为 go.mod 文件所属的包,require 为项目所依赖的包及版本号,indirect 表示间接引用。
- go.sum 文件则是用来记录每个依赖包的版本及哈希值,如下所示。
1
2
3
4github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=常用的 go mod 命令如下表所示:
命令 作用 go mod download 下载依赖包到本地(默认为 GOPATH/pkg/mod 目录 go mod edit 编辑 go.mod 文件 go mod graph 打印模块依赖图 go mod init 初始化当前文件夹,创建 go.mod 文件 go mod tidy 增加缺少的包,删除无用的包 go mod vendor 将依赖复制到 vendor 目录下 go mod verify 校验依赖 go mod why 解析为什么需要依赖
使用 go get 下载指定版本的依赖包
执行 go get 命令,在下载依赖包的同时还可以指定依赖包的版本。
- 运行
go get -u
命令会将项目中的包升级到最新的次要版本或者修订版本; - 运行
go get -u=patch
命令会将项目中的包升级到最新的修订版本; - 运行
go get [包名]@[版本号]
命令会下载对应包的指定版本或者将对应包升级到指定的版本。
提示:
go get [包名]@[版本号]
命令中版本号可以是x.y.z
的形式,例如go get foo@v1.2.3
,也可以是 git 上的分支或 tag,例如go get foo@master
,还可以是 git 提交时的哈希值,例如go get foo@e3702bed2
。
Golang 代理(GOPROXY)
proxy 顾名思义就是代理服务器的意思。大家都知道,国内的网络有防火墙的存在,这导致有些Go语言的第三方包我们无法直接通过 go get
命令获取。GOPROXY
是 Go 语言官方提供的一种通过中间代理商来为用户提供包下载服务的方式。要使用 GOPROXY
只需要设置环境变量 GOPROXY
即可。
目前公开的代理服务器的地址有:
- goproxy.io
- goproxy.cn:(推荐)由国内的七牛云提供。
Windows 下设置 GOPROXY 的命令为:
1
go env -w GOPROXY=https://goproxy.cn,direct
MacOS 或 Linux 下设置 GOPROXY 的命令为:
1
export GOPROXY=https://goproxy.cn
Golang 在 1.13 版本之后 GOPROXY 默认值为 https://proxy.golang.org
,在国内可能会存在下载慢或者无法访问的情况,所以十分建议大家将 GOPROXY 设置为国内的 goproxy.cn
。
Golang 程序的编译和运行
第一个 Golang 程序
创建一个 go 源文件 demo.go,文件内容如下
1 | package main // 声明 main 包 |
package(创建包)
Golang 以“包”作为管理单位,每个 Go 源文件必须先声明它所属的包,所以我们会看到每个 Go 源文件的开头都是一个 package 声明,格式如下:
1 | package name |
其中 package 是声明包名的关键字,name 为包的名字
Golang 的包与文件夹是一一对应的,它具有以下几点特性:
- 一个目录下的同级文件属于同一个包。
- 包名可以与其目录名不同。
- main 包是 Golang 程序的入口包,一个 Golang 程序必须有且仅有一个 main 包。如果一个程序没有 main 包,那么编译时将会出错,无法生成可执行文件。
import(导入包)
在包声明之后,是 import 语句,用于导入程序中所依赖的包,导入的包名使用双引号 ""
包围,格式如下:
1 | import "name" |
其中 import 是导入包的关键字,name 为所导入包的名字。
代码第 4 行导入了 fmt
包,这行代码会告诉 Go 编译器,我们需要用到 fmt
包中的函数或者变量等,fmt
包是 Golang 标准库为我们提供的,用于格式化输入输出的内容(类似于C语言中的 stdio.h 头文件),类似的还有 os 包、io 包等,后面我们会详细介绍。
另外有一点需要注意,导入的包中不能含有代码中没有使用到的包,否则 Go 编译器会报编译错误,例如 imported and not used: "xxx"
,"xxx"
表示包名。
也可以使用一个 import 关键字导入多个包,此时需要用括号( )
将包的名字包围起来,并且每个包名占用一行,也就是写成下面的样子:
1 | import( |
main 函数
代码的第 7 行创建了一个 main 函数,它是 Golang 程序的入口函数,也既是程序启动后运行的第一个函数。
main 函数只能声明在 main 包中,不能声明在其他包中,并且一个 main 包中也必须有且仅有一个 main 函数。
C/C++ 程序的入口函数也是 main(),一个 C/C++ 程序有且只能有一个 main() 函数。
main 函数是自定义函数的一种,在 Golang 中,所有函数都以关键字 func
开头的,定义格式如下所示:
1 | func 函数名 (参数列表) (返回值列表){ |
格式说明如下:
- 函数名:由字母、数字、下画线_组成,其中,函数名的第一个字母不能为数字,并且,在同一个包内,函数名称不能重名。
- 参数列表:一个参数由参数变量和参数类型组成,例如
func foo( a int, b string )
。 - 返回值列表:可以是返回值类型列表,也可以是参数列表那样变量名与类型的组合,函数有返回值时,必须在函数体中使用
return
语句返回。 - 函数体:能够被重复调用的代码片段。
注意:Golang 函数的左大括号
{
必须和函数名称在同一行,否则会报错。
打印 Hello World
代码的第 8 行 fmt.Println("Hello World!")
中,Println
是 fmt
包中的一个函数,它用来格式化输出数据,比如字符串、整数、小数等,类似于 C 语言中的 printf
函数。这里我们使用 Println
函数来打印字符串,也就是( )
里面使用""
包裹的部分。注意,Println
函数打印完成后会自动换行,ln
是 line
的缩写。
点号 .
是 Golang 运算符的一种,这里表示调用 fmt
包中的 Println
函数。
另外,代码 fmt.Println("Hello World!")
的结尾,不需要使用 ;
来作为结束符,Go 编译器会自动帮我们添加,当然,在这里加上 ;
也是可以的。
Golang 编译和运行
Golang 是编译型的静态语言(和 C 语言一样),所以在运行 Golang 程序之前,先要将其编译成二进制的可执行文件。
可以通过Go语言提供的 go build
或者 go run
命令对 Golang 程序进行编译:
- go build 命令: 可以将 Golang 程序代码编译成二进制的可执行文件,但是需要我们手动运行该二进制文件;
- go run 命令: 直接运行 Golang 程序,编译过程中会产生一个临时文件,但不会生成可执行文件,这个特点很适合用来调试程序。
go build 命令
go build
命令用来启动编译,它可以将 Golang 程序与相关依赖编译成一个可执行文件,其语法格式如下。
1 | go build fileName |
其中 fileName 为所需要的参数,可以是一个或者多个 Go 源文件名(当有多个参数时需要使用空格将两个相邻的参数隔开),也可以省略不写。
使用 go build 命令进行编译时,不同参数的执行结果也是不同的。
当参数不为空时,如果 fileName 为同一 main 包下的所有源文件名(可能有一个或者多个),编译器将生成一个与第一个 fileName 同名的可执行文件(如执行go build abc.go def.go …会生成一个 abc.exe 文件);如果 fileName 为非 main 包下的源文件名,编译器将只对该包进行语法检查,不生成可执行文件。
当参数为空时,如果当前目录下存在 main 包,则会生成一个与当前目录名同名的“目录名.exe”可执行文件(如在 hello 目录中执行go build命令时,会生成 hello.exe 文件);如果不存在 main 包,则只对当前目录下的程序源码进行语法检查,不会生成可执行文件。
使用 go build 命令对前面编写的程序进行编译,运行结果如下
1 | % go build ./demo.go |
go run 命令
除了使用 go build
命令外,Golang 还为我们提供了 go run
命令,go run
命令将编译和执行指令合二为一,会在编译之后立即执行 Golang 程序,但是不会生成可执行文件。
go run命令的语法格式如下:
1 | go run fileName |
其中 fileName 为所需要的参数,参数必须是同一 main 包下的所有源文件名,并且不能为空。
使用 go run 命令对我们前面编写的程序进行编译,运行结果如下所示:
1 | % go run ./demo.go |
可以看到第 1 行的 go run
命令执行后,直接在第 2 行输出了程序的运行结果。
除了这里所讲的,
go build
命令和go run 命令
,还有很多其他的编译方法,我们将在后面继续做详细介绍。