Golang基础-8

本文详细介绍了Go语言中的包结构、包声明与导入,包成员访问权限,init函数和defer关键字的用法,以及panic与recover用于异常处理的方式。此外,还涵盖了GoModules包管理工具和godoc文档工具的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Go语言基础

介绍

基础

介绍

  • 本文介绍Go语言中包(包声明、包导入与调用、包成员访问权限、init函数、包管理(Go modules)等)、defer关键字、panic 与 recover、go doc 工具等相关知识。

基础

  • Go语言源码高复用是以包的形式划分源代码。
  • 包就是源文件(函数、数据等组成的*.go文件)的集合(目录),将各自相关的功能源码放到同一个目录下,以独立的模块维护,提供给其它项目使用。
包声明
  • Go源文件中,使用 package 声明包的路径,编译器依据包路径找到对应包源代码并编译库文件,包中可包含多个原文件。
  • 包声明使用 package 包名称。
  • 包名称一般与其源文件所在目录的名称(没有强制要求)。
  • 包名称命名使用简短明确的小写字母,同一个包中的源文件必须使用相同的包名。
// 自定义了两个包,整个应用目录结构如下
 -- test_go01
   |__ src  // 源文件相关信息
        |__ main  // main包
			  |__ main.go
		|__ pkg_my_add  // 自定义包
			  |__ add.go
		|__ pkg_my_sub  // 自定义包
			  |__ sub.go

// main.go
package main

import (
	"fmt"
	"pkg_my_add"   // 导入包 pkg_my_add,注意需要配置GOPATH
	"pkg_my_sub"   // 导入包 pkg_my_sub,注意需要配置GOPATH
)

func main() {
	var a int = 10
	var b int = 20
	var c float32 = 30.3
	var d float32 = 40.5

	fmt.Println("a + b = ", pkg_my_add.Add(a, b))
	fmt.Println("c + d = ", pkg_my_add.Add(c, d))
	fmt.Println("a - b = ", pkg_my_sub.Sub(a, b))
	fmt.Println("c - d = ", pkg_my_sub.Sub(c, d))
}

// add.go
package pkg_my_add  // 声明包

// 普通函数
// func Add(a int, b int) int {
// 	return a + b
// }

// 使用泛型,支持 int, float32类型
func Add[T int | float32](a, b T) T {
	return T(a + b)
}

// sub.go
package pkg_my_sub  // 声明包

// 普通函数
// func Sub(a int, b int) int {
// 	return a - b
// }

// 使用泛型,支持 int, float32类型
func Sub[T int | float32](a, b T) T {
	return T(a - b)
}

输出结果
a + b = 30
c + d = 70.8
a - b = -10
c - d = -10.200001

包导入与调用
  • Go语言中使用 import 按路径名导入需要使用的包,通过包名称访问包中对外可见内容。
  • Go 语言不允许交叉导入包,import 导入方式有多种,随后分别介绍。
  • 工作目录结构:

bin目录: 放置发布的二进制程序
pkg目录: 放置发布的库文件
src目录: 放置源代码

  • 配置工程路径到GOPATH环境变量中,默认从($GOPATH/src路径开始)。
// 在终端使用指令 go env 查看go环境变量

C:\Users\Administrator>go env
set GO111MODULE=off
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOENV=C:\Users\Administrator\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=D:\GoProject\test_go01\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\Administrator\go;
set GOPRIVATE=
set GOPROXY=https://goproxy.cn
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.22.0
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=0
set GOMOD=
set GOWORK=
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\Administrator\AppData\Local\Temp\go-build4166079327=/tmp/go-build -gno-record-gcc-switches

// 看到本机默认 GOPATH=C:\Users\Administrator\go;
// 工程路径在 D:\GoProject\test_go01 目录下
// (Windows10为例)选择桌面此电脑 -> 右键鼠标 -> 属性 -> 高级系统设置 -> 环境变量 -> 用户变量栏中会看到GOPATH变量 -> 选中并点击编辑 -> 添加工程路径 D:\GoProject\test_go01 -> 依次确定退出配置

// 重新打开终端运行 go env 查询 GOPATH
set GOPATH=D:\GoProject\test_go01;C:\Users\Administrator\go;

// 配置完GOPATH,在 main.go 中可导入包使用
  • 编译运行

    • 在终端窗口进入目录 D:\GoProject\test_01\main 目录。
    • 执行指令 go build main.go 编译并在当前目录下生成 main.exe 二进制可执行程序。
    • 若要指定路径和指定二进制程序名称,执行指令 go build -o 路径文件名称 main.go。
    • 执行指令 go run main.exe 运行程序。
    • 执行指令 go install main 编译并发布运行程序,将当前目录下 main.exe 拷贝到 test_go01 目录下的 bin 目录,会自动创建 bin 目录。
    • 执行指令 go install …\pkg_my_add 发布单个库,会自动创建 pkg 目录,与 bin 目录同级。
    • 执行指令 go install … 发布此路径下的所有库,会自动创建 pkg 目录,与 bin 目录同级。
  • 使用绝对路径导入包(GOPATH 是路径前缀, D:\GoProject\test_go01)。

