Skip to content

go 语言

是什么

go 语言常被调侃“大道至简”,虽然常被各种诟病,但仍然在云原生、后端、命令行工具等领域发光发热。

为什么

go 语言缺点:

  1. 语法缺陷,声明和赋值没有明显分割开(如 err 的多次赋值)
  2. GC 性能比较烂
  3. 泛型比较烂
  4. 异常处理比较啰嗦(个人觉得是比较好的异常处理)
  5. 没有中心仓,模块来源于 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()
}

第三方库

参考

访问量 PV:Blocked用户数 UV:Blocked