Go 包

admin
admin 2020年03月09日
  • 在其它设备中阅读本文章

Go 语言是使用 来组织源代码的,(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go 语言中为我们提供了很多内置包,如 fmt、os、io 等。
任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName语句,通过该语句声明自己所在的包。
GOPATH是 Go 语言中使用的一个环境变量,它使用绝对路径提供项目的 工作目录,目的是为了告知 go,需要代码(包括本项目和引用外部项目的)的时候,去哪里查找。代码总是会保存在$GOPATH/src目录下,产生的二进制可执行文件放在$GOPATH/bin目录下,生成的中间缓存文件会被保存在$GOPATH/pkg下。

一、包的基本概念

Go 语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然 Go 语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。
包可以定义在很深的目录中,包名的定义是不包括目录路径的,但是包在引用时一般使用全路径引用。比如在GOPATH/src/a/b/下定义一个包 c。在包 c 的源码中只需声明为package c,而不是声明为package a/b/c,但是在导入 c 包时,需要带上路径,例如import "a/b/c"。包的习惯用法:

  • 包名一般是小写的,使用一个简短且有意义的名称。
  • 包名一般要和所在的目录同名,也可以不同,包名中不能包含-等特殊符号。
  • 包一般使用域名作为目录名称,这样能保证包名的唯一性,比如 GitHub 项目的包一般会放到GOPATH/src/github.com/userName/projectName目录下。
  • 包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。
  • 一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。

二、包的导出

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

1、导出包内标识符

下面代码中包含一系列未导出标识符,它们的首字母都为小写,这些标识符可以在包内自由使用,但是包外无法访问它们,代码如下:

package mypkg
var myVar = 100
const MyConst = "hello"
type MyStruct struct {
}

此时,MyConst 和 MyStruct 可以被外部访问,而 myVar 由于首字母是小写,因此只能在 mypkg 包内使用,不能被外部包引用。

2、导出结构体及接口成员

在被导出的结构体或接口中,如果它们的字段或方法首字母是大写,外部可以访问这些字段和方法,代码如下:

type MyStruct struct {
    // 包外可以访问的字段
    ExportedField int
    // 仅限包内访问的字段
    privateField int
}
type MyInterface interface {
    // 包外可以访问的方法
    ExportedMethod()
    // 仅限包内访问的方法
    privateMethod()
}

在代码中,MyStruct 的 ExportedField 和 MyInterface 的 ExportedMethod()可以被包外访问。

三、包的导入

要在代码中引用其他包的内容,需要使用 import 关键字导入使用的包。import 导入语句通常放在源码文件开头包声明语句的下面; 导入的包名需要使用双引号包裹起来;包名是从 GOPATH/src/ 后开始计算的,使用 / 进行路径分隔。

1、基本导入格式

单行导入:

import "包 1 的路径"
import "包 2 的路径"

多行导入:

import (
    "包 1 的路径"
    "包 2 的路径"
)

包的引用路径有两种写法,分别是 全路径导入 相对路径导入
全路径导入:
包的绝对路径就是 GOROOT/src/ 或 GOPATH/src/ 后面包的存放路径,如下所示:

import "lab/test"
import "database/sql/driver"

上面代码的含义如下:
test 包是自定义的包,其源码位于 GOPATH/src/lab/test 目录下;
driver 包的源码位于 GOROOT/src/database/sql/driver 目录下;
相对路径导入:
相对路径只能用于导入 GOPATH 下的包,标准包的导入只能使用全路径导入。
例如包 a 的所在路径是 GOPATH/src/lab/a,包 b 的所在路径为 GOPATH/src/lab/b,如果在包 b 中导入包 a,则可以使用相对路径导入方式。

// 相对路径导入
import "../a"

2、包的引用格式

包的引用有四种格式,下面以 fmt 包为例来分别演示一下这四种格式。
1) 标准引用格式

import "fmt"

此时可以用 fmt. 作为前缀来使用 fmt 包中的方法,这是常用的一种方式。
2) 自定义别名引用格式
在导入包的时候,我们还可以为导入的包设置别名,如下所示:

import F "fmt"

其中 F 就是 fmt 包的别名,使用时我们可以使用 F. 来代替标准引用格式的 fmt. 来作为前缀使用 fmt 包中的方法。
3) 省略引用格式

import . "fmt"

