Golang 包了解以及程序的执行

简介: 引言  Go 语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go 语言中为我们提供了很多内置包,如 fmt、os、io等。  任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName 语句,通过该语句声明自己所在的包。

一、包介绍


1. 包的基本概念


Go 语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。


包可以定义在很深的目录中,包名的定义是不包括目录路径的,但是包在引用时一般使用全路径引用。


比如在GOPATH/src/a/b/ 下定义一个包c。在包c的源码中只需声明为package c,而不是声明为package a/b/c,但是在导入c包时,需要带上路径,例如import "a/b/c"。


2. 包的用法


包名一般是小写的,使用一个简短且有意义的名称

包名一般要和所在的目录同名,也可以不同,包名中不能包含-等特殊符号

包一般使用域名作为目录名称,这样能保证包名的唯一性,比如 GitHub 项目的包一般会放到GOPATH/src/github.com/userName/projectName目录下

包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件


一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。


3. 标识符可见性


在同一个包内部声明的标识符都位于同一个命名空间下,在不同的包内部声明的标识符就属于不同的命名空间。想要在包的外部使用包内部的标识符就需要添加包名前缀,例如fmt.Println("Hello world!"),就是指调用 fmt 包中的Println 函数。

如果想让一个包中的标识符(如变量、常量、类型、函数等)能被外部的包使用,那么标识符必须是对外可见的(public)。在Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)的。在一个包内部只有首字母大写的标识符才是对外可见的。


例如我们定义一个名为demo的包,在其中定义了若干标识符。在另外一个包中并不是所有的标识符都能通过demo.前缀访问到,因为只有那些首字母是大写的标识符才是对外可见的。


package demo
import "fmt"
// 包级别标识符的可见性
// num 定义一个全局整型变量
// 首字母小写,对外不可见(只能在当前包内使用)
var num = 100
// Mode 定义一个常量
// 首字母大写,对外可见(可在其它包中使用)
const Mode = 1
// person 定义一个代表人的结构体
// 首字母小写,对外不可见(只能在当前包内使用)
type person struct {
 name string
 Age  int
}
// Add 返回两个整数和的函数
// 首字母大写,对外可见(可在其它包中使用)
func Add(x, y int) int {
 return x + y
}
// sayHi 打招呼的函数
// 首字母小写,对外不可见(只能在当前包内使用)
func sayHi() {
 var myName = "七米" // 函数局部变量,只能在当前函数内使用
 fmt.Println(myName)
}

同样的规则也适用于结构体,结构体中可导出字段的字段名称必须首字母大写。

type Student struct {
 Name  string // 可在包外访问的方法
 class string // 仅限包内访问的字段
}

4. 包的引入


  • 要在当前包中使用另外一个包的内容就需要使用import关键字引入这个包,并且 import 语句通常放在文件的开头,package 声明语句的下方。
  • 完整的引入声明语句格式如下:

import importname "path/to/package"

其中:


importname:引入的包名,通常都省略。默认值为引入包的包名。

path/to/package:引入包的路径名称,必须使用双引号包裹起来。

Go语言中禁止循环导入包

  • 一个Go源码文件中可以同时引入多个包,例如:

import "fmt"

import "net/http"

import "os"

  • 当然可以使用批量引入的方式

import (

   "fmt"

  "net/http"

   "os"

)

当引入的多个包中存在相同的包名或者想自行为某个引入的包设置一个新包名时,都需要通过importname指定一个在当前文件中使用的新包名。例如,在引入fmt包时为其指定一个新包名f。

import f "fmt"

这样在当前这个文件中就可以通过使用f来调用fmt包中的函数了。

f.Println("Hello world!")

如果引入一个包的时候为其设置了一个特殊_作为包名,那么这个包的引入方式就称为匿名引入。一个包被匿名引入的目的主要是为了加载这个包,从而使得这个包中的资源得以初始化。 被匿名引入的包中的init函数将被执行并且仅执行一遍。

import _ "github.com/go-sql-driver/mysql"

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


需要注意的是,Go语言中不允许引入包却不在代码中使用这个包的内容,如果引入了未使用的包则会触发编译错误。


二、标准库


1. 标准库概述


标准库API

 

在 Go 的安装文件里包含了一些可以直接使用的包,即标准库。


