前言
Hey,大家好呀,我是星期八,咱们原来写的代码,都是缩在一块的,久而久之咱们可能都能感觉到冗余。
所以今天就来学一下包这个东西,将咱们的代码拆分一下。
包
包可以理解为存放多个.go的文件夹但是这个文件夹下面的第一行的package后面跟的不再是main了而是文件名,就像这样。
目录
clac和main.go文件是同级的。
可以看到clac文件夹下的add.go第一行不再是main而是文件夹名 clac同理sub.go第一行也是。
这个只是单独的解释包的定义方式没有实际意义。
包的注意事项
如果这个文件夹要当包使用文件夹名中不能包含_。
导入包
上面我们知道了包是如何定义的。
并且在和main.go同级的项目目录下建了一个clac包。
在clac包下有俩个文件一个是add.go一个是sub.go两个文件夹分别都有对应的方法。问题来了???
那我们如何在main.go中使用上述建立的包调用里面的方法呢?
这就是要导入它们了。
示例代码
package main
import (
"a3_course/clac"
)
func main() {
clac.Add()
clac.Sub()
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
执行结果

可以看到在main.go中成功调用了clac包中的代码。
注:导入包只需要导入到文件夹即可就可以调用下面所有的方法和属性不再需要包名.xx.go这种方式。
如上述导入calc不管calc下面有几个.go文件里面的方法和属性都可以随便调用。
导入包注意事项
上述我是直接通过
import (
"a3_course/clac"
)
- 1.
- 2.
- 3.
这种方式导入包的但是在你们那可能不太行。
因为我使用的是go mod所以通过项目目录/包名导入。
如果你没有使用go mod是传统的方式那么导入包需要从GOPATH/src进行导入这里不举例了。
如果还不会使用go mod记得爬楼看以往文章,上面有教程,一篇文章教会你如何使用Go语言Modules,记得要拥抱未来噢。
可见性
可见性在其他语言中有的叫私有成员之类的称呼在Go中就叫可见性。
Go中可见性很简单不管是变量还是函数还是结构体。
首字母大写在哪都能访问。
首字母小写只能在当前包使用。
示例
package clac
import (
"fmt"
)
//这是一个公开的变量
var Name = "张三"
//这是一个私有变量,只能在 clac 包中访问
var age = 18
//这是一个公开的函数
func Add() {
fmt.Println("我是做加法的...")
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
main.go文件
func main() {
clac.Add()
clac.Sub()
fmt.Println(clac.Name)
//clac中的age是小写开头,属于私有变量,所以其他包不能访问
//fmt.Println(clac.age) // cannot refer to unexported name clac.age
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
访问私有变量报错信息。

结构体可见性的问题
我们知道结构体是有字段的但是你想过结构体的字段大小写问题吗?
type Student struct {
Name string
age int
}
//age是小写开头
- 1.
- 2.
- 3.
- 4.
- 5.
结构体名开头是不是大写影响的主要是在其他包里面的调用权限问题。
结构体字段开头是不是大写主要影响的是调用里面字段的问题一个明显的问题就是序列化。
更多结构体的相关文章,可前往:Go语言基础之结构体(春日篇)
示例代码
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string
age int
}
func main() {
var s1 = Student{
Name: "张三",
age: 18,
}
serializeBytes,err:=json.Marshal(s1)
if err != nil {
fmt.Println("序列化失败")
}
serializeStr := string(serializeBytes)
fmt.Println(serializeStr)
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
执行结果

会发现执行结果少了一个age。
这是因为age小写开头属于私有变量。
但是json.Marshal(s1)这个已经属于其他包了所以访问不到age。
包别名
我们在导入包时其实还可以自定义包名就像Python中的 from xx import xx as yy。
示例代码
package main
//给 clac 包起别名
import cl "a3_course/clac"
func main() {
cl.Add()
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
执行结果

匿名导入包
匿名导入包就是相当于不用这个包里面的东西。
可能有人就会问了那不用包里面的东西,那还导入作甚呢?
嗯...这个匿名导入包主要要跟包的一个init方法有关系咱们先继续看。
匿名导入包示例代码
package main
//前面有个 _ 就表示是匿名导入
import _ "a3_course/clac"
func main() {
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
包的init方法
其实每次导入其他包的时候都会执行包的init方法。
示例
//clac/add.go
package clac
import "fmt"
func init() {
fmt.Println("clac/add.go/init")
}
func Sub() {
fmt.Println("我是做减法的...")
}
//clac/sub.go
package clac
import "fmt"
func init() {
fmt.Println("clac/sub.go/init")
}
func Sub() {
fmt.Println("我是做减法的...")
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
main.go
package main
import _ "a3_course/clac"
func main() {
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
执行结果

可以发现我虽然是匿名导入包但是仍然还是执行了add.go和sub.go下的init方法。
这就说明了一个问题导入一个包会执行这个包下面所有的init方法不管下面有几个.go文件。
注:包的init方法不能写参数也不能有返回值init方法只能在导入时调用不能主动调用。
init方法比main方法更提前一步执行。
多个包嵌套导入时init方法执行的顺序
代码较多就直接如图所示了:

这意思是main.go导入了other包other包导入了inner包 ,套娃。
先看一下执行结果

执行结果是inner的init方法先执行然后是ohter的init方法。
其实本质是碰到import就执行被导入包的init方法。
它的图应该是这样子的。

意思是main导入了ohter那就执行other的init方法。
但是在导入ohter时发现other导入了inner那就执行inner的init方法。
总结
上述我们学习了Go基础之包,学习了如何创建包,组织包,导入包的注意事项。包的权限问题(大写开头所有可见),匿名导入包,init方法,多个init方法注意事项。
可能有点不太好理解,但是还是要坚持,一定要多敲两次。