一、GO上手
1.1 运行环境
看到这篇文章,希望你的go环境已经配置好了。如果没有,也没关系,这里给出一个在线Go运行环境。
1.2 编写运行hello,world
让我们以输出hello world!来上手Go吧!
1.2.1 创建hello.go文件,编写代码
创将一个hello.go文件,写入以下代码:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
1.2.2 运行hello.go
- 现在我们已经编写好源码文件呢?如何运行呢?
go run hello.go,运行go程序。 细心点会发现,文件目录下没有生成可执行文件,例如hello.exe等。go run 是 go语言的标准命令之一。
-
go run 命令做了那些工作呢,使我们的.go源文件运行起来呢?
Go语言是编译型语言,区别于解释型语言。编译型语言和解释型语言的最大的区别是前者生成可执行文件,后者不生成。常见的编译型语言还有C、C++,常见的解释型语言有python、JavaScript等。
由于Go语言是编译型语言,所以go run指令可以看成分两步完成。第一,编译链接生成二进制可执行程序。第二步,执行。
- go run 命令可以简单的分两步:编译、运行。
- 对于编译,go语言同样为我们提供了一个命令,
go build。 - 编译:
go build hello.go运行后会生成可执行文件 hello.exe。如果想更深入的了解go build的命令,可以用go help build查看go build命令的使用详解。 - 执行:命令行输入
./hello.exe运行程序
-
详细的编译过程,我们可以借助指令
go build -n hello.go。-n的意思是打印go build命令编译文件时用到的命令,但并不执行它们。
1.3 go build详细过程
输入命令:go build -n hello.go,查看go build命令的执行过程。
过程看起来很乱,仔细观看下来可以发现主要由几部分组成,分别是:
- 创建临时目录,mkdir -p $WORK/b001/;
- 查找依赖信息,cat >$WORK/b001/importcfg << ...;
- 执行源代码编译,... compile ...;
- 收集链接库文件,cat >$WORK/b001/importcfg.link << ...;
- 生成可执行文件,... link.exe -o ...;
- 移动可执行文件,mv $WORK\b001\exe\a.out.exe hello.exe;
注意:其中$WORK 是指配置的$GOPATH路径。查看关于go的环境变量,可以用命令 go env 。
整个go build过程如下面流程:
确实和C++编译过程很像,毕竟Go和C++都是编译型语言。
整个编译、链接过程都是在临时目录中进行的,最后将生成的可执行文件移动到源文件同级目录下。
1.4 go run详细过程
输入命令:go run -n hello.go,查看go run命令的执行过程。
仔细观看下来可以发现也是主要由几部分组成,分别是:
- 创建临时目录,mkdir -p $WORK/b001/;
- 查找依赖信息,cat >$WORK/b001/importcfg << ...;
- 执行源代码编译,... compile ...;
- 收集链接库文件,cat >$WORK/b001/importcfg.link << ...;
- 生成可执行文件,... link.exe -o ...;
- 执行可执行文件,$WORK\b001\exe\hello.exe
1.4.1 go run 不“生成”可执行文件
go run与go build 命令执行过程的区别,主要是在最后一步。go build在最后一步将可执行文件hello.exe从临时目录移动到源文件的同级目录,而go run命令不移动可执行文件,而是直接执行hello.exe。
这也就使得go run命令生成的可执行文件hello.exe,在运行后,会随临时目录$WORK/b001/删除。
1.4.2 go run与go build 的关系
宏观上,两条指令存在以下等价关系:
go run hello.go // 编译运行
// 等价于以下命令
go build hello.go // 编译
./hello.exe // 运行
rm ./hello.exe // 删除可执行文件
二、详解Go语言文件
以上面的hello.go程序为例:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
大致可分为三部分:
- 定义包名。package
- 导入包。 import
- 编写函数。
2.1 定义包
2.1.1 包的概念
包(package)是Go语言实现代码复用的重要手段。包的存在可以让工程师只关心包中有用的函数,而其他大多数的函数都不需要去关心。
Go语言自带了100多个标准包,常用的官方标准包有fmt、net/http、io、log等等。
2.1.2 包和go源文件
- 所有的
.go文件都会声明包名。格式为package 包名 - 包中可以有多个
.go源文件 - 任意的
.go源文件只能声明在一个包下。
2.1.3 包名和目录名
windows系统中有目录、文件的概念,Go语言又有包的概念,有何联系呢?
- 包名声明在
.go的源文件中。.go的源文件存放在目录中。 - 同一目录下可以存放多个
.go源文件,这些源文件声明的包名是相同的,即同一目录下的go源文件,属于同一个包。 - 一般来说,我们常用目录名来定义包名。但用package声明包名时,也不一定非要和源文件所处的目录名相同。
2.1.4 包的命名规范
- 包名尽可能与它所在的目录名保持一致,不要与标准包名冲突。
- 包名一般为小写。
- 简短而有意义,避免没有意义的包名。
- 避免不必要的包名冲突。不同目录中,可能含有相同的包名。
2.1.5 main包
一个可执行的go项目,一定会有一个main包,即一定存在个.go文件声明的包名为package main。并且,main包,一定定义了一个名为main的函数,main函数,是整个go语言项目的入口地址。和C/C++的main函数的作用很相似。
2.2 导包
分两类包:官方标准包、自定义包
官方的包存放在:$GOROOT/src目录下
自定义的包:一般存放在$GOPATH/src目录下。当然,自定义包可以存放在任意位置,关键是导包的时候能找到此包。
导包借助import关键字。导包,就是给出包的路径,然go语言编译的时候能够找到包,并使用包中的函数。
注意:Go语言导入的包必须在程序内用到,否则编译无法通过。
2.2.1 两种包管理方式
GOPATH方式
旧版包管理方式。Go1.11之前常使用。就是配置一个GO工作目录,路径为$GOPATH。自定义的包要放在$GOPATH/src下。
GO MOD方式
新版包管理方式。Go1.11推出。对包的创建位置没有限制。
模式切换
通过命令设置环境变量即可。
// 使用旧版 $GOPATH 方式
go env -w GO111MODULE=off
// 使用新版 GO MOD 方式
go env -w GO111MODULE=on
// 两种方式,视情况选择一种
go env -w GO111MODULE=auto
// // 何种情况选择 GO MOD
// 1. 项目不在 $GOPATH 目录下
// 2. 项目中含有go.mod文件
包搜索方式
import "fmt" 是如何根据fmt找到包的呢?
GOPATH方式
- 搜索
$GOROOT/src目录,查找名为fmt的包。找到,则结束。否则进行下一步。 - 继续在
$GOPATH/src目录下找。如果还找不到,则报错。
GO MOD方式
首先需要go mod init 模块名初始化,此时会创建一个go.mod文件,go mod模式正式开启。
- 搜索
$GOROOT/src目录,查找名为fmt的包。找到,则结束。否则进行下一步。 - 继续寻找。此时包的路径为 “模块名/包名”。
-
// hello 模块 hello ├── huft ├── mathclass └── test.go ├── demo1.go hello.go go.mod // 此时你在hello.go的导包路径 import ( "hello/huft" "hello/huft/mathclass" )
注意:$GoROOT和$GOPATH 可通过go env 命令查看。
2.2.2 引用格式
I、标准引用
import "fmt"
import (
"fmt"
"log"
)
II、别名引入
import F "fmt"
import (
F "fmt"
l "log"
)
给包fmt起了别名F,用F. 代替fmt.前缀。
注意:别名仅在当前文件中有效,即便是同一个包下的其他11文件导入相同包也不可以直接使用别名,除非其他文件导入时取了同样的别名。
III、省略引用当时
import . "fmt"
命名空间合并,fmt包中的可导出元素不加fmt.前缀直接访问。
IV、仅执行包初始化init函数
import _ "fmt"
init初始化函数是编译的时候调用的,开发人员不可以在代码中调用。比如,fmt.Println可以直接用Println代替。
2.2.3 包中元素的访问权限
包中成员以名称首字母大小写决定访问权限。
- 首字母大写,可被包外访问;
- 首字母小写,仅包内成员可以访问;