go语言的源码组织形式是包,go语言的main函数只有在main包里面才可以被系统执行。golang中的包和文件夹是对应关系,一般是创建在gopath目录下。golang中的一个包需要引用另一个包,必须使用import关键字进行导入才可以使用。golang中的任何源代码文件必须属于某个包,第一行代码是package pacakageName.。包是多个Go源码的集合,是一种高级的代码复用方案。Go将一些常用的方法封装成一个个系统包,这些包称为标准库。
包定义(不包含目录路径)
go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go没有强制包名必须和其所在的目录名同名,建议包名和所在目录同名。包可以定义在很深的目录,包的定义不包括目录路径,但是包的引用一般是全路径引用。
1.包名一般是小写
2.包名一般和目录同同名,可以不同(不推荐)
3.包一般放到公司的域名目录下,保证包名的唯一性
4.一个目录下的同级文件归属于一个包,如果声明多个包,则程序报错。
5.包名为main的包为应用程序的入口包,编译源码如果没有main包,将无法编译输出可执行的文件
包设计思想
目的是为了简化大型程序的设计和维护工作。每个包一般都定义一个不同的名字空间用于他内部的每个标识符的访问。每个名字空间关联到一个特定的包,给类型、函数选择简短的名字,避免在使用它们的时候减少和部分名字的冲突。
当我们修改一个源文件,必须重新编译该文件对应的包和所有依赖该包的其他包,即使是从头构建,Go语言编译器的编译速度也明细那快于其他编译语言,得益于三个语言特性。
1.显式声明:所有导入的包必须在每个文件的开头显式声明,编译器没有必要读取和分析整个源文件来判断包的依赖关系
2.禁止循环依赖:禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以独立编译
3.目标文件:编译后包的目标文件不仅仅记录包本身的导出信息,同时记录了包的依赖关系,因此,在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖的文件
包引用
包引用路径
Go语言种的包引用方式有两种:使用绝对路径的形式和使用相对路径的形式。
1.绝对路径引入(系统包和自定义的包)
import “a/b/c”:c是自定义的包,其源码位于
G
O
P
A
T
H
/
s
r
c
/
a
/
b
/
c
目
录
下
i
m
p
o
r
t
"
d
a
t
a
b
a
s
e
/
s
q
l
"
:
s
q
l
是
系
统
包
,
其
源
码
位
于
GOPATH/src/a/b/c目录下 import "database/sql":sql是系统包,其源码位于
GOPATH/src/a/b/c目录下import"database/sql":sql是系统包,其源码位于GOROOT/src/database/sql目录下
2.相对路径引入(自定义的包)
import “…/a”:a包自定义的包,源码位于$GOPATH/src/a目录下
注意:包不能出现环形引用,a引用b,b引用c,c引用a,编译不能通过;包重复引用是允许的,a引用了b和c,包b和c都引用了d,相当于重复引用了d,这是允许的,并且Go编译器保证d的init函数只会执行一次。
包引用格式
1.标准引用方式:常见形式导入系统内置的fmt包,导入后,通过fmt.的形式使用包中导出的函数。
import “fmt”
例:fmt.Println(“hello world”)
2.别名引用方式:为系统包fmt设置了一个别名F,导入完包后,直接可以通过F.的形式来使用包中导出的函数
import F “fmt”
例:F.Println(“hello world”)
3.省略方式引入:省略方式引入系统内置的包fmt,相当于把包的命名空间直接合并到当前程序的命名空间中,使用fmt包内可导出元素可以不用前缀fmt.,直接引用。
例:Println(“hello world”)
4.仅执行包init函数方式引入:使用仅执行包init函数方式引入系统内置的包fmt,程序会执行fmt中的init函数。(go中不允许导入不使用的包,因此我们在前面加上_,表明我们不需要使用该包里面的函数,仅仅是执行该包的init函数)
使用标准格式引用包,但是代码中却没有使用包,编译器会报错。
如果包中有init初始化函数,则通过import _"packagename"这种方式引用包,即使没有init初始化函数,也不会引发编译器错误
一个包可以有多个init函数,包加载会执行全部的init函数,但并不能保证执行顺序,不建议在一个包里放入多个init函数,将需要初始化的逻辑放到一个init函数中。
包导入(导入的包通过添加空行来分组)
单行导入和多行导入的效果一样,导入的包之间可以通过空行来分组;通常将来自不同组织的包独立分组。包的导入顺序无关紧要,但是每个分组中一般会根据字符串顺序排列。
多行导入
包名在import中的顺序不影响导入效果
单行导入
导入包重命名
同时导入两个相同名字的包,例如math/rand包和crypto/rand包,至少其中一个要指定一个新的包名,这叫做导入包的重命名。
math/rand包在这个源文件中的包名为mrand
导入包的重命名只影响当前的源文件。其它的源文件如果导入了相同的包,可以用导入包原本默认的名字或重命名为另一个完全不同的名字。
匿名导入包
导入包,而不使用任何包内的结构和类型,也不调用包内任何函数,可以使用匿名导入包,下画线_表示匿名导入包。
匿名导入的包与其他方式导入包一样会导入包编译到可执行文件中,导入包也会触发init()函数调用
包在程序启动前的初始化入口:init
在某些需求的设计上需要在程序启动时统一调用程序引用到的所有包的初始化函数,如果需要通过开发者手动调用这些初始化函数,那么这个过程可能会发生错误或者遗漏。我们希望在被引用的包内部,由包的编写者获得代码启动的通知,在程序启动时做一些自己包内代码的初始化工作。
Go 语言为以上问题提供了一个非常方便的特性:init() 函数。
init() 函数的特性如下:
每个源码可以使用 1 个 init() 函数。
init() 函数会在程序执行前(main() 函数执行前)被自动调用。
调用顺序为 main() 中引用的包,以深度优先顺序初始化。
包导入后的init()函数初始化顺序
go语言包会从main包开始检查其引用的所有包,每个包也可能包含其他的包。go编译器由此构建一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包。在运行时,被最后导入的包会最先初始化并调用init()函数。
1.main
2.pkg1
3.pkg2
4.输出结果
内置包
语言的标准库覆盖网络、系统、加密、编码、图形等各个方面,可以直接使用标准库的http包进行http协议的收发处理;网络库基于高性能的操作系统通信模型(Linux的epoll、Windows的IOCP);所有的加密、编码都内建支持,不需要再从第三方开发者处获取。
go语言的编译器也是标准库的一部分,通过词法器扫描源码,使用语法树获取源码逻辑分支等。Go语言的周边工具也是建立在这些标准库上。
go语言的内置的系统包的源码位于
G
O
R
O
O
T
/
s
r
c
/
目
录
,
可
以
直
接
使
用
。
自
定
义
的
包
和
第
三
方
的
包
的
源
码
必
须
放
到
GOROOT/src/目录,可以直接使用。自定义的包和第三方的包的源码必须放到
GOROOT/src/目录,可以直接使用。自定义的包和第三方的包的源码必须放到GOPATH/src目录才能引用。
常用内置包说明
1.fmt:fmt包实现了格式化和标准输出,fmt.Printf()和fmt.Println()。
2.bytes:对字节切片进行读写操作的一系列函数。字节切片处理的函数分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数。
2.io:原始的I/O操作界面,主要任务时对os这样的原始的I/O进行封装,增加一些其他相关,使其具有抽象功能在公共的接口上。
3.bufio:通过对io包的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。
在 bufio 各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据,只有当缓冲区没有数据时,才会从数据源获取数据更新缓冲。
4.sort:提供了用于对切片和用户定义的集合进行排序的功能。
5.strconv:提供了将 字符串 转换成基本 数据类型,或者从基本数据类型转换为字符串的功能。
6.os:提供了不依赖平台的操作系统函数接口,设计像 Unix 风格,但错误处理是 go 风格,当 os 包使用时,如果失败后返回错误类型而不是错误数量。
7.sync:实现多线程中锁机制以及其他同步互斥机制。
8.flag:提供命令行参数的规则定义和传入参数解析的功能。绝大部分的命令行程序都需要用到这个包。
9.encoding/json:JSON 目前广泛用做网络程序中的通信格式。encoding/json 包提供了对 JSON 的基本支持,比如从一个对象序列化为 JSON 字符串,或者从 JSON 字符串反序列化出一个具体的对象等。
10.html/template:实现了 web 开发中生成 html 的 template 的一些函数。
11.net/http:提供 HTTP 相关服务,主要包括 http 请求、响应和 URL 的解析,以及基本的 http 客户端和扩展的 http 服务。
12.reflect:实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类型 interface{} 的值,并且通过 Typeof 解析出其动态类型信息,通常会返回一个有接口类型 Type 的对象。
13.os/exec:提供了执行自定义 linux 命令的相关实现。
14.strings:主要是处理字符串的一些函数集合,包括合并、查找、分割、比较、后缀检查、索引、大小写处理等等。
15.time:主要用于与时间相关的操作,比如格式化时间、将时间转换为时间戳,将时间戳转换为时间字符串等操作。
16.主要用于在程序中输出日志。
17.regexp:对正则表达式的封装。
自定义包
Go 语言 中,我们除了可以使用系统内置的 包,还可以自己定义包。我们自定义的包必须放在 GOPATH 的 src 目录下,且同一个目录下只能有且只有一个包。
Golang 中的一个包中可以有任意多个文件,文件的名字只要不是以 test_ 开头都可以。
导出包标志符(首字母大写)
在 Golang 中一个 包 想要引用另一个包的标志符,比如:变量、结构体、常量、函数 等,那么这些标志符必须在本包里面是导出的。
Go 语言中,标志符的导出,只需要将其首字母大写即可,所有首字母大小的标志符都是导出的标志符,都可以在另一个包中引用。
在包中,未导出的标志符,首字母都是小写的,这些标志符在本包可以自由的使用,在其他包不可以使用。
Golang 中的 包 的引用有四种引用格式,分别为:使用标准引用方式引入、使用别名引用方式引入、使用省略方式引入和仅执行包 init 函数方式引入。
包结构体可访问性(首字母大写 可访问)
结构体可访问性
在 Golang 中的一个 包 中如果定义了 结构体,该结构体的首字母如果小写,那么在其他包中将无法使用该结构体。
如果定义的结构体中名大写,那么其大写的字段可以在另一个包中访问,而其小写的字段则不可以在另一个包中访问。
结构体字段可访问性
首字母大写字段可以被外部访问,首字母小写字段只能在当前包里面使用。
包函数可访问性
在 Golang 中的一个 包 中如果定义了 函数,该函数的首字母如果小写,那么在其他包中将无法使用该函数。如果定义的函数首字母大写,那么可以在另一个包中访问。
包加载
golang程序在执行之前,首先会从main包开始,在执行main包的main函数之前,Go引导程序会先对整个程序的包进行初始化。对包的初始化所做的工作是从main包引入的第一个包开始,递归式的执行每一个被引入的包的 常量、全局变量、init 函数 等。
等所有包的所有的初始化工作都完成之后,开始从 main 包的 main 函数执行我们的程序。