彻底理解 Go 的包概念
包的概念
包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
如同其它一些编程语言中的类库或命名空间的概念,每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。
你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main 。
package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。package main包下可以有多个文件,但所有文件中只能有一个main()方法,main()方法代表程序入口。
一个应用程序可以包含不同的包,而且即使你只使用 main 包也不必把所有的代码都写在一个巨大的文件里:你可以用一些较小的文件,并且在每个文件非注释的第一行都使用 package main 来指明这些文件都属于 main 包。如果你打算编译包名不是为 main 的源文件,如 pack1,编译后产生的对象文件将会是 pack1.a 而不是可执行程序。另外要注意的是,所有的包名都应该使用小写字母。当然,main包是不能在其他文档import的,编译器会报错:
import "xx/xx" is a program, not an importable package。
简单地说,在含有mian包的目录下,你可以写多个文件,每个文件非注释的第一行都使用 package main 来指明这些文件都属于这个应用的 main 包,只有一个文件能有mian() 方法,也就是应用程序的入口。main包不是必须的,只有在可执行的应用程序中需要。
包的导入
一个 Go 程序是通过 import 关键字将一组包链接在一起。
import “fmt” 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。包名被封闭在半角双引号 “” 中。如果你打算从已编译的包中导入并加载公开声明的方法,不需要插入已编译包的源代码。
import 其实是导入目录,而不是定义的package名字,虽然我们一般都会保持一致,但其实是可以随便定义目录名,只是使用时会很容易混乱,不建议这么做。
例如:package big ,我们import “math/big” ,其实是在src中的src/math目录。在代码中使用big.Int时,big指的才是Go文件中定义的package名字。
当你导入多个包时,最好按照字母顺序排列包名,这样做更加清晰易读。
如果包名不是以 ./ ,如 “fmt” 或者 “container/list”,则 Go 会在全局文件进行查找;如果包名以 ./ 开头,则 Go 会在相对目录中查找。
导入包即等同于包含了这个包的所有的代码对象。
除了符号 _,包中所有代码对象的标识符必须是唯一的,以避免名称冲突。但是相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。
导入包的路径的几种情况:
-
第一种方式相对路径
`import "./module" //当前文件同一目录的module目录, 此方式没什么用容易出错,不建议用`
-
第二种方式绝对路径
import "LearnGo/init" //加载Gopath/src/LearnGo/init模块,一般建议这样使用""
导入多个包的常见的方式是:
import (
"fmt"
"net/http"
)
包的分级声明和初始化
你可以在使用 import 导入包之后定义或声明 0 个或多个常量(const)、变量(var)和类型(type),这些对象的作用域都是全局的(在本包范围内),所以可以被本包中所有的函数调用,然后声明一个或多个函数(func)。
如果存在 init 函数的话,则对该函数进行定义(这是一个特殊的函数,每个含有该函数的包都会首先执行这个函数)。
程序开始执行并完成初始化后,第一个调用(程序的入口点)的函数是 main.main()(如果有 init() 函数则会先执行该函数)。
如果你的 main 包的源代码没有包含 main 函数,则会引发构建错误 undefined: main.main。main 函数既没有参数,也没有返回类型,这一点上 init 函数和 main 函数一样。
main函数一旦返回就表示程序已成功执行并立即退出。
Go 程序的执行(程序启动)顺序如下:\
程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。
Go语言中init函数用于包(package)的初始化,该函数是Go语言的一个重要特性,有下面的特征:
- init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
- 每个包可以拥有多个init函数
- 包的每个源文件也可以拥有多个init函数
- 同一个包中多个init函数的执行顺序Go语言没有明确的定义(说明)
- 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
- init函数不能被其他函数调用,而是在main函数执行之前,自动被调用
关于包应该是有一个规范的:
例:
src/域名或名称/账户名/项目名称
这样就很好的解决了引入的问题