Golang(Go语言)包机制的理解

一、概述

  想学习Golang,包肯定是绕不过去的,就像C语言的标准库,这篇文章里,我将为大家介绍关于Go语言包机制的使用、注意事项和原理

二、package

package遵循以下原则:

  1. package是最基本的分发单位和工程管理中依赖关系的体现
  2. 每个Go语言源代码文件开头都必须要有一个package声明,表示源代码文件所属包
  3. 要生成Go语言可执行程序,必须要有名为mainpackage包,且在该包下必须有且只有一个main函数
  4. 同一个路径下只能存在一个package,一个package可以由多个源代码文件组成

按照我个人的理解,一个目录就是一个包,该目录下的所有文件必须属于同一个包。

三、import

1. 作用与写法

  import语句的唯一作用就是导入源码文件所依赖的包,而且导入的包必须在源码文件里用到,否则会报错(在VS Code里如果输入一个没有导入的包函数,VS Code会自动导入)

  import的写法有两种格式,建议使用第二种,导入包方法如下:

//第一种
import "package1"
import "package2"
import "package3"

//第二种
improt (
    "package1",
    "package2",
    "package3"
)

2. 原理

  跟package类似,import原理遵守以下几个原则:

  1. 如果一个main导入其他的包,包会被顺序导入
  2. 如果导入的包(pkg1)依赖其他的包(包pkg2),会首先导入pkg2,然后初始化pkg2中的常量与变量,如果pkg2中有init函数,会自动执行init
  3. 所有包导入完成后才会对main的常量和变量进行初始化,然后执行main中的init函数(如果有的话),最后执行main函数
  4. 如果一个包被导入多次实际上只会导入一次

关于上面第三条规的import多重依赖的流程如下图所示

3. import别名

import iii "fmt"
// 此处省略一些代码...
iii.Println("hello")

如果别名是.的话,则可以这样调用(一般不建议这样使用)

import . "fmt"
// 此处省略一些代码...
Println("hello")

有一个最特别的情况,如果别名是 的话,表示只注册该包(初始化全局常量和变量,且执行其init函数),并不会实际引入该包

当导入一个包时,它所有的 init() 函数就会被执行。有些时候我们并非真的需要使用这些包,仅仅是希望它的 init() 函数被执行而已。

举个例子,如果我们需要处理图像,通常会导入 Go 标准库支持的所有相关的包,但是并不会用到这些包的任何函数。

import (
    "fmt"
    "image"
    "os"
    "path/filepath"
    "runtime”
    _ "image/gif"
    _ "image/jpeg"
    _ "image/png"
)

这里导入了 image/gif、image/jpeg 和 image/png 包,纯粹是为了让它们的 init() 函数被执行(这些 init() 函数注册了各自的图像格式),所有这些包都以下划线作为别名,所以 Go语言不会发出导入了某个包但是没有使用的警告。

四、创建自定义的包

我们创建的自定义的包最好就放在 GOPATH 的 src 目录下(或者 GOPATH src 的某个子目录),如果这个包只属于某个应用程序,可以直接放在应用程序的子目录下,但如果我们希望这个包可以被其他的应用程序共享,那就应该放在 GOPATH 的 src 目录下,每个包单独放在一个目录里,如果两个不同的包放在同一个目录下,会出现名字冲突的编译错误

作为惯例,包的源代码应放在一个同名的文件夹下面。同一个包可以有任意多个文件,文件的名字也没有任何规定(但后续名必须是 .go),这里我们假设包名就是 .go 的文件名(如果一个包有多个 .go 文件,则其中会有一个 .go 文件的文件名和包名相同)。

例如:

aGoPath/src/stacker/stacker.go
aGoPath/src/stacker/stack/stack.go

五、理解包导入后的init()函数初始化顺序

Go 语言包会从 main 包开始检查其引用的所有包,每个包也可能包含其他的包。Go 编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。

在运行时,被最后导入的包会最先初始化并调用 init() 函数。

通过下面的代码理解包的初始化顺序。

main函数:

package main
import "chapter08/code8-2/pkg1"
func main() {
    pkg1.ExecPkg1()
}

pkg1包对应的函数:

package pkg1
import (
    "chapter08/code8-2/pkg2"
    "fmt"
)
func ExecPkg1() {
    fmt.Println("ExecPkg1")
    pkg2.ExecPkg2()
}
func init() {
    fmt.Println("pkg1 init")
}

pkg2包对应的函数:

package pkg2
import "fmt"
func ExecPkg2() {
    fmt.Println("ExecPkg2")
}
func init() {
    fmt.Println("pkg2 init")
}
执行代码,输出如下:
pkg2 init
pkg1 init
ExecPkg1
ExecPkg2

参考链接:https://www.jianshu.com/p/bc2bcfaf2a0f

                  http://c.biancheng.net/view/91.html

猜你喜欢

转载自blog.csdn.net/weixin_42117918/article/details/100123181