package

包的概念

  1. 使用包来组织管理代码,包是结构化代码的一种方式。
  2. 每个.go文件都必须归属于某一个包,每个.go文件都可能有init()函数。
  3. 包名在源文件中第一行通过关键字package指定,包名要小写
1
package fmt
  1. 每个目录下面可以有多个.go文件,这些文件只能属于同一个包,否则编译时会报错。
  2. 同一个包下的不同.go文件相互之间可以直接引用变量和函数,所以这些文件中定义的全局变量和函数不能重名。
  3. Go语言的可执行应用程序必须有main包,而且在main包中必须且只能有一个main()函数。
    • main函数是应用程序运行开始的入口,在main包中可以使用init()函数。
  4. Go语言不强制要求包的名称和文件所在目录名称相同,但是这两者最好保持相同,否则很容易引起歧义。
  5. 因为导入包的时候会使用目录名作为包的路径,而代码中使用时,却要使用包的名称。

包的初始化

  1. 可执行应用程序的初始化和执行都起始于main包。
  2. 如果main包的源代码中没有包含main()函数,则会引发构建程序错误 undefined: main.mian
  3. main()函数即没有参数,也没有返回类型,init()函数和main()函数在这一点上一样。
  4. 如果main包还导入了其他的包,那么在编译时会将它们依次导入。
    • 有时一个包会被多个包同时导入,那么它只会被导入一次(如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)
  5. 当所有被导入的包都加载完毕
    • 就会对main包中的包级常量和变量进行初始化
    • 然后执行main包中的init()函数,最后执行main()函数
    • 导入包的顺序:导入包文件,对(全局)常量和变量进行初始化,然后执行init()函数(如果函数存在的情况下)
  6. Go语言中的init()函数常用于包的初始化,该函数是Go语言的一个重要特征。
    • init函数是用于程序执行前进行包的初始化的函数,例如初始化包里的变量等。
    • 每个包可以拥有多个init函数,(同一个包下不同的.go文件中都允许定义init()函数)。
    • 同一个包中的多个init()函数的执行顺序是随机的。
    • 不同包的init()函数按照包导入的依赖关系决定该函数的执行顺序。
    • init()函数不能被其他函数调用,其在main函数执行之前,自动被调用。

包的导入

  1. Go语言程序通过导入import关键字将一组包链接在一起通过导入包为程序所使用,程序中未使用的包,不能导入进来。
  2. 导入操作会使用目录名作为包的路径而不是包名,实际应用中一般会保持两者一致。
  3. 例如标准包中定义的big包:package big;导入时语句为import "math/big"
    • 导入时源代码在$GOROOT目录下的src/math/big目录中。
    • 程序代码使用big.Int时,big指的是.go文件中定义的包名称。
  4. 当导入多个包时,一般按照字母顺序排列包名称。
  5. 为避免名称冲突,同一包中所有对象的标识符必须唯一,但是相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。
1
2
3
package main

import "context" // 加载context包
  1. 导入多个包的常见的方式。
1
2
3
4
import (
    "fmt"
    "net/http"
)
1
2
// 调用导入的包函数的一般方式
fmt.Println("hello world!")
  1. 三种特殊的包导入
    1. import (. "fmt")
      • 点操作含义是包导入之后,在调用这个包的函数时,可以省略前缀的包名。
      • fmt.Println("hello world")可以写成Println("hello world")
    2. import (f "fmt")
      • 别名操作就是可以把包命名成另外一个容易记住的名字。
      • fmt.Println("hello world")可以写成f.Println("hello world")
    3. import (_ "fmt")
      • _操作是引入某个包,但不直接使用包里的函数,而是调用该包里面的init函数。
      • 有时在开发中由于某种原因某个原来导入的包现在不在使用,也可以采用这种方式处理。
1
2
3
4
import (
    _ "fmt"
    _ "github.com/go-sql-driver/mysql"
)

标准库

  1. 在Go语言的安装目录里包含标准库的各种包,在$GOROOT/src中可以看到源码,可以根据情况自行重新编译。
  2. 访问https://golang.google.cn/pkg/#stdlib了解更多详情。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
unsafe: 包含了一些打破Gp语言“类型安全”的命令,一边的程序中不会被调用,可用在C++程序的调试中
syscall-os-os/exec:
    os: 提供给我们一个平台无关性的操作系统功能接口,采用类似UNIX设计,隐藏了不同操作系统间的差异,让不同的文件系统和操作系统对象表现一致
    os/exec: 提供运行外部操作系统命令和程序方式
    syscall: 底层的外部包,提供了操作系统底层调用的基本接口
    archive/tar 和 /zip-compress: 压缩(解压缩)文件功能
fmt-io-bufio-path/filepath-flag:
    fmt: 提供格式化输入输出的功能
    io: 提供基本输入输出功能,大多数是围绕系统功能的封装
    bufio: 缓冲输入输出功能的封装
    path/filepath: 用来操作在当前系统中的目标文件名路径
    flag: 对命令行参数的操作
strings-strconv-unicode-regexp-bytes:
    strings: 提供对字符的操作
    strconv: 提供将字符串转换为基础类型的功能
    unicode: 为unicode型的字符串提供特殊的功能
    regexp: 正则表达式功能
    bytes: 提供对字符型分片的操作
