golang
1 go简介
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。
Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。
Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。
对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。
go语言具有以下特色:
简洁、快速、安全
并行、有趣、开源
内存管理、数组安全、编译迅速
你可以尝试编写第一个go程序,记住go程序文件以.go
结尾
package main
import "fmt"
func main() {
fmt.Println("Hello,World!")
}
可以使用go run
直接运行程序,也可以使用go build
编译为二进制文件再执行。
2 Go工作区区和GoPath
在使用Go工作区时需要配置三个环境变量
GOROOT:Go语言安装根目录的路径,也就是GO语言的安装路径。
GOPATH:若干工作区目录的路径。
GOBIN:GO程序生成的可执行文件的路径。
其中GOPATH背后的概念是最多的,也是最重要的。那么为什么我们要设置GOPATH呢?
可以把GOPATH简单理解为GO语言的工作目录,它的值是一个目录的路径,也可以是多个目录路径,每个目录都代表Go语言的一个工作区(workspace)。
我们需要利用这些工作区,去放置Go语言的源码文件(source file),以及安装(install)后的归档文件(archive file,也就是以".a"为扩展名的文件)和可执行文件(executable file)。
事实上,由于Go语言项目在其生命周期内的所有操作(编码、依赖管理、构建、测试、安装等)基本上都是围绕着GOPATH和工作区进行的。所以,它的背后至少有3个知识点,分别是:
Go语言源码的组织方式是怎么样的;
是否了解源码安装后的结果(只有在安装后,Go语言源码才能被我们或其他代码使用);
是否理解构建和安装Go程序的过程
2.1 Go语言源码的组织方式
与许多编程语言一样,Go语言的源码也是以代码包为基本组织单位的。在文件系统中,这些代码包其实是与目录一一对应的。由于目录可以有子目录,所以代码包也可以有子包。
一个代码包中可以包含任意个以.go为扩展名的源码文件,这些源码文件都需要被声明属于同一个代码包。
代码包的名称一般会与源码文件所在的目录同名。如果不同名,那么在构建、安装的过程中会以代码包名称为准。
每个代码包都会有导入路径。代码包的导入路径是其他代码在使用该包中的程序实体时,需要引入的路径。在实际使用程序实体之前,我们必须先导入其所在的代码包。具体的方式就是import该代码包的导入路径。就像这样:
import "github.com/labstack/echo"
在工作区中,一个代码包的导入路径实际上就是从src子目录,到该包的实际存储位置的相对路径。所以说,Go语言源码的组织方式就是以环境变量GOPATH、工作区、src目录和代码包为主线的。一般情况下,Go语言的源码文件都需要被存放在环境变量GOPATH包含的某个工作区(目录)中的src目录下的某个代码包(目录)中。
2.2 了解源码安装后的结果
源码文件以及安装后的结果文件都会放到哪里呢?我们都知道,源码文件通常会被放在某个工作区的src子目录下。那么在安装后如果产生了归档文件(以“.a”为扩展名的文件),就会放进该工作区的pkg子目录;如果产生了可执行文件,就可能会放进该工作区的bin子目录。
再讲一下归档文件存放的具体位置和规则。
源码文件会以代码包的形式组织起来,一个代码包其实就对应一个目录。安装某个代码包而产生的归档文件是与这个代码包同名的。
放置它的相对目录就是该代码包的导入路径的直接父级。比如,一个已存在的代码包的导入路径是
github.com/labstack/echo
那么执行命令:
go install github.com/labstack/echo
生成的归档文件的相对目录就是github.com/labstack ,文件名为echo.a 。
顺便说一下,上面这个代码包导入路径还有另外一层含义,那就是:该代码包的源码文件存在于GitHub网站的labstack组的代码仓库echo中。
再说回来,归档文件的相对目录与pkg目录之间还有一级目录,叫做平台相关目录。平台相关目录的名称是由build(也称“构建”)的目标操作系统、下划线和目标计算架构的代号组成的。
2.3 理解构建和安装Go程序的过程
再来说说构建和安装Go程序的过程都是怎样的,以及它们的异同点。
构建使用命令go build,安装使用命令go install。构建和安装代码包的时候都会执行编译、打包等操作,并且,这些操作生成的任何文件都会先被保存到某个临时的目录中。
如果构建的是库源码文件,那么操作后产生的结果文件只会存在于临时目录中。这里的构建的主要意义在于检查和验证。
如果构建的是命令源码文件,那么操作的结果文件会被搬运到源码文件所在的目录中。
安装操作会先执行构建,然后还会进行链接操作,并且把结果文件搬运到指定目录。
进一步说,如果安装的是库源码文件,那么结果文件会被搬运到它所在工作区的pkg目录下的某个子目录中。
如果安装的是命令源码文件,那么结果文件会被搬运到它所在工作区的bin目录中,或者环境变量GOBIN指向的目录中。
这里你需要记住的是,构建和安装的不同之处,以及执行相应命令后得到的结果文件都会出现在哪里。
3 语言结构
一般来说,go语言由以下几个部分组成:
包声明
引入包
函数
变量
语句/表达式
注释
比如说下面这段程序
// 包声明
package main
// 导入包
import (
"fmt"
"math"
)
// 常量声明
const greeting = "Hello, World!"
// 自定义类型和方法
type greeter struct {
name string
}
func (g greeter) greet() {
fmt.Println(greeting, g.name)
}
func add(a int, b int) int {
return a + b
}
// 主函数
func main()
{
// 变量声明和赋值
var s int = 42
var g = greeter{name: "Go"}
// 方法调用
g.greet()
}
包声明
:第一行是包声明,必须在源文件非注释的第一行指明这个文件属于哪个包,如:package main
指明这是一个可独立执行的程序,每个Go应用程序都应该包含一个名为main的包。导入包
:import
告诉Go编译器这个程序需要使用哪些包。导入的包必须使用,否则报错。常量声明
:使用const
声明常量,常量在声明时必须赋值,且值不能更改。类型声明
:使用type
来声明一个类型。方法声明
:方法是带有接收者参数的函数。在这个例子中,greet
方法的接收者是greeter
类型的变量g
。函数声明
:函数可以定义参数和类型,还能使用return
返回值。主函数
:func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。变量声明
:使用var
声明一个变量,可以在函数外声明全局变量,也可以在函数内声明局部变量。也可以使用s := 123
声明一个短变量,并由Go编译器自动推算变量类型。定义的变量必须使用,否则报错,可以使用_ = 123
避免。注释
:/*...*/是块注释,//是单行注释当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。注意:导入包的包名要使用双引号;函数的左
{
不能单独在一行;Go中一行代表一个语句结束,如果想将多个语句写在一行则必须使用;
人为区分,但并不建议。
4 包导入
在上面我们介绍了如果想要使用一个包的功能,需要使用import
关键字引入才能使用。
可以是相对路径,也可以是绝对路径,推荐使用绝对路径(起始于工程根目录)
点操作
import (
. "fmt"
)
这个点操作的含义就是这个包导入之后在调用这个包的函数时,可以省略前缀的包名。不推荐在大量导入包时使用。
别名操作
import (
f "fmt"
)
在调用时可以使用别名。
_操作
import (
_ "fmt"
)
_操作其实是引入该包,而不直接使用包里面的函数,而是调用包的init函数。
5 Go命令
go常用命令一般有以下几个:
go run
运行go程序
go run main.go
go build
编译go程序,不指定编译文件会编译目录下所有文件
go build main.go
go install
编译并安装包和依赖
go install ./...
go get
下载并安装包和依赖
go get github.com/gin-gonic/gin
go clean
移除对象文件和缓存
go clean
go test
运行测试
go test ./...
go fmt
格式化go源码
go fmt main.go
go vet
静态代码分析工具,用于发现潜在的错误
go vet main.go
go mod
管理go模块
初始化模块:go mod init example.com/mymodule 下载依赖项:go mode tidy 更新依赖项:go get -u
go doc
显示包或符号的文档
go doc fmt.Println
go env
显示或设置Go环境变量
go env
go list
列出包信息
go list ./...
go tool
运行指定的go工具
go tool pprof
在go命令行中./...
是一个特殊的模式,用于递归匹配当前目录及其所有子目录中的所有Go包。.
表示当前目录,...
是一个通配符,表示递归匹配所有子目录。
其中和编译相关的有build,get,install,run这四个。以下是这些命令通用的选项
-a
用于强制重新编译所有涉及的Go语言代码包,即使它们已经是最新的了。该选项可以让我们有机会通过改动底层的代码做一些实验。
-n
使命令打印其执行过程中用到的所有命令,而不去真正执行它们。如果只想查看或者验证命令的执行过程,而不想改变任何东西,使用它正好合适。
-race
用于检测并报告指定Go语言程序中存在的数据竞争问题。当用Go语言编写并发程序的时候,这是很重要的检测手段之一。
-v
用于打印命令执行过程中涉及的代码包。这一定包括我们指定的目标代码包,并且有时还会包括该代码包直接或间接依赖的那些代码包。这会让你知道哪些代码包被执行过了。
-work
用于打印命令执行时生成和使用的临时工作目录的名字,且命令执行完成后不删除它。这个目录下的文件可能会对你有用,也可以从侧面了解命令的执行过程。如果不添加此标记,那么临时工作目录会在命令执行完毕前删除。
-x
使命令打印其执行过程中用到的所有命令,并同时执行它们。
6 基础语法
标识符 标识符一般用来命名变量、类型等程序实体。一个标识符由数字、字母和下划线构成,但不能由数字开头。
以下是无效的标识符:
1ab(数字开头)
case(不能使用关键字)
a+b(不能使用运算)
字符串连接
Go语言中的字符串连接可以通过+
实现。
package main
import "fmt"
func main() {
fmt.Println("Google" + "Runoob")
}
# 结果:GoogleRunoob
格式化字符串 Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
Printf 根据格式化参数生成格式化的字符串并写入标准输出。
Sprintf示例
package main
import (
"fmt"
)
func main() {
var a = 123
var enddata = "2023-12-23"
var url = "Code=%d&endDate=%s"
var target = fmt.Sprintf(url,a,enddata)
fmt.Println(target)
}
# Code=123&endDate=2023-12-23
package main
import (
"fmt"
)
func main() {
var a = 123
var enddata = "2023-12-23"
var url = "Code=%d&endDate=%s"
fmt.Printf(url,a,enddata)
}
# Code=123&endDate=2023-12-23
Sprintf输出后会自动换行,Printf不会。
7 数据类型
在Go中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用到大数据时才申请大内存,这样可以充分利用内存。
Go 语言按类别有以下几种数据类型:
1
布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2
数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
3
字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4
派生类型: 包括: - (a) 指针类型(Pointer) - (b) 数组类型 - (c) 结构化类型(struct) - (d) Channel 类型 - (e) 函数类型 - (f) 切片类型 - (g) 接口类型(interface) - (h) Map 类型
7.1 数字类型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
1
uint8 无符号 8 位整型 (0 到 255)
2
uint16 无符号 16 位整型 (0 到 65535)
3
uint32 无符号 32 位整型 (0 到 4294967295)
4
uint64 无符号 64 位整型 (0 到 18446744073709551615)
5
int8 有符号 8 位整型 (-128 到 127)
6
int16 有符号 16 位整型 (-32768 到 32767)
7
int32 有符号 32 位整型 (-2147483648 到 2147483647)
8
int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
7.2 浮点型
1
float32 IEEE-754 32位浮点型数
2
float64 IEEE-754 64位浮点型数
3
complex64 32 位实数和虚数
4
complex128 64 位实数和虚数
7.3 其他数字类型
以下列出了其他更多的数字类型:
1
byte 类似 uint8
2
rune 类似 int32
3
uint 32 或 64 位
4
int 与 uint 一样大小
5
uintptr 无符号整型,用于存放一个指针
8 Go变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。
变量可以通过变量名访问。
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var identifier type
# 可以一次性声明多个变量
var identifier1, identifier12 type
最后更新于