Go package

包(package)是多个Golang源码的集合,是一种高级的代码复用方案,换言之Golang的【源码复用】建立在package包基础之上。

声明包

Go语言中任何源码文件必须属于某个包,同时源码文件的第一行有效代码必须是package packageName语句,通过此语句来声明自己所在的包。

package packageName

Go语言的包借助了文件目录树的组织形式,一般包名就是源文件所在的目录名。虽然Go语言没有强制要求包名必须和目录同名,但这样做使结构更加清晰。因此一个包可简单理解为一个存放.go文件的文件夹。

包与文件夹

包是Go语言中代码组成和代码编译的主要方式,Go语言中允许将同一个包的代码分割成多个独立的源码文件来单独保存,只需要将独立文件存放在同一个目录下即可。一个包中可以具有任意多个文件,文件名字无规定但后缀必须为.go

  • Go语言中的包与文件夹是一一对应的,所有包相关的操作必须依赖于工作目录GOPATH
  • 包可以定义在很深的目录结构中,包名的定义是不会包括文件目录的,但在包的引用时一般会使用全路径引用。
  • 文件夹下所有源码只能属于同一个包,属于同一个包的源码不能放在多个文件夹下。

包名

  • 包名一般采用小写,建议使用简短且有意义的名称。
  • 包名一般要和所在的目录同名,也可以不同,包名中不能含有-等特殊符号。
  • 包一般会使用域名作为目录名称,以保证包名的唯一性。

主包

包名为main的包是应用程序的入口包,编译时若没有包含main包的源代码则不会生成可执行文件。

  • 入口主函数main()所在的包叫做main
  • 同一目录下只能有且仅有一个main主函数
  • main包想要引用其它代码,必须同样以包的方式进行引用。
$ vim main.go
package main

func main(){

}

可见性

若要在某个包中引用另一个包中的标识符(如变量、常量、类型、函数等),该标识符必须是对外可见的(public)。Golang中仅需将标识符首字母大写就可以让标识符对外可见了。

导入包

要在代码中引用其他包的时候,需要使用import关键字导入所需使用的包。

import "包的路径"
  • import导入语句通常会放在源码文件开头包声明语句的下面
  • 导入的包名需要使用双引号包裹起来
  • 包名会从GOPATH/src/后开始搜索,使用/进行路径分割。
  • 禁止循环导入包,包不能出现环形引用,多个包首尾相互引用时Go编译器不能通过。
  • 包的重复引用是允许的,Go编译器保证包的初始化函数只会执行一次。

包的导入有两种写法,分别是单行导入和多行导入。

  • 单行导入
import "包的路径"
import "包的路径"
  • 多行导入
import (
  "包的路径"
  "包的路径"
)

包的引用路径有两种写法,分别是全路径导入和相对路径导入。

  • 全路径导入

包的绝对路径是GOPATH/srcGOPATH/src后面包的存放路径

例如:sql包是自定义的包,sql包的源码位于GOPATH/src/database/sql目录下。

import "database/sql"
  • 相对路径导入

相对路径只能用于导入GOPATH工作目录下的包,标准包的导入只能采用全路径导入。

import "../packageName"

引用包

包的引用有4种格式

  • 标准格式
import "fmt"

fmt.Println("hello world")

使用fmt.作为前缀来使用fmt包中的方法

  • 自定义别名引用格式

导入包的时候可以为导入的包设置别名

import F "fmt"

F.Println("hello world")

Ffmt包的别名,使用时可以使用F.来代替标准引用格式的fmt.作为前缀来使用fmt包中的方法。

  • 省略引用格式
import . "fmt"

Println("hello world")

省略引用格式相当于把fmt包直接合并到当前程序中,使用fmt包内的方法时可以不添加fmt.前缀,直接引用。

  • 匿名引用格式

当引用某个包时,若只希望执行包初始化的init()函数,而不使用包内部的代码可使用匿名引用格式。匿名导入的包与其他放到导入的包都会被编译到可执行文件中。

import _ "fmt"

使用标准格式引用包后若源码中没有使用则编译器会报错,若包中有init()初始化函数,可通过import _ "包的路径"这种方式来引用,

package main

import (
  _ "database/sql"
  . "fmt"
)

func Main(){
  Println("hello world")
}

初始化函数

Golang程序执行时导入包语句会自动触发包内部的init()函数的调用,init()函数没有参数也没有返回值,init()函数在程序运行时会自动被调用执行,不能在代码中主动调用它。

一个包可以存在多个init()初始化函数,包加载时会执行全部的初始化函数,但并不能够保证执行顺序,因此不建议在一个包中添加多个初始化函数,建议将需要初始化的逻辑放到一个初始化函数即可。

如果仅仅执行包的初始化函数init(),即使包没有init()初始化函数也不会引发编译器错误。

包初始化函数的执行时机:全局声明 > init() > main(),单个包的初始化过程会先初始化常量,然后是全局变量,最后才会执行包的init()初始化函数。

加载包

Go程序启动和加载是在执行main包中的main()函数之前,Go引导程序会先对整个程序中的包进行初始化。

Golang会从main包开始检查其导入的所有包,每个包中可能会导入其它包。包初始化程序会从main()函数所引用的包开始,逐级查找包的引用,直到找到没有引用其它包的包,最终才会生成一个包引用的【有向无环图】。Golang编译器会将有向无环图转换为一棵树,从树的叶子节点开始逐层向上对包进行初始化。

包的初始化

Golang编译器由此构建出一个树状的包引用关系,根据引用顺序决定编译顺序,依次编译这些包中的代码,运行时被最后导入的包会最先初始化并调用其init()函数

导入包的顺序

内置包

标准Go语言代码库中包含了大量的包,Go安装时会自动安装到系统,可在GOROOT/src/pkg目录下查找这些包。

常见内置包

内置包 描述
fmt 格式化标准输入输出,类似C语言中的printf和scanf。
io 提供原始的I/O操作界面,对类似os包这样原始的I/O进行封装。
bufio 对io包的封装,提供数据缓冲功能,减少大块数据读写带来的开销。
strcov 提供将字符串转换为基本数据类型,或将基本数据类型转换为字符串的功能。
os 提供不依赖平台的操作系统函数接口
sync 实现多线程中锁机制及其他同步互斥机制
flag 提供命令行参数规则定义和传入参数解析的功能
encoding/json 提供对JSON的基本支持
html/template 实现Web开发中生成HTML的template模板函数
net/http 提供HTTP相关服务
reflect 实现运行时反射
os/exec 提供执行自定义LINUX命令功能
strings 处理字符串的函数集合
bytes 提供对字节切片进行读写操作的函数
log 程序中输出日志

自定义包

创建自定义的包需将其存放在GOPATH下的src目录下,不同包不能放在同一个目录下,会引发编译错误。

自定义包使用注意

  • 若项目目录不在GOPATH环境变量中,则需将项目移至GOPATH所在目录中,才能完成编译。
  • 使用import语句导入时使用的是包所属文件夹的名称
  • 包中的函数名首字母要大写,否则无法在外部调用。
  • 自定义包的包名不必与其所在文件夹名称保持一致,为了便于维护建议保持一致。
  • 调用自定义包时使用包名.函数名的方式调用

你可能感兴趣的:(Go package)