Go 核心语法精简笔记 (背诵/速查版)

核心红线: 声明了不用的变量导入了不用的包,极必编译报错!


1. 变量与常量初始化

🌈 变量定义

1
2
3
4
5
6
7
8
9
10
11
// 1. 标准声明
var a int = 10

// 2. 类型推断
var b = 20

// 3. 简短声明(🔥 最常用,仅用于函数内部)
str := "fliex"

// 4. 并行声明
x, y, z := 1, 2, 3

🌈 常量与iota枚举

常量的核心:运行期间绝对不能被改变,且必须在编译时就能确定类型值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 普通声明
const PI = 3.14

// 2. 批量声明
const (
a = 1
b = 2
)

// 3. iota 枚举器 (遇到 const 重置为 0,每新起一行加 1)
const (
SUCC = iota // 0
_ // 1 (占位丢弃)
FAIL // 2 (默认继承上一行表达式 iota)
)


2. 基础内置数据类型

🌈 数字与布尔

  • 布尔值 booltruefalse。(⚠️ C++ 那一套 0/1 和空字符串当条件的技巧,在 Go 完全行不通
  • 整型 int:会根据操作系统自适应变成 32/64 位。(定长型有:int32 / int64
  • 浮点数 float64:Go 省略小数类型时的默认推断类型

🌈 字符串与字符

  • 字符(单引号 ''):
    • byte (等同 uint8) —— 存 ASCII 纯英文。
    • rune (等同 int32) —— 存 Unicode(中文汉字)。
  • 字符串(双引号 "" / 反引号 ``):
    • ⚠️ 切记字符串不可修改! 不能 s[0] = 'H'
    • 必须转切片修改:b := []rune(s) -> 修改 b -> string(b)
    • 💡 高频拼接用 strings.Builder,性能远大于 +


3. 分支控制流

⚠️ Go 语言中没有 a ? b : c 三元运算符
⚠️ a++ 只能当一行的独立语句,不能放进式子算数,也没 ++a

🌈 if 条件

好处是自带初始化,将变量作用域锁死在 if 内部

1
2
3
if a := 10; a > 0 {
// a 仅在这对 {} 里活着
}

🌈 switch 分支

  • 匹配即退出,不再需要手写 break
  • case 1, 2, 3: 支持单行并列多个条件。
  • 想强制穿透执行紧跟的下一个分支:加 fallthrough
  • Type Switch (给空接口探明真身):switch v := interface变量.(type)


4. Struct 结构与 OOP

Go 虽然没有面向对象的 Class 类系统,但是利用结构体方法函数完美承接。

💡 首字母大写公开可见,小写私有

🌈 定义与初始化

1
2
3
4
5
6
7
8
9
10
type Student struct {
Name string
Age int
}

// 推荐:大括号键值对点名
stu := Student{
Name: "fliex",
Age: 25, // ⚠️ 最后哪怕只有一行,只要回车了,必须带逗号
}

🌈 方法接收者(继承的原理)

把普通函数前面塞个括弧 (s Student) 就成了方法。

1
2
3
4
5
6
7
// 1. 值接收者(拿的是副本,怎么改原本对象都不受影响)
func (s Student) Read() {}

// 2. 指针接收者(拿的是对象原件地址,里面随便改都能同步到外部对象身上🔥)
func (s *Student) Write() {
s.Age = 99
}

🌈 组合 (替代继承机制)

在结构体里嵌个别的结构体且不给起名字。如果有重命名的功能,外层优先遮蔽里层

1
2
3
4
5
type Animal struct { Name string }
type Dog struct {
Animal
}
// dog.Name 调到的是 Animal 里的 Name


5. 数组与切片 (Slice)

🌈 核心区分

  • 数组 定长! [3]int[4]int 是两种完全不兼容的异度类型。
  • 切片 引用型不定长数组视角,靠 make 生成:make([]int, 长度, 容量)

🌈 扩容解绑大坑 ⚠️

通过 s[low : high] 取一段范围出来造的切片,一旦遭遇 append() 数据且原切片容量已经爆满了需要系统自动扩容时

系统会强制拿走老数据在新地方重开辟一个豪华新大底。此后这个新切片跟原来的底子数组彻底一刀两断恩断义绝,修改互不干涉。



6. Map 字典集

⚠️ Map是超级容易 Panic 挂程序的祖宗!只声明是空指针 nil 绝对不能存数据!

🌈 正确的创建与判空

1
2
3
4
5
6
7
8
9
// 必须做 make !
m := make(map[string]int)
m["李四"] = 18

// 判断是否真有的 经典 comma ok
value, ok := m["王五"]
if !ok {
// 不存在,但此刻系统还是赏了个 int 的老底:0,千万别当真
}

🌈 Map 严格规定 ⚠️

  • 休想== 判断两个 Map 里面长得一模一样。
  • 能拿来给 Map 做前面那个 Key 头的,绝不能是:slice 动态切片map 字典func 函数


7. for 循环与 Range 陷阱

Go的循环只有一个 for 关键字打天下。
不再提旧三段式,着重提醒专门给切片Map配套遍历的黑科技:for range

🌈 Range 是值拷贝!修改无效 ⚠️

1
2
3
4
5
6
7
8
9
10
11
s := []int{1, 2, 3}

// ❌ 错误示范:修改这里的 v 根本没用,v 是临时造出来的替身!
for _, v := range s {
v *= 10
}

// ✅ 正确示范:永远只信任原汁原味的底层地址索引
for i := range s {
s[i] *= 10
}


8. 函数值传递与真假指针

🌈 统一都是值传递!

所有的传参,一律全都是在拷贝复印件!
如果你发现这复印件你在函数里涂改,咋外面的也变了?

那是因为这个件印的是**“带指针锁眼的大门钥匙地址”**,拿着复印的地址你去捣腾的还是原来大马路上的那套房。

🌈 彻底阉割的指针 *&

  • 取地址: &变量
  • 去地址的房里拿真身值: *指针
  • ⚠️ 绝对不允许运算(如 p++),指针的唯一作用就是替值传递留一个修改实体的通道门匙。
  • ⚠️ 面对未初始化的虚空指针直接 *p -> Panic!

切片、Map、Channel 不需要加 * ,它三哥俩骨子里就带着原生基因“隐形指针”。



9. Interface 接口机制

在 Go,接口实现了鸭子模型的极简多态:

你只要走路像鸭子,叫声像鸭子,你就算没办证书写明 implements 鸭子,它也认你是一只钦定接口鸭!

🌈 唯一的大坑:接收者限定

如果你是用值接收者实现了方法:那么你传 狗体&狗体指针 给接口对象都能兜住。
如果你是用指针接收者实现了方法:死规定,你特么只能塞且必须强行塞 &狗地址 给我才能行。

🌈 万能的空接口与破壳

所有类型不论是原始数据还是自定义奇葩类,都天生实现了 interface{} 空接口囊。

1
2
3
4
5
6
7
var unknown interface{} = "我是个刺客"

// 类型断言:剥壳求证(也叫 comma ok 安全测试)
str, ok := unknown.(string)
if ok {
// 是字符串没跑了
}


10. 错误捕获 (defer/err/panic)

🌈 错误提取断言

  • 判断它底层根源是不是预定义常量错(Sentinel Error): errors.Is(err, 拿来对比的预设变量)
  • 强行把它破壳拿里面的特定实体类型的属性错: errors.As(err, &特定的自定错误大类型)

🌈 defer 的回旋镖(后进先出栈)

**你写的 return 语句根本就不是一劳永逸!**它有三步曲:

  1. 它先把值丢在后备箱变量里。
  2. 逆向顺藤摸走所有预先挂着的 defer。如果此刻遇到有命名返回值的大哥搞破坏影响了后备箱,你退出的答案就被改了!
  3. 这才退出。

🌈 Panic 急救包

只把 recover() 塞进提前挂着的 defer 匿名兜底函数里才能用!
只要逮着了报错就吃进隐患,让上级功能安然无恙接着大踏步执行。



11. 终极并发 (Goroutine & Channel)

💡 通过通信来共享系统内存

🌈 解决主死全僵的问题 (sync.WaitGroup)

放弃 time.Sleep 土鳖等协程。靠精准算术下发任务数。

  • 加任务:在协程发动 go func() 之前:wg.Add(1)
  • 退任务:切记在协程的第一行埋死兜底:defer wg.Done()
  • 查结果:大主子线程结尾等收网:wg.Wait()
  • ⚠️ 切记协程如果外部封装函数里带着这个计数器往下发,必定全用指针 *sync.WaitGroup 传,绝不能发影子副本去糊弄

🌈 Channel 管道交互

在不同的 Go 并行管线之间安全接抛快递件。不许在声明里画蛇添足加 *它是自带原生引用指针体制的)。

  1. 强同步版 make(chan int) -> (无缓冲)双方不到齐就不准过,死锁堵塞。
  2. 接活版 make(chan int, 3) -> (有缓冲)放信箱不超过 3 的上限随便撒手,不用等对方拿货立马往下跑。
  3. 读死空结案 -> 如果使用 for range 去拿货,发送端用完如果不自己发 close(通道名) 倒闭关门拉电闸,那收货这段就会永久处于接收痴汉傻等死机状态 (PanicDeadlock)