Go语言之包(package)

Go 的源码复用建立在包( package )基础之上。 Go 的入口 main()函数所在的包( package )叫 main, main 包想要引用别的代码,必须同样以包的方式进行引用,Go 的包与文件夹 一一对应,所有与包相关的操作,必须依赖于工作目录(GOPATH )

、工作目录(GOPATH)

GOPATH是 Go 中使用的一个环境变量 ,它使用绝对路径提供项目的工作目录,工作目录是 工程开发的相对参考目录

1、通过命令行查看GOPATH路径,执行命令如下:

go env

在安装go的时候,可以设置GOPATH的地址,或者采用默认的地址,参考链接:https://blog.ywdevops.cn/index.php/2022/01/22/go-10/

二、创建包(package)

包是是多个 Go 源码的集合,是高级的代码复用方案, GO语言默认为我们提供了很多包,如 fmt、os、io ,开发者可以根据自己的需要创建自己的包

包要求在同一个目录下的所有文件的第一行添加如下代码,以标记该文件归属的包:

package 包名

包的特性如下:

  • 一个目录下的同级文件归属一个包。
  • 包名可以与其目录不同名。
  • 包名为main 的包为应用程序的入口包, 编译源码没有main 包时,将无法编译输出可执行的文件

在GOPATH/src目录下创建的包可以理解为工程目录,也就是项目目录

三、导出标识符一一让外部访问包的类型和值

在Go 语言中,如果想在一个包里引用另外一个包里的标识符(如类型、变量、常量等)时,必须首先将被引用的标识符导出,将要导出的标识符的首字母大写就可以让引用者可以访问这些标识符了。

1、导出包内标识符:

下面例子中,在包class中定义两个文件,demo.go和main.go,其中main.go作为入口文件,调用demo.go中的test()方法,demo.go中的test()为小写,因此可以在当前包下调用(test可以称之为标识符),如图:

运行结果如下:

标识符不只是局限于函数,也可以是变量,结构体等等,在demo.go中定义变量,然后main.go中引用,如图:

注意:同一个包下的文件之间引用,不需要执行import来导入

四、导入包:

导入有两种基本格式,即单行导入和多行导入,两种导入方法的导入代码效果是一致的。

1、单行导入,格式如下

import "包1"
import "包2"

2、多行导入:

当多行导入时,包名在import 中的顺序不影响导入效果,格式如下:

import (
    "包1"
    "包2"
    ....
)

下面的例子演示了在工程目录world下创建了程序的入口文件main.go,在world目录下创建包test1,在test1内部创建文件guan.go,项目结构以及main.go的内容如下:

guan.go的内容如下:

注意:上图中main.go调用guan.go后,访问其内容,前面需要用“包名.函数名”的方式访问,否则报错

3、导入包后自定义引用的包名:

在默认导入包的基础上,在导入包路径前添加标识符即可形成自定义引用包,格式如下:

customName ” path/to/package ”

  • path/to/package : 要导入的包路径
  • customName : 自定义的包名

修改上面例子中的main.go,在导入包前面自定义包名,然后通过自定义包名调用函数,如图:

4、匿名导入包–只导入包但是不使用包内的类型和数值

如果希望导入包,但不使用任何包内的结构和类型以及函数,只调用里面的init()函数,可以使用匿名导入包, 格式如下:

import(
  _ "path/to/package"
)
  • path/to/package 表示要导入的包名
  • 下画线: 表示匿名导入包

匿名导入的包与其他方式导入包一样会让导入包编译到可执行文件中,同时,导入包仅仅会触发init()函数调用

例如:在上面例子的基础上,在test1包中新建文件c.go,内容中定义一个init()函数,如图:

在主程序文件main.go中通过匿名方式导入包,只导入包,并没有任何地方调用init()函数,如图:

执行结果如下:

从上图结果可知,虽然主程序文件中没有主动调用init(),但是实际执行时还是会触发c.go中的init()函数,而且是在main()函数执行前就被自动调用

注意:匿名导入包只调用包中的init()函数,其余都不会调用

5、导入的包前面加一个点

导入包的时候前面加一个点,那么在使用包中函数的时候可以省略包名,如图:

6、Init函数和main函数

go语言中 init 函数用于包 (package) 的初始化,该函数是go语言的一个重要特性

  • init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
  • 每个包可以拥有多个init函数
  • 包的每个源文件也可以拥有多个init函数
  • 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明
  • 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
  • init函数不能被其他函数调用,而是在main函数执行之前,自动被调用

一个文件中同时包含全局变量定义,init函数和main函数,那么执行流程是:全局变量定义>init函数>main函数

例如:定义全局变量,赋值为函数的返回值,同时定义init函数和main函数,如图:

执行结果如下:

从上图可以看出,全局变量最先被执行,然后是init函数,最后是main函数

下面例子是通过init函数完成的初始化动作,在world包里的test1包中的cc.go文件中定义全局变量,在init函数内初始化变量,如图:

在入口文件main.go中,导入包,并访问文件cc.go中的字段,如图:

执行结果如下:

注意:字段名大写才能被包外访问,小写只能包内访问

main函数:

Go语言程序的默认入口函数(主函数):func main() 函数体用{}一对括号包裹

func main(){
     函数体
}

init函数和main函数的异同:

相同点:两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用

不同点:init可以应用于任意包中,且可以重复定义多个。main函数只能用于main包中,且只能定义一个

两个函数的执行顺序:

  • 对同一个go文件的 init() 调用顺序是从上到下的
  • 对同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的 init() 函数
  • 对于不同的 package ,如果不相互依赖的话,按照main包中”先 import 的后调用”的顺序调用其包中的 init() ,如果 package 存在依赖,则先调用最早被依赖的 package 中的 init(),最后调用 main 函数。
  • 如果 init 函数中使用了 println() 或者 print() 你会发现在执行过程中这两个不会按照你想象中的顺序执行。这两个函数官方只推荐在测试环境中使用,对于正式环境不要使用。

7、自定义包的时候,给包以及函数都加上注释是一个好的习惯,方便go doc时查看,如图:

定义好了后,我们在使用go doc errcode等命令查看包以及函数用法的时候就可以看到注释,如图:

自定义github.com包

1、下面例子在/data/goproject目录下创建github.com/gongguan2018/gotable,如图:

然后在执行go mod init的时候,从github.com开始,如图:

在main.go中如果要调用同级目录table下的函数,即可导入完整的包路径,如图:

标签