Go语言--第8章 包(package)

第8章 包(package)

8.1 工作目录

GOPATH是go语言使用的一个环境 变量,使用绝对路径

8.1.1 命令行查看GOPATH信息

go env

显示go开发环境配置信息:

$ go env
// 表示目标处理器架构
GOARCH="amd64"
//表示编译器和链接器的安装位置
GOBIN=""
GOCACHE="/Users/jihan/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
//目标操作系统
GOOS="darwin"
//当前工作目录
GOPATH="/Users/jihan/go"
GOPROXY=""
GORACE=""
//GO开发包安装目录
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/vm/zby04lq56tg1y08qs5c6kkq80000gn/T/go-build459742728=/tmp/go-build -gno-record-gcc-switches -fno-common"

GOPATH在不同平台上的安装路径

平台 GOPATH默认值 举例
Window平台 %USERPROFILE%/go C:Users用户名go
Uninx平台 $HOME/go /home/用户名/go

8.1.2 使用GOPATH的工程结构

GOPATH工作目录下,代码保存在$GOPATH/src目录

通过go buildgo installgo get等命令,将产生二进制可执行文件放在(GOPATG/bin目录**下,生成中间缓存文件保存放在**)GOPATG/pkg目录

若需要将整个源码添加到版本控制工具中,只需将$GOPATH/src目录源码添加即可

8.1.3 设置和使用GOPATH

  1. 设置当前工作目录为GOPATH
  2. 建立GOPATH中源码目录
  3. 添加main.go源码文件
  4. 编译源码并运行

以下示例为linux平台操作

设置当前工作目录为GOPATH

export GOPATH=path

例如:

GOPATH="/Users/jihan/go"

export GOPATH=/Users/jihan/go

建立GOPATH中源码目录

mkdir -p src/hello

添加main.go源码文件

创建$GOPATH/src/main.go

package main
import "fmt"
func main() {
  fmt.Println("hello")
}

编译源码并运行

go install hello

编译完可执行文件会保存在$GOPATH/bin目录下

./hello //进行执行

8.2 创建包package -- 编写自己的代码扩展

包要求在目录下的所有文件的第一行添加

package 包名

特性:

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

8.3 导出标识符 -- 让外部访问包的类型和值

如果一个包引用另一个包的标识符(类型、变量、 常量时)必须首先将被引用的标识符导出

package mypkg
var myVar = 100
const myConst = "go"
type myStruct struct {}

将myStruct和myConst首字母大写,导出这些标识符

package mypkg
var MyVar = 100
const MyConst = "go"
type MyStruct struct {}

导出结构体及接口成员

结构体和接口成员同样字段或方法首字母大写,外部可以访问

type MyStruct struct {
  //包外可以访问的字段
  ExportField int
}

8.4 导入包(import) -- 在代码中使用其他的代码

使用import关键字,导入包包名使用双引号包围

8.4.1默认导入写法

  1. 单行导入

    import "包1"
    import "包2"
    
  2. 多行导入

    import (
    	"包1"
    	"包2"
    ) 
    

导入包例子

importadd/mylib/add.go

package mylib

func Add(a, b int) int {
	return a + b
}

eight/8.4/main.go

package main
import (
  // 文件目录相对GOPATH的相对路径
	"../../eight/8.4/mylib"
	"fmt"
)
func main() {
	fmt.Println(mylib.Add(1, 2))
}

8.4.2 自定义命名导入包名

格式

customName "path	opackage"
  • path/to/package 导入包路径

  • customName 自定义包名

package main
import (
	renameMylib "../../eight/8.4/mylib"
	"fmt"
)
func main() {
	fmt.Println(renameMylib.Add(1, 2))
}

8.4.3 匿名导入包 -- 只导入包但不适用包内类型和数值

package main
import (
	_ "../../eight/8.4/mylib"
	"fmt"
)
func main() {
	// fmt.Println(renameMylib.Add(1, 2))
}

8.4.4 包在程序启动前的初始化入口:init