在Windows下,标准库的位置在 Go 根目录下的子目录 pkg\windows_amd64中 ; 在 Linux下,标准库在 Go 根目录下的子目录 pkg\linux_amd64中(如果是安装的是32位,则在(linux_386目录中)。


一般情况下,标准包会存放在$GOROOT/pkg/$GOOS_$GOARCH/目录下。

2387773-20220325225618095-1809704564.png


Go 的标准库包含了大量的包(如: fmt和os),但是也可以创建自己的包。


如果想要构建一个程序,则包和包内的文件都必须以正确的顺序进行编译。包的依赖关系决定了其构建顺序。属于同一个包的源文件必须全部被一起编译,一个包即是编译时的一个单元,因此根据惯例,每个目录都只包含一个包。


如果对一个包进行更改或重新编译,所有引用了这个包的客户端程序都必须全部重新编译。


Go 中的包模型采用了显式依赖关系的机制来达到快速编译的目的,编译器会从后缀名为.o的对象文件(需要且只需要这个文件)中提取传递依赖类型的信息。


如果A.go依赖B.go,而B.go又依赖c.go:

编译c.go,B.go,然后是A.go

为了编译A.go,编译器读取的是 B.o 而不是c.o

这种机制对于编译大型的项目时可以显著地提升编译速度。


示例:

一个程序包含两个包: cat和main,其中 add 包中包含两个变量 Name 和 Age,请问 main 包中如何访问 Name 和 Age。


2387773-20220325225738080-1066203465.png


package cat
import "fmt"
var Name string = "tom"
var Age int = 5
//初始化函数
func init() {
 fmt.Println("this is cat package")
 fmt.Println("init函数修改前:", Name, Age)
 Name = "jack"
 Age = 3
 fmt.Println("init函数修改后:", Name, Age)
}
package main
import (
 //包的别名定义
 a "dev_code/day9/example3/cat"
 b "fmt"
)
func main() {
 b.Println("猫的名字:", a.Name)
 b.Println("猫的年龄:", a.Age)
}
输出结果如下
this is cat package
init函数修改前: tom 5
init函数修改后: jack 3
猫的名字: jack
猫的年龄: 3


  • 总结:

  • 调用其他包程序加载顺序:
    cat.go中的全局变量----->cat.go中的init()函数--------->main.go中的main()函数

 

 

2. 标准库常见的包及其功能


  • Go语言的标准库以包的方式提供支持,下表列出了Go语言标准库中常见的包及其功能。

 

Go语言标准库包名 功 能
bufio 带缓冲的 I/O 操作
bytes 实现字节操作
container 封装堆、列表和环形列表等容器
crypto 加密算法
database 数据库驱动和接口
debug 各种调试文件格式访问及调试功能
encoding 常见算法如 JSON、XML、Base64 等
flag 命令行解析
fmt 格式化操作
go Go语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改
html HTML 转义及模板系统
image 常见图形格式的访问及生成
io 实现 I/O 原始访问接口及访问封装
math 数学库
net

网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等

os 操作系统平台不依赖平台操作封装
path 兼容各操作系统的路径操作实用函数
plugin Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载
reflect 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值
regexp

正则表达式封装

runtime 运行时接口
sort 排序接口
strings 字符串转换、解析及实用函数
time 时间接口
text 文本模板及 Token 词法器

 

 

 

3. 程序执行顺序


Go 程序的执行(程序启动)顺序如下:

① 按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程:

② 如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。

③ 然后以相反的顺序在每个包中初始化常量和变量,如果该包含有init 函数的话,则调用该函数。

④ 在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。


2387773-20220325230433701-645848959.png


demo.go
package demo
import "fmt"
var Name string = "this is demo package"
var Age int = 20
func init() {
 fmt.Println("this is  demo  init()")
 fmt.Println("demo.package.Name=", Name)
 fmt.Println("demo.package.Age=", Age)
 Name = "this is demo New"
 Age = 200
 fmt.Println("demo.package.Name=", Name)
 fmt.Println("demo.package.Age=", Age)
}
main.go
package main
import (
 "dev_code/day9/example4/test"
 "fmt"
)
func main() {
 //main---->test----->demo
 fmt.Println("main.package:", test.Name)
 fmt.Println("main.package:", test.Age)
}
test.go
package test
import (
 "fmt"
 //对指定包做初始化,并不做调用处理
 _ "dev_code/day9/example4/demo"
)
var Name string = "this is  test  package"
var Age int = 10
func init() {
 fmt.Println("this is test init()")
 fmt.Println("test.package.Name=", Name)
 fmt.Println("test.package.Age=", Age)
 Name = "this is test New"
 Age = 100
 fmt.Println("test.package.Name=", Name)
 fmt.Println("test.package.Age=", Age)
}
执行结果如下
this is  demo  init()
demo.package.Name= this is demo package
demo.package.Age= 20
demo.package.Name= this is demo New
demo.package.Age= 200
this is test init()
test.package.Name= this is  test  package
test.package.Age= 10
test.package.Name= this is test New
test.package.Age= 100
main.package: this is test New
main.package: 100


2387773-20220325230540155-1114906631.png


包引用的时候顺序:

main引用add包,add再引用demo包;

编译执行流程:

先从demo包这里编译加载里面的全局变量,然后执行init函数,当init函数执行完成以后再去执行add中的全局变量,再执行里面的init函数,最后执行main包中的函数

 

相关文章
|
11天前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
43 4
|
5月前
|
Kubernetes Linux Go
使用 Uber automaxprocs 正确设置 Go 程序线程数
`automaxprocs` 包就是专门用来解决此问题的,并且用法非常简单,只需要使用匿名导入的方式 `import _ "go.uber.org/automaxprocs"` 一行代码即可搞定。
258 78
|
3月前
|
设计模式 Kubernetes Go
​​什么是Golang项目的“主包精简,逻辑外置”?​
“主包精简,逻辑外置”是Go语言项目的一种设计原则,强调将程序入口保持简单,核心逻辑拆分至其他包,以提升代码可维护性、可测试性及扩展性,适用于CLI工具、Web服务等场景。
95 7
|
Go
Golang的math包常用方法
这篇文章介绍了Golang的math包中的常量和常用方法,并通过示例代码展示了如何使用这些常量和方法。
321 87
Golang的math包常用方法
|
11月前
|
Go 数据处理 开发者
Go 语言的反射机制允许程序在运行时动态检查和操作类型信息,提供极大的灵活性和扩展性
Go 语言的反射机制允许程序在运行时动态检查和操作类型信息,提供极大的灵活性和扩展性。本文探讨了反射的基本原理、主要操作、应用场景及注意事项,并通过实例展示了反射的实际应用,帮助开发者更好地理解和使用这一强大特性。
141 2
|
Kubernetes Go 持续交付
一个基于Go程序的持续集成/持续部署(CI/CD)
本教程通过一个简单的Go程序示例,展示了如何使用GitHub Actions实现从代码提交到Kubernetes部署的CI/CD流程。首先创建并版本控制Go项目,接着编写Dockerfile构建镜像,再配置CI/CD流程自动化构建、推送Docker镜像及部署应用。此流程基于GitHub仓库,适用于快速迭代开发。
256 3
|
Kubernetes 持续交付 Go
创建一个基于Go程序的持续集成/持续部署(CI/CD)流水线
创建一个基于Go程序的持续集成/持续部署(CI/CD)流水线
|
Go
Golang语言之包依赖管理
这篇文章详细介绍了Go语言的包依赖管理工具,包括godep和go module的使用,以及如何在项目中使用go module进行依赖管理,还探讨了如何导入本地包和第三方库下载的软件包存放位置。
258 4
|
存储 Go
Golang语言基于go module方式管理包(package)
这篇文章详细介绍了Golang语言中基于go module方式管理包(package)的方法,包括Go Modules的发展历史、go module的介绍、常用命令和操作步骤,并通过代码示例展示了如何初始化项目、引入第三方包、组织代码结构以及运行测试。
415 3
|
Go
Golang语言基于GOPATH方式管理包(package)
这篇文章详细介绍了Golang语言中基于GOPATH方式管理包(package)的方法,包括包的概述、定义、引入格式、别名使用、匿名引入,以及如何快速入门自定义包,并通过具体代码案例展示了包的环境准备、代码编写、细节说明和程序运行。
136 3

推荐镜像

更多