包
任何包系统设计的目的都是为了简化大型程序的设计和维护工作, 通过将一组相关的特性放进一个独立的单元以便于理解和更新, 在每个单元更新的同时保持和程序中其它单元的相对独立性Golang
编译速度快的原因主要有
- 所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系。
- 禁止包的环状依赖, 因为没有循环依赖, 包的依赖关系形成一个有向无环图, 每个包可以被独立编译, 而且很可能是被并发编译。
- 编译后包的目标文件不仅仅记录包本身的导出信息, 目标文件同时还记录了包的依赖关系。 因此, 在编译一个包的时候, 编译器只需要读取每个直接导入包的目标文件, 而不需要遍历所有依赖的的文件
导入路径
每个包是由一个全局唯一的字符串所标识的导入路径定位。 出现在import语句中的导入路径也是字符串。 Go语言的规范并没有指明包的导入路径字符串的具体含义, 导入路径的具体含义是由构建工具来解释的
为了避免冲突, 所有非标准库包的导入路径建议以所在组织的互联网域名为前缀; 而且这样也有利于包的检索。 例如, 上面的import
语句导入了Go
团队维护的HTML
解析器和一个流行的第三方维护的MySQL
驱动包声明
在每个Go
语音源文件的开头都必须有包声明语句。 包声明语句的主要目的是确定当前包被其它包导入时默认的标识符( 也称为包名),通常来说, 默认的包名就是包导入路径名的最后一段, 因此即使两个包的导入路径不同, 它们依然可能有一个相同的包名例如,
math/rand
包的每个源文件的开头都包含package rand
包声明语句, 所以当你导入这个包, 你就可以用rand.Int
、rand.Float64
类似的方式访问包的成员
关于默认包名一般采用导入路径名的最后一段的约定也有三种例外情况
- 包对应一个可执行程序, 也就是
main
包, 这时候main
包本身的导入路径是无关紧要的。 名字为main
的包是给go build
构建命令一个信息, 这个包编译完之后必须调用连接器生成一个可执行程序 - 包所在的目录中可能有一些文件名是以
test.go
为后缀的Go
源文件,并且这些源文件声明的包名也是以e为后缀名的。 这种目录可以包含两种包: 一种普通包, 加一种则是测试的外部扩展包。所有以_test
为后缀包名的测试外部扩展包都由go test
命令独立编译, 普通包和测试的外部扩展包是相互独立的。 测试的外部扩展包一般用来避免测试代码中的循环导入依赖 - 一些依赖版本号的管理工具会在导入路径后追加版本号信息, 例如
"gopkg.in/yaml.v2"
。 这种情况下包的名字并不包含版本号后缀, 而是yaml
导入声明
可以在一个Go语言源文件包声明语句之后, 其它非导入声明语句之前, 包含零到多个导入包声明语句
也可以通过添加空格来分组
如果我们想同时导入两个有着名字相同的包, 例如math/rand包和crypto/rand包, 那么导入声明必须至少为一个同名包指定一个新的包名以避免冲突。 这叫做导入包的重命名,导入包的重命名只影响当前的源文件, 同时可以将名称较长的包名重命名为一个简单的名字,但是要注意风格统一
匿名导入
只是导入一个包而并不使用导入的包将会导致一个编译错误。 但是有时候只是想利用导入包而产生的副作用: 它会计算包级变量的初始化表达式和执行导入包的init
初始化函数
可以用下划线_
来重命名导入的包。 像往常一样, 下划线_
为空白标识符, 并不能被访问包的命名
当创建一个包, 一般要用短小的包名, 但也不能太短导致难以理解。 标准库中最常用的包有bufio
、bytes
、flag
、fmt
、http
、io
、json
、os
、sort
、sync
和time
等要尽量避免包名使用可能被经常用于局部变量的名字
包名一般采用单数的形式。 但是标准库的bytes
、errors
和strings
使用了复数形式, 这是为了避免和预定义的类型冲突, 同样还有go/types
是为了避免和type关键字冲突
要避免包名有其它的含义,如temp
设计一个包的时候, 需要考虑包名和成员名两个部分如何很好地配合
如stings
包
调用:strings.Index
strings.Replacer
...