// D:\GoProject\test_go01\src\pkg_my_add
// D:\GoProject\test_go01\src\pkg_my_sub
import (
	"fmt"
	"pkg_my_add"
	"pkg_my_sub"
)
  • 使用相对路径导入包(在当前文件所在的目录查找)。
// D:\GoProject\test_go01\src\main\..\pkg_my_add
// D:\GoProject\test_go01\src\main\..\pkg_my_sub
import (
	"fmt"
	"../pkg_my_add"
	"../pkg_my_sub"
)
  • 使用点导入包(使用包中的成员时可以省略包名)。
package main

import (
	. "fmt"
)

func main() {
	Println("fmt println")  // 省略包名
}
  • 使用别名导入(可以给包重命名)。
package main

import (
	"fmt"
	format "fmt"
)

func main() {
	format.Println("formt println")
	fmt.Println("fmt println")
}
  • 使用下划线导入(可以导入包但不使用,这种情况下包的初始化函数正常执行)。
package main

import (
	"fmt"
	_ "pkg_my_add"
)

func main() {
	fmt.Println("fmt println")
}
包成员访问权限
  • Go语言中,通过变量或函数名称首字母大小写来控制包成员对外的访问权限,首字母大写表示公开的,外部可访问,首字母小写表示私有的,只允许包内部访问。
  • main 包用于声明告知编译器将包编译为二进制可执行文件,main 包中的 main 函数是程序的入口,无返回值,无参数。
  • 同包下的不同文件间的不同内容引用时,需要这几个源文件文件一起编译。
// 假设 main 包下存在 main.go test.go 两个源文件
// test.go
package main

import "fmt"

func test() {
	fmt.Println("test func")
}

// main.go
package main

import (
	"fmt"
)

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

// 执行编译指令: go build .\main.go .\test.go

输出结果:
test func
main func

init函数
  • init 函数无返回值,无参数, 用于包的初始化,是一种特殊的函数。
  • init 函数在 import包时自动被调用。不能被显示调用。
  • 多个包同时导入另一个包时,init 函数在应用程序运行期间只会自动调用一次。
package main

import (
	"fmt"
)

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

// 自动调用 init()
func main() {
	fmt.Println("main func")

	// init() // 不支持显示调用
}

输出结果
init func
main func

  • 若在同一个包中存在多个 init 函数,同一个源文件中定义的多个 init 函数会按照在代码中的出现顺序执行,多个源文件中的 init 函数执行顺序不确定。
defer关键字
  • defer关键字将其后面跟随的语句进行延迟处理,在函数 return 之后、退出之前调用。
  • 一般用于资源释放、异常捕获、记录日志等。
  • 相同作用域内多个函数使用 defer 声明,执行时与声明顺序相反(栈特点:先进后出)。
package main

import (
	"fmt"
)

func main() {

	fmt.Println("1 exec")
	defer fmt.Println("2 exec")
	defer fmt.Println("3 exec")
	defer fmt.Println("4 exec")
	defer test()
	fmt.Println("5 exec")
}

func test() {
	fmt.Println("test func")
}

输出结果
1 exec
5 exec
test func
4 exec
3 exec
2 exec

  • defer 声明时,其对应的参数会根据当前获取的数据作为参数,也就是说将当前传入时的变量参数作为入参,不会等到函数调用时掺入形参。
package main

import (
	"fmt"
)

func main() {

	var a int = 10
	defer test1(a)
	defer test2(a)
	defer func() {
		fmt.Println("func a = ", a)
	}()

	a++
	fmt.Println("main a = ", a)
}

func test1(a int) {
	fmt.Println("test1 a = ", a)
}

func test2(a int) {
	fmt.Println("test2 a = ", a)
}

输出结果
main a = 11
func a = 11
test2 a = 10
test1 a = 10

  • defer语句放在return后面时不会被执行。
package main

import (
	"fmt"
)

func main() {

	fmt.Println("return before")
	return
	defer fmt.Println("return after")
}

输出结果: return before

  • 函数返回值匿名时,执行 return 语句后,defer 操作无法对返回值进行修改。
func test() int {
	var ret int
	defer func() {
		ret++
		fmt.Println("defer1 func: ", ret)
	}()

	defer func() {
		ret++
		fmt.Println("defer2 func: ", ret)
	}()
	return ret
}

