语言要求
格式化:Go 官方对语言风格本身是强行要求的,所以不用管。 错误处理:Go 没有异常机制(try-catch),习惯 if err != nil,不要忽略错误。 关于分号:Go 的源码里基本看不到分号,编译器会在换行时自动加,所以不要手欠自己加分号。
包
依赖管理
现在都用 Go Modules:
go mod init <module_name>: 初始化项目go mod tidy: 自动下载缺少的包,移除没用的包go get <package>: 下载第三方包
可见性
Go 没有 public、private 关键字,全靠首字母大小写控制访问权限:
- 首字母大写:公开的(Public),其他包可以访问。如
fmt.Println。 - 首字母小写:私有的(Private),只能在当前包(Package)内使用。
类型
基础类型
| 类型 | 描述 | 默认值(Zero Value) |
|---|---|---|
bool | 布尔型 | false |
string | 字符串(不可变,UTF-8) | ""(空串) |
int, int64 | 整型(int 随系统位数变化) | 0 |
uint, uint64 | 无符号整型 | 0 |
float32, float64 | 浮点型 | 0.0 |
byte | uint8 的别名(存 ASCII) | 0 |
rune | int32 的别名(存 Unicode) | 0 |
complex64, complex128 | 复数 | (0+0i) |
nil | 指针、切片、Map、接口、通道的零值 | nil |
重要:Go 没有“未初始化”的变量,所有变量声明后都会被赋予零值。
指针
和 C 类似,但不支持指针运算(不能 p++)。
i := 42
p := &i // 取地址
fmt.Println(*p) // 取值
*p = 21 // 修改值变量和常量
Go 是强类型语言,但支持类型推导。声明了变量但没使用,编译会报错。常量大部分规则等同于变量
var a int = 10 // 标准声明
var b = 20 // 类型推导(自动识别为int)
c := 30 // 短变量声明(语法糖,只能在函数内部使用)
// 多变量声明
var x, y int = 1, 2
name, age := "Alice", 18
// 常量
const c int = 100
const big = 1 << 10 // 编译期计算复合数据结构
数组
长度固定,是值类型。
var a [5]int // [0 0 0 0 0]
b := [3]int{1, 2, 3} // 初始化
c := [...]int{4, 5, 6} // 自动推导长度
d := b // 会拷贝整个数组切片
切片是对数组的动态视图,长度可变,引用类型。
切片截取后底层数组是共享的,修改子切片会影响原切片,除非发生扩容导致内存重新分配。
// 声明
var s []int // nil 切片
s := []int{1, 2, 3}
s := make([]int, 5, 10) // make(类型, 长度, 容量),会建立一个底层数组,然后返回切片
// 操作
s = append(s, 4) // 追加元素(可能会触发扩容)
sub := s[1:3] // 切片操作 [startIndex : endIndex] (左闭右开)Map
无序的键值对,引用类型。
// 初始化
m := make(map[string]int) // 这里是初始化
m := make(map[string]int, 100)
m["one"] = 1
// 字面量
scores := map[string]int{
"Alice": 100,
"Bob": 98,
}
// 查找
v, ok := scores["Charlie"] // 这里的 ok 模式非常经典
if ok {
fmt.Println("存在:", v)
} else {
fmt.Println("不存在")
}
delete(scores, "Bob") // 删除结构体
Go 没有 class,只有 struct。没有继承,只有组合(Embedding)。
type User struct {
Name string `json:"name"` // 后面是 Tag,用于 JSON 解析等
Age int
}
// 匿名结构体
u := struct {
x, y int
}{1, 2}
// 组合(类似继承)
type Admin struct {
User // 匿名字段,Admin 直接拥有 User 的字段
Level int
}iota 枚举生成器
// iota 枚举生成器
const (
A = iota // 0
B // 1 (自动递增)
C // 2
_ // 跳过 3
E // 4
)运算符
基本和 C/Python 一样,除了以下几点
- 没有前置自增:
++i是错的,只有i++。 i++是语句:不能写a = i++。- 没有三元运算符:
a > b ? a : b是错的,老老实实写if-else。
控制流
// if
if x > 0 {
// ...
} else {
// ...
}
// 带初始化的 if(scope 限制在 if 块内)
if err := compute(); err != nil {
fmt.Println(err)
return
}
// for
// 标准循环
for i := 0; i < 10; i++ { }
// 类似 while
for i < 10 { }
// 死循环
for { }
// Range 遍历(最常用)
nums := []int{2, 3, 4}
for i, v := range nums { // i 是索引, v 是值的拷贝
fmt.Printf("Index: %d, Value: %d\n", i, v)
}
// 如果不需要索引,用 _ 丢弃
for _, v := range nums { }
// switch
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "linux":
fmt.Println("Linux")
default:
fmt.Println("Other")
}函数
支持多返回值,函数是一等公民,定义如下:
func add(a, b int) (int, error) {
if a < 0 {
return 0, errors.New("negative")
}
return a + b, nil
}
res, err := add(1, 2)Go 中有 Defer(延迟执行)这个特殊语法,用来让代码在函数返回前执行,常用于资源释放(关闭文件、锁)。执行顺序:先进后出(栈)。
func readFile() {
f, _ := os.Open("file.txt")
defer f.Close() // 无论下面发生什么,函数退出前一定会 Close
// Do something...
}面向对象
方法
方法是附着在特定类型上的函数。通过接收者(Receiver)定义。
type Rect struct{ w, h int }
// 值接收者(不修改原对象)
func (r Rect) Area() int {
return r.w * r.h
}
// 指针接收者(修改原对象,或者对象很大时节省拷贝)
func (r *Rect) Scale(f int) {
r.w *= f
r.h *= f
}接口
Go 的接口是隐式实现的(Duck Typing)。只要你实现了接口里的所有方法,你就实现了该接口,不需要显式写 implements。
// 声明
type Shape interface {
Area() float64
}
// 构造
type Rect struct {
Width, Height float64
}
// Rect 有一个方法 Area(),签名和 Shape 要求的一模一样
func (r Rect) Area() float64 {
return r.Width * r.Height
}
// 只要 Rect 实现了 Area(),它就是 Shape
var s Shape = &Rect{10, 5}
// 空接口 interface{}
// 代表任意类型(类似 C 的 void* 或 Python 的 object)
// Go 1.18 后可以使用别名 any 替换 interface{}
func printAny(v interface{}) {
fmt.Println(v)
}类型断言
猜变量类型。如果猜对了,val 就是那个 int 值,ok 为 true;如果猜错了,ok 为 false,val 为 int 零值,不会 panic。
val, ok := v.(int) 并发编程
口号:不要通过共享内存来通信,要通过通信来共享内存。
Goroutine
轻量级线程,启动成本极低。
go func() {
fmt.Println("I'm running in background")
}()Channel(通道)
用于 Goroutine 之间传递数据。
ch := make(chan int) // 无缓冲通道
ch := make(chan int, 3) // 有缓冲通道
// 发送
ch <- 42
// 接收
val := <-ch
// 关闭
close(ch)Select
多路复用,监听多个 Channel。
select {
case msg1 := <-c1:
fmt.Println(msg1)
case msg2 := <-c2:
fmt.Println(msg2)
case <-time.After(time.Second):
fmt.Println("Timeout")
}常用库函数
fmt
| 函数 | 说明 |
|---|---|
Print / Println | 标准输出 |
Printf | 格式化输出 |
Sprintf | 格式化并返回字符串 |
Errorf | 格式化并返回 error |
常用占位符:
%v:值的默认格式(万能)%+v:打印结构体时带字段名%#v:Go 语法表示的值%T:打印类型
strings
| 函数 | 说明 |
|---|---|
Contains(s, substr) | 是否包含 |
Split(s, sep) | 切割 |
Join(a, sep) | 拼接 |
TrimSpace(s) | 去除首尾空白 |
ReplaceAll(s, old, new) | 替换 |
strconv (字符串转换)
| 函数 | 说明 |
|---|---|
Atoi(s) | string → int |
Itoa(i) | int → string |
ParseFloat(s, 64) | string → float64 |
encoding/json
| 函数 | 说明 |
|---|---|
Marshal(v) | 结构体/Map → JSON 字节切片 |
Unmarshal(data, &v) | JSON 字节切片 → 结构体/Map |
Tag 用法:
type User struct {
Name string `json:"username"` // 重命名 Key
Age int `json:"age,omitempty"` // 如果是 0 值则忽略
Pwd string `json:"-"` // 永远忽略
}