1.3 包及作用域
包是数据和函数的集合,Go语言使用包实现程序的模块化。使用package关键字定义一个包时,一般约定使用小写字母对包命名。每个包下面可以有一个或者多个以.go结尾的文件,不过每个文件开头的package语句都是相同的,以此定义同一个包。
一个包意味着一个独立的命名空间,比如p1包里有个Num变量,而在p2包也有一个Num变量,这两个变量可能是完全不同的,也没有任何关系,我们使用的时候要分别导入,如p1.Num和p2.Num。
需要注意的是,并不是一个包内所有的函数、变量都可以被外部程序调用。Go语言通过函数、变量首字母的大小写来控制可见性,首字母大写的才是包外可见的,首字母小写的是包内私有的。
说明
Go语言中标识符的命名规则为驼峰式,比如MakeSlice,即命名为多个单词时,各单词的首字母均大写。
在程序中需要用到某个包的时候,通常会使用import关键字进行导入,比如:
import "fmt"
导入之后,就可以通过包来调用其对外可见(或称之为可导出)的函数或变量了。比如:
fmt.Println("")
包名并不是全局唯一的,它仅仅是在上一层目录下唯一。比如,虽然ch01/learn包和ch02/learn包的名字都是learn,但它们是两个不同的learn包,不过导入之后都是learn包,所以Go语言代码在导入的时候会取别名,比如:
import f "fmt"
这样就可以通过f来调用fmt内的函数了。
对每个包的注释,只需要出现在一个文件里就可以了,一般在package命令之前书写本包的说明,紧挨着该命令。使用go doc可以生成说明文档。
注意
如果包的注释说明比较多,超过了三行,则可以考虑在该包下单独创建doc.go文件记录说明。建议在一个包下有多个文件时采用这种方式。
关于包,此处还要介绍一个重要的函数init。该函数在包初始化的时候调用,且仅仅允许初始化的时候自动执行一次。该函数最常见的应用场景就是初始化数据库连接池,后面会介绍。
Go语言程序的执行是从main包内的main函数开始的,如果main中导入了其他包,则会按照导入main包的顺序(具体的顺序依赖于具体实现,一般是根据包的路径字母的顺序来确定)进行初始化。每个包先初始化常量,再初始化变量,然后执行init函数,init函数执行完以后开始执行main包下的main函数。
注意
一个包内可以有多个init函数,初始化的时候会按照出现的顺序执行。
在介绍完包以后,再来探讨一下作用域。我们定义的变量,其有效范围有多大呢?也就是说作用域如何呢?在包内定义的变量在整个包内可见。在方法内声明的变量仅在这个方法块(指方法的两个大括号内)内有作用,在方法块外是不可见的。我们可以理解为在大括号内声明的变量,在大括号外是不可见的。