特性:init()函数

  • 每个源码可以食欲哦那个1个init()函数

  • init()会在程序执行前(main()函数执行前)被自动调用

  • 调用顺序为main()中引用的包,以深度优先顺序初始化

    例如:包引用关系 main -> A->B->C 调用顺序为:c.init->B.init->A.init->main

  • 同一个包的多个init()函数调用顺序不可以预期

  • init()不能憋其他函数调用

8.4.5 理解包导入后的init()函数初始化顺序

go语言包会从main包开始检查其所有引用所有包,每个包也可能包含其他的包

go编译器由此构建一个树状的包引用关系,在根据引用顺序决定编译顺序,依次编译这些包的代码

在运行时,被最后导入的包会最先初始化并调用init()函数

例子:

文件目录:

.../eight/8.4.5
.../eight/8.4.5/pkginit
.../eight/8.4.5/pkginit/pkg1
.../eight/8.4.5/pkginit/pkg1/pkg1.go
.../eight/8.4.5/pkginit/pkg2
.../eight/8.4.5/pkginit/pkg2/pkg2.go
.../eight/8.4.5/pkginit/main.go

.../eight/8.4.5/pkginit/main.go

package main
import (
	"../../../eight/8.4.5/pkginit/pkg1"
)
func main()  {
	pkg1.ExecPkg1()
}

.../eight/8.4.5/pkginit/pkg1/pkg1.go

package pkg1

import(
	"../../../../eight/8.4.5/pkginit/pkg2"
	"fmt"
)
func ExecPkg1()  {
	fmt.Println("ExecPkg1")
	pkg2.ExecPkg2()
}
func init()  {
	fmt.Println("pkg1  init")
}

.../eight/8.4.5/pkginit/pkg2/pkg2.go

package pkg2

import(
	"fmt"
)
func ExecPkg2()  {
	fmt.Println("ExecPkg2")
}
func init()  {
	fmt.Println("pkg2  init")
}

运行结果:

pkg2 init
pkg1 init
ExecPkg1
ExecPkg2

8.5 示例:工厂模式自动注册--管理过个包的结构体

利用包init()函数特性,将cls1和cls2两个包注册到工厂,使用字符串创建这两个注册好的结构实例

$HOME:src目录路径

目录结构:

$HOME/eight/8.5
$HOME/eight/8.5/clsfactory
$HOME/eight/8.5/clsfactory/base
$HOME/eight/8.5/clsfactory/base/factory.go
$HOME/eight/8.5/clsfactory/cls1
$HOME/eight/8.5/clsfactory/cls1/reg.go
$HOME/eight/8.5/clsfactory/cls2
$HOME/eight/8.5/clsfactory/cls2/reg.go
$HOME/eight/8.5/clsfactory/main.go

$HOME/eight/8.5/clsfactory/base/factory.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")
	}
}

$HOME/eight/8.5/clsfactory/cls1/reg.go

package cls1

import (
	"../../../../eight/8.5/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)
	})
}

$HOME/eight/8.5/clsfactory/cls2/reg.go

package cls2

import (
	"../../../../eight/8.5/clsfactory/base"
	"fmt"
)

// 定义类2
type Class2 struct {

}
// 实现Class接口
func (c  *Class2) Do() {
	fmt.Println("Class2")
}

func init()  {
	// 在启动时注册类1的工厂
	base.Register("Class2", func() base.Class {
		return new (Class2)
	})
}

$HOME/eight/8.5/clsfactory/main.go

package main

import(
	"../../../eight/8.5/clsfactory/base"
	_ "../../../eight/8.5/clsfactory/cls1"
	_ "../../../eight/8.5/clsfactory/cls2"
)

func main()  {
	// 根据字符串动态创建一个Class1的实例
	c1 := base.Create("Class1")
	c1.Do()
	// 根据字符串动态创建一个Class2的实例
	c2 := base.Create("Class2")
	c2.Do()
}

运行结构:

Class1
Class2

原文地址:https://www.cnblogs.com/smallyi/p/12684478.html