golang_包

 

简介

Go 语言的源码复用建立在包(package)基础之上。Go 语言的入口 main() 函数所在的包(package)叫 main,main 包想要引用别的代码,必须同样以包的方式进行引用

Go 语言的包与文件夹一一对应,所有与包相关的操作,必须依赖于工作目录(GOPATH)。

 

GOPATH

GOPATH 是 Go 语言中使用的一个环境变量,它使用绝对路径提供项目的工作目录。GOPATH 适合处理大量 Go 语言源码、多个包组合而成的复杂工程。

使用GOPATH的工程结构

在 GOPATH 指定的工作目录下,代码总是会保存在 $GOPATH/src 目录下。在工程经过 go build、go install 或 go get 等指令后,会将产生的二进制可执行文件放在 $GOPATH/bin 目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg 下。

如果需要将整个源码添加到版本管理工具(Version Control System,VCS)中时,只需要添加 $GOPATH/src 目录的源码即可。bin 和 pkg 目录的内容都可以由 src 目录生成。

一般地,GO项目在GOPATH洗有如下三个目录

|--bin

|--pkg

|--src


- bin存放编译后的可执行文件;
- pkg存放编译后的包文件;
- src存放项目源文件

 

go env

// 我的windows系统go env
D:\go dev\PROJECT\bin>go env set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\fat39\AppData\Local\go-build set GOEXE=.exe set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOOS=windows set GOPATH=D:\go dev\PROJECT set GOPROXY= set GORACE= set GOROOT=C:\Go set GOTMPDIR= set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64 set GCCGO=gccgo set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD= set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\fat39\AppData\Local\Temp\go-build637627194=/tmp/go-build - gno-record-gcc-switches

在 Go 1.8 版本之前,GOPATH 环境变量默认是空的。从 Go 1.8 版本开始,Go 开发包在安装完成后,将 GOPATH 赋予了一个默认的目录,参见下表。

 

GOPATH 在不同平台上的安装路径
平  台GOPATH 默认值举 例
Windows 平台 %USERPROFILE%/go C:\Users\用户名\go
Unix 平台 $HOME/go /home/用户名/go

 

package

语法
package 包名

举例:package main

包的特性如下:
  • 一个目录下的同级文件归属一个包。
  • 包名可以与其目录不同名。
  • 包名为 main 的包为应用程序的入口包,编译源码没有 main 包时,将无法编译输出可执行的文件。

包引入

包引入的方法

方法一:单行导入

import "fmt"
import "os"

方法二:

import "fmt"; import "os"

方法三(推荐!)

import (
   "fmt"
   "os"
)

 

 引入自定义包

go语言中,引入包的路径是根据GOPATH/src 这个环境变量作为相对路径进行引入的,如果GOPATH存在多个,那么go将逐一寻找路径,直到找到,若GOPATH/src未找到则编译出错。

 

给引入的包改名

package main
import (
    renameLib "chapter08/importadd/mylib"
    "fmt"
)

// renameLib是别名,后续代码使用renameLib

使用"."引入包 

import . "fmt"    

// 将fmt启用别名".",这样就可以直接使用其内容,而不用再添加fmt。
// 如fmt.Println可以直接写成Println。

 

匿名引入包"_"

如果只希望导入包,而不使用任何包内的结构和类型,也不调用包内的任何函数时,可以使用匿名导入包,使用下划线"_".匿名导入的包与其他方式导入包一样会让导入包编译到可执行文件中,同时,导入包也会触发 init() 函数调用。

import (
    _ "path/to/package"
)

 

 func init ——程序启动前的初始化入口

init() 函数的特性如下:
每个源码可以使用 1 个 init() 函数。
init() 函数会在程序执行前(main() 函数执行前)被自动调用。
调用顺序为 main() 中引用的包,以深度优先顺序初始化。

例如,假设有这样的包引用关系:main→A→B→C,那么这些包的 init() 函数调用顺序为:
C.init→B.init→A.init→main
说明:
同一个包中的多个 init() 函数的调用顺序不可预期。
init() 函数不能被其他函数调用。

验证init顺序

package A

import (
	_ "test/B"
	"fmt"
)


func init(){
	fmt.Println("A init")
}
a.go
package B

import "fmt"

func init(){
	fmt.Println("B init")
}
b.go
package main

import (
	_ "test/A"
	"fmt"
)

func init(){
	fmt.Println("main init")
}

func main(){
	fmt.Println("main run")
}
main.go
D:\go dev\PROJECT\bin>go build -o testInit.exe test/main

D:\go dev\PROJECT\bin>testInit.exe
B init
A init
main init
main run

 使用init自动注册类

package main
import (
	"test/clsfactory/base"
	_ "test/clsfactory/cls1"  // 匿名引用cls1包, 自动注册
	_ "test/clsfactory/cls2"  // 匿名引用cls2包, 自动注册
)
func main() {
	// 根据字符串动态创建一个Class1实例
	c1 := base.Create("Class1")
	c1.Do()
	// 根据字符串动态创建一个Class2实例
	c2 := base.Create("Class2")
	c2.Do()
}
main.go
package base
// 类接口
type Class interface {
	Do()
}
var (
	// 保存注册好的工厂信息
	factoryByName = make(map[string]func() Class)
)
// 注册一个类生成工厂
func Register(name string, factory func() Class) {
	factoryByName[name] = factory
}
// 根据名称创建对应的类
func Create(name string) Class {
	if f, ok := factoryByName[name]; ok {
		return f()
	} else {
		panic("name not found")
	}
}
factory.go
package cls1
import (
	"test/clsfactory/base"
	"fmt"
)
// 定义类1
type Class1 struct {
}
// 实现Class接口
func (c *Class1) Do() {
	fmt.Println("Class1")
}
func init() {
	// 在启动时注册类1工厂
	base.Register("Class1", func() base.Class {
		return new(Class1)
	})
}
cls1/reg.go
package cls2
import (
	"test/clsfactory/base"
	"fmt"
)
// 定义类2
type Class2 struct {
}
// 实现Class接口
func (c *Class2) Do() {
	fmt.Println("Class2")
}
func init() {
	// 在启动时注册类2工厂
	base.Register("Class2", func() base.Class {
		return new(Class2)
	})
}
cls2/reg.go
D:\go dev\PROJECT\bin>clsfactory.exe
Class1
Class2

 

 包中的标识符

在 Go 语言中,如果想在一个包里引用另外一个包里的标识符(如类型、变量、常量等)时,必须首先将被引用的标识符导出,将要导出的标识符的首字母大写就可以让引用者可以访问这些标识符了。

- 首字母都为小写,这些标识符可以在包内自由使用,但是包外无法访问它们
- 在被导出的结构体或接口中,如果它们的字段或方法首字母是大写,外部可以访问这些字段和方法

 

posted @ 2019-02-18 00:28  fat39  阅读(272)  评论(0)    收藏  举报