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 包了解以及程序的执行的更多相关文章

  1. 利用命令行引用外部jar包以使程序正常执行的4种方法

    声明:本博客为原创博客.未经同意.不得转载!原文链接为http://blog.csdn.net/bettarwang/article/details/30976069 平时写一些小的Java Demo ...

  2. golang包time用法详解

    在我们编程过程中,经常会用到与时间相关的各种务需求,下面来介绍 golang 中有关时间的一些基本用法,我们从 time 的几种 type 来开始介绍. 时间可分为时间点与时间段,golang 也不例 ...

  3. ajax跨域往php程序post数据时,php程序总是执行两次的解决方法

    php程序是部署在IIS7上面,ajax提交数据时,遇到了两个问题,一个就是跨域,一个php程序总会被执行两次. 第一个问题的解决方法,是百度出来的,添加下面几行代码就可以了: header('Acc ...

  4. 怎么优化JAVA程序的执行效率和性能?

    现在java程序已经够快的了,不过有时写出了的程序效率就不怎么样,很多细节值得我们注意,比如使用StringBuffer或者StringBuilder来拼接或者操作字符串就比直接使用String效率高 ...

  5. Java中的static关键字解析(转自海子)__为什么main方法必须是static的,因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

    Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...

  6. 理解Golang包导入

    Golang使用包(package)这种语法元素来组织源码,所有语法可见性均定义在package这个级别,与Java .python等语言相比,这算不上什么创新,但与C传统的include相比,则是显 ...

  7. Golang包管理工具glide简介

    Golang包管理工具glide简介 前言 Golang是一个十分有趣,简洁而有力的开发语言,用来开发并发/并行程序是一件很愉快的事情.在这里我感受到了其中一些好处: 没有少了许多代码格式风格的争论, ...

  8. golang包引用解析

    golang包引用解析 环境变量配置如下: GOROOT----[C:\Go] GOPATH----[F:\workspace\go_home] vs code配置如下: F:\workspace\g ...

  9. Android开发第一讲之目录结构和程序的执行流程

    1.如何在eclipse当中,修改字体 下面的这种办法,可以更改xml的字体 窗口--首选项--常规--外观--颜色和字体--基本--文本字体--编辑Window --> Preferences ...

随机推荐

  1. suse 12 二进制部署 Kubernetets 1.19.7 - 第07章 - 部署kube-controller-manager组件

    文章目录 1.7.部署kube-controller-manager 1.7.0.创建kube-controller-manager请求证书 1.7.1.生成kube-controller-manag ...

  2. 关于 ios 动画枚举翻译

    例子 + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewA ...

  3. 《手把手教你》系列技巧篇(六十九)-java+ selenium自动化测试 - 读取csv文件(详细教程)

    1.简介 在实际测试中,我们不仅需要读取Excle,而且有时候还需要读取CSV类的文件.如何去读取CSV的文件,宏哥今天就讲解和分享一下,希望对你能够有所帮助.前面介绍了如何读取excel文件,本篇介 ...

  4. 免费开源Blazor在线Ico转换工具

    行文目录 功能效果演示 实现说明 2.1 其他图片上传 2.2 核心代码:其他图片转Ico 2.3 转换后的Ico文件下载 总结 1. 功能效果演示 仓库地址:IcoTool 在线演示地址:https ...

  5. CobaltStrike逆向学习系列(15):CS功能分析-BOF

    这是[信安成长计划]的第 15 篇文章 0x00 目录 0x01 BOF功能分析 0x02 BOF功能执行 0x03 写在最后 其实在看过 RDI 与 DotNet 功能执行之后,BOF 的执行基本就 ...

  6. 巧用阿里云同步k8s.gcr镜像

    问题 谷歌云镜像仓库:k8s.gcr.io 镜像拉取不下来 阿里云操作配置 注册阿里云账号:点击注册 右上角点击"控制台" 点击左上角 选择"容器镜像服务" 第 ...

  7. HMS Core在MWC2022展示最新开放能力,助力开发者构建精品应用

    [2022年2月28日,巴塞罗那]世界移动通信大会MWC2022在巴塞罗那开幕.HMS Core设立了3个展台(Fira Gran Via,Hall 1),向全球开发者展示HMS Core 6的全新开 ...

  8. [error]ModuleNotFoundError: No module named 'MySQLdb'

    错误详情(错误详情如标题,图是网上找的): 解决方法: 在项目目录下 settings.py 的同级目录中的 __init__.py 中加入如下代码: import pymysql ​ pymysql ...

  9. RFC3918协议测试——网络测试仪实操

    一.简介1.RFC3918简介历史· 在1999年3月成为正式标准 功能· 评测网络互连设备或网络系统的性能· 网络设备: 交换机,路由器- 内容· 定义了一整套测试方法,为不同厂家的设备/系统提供了 ...

  10. 【C# 线程】C++与C#数据类型对照表

    详细:http://lzltool.com/doc/csharptocaddadd