Golang通脉之包的管理
在工程化的开发项目中,Go语言的源码复用是建立在包(package
)基础之上的。
包(package)
是多个Go源码的集合,是一种高级的代码复用方案,Go语言提供了很多内置包,如fmt
、os
、io
等。
包的定义
src 目录是以代码包的形式组织并保存 Go 源码文件的。每个代码包都和 src 目录下的文件夹一一对应。每个子目录都是一个代码包。
代码包包名和文件目录名,不要求一致。比如文件目录叫 server,但是代码包包名可以声明为 “main”,但是同一个目录下的源码文件第一行声明的所属包,必须一致!
还可以根据自己的需要创建自己的包。一个包可以简单理解为一个存放.go
文件的文件夹。 该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包。
package 包名
注意事项:
- 一个文件夹下面直接包含的文件只能归属一个
package
,同样一个package
的文件不能在多个文件夹下。 - 包名可以不和文件夹的名字一样,包名不能包含
-
符号。 - 包名为
main
的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main
包的源代码则不会得到可执行文件。 - 在同一个包下面的文件属于同一个工程文件,不用
import
包,可以直接使用
可见性
如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public)。在Go语言中只需要将标识符的首字母大写就可以让标识符对外可见了:
package pkg
import "fmt"
// 包变量可见性
var a = 100 // 首字母小写,外部包不可见,只能在当前包内使用
// 首字母大写外部包可见,可在其他包中使用
const Mode = 1
type person struct { // 首字母小写,外部包不可见,只能在当前包内使用
name string
}
// 首字母大写,外部包可见,可在其他包中使用
func Add(x, y int) int {
return x + y
}
func age() { // 首字母小写,外部包不可见,只能在当前包内使用
var Age = 18 // 函数局部变量,外部包不可见,只能在当前函数内使用
fmt.Println(Age)
}
结构体中的字段名和接口中的方法名如果首字母都是大写,外部包可以访问这些字段和方法:
type Student struct {
Name string //可在包外访问的方法
class string //仅限包内访问的字段
}
type Payer interface {
init() //仅限包内访问的方法
Pay() //可在包外访问的方法
}
main包
Go 语言的入口 main() 函数所在的包(package)叫 main,main 包想要引用别的代码,需要import导入!
包的导入(import)
要在代码中引用其他包的内容,需要使用import
关键字导入使用的包。具体语法如下:
A:通常导入
// 单个导入
import "package"
// 批量导入
import (
"package1"
"package2"
)
B:点操作 有时候会看到如下的方式导入包
import(
. "fmt"
)
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")
可以省略的写成Println("hello world")
注意事项:
- import导入语句通常放在文件开头包声明语句的下面。
- 导入的包名需要使用双引号包裹起来。
- 包名是从
$GOPATH/src/
后开始计算的,使用/
进行路径分隔。 - Go语言中禁止循环导入包。
自定义包名
自定义包名顾名思义可以把包命名成另一个用起来容易记忆的名字。导入时,可以为包定义别名,语法演示:
import (
p1 "package1"
p2 "package2"
)
// 使用时:别名操作,调用包函数时前缀变成了自定义的前缀
p1.Method()
匿名导入包
_
操作 如果仅仅需要导入包时执行初始化操作,并不需要使用包内的其他函数,常量等资源。则可以在导入包时,匿名导入。
这个操作经常是让很多人费解的一个操作符:
import (
"database/sql"
_ "github.com/mysql/driver"
)
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。也就是说,使用下划线作为包的别名,会仅仅执行init()
。
导入的包的路径名,可以是相对路径也可以是绝对路径,推荐使用绝对路径(起始于工程根目录)。
匿名导入的包与其他方式导入的包一样都会被编译到可执行文件中。
init()函数
init()函数介绍
在Go语言程序执行时导入包语句会自动触发包内部init()
函数的调用。需要注意的是: init()
函数没有参数也没有返回值。 init()
函数在程序运行时自动被调用执行,不能在代码中主动调用它,init() 中的代码会在 main() 函数执行前执行,用于初始化包所需要的特定资源。
包初始化执行的顺序如下图所示:
init()函数执行顺序
Go语言包会从main
包开始检查其导入的所有包,每个包中又可能导入了其他的包。Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。
在运行时,被最后导入的包会最先初始化并调用其init()
函数, 如下图示:
禁止循环引用包:A -> B -> C -> A
init函数与main函数的异同
相同点:
- 两个函数在定义时不能有任何的参数和返回值。 都只能由 go 程序自动调用,不可以被引用。
不同点:
init 可以应用于任意包中,且可以重复定义多个。 main 函数只能用于 main 包中,且只能定义一个。
两个函数的执行顺序:
在 main 包中的 go 文件默认总是会被执行。对同一个 go 文件的 init( ) 调用顺序是从上到下的。
对同一个 package 中的不同文件,将文件名按字符串进行“从小到大”排序,之后顺序调用各文件中的init()函数。
对于不同的 package,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 init() 函数。
如果 package 存在依赖,调用顺序为最后被依赖的最先被初始化,例如:导入顺序 main –> A –> B –> C,则初始化顺序为 C –> B –> A –> main,一次执行对应的 init 方法。main 包总是被最后一个初始化,因为它总是依赖别的包
Golang通脉之包的管理的更多相关文章
- Golang Gin 项目包依赖管理 godep 使用
Golang Gin 项目包依赖管理 godep 使用 标签(空格分隔): Go 在按照github.com/tools/godep文档go get完包以后,调整项目结构为$GOPATH/src/$P ...
- golang多个项目时如何配置GOPATH,使用gb包依赖管理工具,不同项目配置不同的GOPATH的
golang多个项目时如何配置GOPATH,使用gb包依赖管理工具,不同项目配置不同的GOPATH的 1:执行脚本setGoPath.sh#!/bin/bashif [[ $GOPATH =~ .*$ ...
- 一键解决 go get golang.org/x 包失败
问题描述 当我们使用 go get.go install.go mod 等命令时,会自动下载相应的包或依赖包.但由于众所周知的原因,类似于 golang.org/x/... 的包会出现下载失败的情况. ...
- Go 包依赖管理工具 —— govendor
govendor 是一个基于 vendor 机制实现的 Go 包依赖管理命令行工具.与原生 vendor 无侵入性融合,也支持从其他依赖管理工具迁移,可以很方便的实现同一个包在不同项目中不同版本.以及 ...
- [20150522]RPM包的管理
RPM包的管理 RPM包的分类 RPM包可分为源码包和二进制包两类.源码包的主要优点是开源,如果有足够的能力,可以修改源代码,源码包可以自由选择所需要安装的功能,软件是编译安装,所以更加适合自己的系统 ...
- GridBagLayout:网格包布局管理器
GridBagLayout:网格包布局管理器 GridBagLayout可以说是布局管理器Layout中最复杂的一个,其中涉及到的参数也比较得多,比如说: GridBagConstraints g ...
- Golang爬虫示例包系列教程(一):pedaily.com投资界爬虫
Golang爬虫示例包 文件结构 自己用Golang原生包封装了一个爬虫库,源码见go get -u -v github.com/hunterhug/go_tool/spider ---- data ...
- 19-03【golang】strings包
golang的strings包提供了字符串操作的一系列函数.下面做个简单介绍 函数 用法 备注 Compare(a,b sring) 比较两个字符串 Contains(s, substr stri ...
- 关于golang.org/x包问题
关于golang.org/x包问题 由于谷歌被墙,跟谷歌相关的模块无法通过go get来下载,解决方法: git clone https://github.com/golang/net.git $GO ...
随机推荐
- Git使用教程六
冲突的产生与解决 案例:模拟产生冲突. ①同事在下班之后修改了线上仓库的代码 注意:此时我本地仓库的内容与线上不一致的. 2.第二天上班的时候,我没有做git pull操作,而是直接修改了本地的对应文 ...
- Django的form组件——正则校验
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = ...
- [考试总结]noip模拟46
脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...
- 依赖注入Bean属性——手动装配Bean
一.构造方法注入 其中,可以根据不同的参数列表调用不同的重载的构造方法: 其中,基本数据类型没有包,引用类型都有包路径,基本类型对应封装类: 二.通过property标签调用类的set方法注入 三.通 ...
- python模块--collections(容器数据类型)
Counter类(dict的子类, 计数器) 方法 返回值类型 说明 __init__ Counter 传入可迭代对象, 会对对象中的值进行计数, 值为键, 计数为值 .elements() 迭代器 ...
- RIAD配置
一.RIAD 磁盘阵列介绍 二.阵列卡介绍 三.案例举例 一.RAID磁盘阵列介绍 是Redundant Array of Independent Disks的缩写,中文简称为独立冗余磁盘阵列 把 ...
- 如何将 Ubuntu 版本升级到新版本
@ 目录 0.将 Ubuntu 版本升级到新版本的注意事项 1.以图形方式升级到 Ubuntu 20.04(适用于桌面用户) 2.使用命令行升级到 Ubuntu 21.10 本教程通过从 Ubuntu ...
- 关于FeignClient上的RequestMapping不能生效的问题
问题 我有两个FeignClient共同继承了一个接口,两个Client有各自不同的url实现,其中一个需要加上类似于@RequestMapping作用在类上的效果,因为@RequestMapping ...
- Optional容器类
一.Optional 容器类:用于尽量避免空指针异常 方法 /* * Optional.of(T t) : 创建一个 Optional 实例 * Optional.empty() : 创建一个空的 O ...
- 了解PHP-FPM
在服务器上,当我们查看php进程时,全都是php-fpm进程,大家都知道这个就是php的运行环境,那么,它到底是个什么东西呢? PHP-FPM简介 PHP-FPM,就是PHP的FastCGI管理器,用 ...