Appearance
go 语言
是什么
go 语言常被调侃“大道至简”,虽然常被各种诟病,但仍然在云原生、后端、命令行工具等领域发光发热。
为什么
go 语言缺点:
- 语法缺陷,声明和赋值没有明显分割开(如 err 的多次赋值)
- GC 性能比较烂
- 泛型比较烂
- 异常处理比较啰嗦(个人觉得是比较好的异常处理)
- 没有中心仓,模块来源于 GitHub 仓库,具有非常高的丢失、安全等风险(个人认为是最严重的问题)
历史缺陷
随着版本更新,历史缺陷已经逐渐移除,如:无泛型、烂依赖管理
语法备忘清单
○ 工程化
sh
go mod init ${project}
go env -w GOPROXY=https://goproxy.cn,direct
go get ${module}
go run .
go build
○ 声明、初始化、赋值
可见性
对于模块和 struct 中的顶级变量、方法,标识符大写开头表示其公开可见
模块中的 init 方法会在模块加载后自动执行
go
var intA int
var intB int = 10
var intC = 20
intD := 30
var sliceA = []int{1, 2, 3}
sliceB := make([]int, 3, 3)
var mapA = map[string]int{
"Chinese": 90,
"Math": 90,
"English": 85
}
mapB := make(map[string]int)
○ 打印
go
name := "Aubur"
// ○ fmt
fmt.Println("Hello World")
// 占位符,%s 字符串,%d 十进制整数,%f 浮点数,%t 布尔值,%p 指针地址
// %v 值的默认格式,%+v 结构体字段名和值展开,%#v go 语法格式的值
// %T 值类型,%% 百分号
fmt.Printf("Hello, %s\n", name)
// ○ log
log.Println("Hello World")
log.Printf("Hello, %s\n", name)
// ○ io
file, _ := os.Create("output.txt")
defer file.Close()
fmt.Fprintf(file, "Name: %s", name)
○ 数组、切片
go
// ○ 增
// 数组,值类型,赋值和传参会复制整个数组
// 深层复制?
arrA := [3]int{1, 2, 3}
// 自动推断数组长度,多维数组只能有一层使用 ...
arrB := [...]int{1, 2, 3}
// 切片
sliceA := []int{1, 2, 3}
// 复制切片
sliceB := make([]int, 3, 3)
copy(sliceB, sliceA)
// 👍预分配提升性能:初始化长度空,大容量,append 时可从开始位置填充
sliceC := make([]int, 0, 100)
sliceC = append(sliceC, []int{1, 2, 3}...)
// ○ 改
// 尾部添加,动态扩容
sliceA = append(sliceA, []int{4, 5, 6}...)
// 去头去尾
sliceA = sliceA[1:]
sliceA =sliceA[:len(sliceA) - 1]
// 去中间,注意解构
index := 2
sliceA = append(slice[:index], slice[index+1:]...)
// ○查
length := len(sliceA)
capLength := cap(sliceA)
isEmpty := len(sliceA) == 0
firstItem := sliceA[0]
i := 0
for ; i < len(sliceA); i++ {
log.Println(i, sliceA[i])
}
for i, item := range sliceA {
log.Println(i, item)
}
○ map
go
// ○ 增
mapA := map[string]string{
"username": "小明",
"password": "xiaoming",
}
mapB := make(map[string]string)
// ○ 改
delete(mapA, "username")
mapB["username"] = "小红"
mapB["password"] = "xiaohong"
// ○ 查
value, hasKey := mapA["username"]
for key, value := range mapA {
log.Println(key, value)
}
○ struct 和 interface
go
// 接口
type UserItem interface {
Name string
Age string
run()
}
// 结构体字段
// 默认值:整型 0,布尔值 false,字符串 "",指针/切片/映射/通道 nil
type User struct {
Name string `json:"name"`
Age int `json:"age,string"`
}
// 结构体方法
func (u *User) run() {}
// 通过隐式转换增强 IDE 提示,如果没有 User 没有完全实现 UserItem,会提示
var _ UserItem = (*User)(nil)
○ Once
sync.Once
只会执行一次 Do 调用的函数,可用来实现对象单例
go
type User struct {}
var user *User
var once sync.Once
func GetUserSingle() *User {
once.Do(func() {
user = &User{}
})
return user
}
○ Pool
:::warn 如果对象的创建、销毁性能开销并不大,且其生命周期长,使用 sync.Pool 可能会带来更多负面影响。 :::
go
type User struct {}
var UserPool = sync.Pool{
New: func() any { return &User{} },
}
func GetUserSingle() *User {
user := UserPool.Get().(*User)
UserPool.Put(user)
return user
}
○ 并发、通道
go
// later 待补充
○ 字符串常用方法
go
// ○ 正则
input := "Go123Lang456"
// 查找所有
reA := regexp.MustCompile(`\d+`)
numListA := reA.FindAllString(input, -1)
// 查找所有分组
reB := regexp.MustCompile(`(\d+)\w+(\d)+`)
numListB := reB.FindStringSubmatch(input)
// ○ 查找
foundA := strings.Contains(" Hello World ", "Wo")
foundB := strings.HasPrefix("Hello World", "He")
foundC := strings.HasSuffix("Hello World", "ld")
foundIndex := strings.Index("Hello World", "World")
// ○ 拆拼
name := "Aubur 2024"
msg := fmt.Sprintf("Hello, %s", name)
splitResultA := strings.Split("a,b,c", ",")
splitResultB := strings.Fields(" foo bar baz ")
strA := strings.Join([]string{"a", "b", "c"}, "-")
strB := strings.Replace("Hello, World", "o", "-", -1)
○ json 常用方法
go
// struct 中的字段可以添加标签信息,用于处理 json 相关的行为。多个标签用 , 间隔
// -:序列化、反序列化时忽略
// omitempty:空值时忽略,如 false, 0, nil, 空长度的数组、切片、映射、字符串
// string:序列化时转化为字符串
type User struct {
Name string `json:"name"`
Age int `json:"age,string"`
}
// 序列化
userA := User{Name: "Aubur"}
userByteSlice, err := json.Marshal(userA)
log.Println(string(userByteSlice))
// 反序列化
var userB User
err := json.Unmarshal([]byte(`{"name":"Alice","age":28}`), &userB)
fmt.Printf("UserB: %+v\n", userB)
○ 切片常用方法
go
// later 补充排序、最值、去重
○ 日期处理
go
// Go 中,原生不使用 yyyy-MM-dd,使用魔数/单词
// 月 1 January,日 2,时 3,分 4,秒 5,年 6 2006,周几 7 Monday
// 日期格式化
var t time.Time = time.Date(2025, 10, 1, 0, 0, 0, 0, time.Local)
fmt.Println(t.Format("2006-01-02 是 Monday")) // 2025-10-01 是 Wednesday
// 字符串转日期
DateFormat := "2006-01-02"
t, err := time.ParseInLocation(DateFormat, "2025-10-01", time.Local)
// 日期前进
t := time.Date(2025, 1, 31, 0, 0, 0, 0, time.Local)
nextDay := t.AddDate(0, 0, 1) // 加 1 天,2025-02-01
nextMonth := t.AddDate(0, 1, 0) // 加 1 月,2025-03-03,具体加的天数由 t 月天数决定
// 日期比较
DateFormat := "2006-01-02"
start, err := time.ParseInLocation(DateFormat, "2025-10-01", time.Local)
end, err := time.ParseInLocation(DateFormat, "2025-10-07", time.Local)
end.After(start) // true
start.Before(end) // true
○ 文件处理
go
// 创建文件,已存在则覆盖
file, err := os.Create("example.txt")
defer file.Close()
// 写入内容到文件
file.WriteString("添加新的一行\n")
○ 客户端请求
go
// ○ 请求
// 简易版
res, err := http.Get(targetURL)
// 复杂版
body := []byte(`{"key":"value"}`)
req, err := http.NewRequest("POST", targetURL, bytes.NewBuffer(body))
client := &http.Client{
// 跳过 https 证书校验
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
res, err := client.Do(req)
// ○ 请求头
// 请求头大驼峰+横线
req.Header.Set("Authorization", "Bearer abcdef123456")
req.Header.Set("Content-Type", "application/json")
// ○ 读取响应
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
log.Println(string(body))
○ 服务端响应
go
// w 为 http.ResponseWriter
// 响应文本
w.Write([]byte("Hello World"))
// 响应 json 文本
user := User{Name: "Aubur"}
userByteSlice, _ := json.Marshal(user)
w.Write(userByteSlice)
// 响应 json 另一种方法
userList := []User{user}
encoder := json.NewEncoder(w)
err := encoder.Encode(userList)
// 响应二进制
imgData, _ := io.ReadFile("./test.jpg")
w.Write(imgData)
// 响应错误
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, "error message", http.StatusNotFound)
// 响应码、响应头
w.WriteHeader(http.StatusNotFound)
w.Header().Set("Content-Type", "iamge/jpeg")
○ 错误处理
go
// 错误信息规范化
wrappedErr := fmt.Errorf("%w: do something fail", err)
fmt.Println(wrappedErr)
// 错误堆栈
err := recover()
maybeError, ok := err.(error)
if ok {
detailError := fmt.Errorf("%w", maybeError)
log.Printf("%+v\n", detailError)
} else {
log.Printf("%+v\n", err)
}
○ 命令行帮助与参数 flag
go
// later 深层级的命令行帮助
// later 深层级 map 配置赋值
var dir string
func init() {
flag.StringVar(&dir, "s", "./", "接收字符串,默认使用当前目录 ./")
flag.Parse()
}
○ 各种路径
go
// 工作目录,绝对路径
workDir, err := os.Getwd()
// 可执行文件路径
binPath, err := os.Executable()
// 调用者源文件名、行号
_, callerFilename, _, od := runtime.Caller(0)
callerFileDir := path.Dir(callerFilename)
○ 性能分析
go
func main() {
// 创建 CPU 性能分析文件
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 开始、停止
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
第三方库
- h2non/filetype,使用魔法数字检查文件类型,速度快
- glebarez/go-sqlite,sqlite 驱动,内置 sqlite