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. Note -「矩阵树定理」学习笔记

      大概--会很简洁吧 qwq. 矩阵树定理   对于无自环无向图 \(G=(V,E)\),令其度数矩阵 \(D\),邻接矩阵 \(A\),令该图的 \(\text{Kirchhoff}\) 矩阵 \ ...

  2. JVM基础学习(一):JVM内存模型

    在Java进阶知识的学习中,JVM都是避不过去的一关,我个人对于JVM的理解其实就是相当于在操作系统的外层再加了一层中间层,从来屏蔽了具体硬件之间的不同实现,使得Java实现了最重要的特性:一次编译, ...

  3. JUC并发工具类之 CyclicBarrier同步屏障

    首先看看CyclicBarrier的使用场景: 10个工程师一起来公司应聘,招聘方式分为笔试和面试.首先,要等人到齐后,开始笔试:笔试结束之后,再一起参加面试.把10个人看作10个线程,10个线程之间 ...

  4. k8s-cka考试题库

    本次测试的所有问题都必须在指定的cluster配置环境中完成.为尽量减少切换,系统已对问题进行分组,同一cluster内的所有问题将连续显示. 开启TAB补全 做题前先配置k8s自动补齐功能,否则无法 ...

  5. c++基础的记录(随笔记录一些基础的东西)

    1.父类的析构函数为什么要加上virtual关键字. 比如说,父类A,子类B.在A* a = new B()的语句的时候,如果父类析构函数没有virtual,我们在delete指针a的时候,会走父类的 ...

  6. 2022李宏毅作业hw1—新冠阳性人员数量预测。

    ​ 事前  : kaggle地址:ML2021Spring-hw1 | Kaggle 我的git地址: https://github.com/xiaolilaoli/lihongyi2022homew ...

  7. mysql 的奇妙历险

    mysql 的奇妙历险 这几天在练习sql的时候,碰到下面几个题, 如下 他的表字段是这些 create table Student( SId varchar(10), # 学生id Sname va ...

  8. Ng ML笔记

    目录 一.线性回归 1,假设函数.代价函数,梯度下降 2,特征处理 3,代价函数和学习速率 4,特征和多项式回归 5,正规方程 二.逻辑回归(Logistic Regression,LR) 1,假设函 ...

  9. 优达学城 UdaCity 纳米学位

    优达学城 UdaCity 纳米学位 Num Course desc 1 AI Programming with Python 使用Python编程基础 2 Android Basics 安卓基础 3 ...

  10. Docker入坑系列(二)

    Docker入坑系列(二) 上一篇我们为Docker创造了一个良好的生活环境,这一篇我们就开始让Docker活起来. 安装Docker ok,原文地址在这里. 当然,我只是自己翻译了一下而已- -跟着 ...