Golang 包了解以及程序的执行
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/目录下。
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。

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 函数开始执行程序。

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

包引用的时候顺序:
main引用add包,add再引用demo包;
编译执行流程:
先从demo包这里编译加载里面的全局变量,然后执行init函数,当init函数执行完成以后再去执行add中的全局变量,再执行里面的init函数,最后执行main包中的函数
Golang 包了解以及程序的执行的更多相关文章
- 利用命令行引用外部jar包以使程序正常执行的4种方法
声明:本博客为原创博客.未经同意.不得转载!原文链接为http://blog.csdn.net/bettarwang/article/details/30976069 平时写一些小的Java Demo ...
- golang包time用法详解
在我们编程过程中,经常会用到与时间相关的各种务需求,下面来介绍 golang 中有关时间的一些基本用法,我们从 time 的几种 type 来开始介绍. 时间可分为时间点与时间段,golang 也不例 ...
- ajax跨域往php程序post数据时,php程序总是执行两次的解决方法
php程序是部署在IIS7上面,ajax提交数据时,遇到了两个问题,一个就是跨域,一个php程序总会被执行两次. 第一个问题的解决方法,是百度出来的,添加下面几行代码就可以了: header('Acc ...
- 怎么优化JAVA程序的执行效率和性能?
现在java程序已经够快的了,不过有时写出了的程序效率就不怎么样,很多细节值得我们注意,比如使用StringBuffer或者StringBuilder来拼接或者操作字符串就比直接使用String效率高 ...
- Java中的static关键字解析(转自海子)__为什么main方法必须是static的,因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...
- 理解Golang包导入
Golang使用包(package)这种语法元素来组织源码,所有语法可见性均定义在package这个级别,与Java .python等语言相比,这算不上什么创新,但与C传统的include相比,则是显 ...
- Golang包管理工具glide简介
Golang包管理工具glide简介 前言 Golang是一个十分有趣,简洁而有力的开发语言,用来开发并发/并行程序是一件很愉快的事情.在这里我感受到了其中一些好处: 没有少了许多代码格式风格的争论, ...
- golang包引用解析
golang包引用解析 环境变量配置如下: GOROOT----[C:\Go] GOPATH----[F:\workspace\go_home] vs code配置如下: F:\workspace\g ...
- Android开发第一讲之目录结构和程序的执行流程
1.如何在eclipse当中,修改字体 下面的这种办法,可以更改xml的字体 窗口--首选项--常规--外观--颜色和字体--基本--文本字体--编辑Window --> Preferences ...
随机推荐
- Solution -「AT 3913」XOR Tree
\(\mathcal{Description}\) Link. 给定一棵树,边 \((u,v)\) 有边权 \(w(u,v)\).每次操作可以使一条简单路径上的边权异或任意非负整数.求最少的操 ...
- Spring 配置概述
理解了IoC的概念,那Spring框架是如何具体操作的呢?Spring IoC容器(ApplicaitonContext)负责创建Bean,并通过容器将功能类Bean注入到其他需要的Bean中.Spr ...
- const 和指针之间的姻缘
const和指针到底有何姻缘呢? char const *p = NULL; //char const 和 const char 是一样的,p 是一个指向常整型的指针变量 ,指针变量的值不能改变 ch ...
- java高版本下各种JNDI Bypass方法复现
目录 0 前言 1 Java高版本JNDI绕过的源代码分析 1.1 思路一的源码分析 1.2 思路二的源码分析 2 基于本地工厂类的利用方法 2.1 org.apache.naming.factory ...
- web安全之快速反弹 POST 请求
在 CTF Web 的基础题中,经常出现一类题型:在 HTTP 响应头获取了一段有效期很短的 key 值后,需要将经过处理后的 key 值快速 POST 给服务器,若 key 值还在有效期内,则服务器 ...
- [杂记]BrainFuck语言及编译器(c++实现)
BrainFuck语言 极简的一种图灵完备的语言,由Urban Müller在1993年创造,由八个指令组成(如下表).工作机制与图灵机非常相似,有一条足够长的纸带,初始时纸带上的每一格都是0,有一个 ...
- MegaRAID管理工具操作手册-从零到无
--时间:2021年1月25日 --作者:飞翔的小胖猪 前言 文档约定红色字体的E表示Enclosure Device ID.红色字体S表示Slot Number.红色字体A表示Adapter号.红色 ...
- spark submit local遇到路径hdfs的问题
有时候第一次执行 spark submit --master local[*] 单机模式的时候,可以对linux本地路径进行输出.但是有时候提交到yarn的时候,是自动加上hdfs的路径这没问题, 但 ...
- 基于Kubernetes/K8S构建Jenkins持续集成平台(上)-2
基于Kubernetes/K8S构建Jenkins持续集成平台(上)-2 Kubernetes实现Master-Slave分布式构建方案 传统Jenkins的Master-Slave方案的缺陷 Mas ...
- Linux下安装mysql你又踩过多少坑【宇宙最全教程】
一.检查以前是否安装过MySql 因为cnetos7一般默认安装mariadb,所以要检查mysql或者mariadb是否安装 rpm -pa | grep -i mysql rpm -pa | gr ...