第一章-Golang基础#
环境搭建#
相关文章: vscode搭建go语言开发环境
go build#
-
将go文件编译成可执行文件
go build hello.go
-
去除调试信息和符号表以节约可执行文件大小
go build -ldflags "-w -s"
-
交叉编译(如使用windows编译成linux的可执行文件)
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build -ldflags "-s -w" -o hello hello.go
go doc#
相关文章: godoc 命令和 golang 代码文档管理
# 查看目标包的文档索引
go doc [targetPackage]
# 查看目标包的某内容的文档索引
go doc [targetPackage].[函数名]
# 或者空格隔开也显示某内容的文档
go doc [targetPackage] [函数名]
# 子包的文档注释
go doc [targetPackage]/[subpackage]
# 代码文档编写, 按 go 的标准注释写法编写
// Biz implements a business
type Biz struct {
}
// business initialization
func (b *Biz) Init() {
}
go mod(golang v1.11及以上)#
相关文章: go mod 使用
# 配置 go mod
export GO111MODULE=true
# 创建新项目
go mod init hello
# 接下来几乎可以不用管了,在使用第三方包时import, 在运行时会自动从网上下载依赖
# go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit
# 使用go get时也会由go mod接管
# 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
# 运行 go get -u=patch 将会升级到最新的修订版本
# 运行 go get package@version 将会升级到指定的版本号version
# 运行go get如果有版本的更改,那么go.mod文件也会更改
# 使用go mod时引入当前文件夹下的包不能再使用 ./package, 而是使用 project/package
# 常用的go mod命令
go mod tidy # 自动安装所缺的依赖包以及去除无用的依赖包
go 变量与常量#
-
相关文章 Go五种定义变量的方法
-
在函数之外不能使用
a := value
的语句定义变量 -
通常使用以下语句定义全局变量
var (
WG sync.WaitGroup
LOCK sync.Mutex
)
- 通常使用以下语句定义常量
const (
PI float64 = 3.14
WEIGHT int = 5
)
go 结构体#
-
相关文章 Go Struct 超详细讲解
-
特别注意的是go结构体中的匿名字段,看以下两种写法
// 写法一
package main
import (
"fmt"
)
type Animal struct {
Name string
Color string
}
func (a *Animal) Run() {
fmt.Println(a.Name, a.Color)
}
type Lion Animal // Lion是Animal的别名,这样只能继承Animal的属性,而无法调用Animal的方法,解决方法是使用接口/实现func (a *Lion) Run()
func main() {
var lion = Lion{
Name: "小狮子",
Color: "灰色",
}
// 无法调用lion.Run()
fmt.Println(lion.Name)
}
// 写法二
package main
import (
"fmt"
)
type Animal struct {
Name string
Color string
}
func (a *Animal) Run() {
fmt.Println(a.Name, a.Color)
}
type Lion struct {
Animal //匿名字段,这样组合了Animal的特性,可以直接使用func (a *Animal) Run()
}
func main() {
var lion = Lion{
Animal{
Name: "小狮子",
Color: "灰色",
},
}
lion.Run()
fmt.Println(lion.Name)
}
go 接口#
-
go中定义接口使用type和interface关键字
-
在Go中,定义一个interface类型,该类型说明了它有哪些方法,然后在其他的函数中,将该interface类型作为函数的形参,任意一个实现了interface类型的实参都能作为该interface的实例对象
-
示例如下
package main
import "fmt"
type Animal interface {
Say()
}
type Dog struct {
age int
}
func (a *Dog) Say() {
fmt.Println("Dog say: I am ", a.age)
}
type Cat struct {
age int
}
func (a *Cat) Say() {
fmt.Println("Cat say: I am ", a.age)
}
func AnimalSay(a Animal) {
a.Say()
}
func main() {
dog := Dog{age: 2}
cat := Cat{age: 3}
AnimalSay(&dog)
AnimalSay(&cat)
}
go 异常处理#
相关文章: Golang异常处理机制 Golang错误和异常处理的正确姿势
go中使用defer, error, panic, recover处理异常
-
defer
-
defer关键字用来标记最后执行的Go语句,一般用在资源释放、关闭连接等操作,会在函数关闭前调用。
-
多个defer的定义与执行类似于栈的操作:先进后出,最先定义的最后执行。
import (
"net"
"fmt"
)
func func() {
conn, err := net.Dial("tcp","127.0.0.1:8080")
defer conn.close()
if err != nil {
fmt.Println("Connect Error:", err)
} else {
fmt.Println("Connect OK.")
}
}
- error
error的接口如下
type error interface {
Error() string
}
-
一般我们使用errors.New()或fmt.Errorf()来生成自定义的错误信息
-
假如error比较复杂需要自定义,我们可以自定义一个结构体并且实现对应方法来自定义错误
package main
import (
"fmt"
"strconv"
)
type TestError struct {
ErrorCode int
}
func (err *TestError) Error() string {
return "TestError: error code " + strconv.Itoa(err.ErrorCode)
}
func Test() error {
return &TestError{
ErrorCode: 1,
}
}
func main() {
err := Test()
if err != nil {
fmt.Println(err)
fmt.Println("Golang custom error is easy!")
}
}
-
panic和recover
-
panic为golang内置函数, 类似于python中的raise, 用于主动抛出异常, panic可以接受任意类型的对象
-
当主动抛出异常或者遇到runt-ime panics时代码将会终止执行,然后按照FILO的规则执行defer函数
-
recover为golang内置函数, 只能在defer函数中被调用,用于接收panic 函数的参数信息
-
如果在 defer 语句中也调用 panic 函数,则只有最后一个被调用的 panic 函数的参数会被 recover 函数获取到。如果 goroutine 没有 panic,那调用 recover 函数会返回 nil
package main
import (
"fmt"
)
func Test() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Panic info is:", r)
}
}()
panic("Oh no panic!")
}
func main() {
Test()
}
go 并发与信道#
-
并发
-
golang中使用go关键字开启一个新的协程(goroutine)
-
协程默认是单核并发的,可以通过
runtime.GOMAXPROCS(cpuNum)
来使其多核并行 -
子协程会在主协程退出时退出
-
示例如下
package main
import (
"os"
"time"
)
func test() {
for {
file, _ := os.OpenFile("test2.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0664)
file.WriteString("hello world\n")
file.Sync()
file.Close()
time.Sleep(1 * time.Second)
}
}
func main() {
go test()
time.Sleep(3 * time.Second)
}
-
信道
-
相关文章 详解Go信道
-
golang中使用chan来声明一个信道
-
信道有容量和长度之分,有缓存与无缓存之分,有双向与单向之分
-
可以使用信道做锁, 示例如下
package main import ( "fmt" "os" "time" ) func test(ch chan<- bool) { file, _ := os.OpenFile("test2.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0664) file.WriteString("hello world\n") file.Sync() file.Close() time.Sleep(1 * time.Second) ch <- true } func main() { ch := make(chan bool) go test(ch) <-ch fmt.Println("finish") }
go 锁与WaitGroup#
-
go中存在标准库sync
-
锁的使用, 示例如下
// 定义一个锁
var lock sync.Mutex
// 使用锁
func test() {
lock.Lock()
defer lock.Unlock()
// do something
}
// 注意不要在锁已Lock的时候再次请求锁,会造成死锁并且不会引发panic
- WaitGroup的使用, 示例如下
// 定义一个WaitGroup
var wg sync.WaitGroup
// 使用WaitGroup优雅地等待子协程结束
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func test(i int) {
wg.Add(1)
defer wg.Done()
fmt.Println("here is ", i, "goroutine")
}
func main() {
for i := 0; i < 10; i++ {
go test(i)
}
wg.Wait()
fmt.Println("Done")
}