上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
10. 包
包(package)由同一目录下的多个源文件构成。
- 含
.go
、.c
、.s
等文件。 - 在
.go
头部通过package
定义所属包。
- 源文件必须是
UTF-8
格式。
- 包是成员作用域边界,包内成员可相互访问。
- 名称首字母大写为 导出成员 (exported),可外部访问。
包名可与目录名不同,通常小写单数模式。同一目录下源文件必须使用相同包名。
package mylib // 包名通常与目录一致。 func ddd(x, y int) int { // 私有成员。 return x + y } func Hello() { // 导出成员,可外部访问。 println("Hello, World!") }
另有几个特殊含义的包名:
main
: 用户可执行文件入口包。all
: 所有包,包括标准库和依赖项。std
: 标准库。cmd
: 工具链。
documentation
: 文档。(工具链忽略,无法导入)
为命令行提供包名为参数时,有以下几种方式。
- 直接提供包名,如标准库。(
go list math
) - 以
.
或..
开始的相对路径。(go build ./mylib
) - 用
...
作为通配符,表示任意字符串。(go build ./...
)
提示:
net/...
表示net
和其所有子包,但不包括vendor
。除非特别指定,如
./vendor/...
,./mylib/vendor/...
。
访问权限
同一包内不同源文件的成员可相互访问,但只有首字母大写为导出成员。
此规则适用于全局变量、全局常量、函数、类型、字段和方法等。
// test/mylib/add.go package mylib func add(x, y int) int { return x + y }
// test/main.go package main import ( "test/mylib" ) func main() { z := mylib.add(1, 2) ~~~ undefined: mylib.add println(z) }
某些时候,需临时访问私有成员,可参考如下手段。
// test/mylib/data.go package mylib type data struct { x int y int } // --- tmp ------------ func NewData() *data { return &data{1, 2} }
package main import ( "fmt" "unsafe" "test/mylib" ) func main() { p := mylib.NewData() // p.x = 100 // ~~~ p.x undefined d := (*struct{ _ int y int // 仅需要访问的字段,注意对齐。 })(unsafe.Pointer(p)) d.y = 100 fmt.Println(*d) // {1, 100} }
还可用别名。
// test/mylib/data.go package mylib type data struct { x int y int } func (d *data) test() { println(d.x, d.y) } // --- tmp ------------ type DataTmp = data func (d *DataTmp) SetY(y int) { d.y = y } func (d *DataTmp) Test() { d.test() }
// test/main.go package main import ( "test/mylib" ) func main() { d := mylib.DataTmp{} d.SetY(100) d.Test() }
临时提升访问权限,应避免直接修改原目标。
可将临时代码单独放在 _tmp.go
文件内,随后直接删除即可。
初始化
包内任意 .go
文件内都可定义一到多个 init
初始化函数。初始化函数由编译器生成代码自动执行(仅执行一次),不能被其他代码调用。
所有初始化函数被编译器整合到一个特殊数据结构内。在程序启动初始化阶段,在同一 goroutine 内依次执行。详情参考《源码剖析》。
包内全局变量按照依赖关系,逐步初始化(或零值)。每个初始化函数只完成一组相关逻辑,且相互之间不应该有执行次序依赖。
自 1.21 起,包初始化次序有了正式规范,但依然不建议对其有所依赖。
package main var x = 100 func init() { println("a", x) // a 100 } func init() { println("b") } func main() { // init() // ~~~~ undefined: init }
可使用 GODEBUG
查看初始化执行情况。
$ GODEBUG=inittrace=1 ./test init internal/bytealg @0.008 ms, 0.008 ms clock, 0 bytes, 0 allocs init runtime @0.040 ms, 0.048 ms clock, 0 bytes, 0 allocs init main @0.31 ms, 0.014 ms clock, 0 bytes, 0 allocs # 包名 启动时刻 执行耗时 堆内存分配数量和次数。
内部包
代码重构时,将一些内部模块陆续分离出来,以独立包形式维护。此时,首字母大小写访问控制就过于粗旷。因为我们希望其 导出成员 仅限特定范围内访问,而不是向所有用户公开。
内部包 (internal package)机制相当于增加了新访问权限控制:
- 内部包(含自身)只能被其父目录(含所有层次子目录)访问。
- 内部包私有成员,依然只能在自己包内访问。
test/ | +-- main.go | +-- mylib/ # 内部包 internal、a、b 的导出成员仅能 | # 被 mylib、mylib/x、mylib/x/y 访问。 | +-- internal/ # 内部包之间可相互访问。 | | # 可导入外部包。 | +-- a/ | | | +-- b/ | +-- x/ | +-- y/
package main import ( "test/mylib" "test/mylib/x" "test/mylib/x/y" // "test/mylib/internal/a" // ~~~~~~~~~~~~~~~~~~~~~~ use of internal package not allowed ) func main() { }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论