这种格式相当于把 fmt 包直接合并到当前程序中,在使用 fmt 包内的方法是可以不用加前缀 fmt.,直接引用。
4) 匿名引用格式
在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式:

import _ "fmt"

匿名导入的包与其他方式导入的包一样都会被编译到可执行文件中。

使用标准格式引用包,但是代码中却没有使用包,编译器会报错。如果包中有 init 初始化函数,则通过import _ "包的路径"这种方式引用包,仅执行包的初始化函数,即使包没有 init 初始化函数,也不会引发编译器报错。
注意:

  • 一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
  • 包不能出现环形引用的情况,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,则编译不能通过。
  • 包的重复引用是允许的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d,这种情况是允许的,并且 Go 编译器保证包 d 的 init 函数只会执行一次。

    3、完整的引入声明语句形式

    事实上,一个引入声明语句的完整形式为:

    import importname "path/to/package"

其中引入名 importname 是可选的,它的默认值为被引入的包的包名(不是目录名)。如果一个包引入声明中的 importname 没有省略,则限定标识符使用的前缀必须为 importname,而不是被引入的包的名称。

四、标准库

标准的 Go 语言代码库中包含了大量的包,并且在安装 Go 的时候多数会自动安装到系统中,这些包称为 标准库 。我们可以在 $GOROOT/src/pkg 目录中查看这些包。下面简单介绍一些我们开发中常用的包。
1) fmt
fmt 包实现了格式化的标准输入输出,这与 C 语言中的 printf 和 scanf 类似。其中的 fmt.Printf() 和 fmt.Println()是开发者使用最为频繁的函数。
格式化短语派生于 C 语言,一些短语(%- 序列)是这样使用:

  • %v:默认格式的值。当打印结构时,加号(%+v)会增加字段名;
  • %#v:Go 样式的值表达;
  • %T:带有类型的 Go 样式的值表达。
    2) io
    这个包提供了原始的 I / O 操作界面。它主要的任务是对 os 包这样的原始的 I / O 进行封装,增加一些其他相关,使其具有抽象功能用在公共的接口上。
    3) bufio
    bufio 包通过对 io 包的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。
    在 bufio 各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据,只有当缓冲区没有数据时,才会从数据源获取数据更新缓冲。
    4) sort
    sort 包提供了用于对切片和用户定义的集合进行排序的功能。
    5) strconv
    strconv 包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串的功能。
    6) os
    os 包提供了不依赖平台的操作系统函数接口,设计像 Unix 风格,但错误处理是 go 风格,当 os 包使用时,如果失败后返回错误类型而不是错误数量。
    7) sync
    sync 包实现多线程中锁机制以及其他同步互斥机制。
    8) flag
    flag 包提供命令行参数的规则定义和传入参数解析的功能。绝大部分的命令行程序都需要用到这个包。
    9) encoding/json
    JSON 目前广泛用做网络程序中的通信格式。encoding/json 包提供了对 JSON 的基本支持,比如从一个对象序列化为 JSON 字符串,或者从 JSON 字符串反序列化出一个具体的对象等。
    10) html/template
    主要实现了 web 开发中生成 html 的 template 的一些函数。
    11) net/http
    net/http 包提供 HTTP 相关服务,主要包括 http 请求、响应和 URL 的解析,以及基本的 http 客户端和扩展的 http 服务。通过 net/http 包,只需要数行代码,即可实现一个爬虫或者一个 Web 服务器,这在传统语言中是无法想象的。
    12) reflect
    reflect 包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类型 interface{}的值,并且通过 Typeof 解析出其动态类型信息,通常会返回一个有接口类型 Type 的对象。
    13) os/exec
    os/exec 包提供了执行自定义 linux 命令的相关实现。
    14) strings
    strings 包主要是处理字符串的一些函数集合,包括合并、查找、分割、比较、后缀检查、索引、大小写处理等等。
    strings 包与 bytes 包的函数接口功能基本一致。
    15) bytes
    bytes 包提供了对字节切片进行读写操作的一系列函数。字节切片处理的函数比较多,分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等。
    16) log
    log 包主要用于在程序中输出日志。
    log 包中提供了三类日志输出接口,Print、Fatal 和 Panic。
  • Print 是普通输出;
  • Fatal 是在执行完 Print 后,执行 os.Exit(1);
  • Panic 是在执行完 Print 后调用 panic()方法。