func main() {
	fmt.Println("test value:", test())
}

输出结果
defer2 func: 1
defer1 func: 2
test value: 0

  • 函数返回值命名时,执行 return 语句时,defer 操作对返回值修改。
package main

import "fmt"

func test() (ret int) {
	defer func() {
		ret++
		fmt.Println("defer1 func: ", ret)
	}()
	defer func() {
		ret++
		fmt.Println("defer2 func: ", ret)
	}()
	return ret
}

func main() {
	fmt.Println("test value: ", test())
}

输出结果
defer2 func: 1
defer1 func: 2
test value: 2

panic 与 recover
  • panic 和 recover是用于异常处理的两个关键概念,用于处理程序运行时出现的错误。
  • panic 用于异常的触发,程序调用 panic 抛出错误时,程序会被中断,不会继续执行 panic 之后的程序逻辑,defer 声明的函数会被执行。
package main

import "fmt"

func main() {
	n := 10
	defer func() {
		fmt.Println("defer n = ", n)
	}()
	fmt.Println("n = ", n)
	panic("create error")
	fmt.Println("panic")
}

输出结果
n = 10
defer n = 10
panic: create error

goroutine 1 [running]:
main.main()
D:/GoProject/test_go01/src/main/main.go:11 +0xba
exit status 2

  • recover 用于异常的捕获和异常的处理,仅在 defer 语句的函数中有效,注意 recover 只能捕获到最后一个错误。
  • 必须在发生Panic后的代码块中调用,无错误返回 nil。
package main

import "fmt"

func main() {
	n := 10
	defer func() {   // 捕获并处理错误,去掉 defer 后 recover 函数无效
		if err := recover(); err != nil {
			fmt.Println("recv panic error")
		} else {
			fmt.Println("no error")
		}
	}()
	fmt.Println("n = ", n)
	panic("create error")
	fmt.Println("panic")
}

输出结果
n = 10
recv panic error

包管理(Go modules)
  • Go 版本 < 1.5,无版本管理,所有的依赖包都放在 GOPATH 下,比较混乱。
  • Go 版本 = 1.5,使用vendor 机制,并在 Go1.6 中默认启用。
  • Go 版本 = 1.9,Golang 官方集成了由社区组织合作开发的 Dep。
  • Go 版本 = 1.11,此版本推出了 Go Modules 机制来管理包,是由 vgo 演变而来。
  • Go 版本 = 1.13,将 Go Modules 设置为默认的 Go 管理工具。
  • Go 版本 >= 1.14,Golang 官方正式推荐在生产环境使用 Go Modules作为管理工具。
  • Go Modules 的管理命令为go mod,具体使用 go help mod查看帮助。
Usage:

        go mod <command> [arguments]

The commands are:

download:下载 go.mod 文件中记录的依赖包到本地缓存。指令:go mod download
edit:编辑 go.mod 文件。指令:go mod edit
graph:查看现有的依赖结构。指令:go mod graph
init:初始化新模块。指令:go mod init 模块名称,创建 go.mod 文件
tidy:添加丢失的模块,并移除无用的模块。指令:go mod tidy
vendor:将所有依赖包存到当前目录下的 vendor 目录。指令:go mod vendor
verify:校验当前模块依赖包是否缓存或是否有修改。指令:go mod verify
why:查看为什么需要依赖某模块。指令:go mod why
  • Golang 使用环境变量 GO111MODULE 来控制版本管理,此环境变量有三个值可设置。

GO111MODULE=off时,始终在GOPATH 和 vendor 目录下搜索依赖包
GO111MODULE=on时,使用 Go modules 机制,在 GOPATH/pkg/mod 目录下搜索依赖包。
GO111MODULE=auto时,当源代码不在 GOPATH/src 的子目录且包含 go.mod 文件,则使用 Go modules 机制,否则使用 GOPATH 和 vendor 机制。

  • 关于Modules的官方定义为:

Modules是相关Go包的集合,是源代码交换和版本控制的单元。go命令直接支持使用Modules,包括记录和解析对其他模块的依赖性。Modules替换旧的基于GOPATH的方法,来指定使用哪些源文件。
Modules和传统的GOPATH不同,不需要包含例如src,bin这样的子目录,一个源代码目录甚至是空目录都可以作为Modules,只要其中包含有go.mod文件。

go doc 工具
  • 此工具用于在命令行输出 Go 源码相关的文档说明。使用指令:go help doc查看具体用法。
// 查看 pkg_my_add 包
PS D:\GoProject\test_go01\src\main> go doc pkg_my_add
package pkg_my_add // import "pkg_my_add"

func Add[T int | float32](a, b T) T

起始

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值