5.6.2. init()和main()函数
Go语言有两个特殊作用的保留函数:init()(在所有包中)和main()(只在main包中),这两个函数必须始终定义且不接受任何参数,也没有任何返回值。一个包中可以有多个init()函数。
Go语言会自动调用所有包中的init()函数和main包中的main()函数,因此我们不必显式调用这些函数。对于程序来说init()函数是可选的,但main包中的main()函数有且只能有一个。
Go语言的程序其初始化和执行总是从main包开始。如果使用了import,则会依次导入每个包。包只被导入一次,即使是多个包都导入了同一个包。(例如,多个包可能会同时导入fmt包,但在第一次导入fmt包之后,该包将不会被再次导入,因为已没有必要。)在导入包时,如果被导入的包又导入了其它的包,则会首先执行这些其它包的导入,然后,创建包级别的常量和变量,接着如果包有init()函数的话,就调用该函数,最后,main包完成所有包的导入(包括包中又导入的其它包,以此类推),这时将创建main包中的常量和变量,并调用main包的init()和main()函数,程序执行正常开始。详见下图。
图5.1 程序启动顺序
init()函数可以使用go关键字修饰,但请注意,该函数依旧会在调用main.main()之前运行,且不能依赖于在main()函数中创建的任何内容。
让我们演示一个例子来看下这些函数在实际中是如何执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import ( "os" // ... "path/filepath" ) var britishAmerican = "british-american.txt" func init() { dir, _ := filepath.Split(os.Args[0]) britishAmerican = filepath.Join(dir, britishAmerican) } func main() { // ... } |
Go语言的程序从main包开始执行,因为有import导入了包,所以会先按顺序导入所有包,这里是先导入bufio包。bufio 包又导入了其它包,所以接下来将执行这些导入:在任何情况下导入的包又导入了其它包,则先执行其它包的导入,然后创建其包级别的常量和变量, 然后调用其init()函数。一旦bufio包被导入,fmt和strings包也会被导入,所以,Go语言编译器会跳过main包中strings包的导入操作,因为该包已经在导入bufio包时被导入。
当导入所有的包后,britishAmerican变量将被创建,然后调用main包中的init()函数,最后调用main包中的main()函数,程序开始执行。