math-math/cmath-math/big-math/rand-sort:
    math: 基本的数学函数
    math/cmath: 对复数的操作
    math/rand: 伪随机数生成
    sort: 为数组排序和自定义集合
    math/big: 大数的实现和计算
container-/list-ring-heap: 实现对集合的操作
    list: 双链表
    ring: 环形链表
time-log:
    time: 日期和时间的基本操作
    log: 记录程序运行时产生的日志
encoding/JSON-encoding/xml-text/template:
    encoding/json: 读取并解码和写入并编码json数据
    encoding/xml: 简单的XML1.0解析器
    text/template: 生成像HTML一样的数据与文本混合的数据驱动模板
net-net/http-html:
    net: 网络数据的基本操作
    http: 提供了一个可扩展的HTTP服务器和客户端,解析HTTP请求和回复
    html: HTML5解析器
runtime: Go程序运行时的交互操作,例如垃圾回收和协程创建
reflect: 实现通过程序运行时反射,让程序操作任意类型的变量

github安装包

  1. 如果想安装github上的项目到本地计算机,可打开终端执行:go get -u github.com/ffhelicopter/tmm
  2. 现在这台计算机上的其他Go引用程序也可以通过导入路径github.com/ffhelicopter/tmm来使用。
    • import "github.com/ffhelicopter/tmm"
  3. Go对包的版本管理不是很友好,至少在go1.10前是如此,不过现在第三方项目做得不错.
    • 有兴趣的读者可以了解一下(glidegodepgovendor).
    • Gomodules是1.11版本解决“包依赖管理”的实验性技术方案,后面章节学习。

导入外部安装包

  1. 如果要在应用中使用一个或多个外部包,可以使用go install在本地计算机上安装它们。
  2. go install是自动包安装工具,如需要将包安装到本地,它会从远端仓库下载包,完成检出、编译和安装。
  3. 包安装的先决条件是要自动处理包自身依赖关系,被依赖的包也会安装到子目录下。
  4. 如果想使用https://github.com/gocolly/colly这种托管在Google CodeGitHubLaunchpad等代码网站上的包。
    • 也可以通过如下命令安装:go install github.com/gocolly/colly
  5. 将一个名为github.com/gocolly/colly的包安装在$GoPATH/pkg/目录下。
  6. go install/build用来编译包和依赖的包,区别如下:
    • go build只对main包有效,在当前目录编译生成一个可执行的二进制文件,依赖包生成的静态库文件放在$GOOATH/pkg
    • go install一般生成静态文件,放在$GOPATH/pkg目录下,文件扩展名为a。
    • 如果为main包,运行go build则会在$GOPATH/bin生成一个可执行的二进制文件。

使用Godoc

  1. 在程序中一般都会使用注释,按照一定规则,Godoc工具会收集这些注释并产生一个技术文档。
  2. Godoc会为每个文件生成一系列的网页。
  3. 访问Godoc文档的方法是:
    • 命令行下进入目录并输入命令:godoc -http=:6060 -goroot="."
    • 然后在浏览器中打开地址:http://localhost:6060
    • 此时会看到本地的Godoc页面,从左到右一次显示出目录中的包。
    • 或者直接在浏览器中打开地址http://localhost:6060/pkg/go42/chapter-4/4.2/1/

Go程序的编译

  1. Go语言中,和编译有关的命令主要是go rungo buildgo install这三个命令。
  2. go run只能作用于main包文件。
    • 先运行compile命令生成.a文件。
    • 然后链接命令生成最终可执行文件并运行程序,此过程中产生的是临时文件。
    • go run退出前会删除这些临时文件(含.a文件和可执行文件)。
    • 最后直接在命令行输出程序执行结果。
    • go run命令在第二次执行的时候,如果发现导入的代码包没有发生变化。
    • 则不会再次编译这个导入的代码包,而是直接进行链接生成最终可执行文件并运行程序。
  3. go install用于编译并安装指定的代码包及它们的依赖包。
    • 并且将编译后生成的可执行文件放到bin目录下($GOPATH/bin)。
    • 编译后的包文件放到当前工作区的pkg的平台相关目录下。
  4. go build用于编译指定的代码包以及它们的依赖包。
    • 如果用来编译非main包的源码,则只做检查性的编译,而不会输出任何结果文件。
    • 如果是一个可执行程序的源码(即main包),过程与go run大体相同,只是会在当前目录生成一个可执行文件。
  5. 使用go build是有一个地方注意:
    • 对外发布编译文件时如果不希望被人看到源代码,可使用go build -ldflags命令。
    • 设置参数【-ldflags "-w -s"】再编译发布,这样使用gdb调试时无法看到源代码。

GO111MODULE

  1. Go 1.11新增了对模块的支持,希望借此解决“包依赖管理”问题。
  2. 可以通过设置环境变量GO111MODULE来开启或关闭模块支持,它有三个可选值:offonauto,默认值是auto
    • GO111MODULE = off
      • 无模块支持,go会从GOPATHvendor文件夹寻找包。
    • GO111MODEL = on
      • 模块支持,go会从GOPATHvendor文件夹,值根据go.mod下载依赖。
    • GO111MODEL = auto
      • $GOPATH/src外面且根目录有go.mod文件时,开启模块支持。