你可能已经注意到------Docker、Kubernetes、etcd、Prometheus、TiDB、CockroachDB......这些云原生时代的明星项目,几乎全是 Go 写的。你刷到过的后端岗位 JD,也越来越多地写着「熟悉 Go 优先」。
但当你真的打开一份 Go 代码,第一反应可能是:
:=是个什么鬼?- 为什么没有
class,只有struct?- 为什么一个
go关键字就能「并发」,连线程池都不用?- 为什么满屏都是
if err != nil?- 为什么函数能「返回两个值」?
一、认识 Go:它是一门什么样的语言
1.1 它为什么被造出来?
Go(也叫 Golang)诞生于 2007 年的 Google ,由三位大佬联手设计,其中一位是 C 语言的作者 Ken Thompson ,一位是 Unix 和 UTF-8 的重要参与者 Rob Pike。
他们造 Go 的初衷很朴素:受够了 C++ 那套又慢又复杂的体系。
在 Google,一个稍微大点的 C++ 项目,编译一次要等好几分钟;并发编程要小心翼翼地管理线程和锁;语法又复杂得让人头大。于是他们想造一门新语言,同时满足三个诉求:
- 编译要快------像解释型语言一样快,几秒就出结果;
- 并发要简单------轻松写出能同时处理海量请求的程序;
- 语法要少------让团队里所有人都能很快上手,代码风格高度统一。
1.2 Go 的三大招牌
| 招牌 | 含义 | 一句话理解 |
|---|---|---|
| 简单 | 语法刻意做「少」,关键字只有 25 个 | 少,是它的设计目标,不是缺陷 |
| 并发 | go 一个关键字就能启动协程 |
并发是「内置能力」,不是「高级技巧」 |
| 编译快 | 静态编译,产物是单个可执行文件 | 编译后直接丢给服务器就能跑 |
1.3 一张图看懂 Go 的特性版图
这篇文章要讲的特性,可以分成四大块,先给你一张全景图:
#mermaid-svg-q6M2lgQ5SJ2iuHwD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .error-icon{fill:#552222;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .marker.cross{stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD p{margin:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label text{fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label span{color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label span p{background-color:transparent;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD span{fill:#333;color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node rect,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node circle,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node ellipse,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node polygon,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .rough-node .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label{text-anchor:middle;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .rough-node .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label{text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node.clickable{cursor:pointer;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .arrowheadPath{fill:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster text{fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster span{color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD rect.text{fill:none;stroke-width:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape p,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label rect,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q6M2lgQ5SJ2iuHwD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go 语言特性
基础语法
核心特性
并发模型
工程工具
变量 / 类型 / 常量
函数 / 指针 / 方法
流程控制 for/if/switch
切片 slice
defer 延迟执行
接口 interface(鸭子类型)
错误处理 error
goroutine 协程
channel 通道
select 多路复用
go mod 依赖
go build/fmt/test
一句话 :Go 的定位很清晰------做基础设施、后端服务、云原生工具的「趁手家伙」。它不求语法花哨,只求「简单、快、并发好」这三件事做到极致。
二、环境与第一个程序:Hello World
2.1 安装与项目初始化
装好 Go 之后,先建一个项目目录,然后初始化模块(Go 用「模块」管理项目,类似给它起个唯一的名字):
bash
mkdir hello
cd hello
go mod init example.com/hello
# 会生成一个 go.mod 文件,记录模块名和 Go 版本
2.2 第一个程序逐行拆解
新建 main.go,写上:
go
package main // ① 声明包名:main 包是「可执行程序」的入口
import "fmt" // ② 导入标准库 fmt(格式化输出)
func main() { // ③ main 函数:程序的入口
fmt.Println("Hello, World!") // ④ 打印一行
}
几个和别的语言不太一样的点,先混个脸熟:
package main:每个 Go 文件都归属一个包。只有main包里的main()函数,才是程序的启动入口。- 导入用字符串 :
import "fmt",不用尖括号,也不用分号。 func是 Go 声明函数的唯一关键字。
2.3 go run vs go build
bash
go run main.go # 编译 + 运行,适合开发调试
go build main.go # 只编译,生成可执行文件(Windows 上是 .exe),适合部署
| 命令 | 作用 | 场景 |
|---|---|---|
go run |
边编译边运行 | 快速验证、开发 |
go build |
编译成二进制文件 | 生产部署 |
go build出来的产物是静态编译的单个文件------不依赖目标机器装任何运行时,直接丢上去就能跑。这是 Go 在运维圈特别吃香的重要原因之一。
一句话 :Go 的程序从package main+func main开始,用go run跑,用go build编译成独立的可执行文件。
三、基础语法:变量、类型、常量
3.1 变量声明的两种方式
Go 声明变量,有「完整版」和「简写版」两种:
go
// ① 完整版:var 声明,类型写在变量名后面
var age int = 18
var name string = "小明"
// ② 简写版:短变量声明(最常用),类型自动推断
age := 18
name := "小明"
⚠️ 一个重要的坑 :
:=(短变量声明)只能用在函数内部 。包级别(函数外)必须用var。
go
package main
var globalCount = 100 // ✅ 包级别,用 var
func main() {
local := 1 // ✅ 函数内,可以用 :=
}
3.2 基本类型一览
| 类型 | 说明 | 示例 |
|---|---|---|
int / int64 |
整数 | 42 |
float64 |
浮点数(默认) | 3.14 |
string |
字符串 | "hello" |
bool |
布尔 | true / false |
byte |
单个字节 | 'a' |
rune |
单个 Unicode 字符(中文等) | '中' |
3.3 Go 特色:零值(zero value)
这是 Go 和很多语言很不一样 的地方------变量声明后,不需要你赋值,它自动有一个「零值」,绝不会是「未定义」的垃圾值:
go
var i int // 0
var s string // ""(空字符串)
var b bool // false
var p *int // nil
var f float64 // 0
#mermaid-svg-pWzMprhswmCALyfK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pWzMprhswmCALyfK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pWzMprhswmCALyfK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pWzMprhswmCALyfK .error-icon{fill:#552222;}#mermaid-svg-pWzMprhswmCALyfK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pWzMprhswmCALyfK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK .marker.cross{stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pWzMprhswmCALyfK p{margin:0;}#mermaid-svg-pWzMprhswmCALyfK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label text{fill:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label span{color:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label span p{background-color:transparent;}#mermaid-svg-pWzMprhswmCALyfK .label text,#mermaid-svg-pWzMprhswmCALyfK span{fill:#333;color:#333;}#mermaid-svg-pWzMprhswmCALyfK .node rect,#mermaid-svg-pWzMprhswmCALyfK .node circle,#mermaid-svg-pWzMprhswmCALyfK .node ellipse,#mermaid-svg-pWzMprhswmCALyfK .node polygon,#mermaid-svg-pWzMprhswmCALyfK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .rough-node .label text,#mermaid-svg-pWzMprhswmCALyfK .node .label text,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label,#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label{text-anchor:middle;}#mermaid-svg-pWzMprhswmCALyfK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .rough-node .label,#mermaid-svg-pWzMprhswmCALyfK .node .label,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label,#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label{text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .node.clickable{cursor:pointer;}#mermaid-svg-pWzMprhswmCALyfK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK .arrowheadPath{fill:#333333;}#mermaid-svg-pWzMprhswmCALyfK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pWzMprhswmCALyfK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pWzMprhswmCALyfK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .cluster text{fill:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster span{color:#333;}#mermaid-svg-pWzMprhswmCALyfK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pWzMprhswmCALyfK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pWzMprhswmCALyfK rect.text{fill:none;stroke-width:0;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape,#mermaid-svg-pWzMprhswmCALyfK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape p,#mermaid-svg-pWzMprhswmCALyfK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label rect,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pWzMprhswmCALyfK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pWzMprhswmCALyfK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 声明变量
var i int
自动初始化为零值
i = 0
不存在「未初始化」
的脏数据
这个设计能消灭一大类「用了没初始化的变量」的 bug。你永远不用担心读到一个内存里的随机值。
3.4 常量 const 与 iota
go
const Pi = 3.14159
const (
StatusOK = 200
StatusFail = 500
)
iota 是 Go 的一个小甜点------在 const 块里,它能自动递增,很适合定义「一组连续的枚举值」:
go
const (
Monday = iota // 0
Tuesday // 1(自动接上)
Wednesday // 2
Thursday // 3
)
3.5 类型转换(显式,不隐式)
Go 不会自动做类型转换,必须显式写出来:
go
var i int = 42
var f float64 = float64(i) // 必须显式转换
一句话 :Go 的变量用
var或:=声明,类型写在变量名后;变量有「零值」兜底;类型转换必须显式写。这些「反直觉」的细节,都是 Go「把话说清楚」哲学的体现。
四、复合类型:数组、切片、map、结构体
这是 Go 里最常用的几种「装数据的容器」,其中 slice(切片)是重中之重。
4.1 数组:定长,其实很少用
go
var arr [3]int // 长度为 3 的数组
arr[0] = 1
arr[1] = 2
arr[2] = 3
// 更常见的写法
arr2 := [3]int{1, 2, 3}
数组的特点是长度固定,长度属于类型的一部分 。因为不够灵活,实际开发中你很少直接用数组,绝大多数场景用的是切片。
4.2 切片 slice:Go 的「动态数组」(重点)
切片才是 Go 里真正的「王者容器」,你可以把它理解成「能自动扩容的数组视图」。
创建切片:
go
// ① 直接字面量
s := []int{1, 2, 3}
// ② make 创建(指定长度,可选容量)
s2 := make([]int, 5) // 长度 5,容量 5
s3 := make([]int, 0, 10) // 长度 0,容量 10(预分配)
// ③ 从数组或切片「切」出来
arr := [5]int{1, 2, 3, 4, 5}
sub := arr[1:3] // [2, 3](下标 1 到 2,左闭右开)
追加元素 append:
go
s := []int{1, 2, 3}
s = append(s, 4) // [1, 2, 3, 4]
s = append(s, 5, 6) // [1, 2, 3, 4, 5, 6]
⚠️ 注意:
append返回一个新切片 ,所以要用s = append(s, ...)接住返回值,不能直接append(s, ...)就完事。
切片底层长什么样?
这是理解切片的钥匙。一个切片内部由三部分组成:
#mermaid-svg-qd83ZQZv5vjKjj2i{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qd83ZQZv5vjKjj2i .error-icon{fill:#552222;}#mermaid-svg-qd83ZQZv5vjKjj2i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qd83ZQZv5vjKjj2i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .marker.cross{stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qd83ZQZv5vjKjj2i p{margin:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label text{fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label span{color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label span p{background-color:transparent;}#mermaid-svg-qd83ZQZv5vjKjj2i .label text,#mermaid-svg-qd83ZQZv5vjKjj2i span{fill:#333;color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .node rect,#mermaid-svg-qd83ZQZv5vjKjj2i .node circle,#mermaid-svg-qd83ZQZv5vjKjj2i .node ellipse,#mermaid-svg-qd83ZQZv5vjKjj2i .node polygon,#mermaid-svg-qd83ZQZv5vjKjj2i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .rough-node .label text,#mermaid-svg-qd83ZQZv5vjKjj2i .node .label text,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label,#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label{text-anchor:middle;}#mermaid-svg-qd83ZQZv5vjKjj2i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .rough-node .label,#mermaid-svg-qd83ZQZv5vjKjj2i .node .label,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label,#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label{text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .node.clickable{cursor:pointer;}#mermaid-svg-qd83ZQZv5vjKjj2i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .arrowheadPath{fill:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qd83ZQZv5vjKjj2i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster text{fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster span{color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qd83ZQZv5vjKjj2i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i rect.text{fill:none;stroke-width:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape p,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label rect,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qd83ZQZv5vjKjj2i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qd83ZQZv5vjKjj2i :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 切片 s
(一个小的结构体)
① 指针
指向底层数组
② len 长度
当前有几个元素
③ cap 容量
底层数组能装几个
底层数组
1,2,3,4,5,...
go
s := make([]int, 3, 5)
// len(s) = 3(已有 3 个元素)
// cap(s) = 5(底层数组能装 5 个)
关键点:
len是你「能用到」的元素个数 ,cap是底层数组「实际分配」的空间;- 当
append超出cap时,Go 会分配一块更大的新数组,把老数据拷过去,这就是「扩容」。
切片共享底层数组的坑(重点,一定要懂):
go
a := []int{1, 2, 3, 4, 5}
b := a[0:3] // b 和 a 共享同一个底层数组!
b[0] = 999
fmt.Println(a) // [999, 2, 3, 4, 5] ← a 也被改了!
#mermaid-svg-mv6bnCmVMCgyXOpt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mv6bnCmVMCgyXOpt .error-icon{fill:#552222;}#mermaid-svg-mv6bnCmVMCgyXOpt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mv6bnCmVMCgyXOpt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .marker.cross{stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mv6bnCmVMCgyXOpt p{margin:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label text{fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label span{color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label span p{background-color:transparent;}#mermaid-svg-mv6bnCmVMCgyXOpt .label text,#mermaid-svg-mv6bnCmVMCgyXOpt span{fill:#333;color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .node rect,#mermaid-svg-mv6bnCmVMCgyXOpt .node circle,#mermaid-svg-mv6bnCmVMCgyXOpt .node ellipse,#mermaid-svg-mv6bnCmVMCgyXOpt .node polygon,#mermaid-svg-mv6bnCmVMCgyXOpt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .rough-node .label text,#mermaid-svg-mv6bnCmVMCgyXOpt .node .label text,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label,#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label{text-anchor:middle;}#mermaid-svg-mv6bnCmVMCgyXOpt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .rough-node .label,#mermaid-svg-mv6bnCmVMCgyXOpt .node .label,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label,#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label{text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .node.clickable{cursor:pointer;}#mermaid-svg-mv6bnCmVMCgyXOpt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .arrowheadPath{fill:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mv6bnCmVMCgyXOpt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster text{fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster span{color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mv6bnCmVMCgyXOpt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt rect.text{fill:none;stroke-width:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape p,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label rect,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mv6bnCmVMCgyXOpt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mv6bnCmVMCgyXOpt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 共享底层数组
切片 a
len=5
底层数组
999,2,3,4,5
切片 b
len=3
因为
b只是a的一个「视图」,它们指向同一块内存。改b会影响到a。这既是切片高效的原因,也是新手最容易踩的坑。
4.3 map:键值对
go
// 创建(必须用 make 或字面量,不能只 var 声明就塞值)
m := make(map[string]int)
m["apple"] = 1
m["banana"] = 2
// 读取
v := m["apple"] // 1
// 判断 key 是否存在(重点写法)
v, ok := m["orange"]
if ok {
fmt.Println("存在,值是", v)
} else {
fmt.Println("不存在")
}
// 删除
delete(m, "apple")
value, ok := m[key] 这个「双返回值判断存在」的写法,是 Go 的经典惯用法,务必记住。
4.4 结构体 struct
结构体是把多个字段「打包」成一种自定义类型:
go
type User struct {
Name string
Age int
}
u := User{Name: "小明", Age: 18}
fmt.Println(u.Name) // 小明
一句话 :数组定长少用;切片是动态数组 ,记住它底层是「指针 + len + cap」,记住
append要接返回值、切片共享底层数组;map用value, ok判断存在;struct用来打包字段。
五、流程控制:for / if / switch / defer
5.1 for:Go 唯一的循环
Go 里没有 while ,一个 for 就能表达所有循环:
go
// ① 经典 for
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// ② 相当于 while
n := 0
for n < 10 {
n++
}
// ③ 无限循环
for {
// 配合 break 跳出
}
5.2 if:可以带初始化语句
go
// 经典 if
if x > 0 {
fmt.Println("正数")
} else {
fmt.Println("非正数")
}
// Go 特色:if 前面可以跟一句初始化,作用域只在 if 内
if err := doSomething(); err != nil {
fmt.Println("出错了", err)
}
5.3 switch:比想象中灵活
Go 的 switch 有几处「省心」的设计:
go
// ① 不用写 break(自动跳出,不像某些语言要手动 break)
switch day {
case 1:
fmt.Println("周一")
case 2, 3: // ② 一个 case 可以匹配多个值
fmt.Println("周二或周三")
default:
fmt.Println("其他")
}
// ③ 甚至可以不带表达式,直接当 if-else 用
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
5.4 defer:延迟执行(重点)
defer 是 Go 非常有辨识度的特性------它声明的语句,不会立即执行,而是等「当前函数返回之前」再执行。
最常见的用途是保证资源一定被释放,比如关闭文件:
go
func readFile() {
f, err := os.Open("file.txt")
if err != nil {
return
}
defer f.Close() // 无论函数怎么返回,Close 都会在最后执行
// ... 处理文件 ...
}
defer 的两个关键规则:
规则一:defer 是「后进先出」(LIFO)------后 defer 的先执行:
go
func main() {
defer fmt.Println("第 1 个 defer")
defer fmt.Println("第 2 个 defer")
defer fmt.Println("第 3 个 defer")
fmt.Println("正常执行")
}
// 输出:
// 正常执行
// 第 3 个 defer
// 第 2 个 defer
// 第 1 个 defer
#mermaid-svg-6CwBOipgt6csDMXa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6CwBOipgt6csDMXa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6CwBOipgt6csDMXa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6CwBOipgt6csDMXa .error-icon{fill:#552222;}#mermaid-svg-6CwBOipgt6csDMXa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6CwBOipgt6csDMXa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .marker.cross{stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6CwBOipgt6csDMXa p{margin:0;}#mermaid-svg-6CwBOipgt6csDMXa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label text{fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label span{color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label span p{background-color:transparent;}#mermaid-svg-6CwBOipgt6csDMXa .label text,#mermaid-svg-6CwBOipgt6csDMXa span{fill:#333;color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .node rect,#mermaid-svg-6CwBOipgt6csDMXa .node circle,#mermaid-svg-6CwBOipgt6csDMXa .node ellipse,#mermaid-svg-6CwBOipgt6csDMXa .node polygon,#mermaid-svg-6CwBOipgt6csDMXa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .rough-node .label text,#mermaid-svg-6CwBOipgt6csDMXa .node .label text,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label,#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label{text-anchor:middle;}#mermaid-svg-6CwBOipgt6csDMXa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .rough-node .label,#mermaid-svg-6CwBOipgt6csDMXa .node .label,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label,#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label{text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .node.clickable{cursor:pointer;}#mermaid-svg-6CwBOipgt6csDMXa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .arrowheadPath{fill:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6CwBOipgt6csDMXa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6CwBOipgt6csDMXa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .cluster text{fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster span{color:#333;}#mermaid-svg-6CwBOipgt6csDMXa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6CwBOipgt6csDMXa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa rect.text{fill:none;stroke-width:0;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape,#mermaid-svg-6CwBOipgt6csDMXa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape p,#mermaid-svg-6CwBOipgt6csDMXa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label rect,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6CwBOipgt6csDMXa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6CwBOipgt6csDMXa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} defer 第1个
defer 第2个
defer 第3个
函数要返回了
先执行 第3个
再执行 第2个
最后执行 第1个
规则二:defer 会「快照」参数值 ------如果 defer 后面的表达式里有变量,会当场求出值存下来:
go
func main() {
x := 1
defer fmt.Println("x 的值是", x) // 这里已经把 x=1 记下来了
x = 100
// 输出:x 的值是 1(不是 100!)
}
一句话 :
for一统循环,switch不用 break 还很灵活,defer让「善后工作」和「主体逻辑」写在了一起、执行却放在最后。defer 记住两条:后进先出 、参数快照。
六、函数:Go 的一等公民
6.1 函数定义与多返回值
go
// 基本函数:func 函数名(参数) 返回类型
func add(a int, b int) int {
return a + b
}
// 参数类型相同可以合并写
func add2(a, b int) int {
return a + b
}
Go 函数最鲜明的特色是多返回值------这让「返回结果 + 返回错误」成了标准姿势:
go
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为 0")
}
return a / b, nil
}
// 调用
result, err := divide(10, 0)
if err != nil {
fmt.Println("出错了:", err)
}
6.2 可变参数
go
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2) // 3
sum(1, 2, 3, 4) // 10
6.3 函数是一等公民:可作参数、可作返回值、可有闭包
go
// 函数作为参数
func apply(f func(int) int, x int) int {
return f(x)
}
// 函数作为返回值(闭包)
func counter() func() int {
count := 0
return func() int {
count++ // 闭包:捕获了外部的 count
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
6.4 命名返回值
返回值可以「先命名」,这样在函数里直接赋值,最后裸 return 即可:
go
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 裸 return,自动返回 x、y
}
一句话 :Go 函数最大的特色是多返回值(尤其是「结果 + error」组合),此外它支持可变参数、闭包、函数当参数/返回值,是货真价实的「一等公民」。
七、指针与方法:值类型世界的「引用」
7.1 指针:& 取地址,* 解引用
Go 有指针,但没有指针运算 (不能像某些语言那样 p+1 乱跳),所以既安全又有用:
go
x := 10
p := &x // p 是指向 x 的指针
fmt.Println(*p) // 10(解引用,取出 x 的值)
*p = 20 // 通过指针修改 x
fmt.Println(x) // 20
7.2 Go 默认「传值」(重点)
这是理解 Go 的关键心智模型:函数传参,默认是「把值复制一份」传进去,改副本不影响原变量:
go
func change(x int) {
x = 999 // 改的是副本
}
func main() {
a := 10
change(a)
fmt.Println(a) // 还是 10(没被改)
}
想真正改原变量,就传指针:
go
func change(x *int) {
*x = 999 // 通过指针改原变量
}
func main() {
a := 10
change(&a)
fmt.Println(a) // 999(被改了)
}
7.3 方法 + 接收者:值接收者 vs 指针接收者(重点)
Go 没有类,但可以把函数「绑定」到某个类型上 ,这个被绑定的参数叫「接收者(receiver)」,这样的函数就是「方法」:
go
type User struct {
Name string
}
// ① 值接收者:操作的是副本,改不了原对象
func (u User) SetNameByValue(name string) {
u.Name = name // 改的是副本,外面没影响
}
// ② 指针接收者:操作的是原对象,能改
func (u *User) SetName(name string) {
u.Name = name // 改的是原对象
}
#mermaid-svg-ptGhAhEQpEhaTFCc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ptGhAhEQpEhaTFCc .error-icon{fill:#552222;}#mermaid-svg-ptGhAhEQpEhaTFCc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ptGhAhEQpEhaTFCc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .marker.cross{stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ptGhAhEQpEhaTFCc p{margin:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label text{fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label span{color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label span p{background-color:transparent;}#mermaid-svg-ptGhAhEQpEhaTFCc .label text,#mermaid-svg-ptGhAhEQpEhaTFCc span{fill:#333;color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .node rect,#mermaid-svg-ptGhAhEQpEhaTFCc .node circle,#mermaid-svg-ptGhAhEQpEhaTFCc .node ellipse,#mermaid-svg-ptGhAhEQpEhaTFCc .node polygon,#mermaid-svg-ptGhAhEQpEhaTFCc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .rough-node .label text,#mermaid-svg-ptGhAhEQpEhaTFCc .node .label text,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label,#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label{text-anchor:middle;}#mermaid-svg-ptGhAhEQpEhaTFCc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .rough-node .label,#mermaid-svg-ptGhAhEQpEhaTFCc .node .label,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label,#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label{text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .node.clickable{cursor:pointer;}#mermaid-svg-ptGhAhEQpEhaTFCc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .arrowheadPath{fill:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ptGhAhEQpEhaTFCc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster text{fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster span{color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ptGhAhEQpEhaTFCc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc rect.text{fill:none;stroke-width:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape p,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label rect,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ptGhAhEQpEhaTFCc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ptGhAhEQpEhaTFCc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指针接收者 (u *User)
传入原对象的地址
方法里改的是原对象
原对象被改
值接收者 (u User)
调用时复制一份
方法里改的是副本
原对象不变
| 接收者 | 作用 | 何时用 |
|---|---|---|
值接收者 (u User) |
改的是副本 | 只读、不改字段、小结构体 |
指针接收者 (u *User) |
改的是原对象 | 要改字段、大结构体(省拷贝) |
一句话 :Go 默认「传值」,struct 是值类型,赋值和传参都是「复制一份」。想共享、想修改,就用指针(
*)。「值接收者 vs 指针接收者」的差别,本质就是「改副本还是改原对象」。
八、接口 interface:Go 的灵魂(重点)
如果只让你记住 Go 的一个设计,那一定是接口的「隐式实现」。它是 Go 区别于绝大多数语言的地方。
8.1 接口的定义
接口就是「一组方法的约定」,谁实现了这些方法,谁就满足这个接口:
go
type Speaker interface {
Speak() string
}
8.2 隐式实现(鸭子类型)
关键来了------在 Go 里,一个类型不需要显式声明「我实现了某个接口」,只要它的方法「对得上」,就自动满足接口:
go
type Dog struct{}
func (d Dog) Speak() string {
return "汪汪"
}
// Dog 自动满足 Speaker 接口,不需要写 implements!
var s Speaker = Dog{}
fmt.Println(s.Speak()) // 汪汪
这就是所谓的「鸭子类型」哲学:
如果它走路像鸭子、叫起来也像鸭子,那它就是鸭子。
#mermaid-svg-ex4Y1bjKj8R6Yi4t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .error-icon{fill:#552222;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .marker.cross{stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t p{margin:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label text{fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label span{color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label span p{background-color:transparent;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t span{fill:#333;color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node rect,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node circle,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node ellipse,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node polygon,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .rough-node .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label{text-anchor:middle;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .rough-node .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label{text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node.clickable{cursor:pointer;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .arrowheadPath{fill:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster text{fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster span{color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t rect.text{fill:none;stroke-width:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape p,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label rect,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ex4Y1bjKj8R6Yi4t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go:隐式实现
方法对上了,自动成立
Dog 有 Speak() 方法
Speaker
传统语言:显式声明
必须写 implements Speaker
Dog
Speaker
8.3 空接口 interface{}:装任何类型
空接口 interface{} 不约束任何方法,所以任何类型都满足它,可以用来装「任意值」(类似万能容器):
go
var anything interface{}
anything = 42
anything = "hello"
anything = Dog{}
Go 1.18 之后,官方推荐用
any代替interface{},它们是同一个东西(any是interface{}的别名)。
8.4 类型断言:把「万能容器」里的东西取出来
装进去容易,取出来要「断言」回具体类型:
go
var v interface{} = "hello"
// ① 类型断言(失败会 panic)
s := v.(string)
fmt.Println(s) // hello
// ② 带 ok 的安全断言(推荐)
s, ok := v.(string)
if ok {
fmt.Println("是字符串:", s)
}
8.5 type switch:按类型分流
go
func describe(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("字符串:", v)
case int:
fmt.Println("整数:", v)
default:
fmt.Println("未知类型")
}
}
一句话 :Go 的接口是「隐式实现 」------你不需要声明实现关系,只要方法对上了,关系就自动成立。这让接口极其灵活:你甚至能让自己的类型,无缝满足第三方库的接口,而不需要改任何第三方代码。
九、错误处理:error 与 panic/recover
Go 处理错误的方式,和「抛异常」的思路完全不同。
9.1 error 当返回值:显式检查
Go 里,错误是普通的值,通过函数的返回值传回来,调用方显式判断:
go
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为 0")
}
return a / b, nil // nil 表示「没有错误」
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("出错:", err)
return
}
fmt.Println("结果:", result)
}
9.2 fmt.Errorf 与 %w(错误包装)
go
func openConfig() error {
_, err := os.Open("config.yaml")
if err != nil {
// %w 会「包装」原始错误,保留错误链
return fmt.Errorf("打开配置失败: %w", err)
}
return nil
}
9.3 panic / recover:真正的「灾难」
普通错误用 error 返回值处理;而 panic 是「程序遇到了没法继续的情况 」,会立刻崩溃。它配合 defer + recover 可以「接住」崩溃,避免整个程序挂掉:
go
func safeRun() {
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到 panic:", r)
}
}()
panic("出大事了")
// 上面的 panic 会被 defer 里的 recover 接住,程序不会崩
}
defer+recover panic 函数体 defer+recover panic 函数体 #mermaid-svg-YWWFsRieFhwzdtDR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YWWFsRieFhwzdtDR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YWWFsRieFhwzdtDR .error-icon{fill:#552222;}#mermaid-svg-YWWFsRieFhwzdtDR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YWWFsRieFhwzdtDR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YWWFsRieFhwzdtDR .marker.cross{stroke:#333333;}#mermaid-svg-YWWFsRieFhwzdtDR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YWWFsRieFhwzdtDR p{margin:0;}#mermaid-svg-YWWFsRieFhwzdtDR .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YWWFsRieFhwzdtDR .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-YWWFsRieFhwzdtDR .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .sequenceNumber{fill:white;}#mermaid-svg-YWWFsRieFhwzdtDR #sequencenumber{fill:#333;}#mermaid-svg-YWWFsRieFhwzdtDR #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .messageText{fill:#333;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR .labelText,#mermaid-svg-YWWFsRieFhwzdtDR .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .loopText,#mermaid-svg-YWWFsRieFhwzdtDR .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YWWFsRieFhwzdtDR .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-YWWFsRieFhwzdtDR .noteText,#mermaid-svg-YWWFsRieFhwzdtDR .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .actorPopupMenu{position:absolute;}#mermaid-svg-YWWFsRieFhwzdtDR .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-YWWFsRieFhwzdtDR .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR .actor-man circle,#mermaid-svg-YWWFsRieFhwzdtDR line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-YWWFsRieFhwzdtDR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} panic("出大事了")崩溃信号向上抛recover() 接住程序恢复,继续(不会崩)
9.4 三种「出错」手段的边界
| 机制 | 用途 | 会不会崩 |
|---|---|---|
error 返回值 |
预期内的普通错误 | 不会(由你处理) |
panic |
不可恢复的严重错误 | 会(除非 recover) |
recover |
在 defer 里「接住」panic | 阻止崩溃 |
一句话 :Go 把「普通错误 」和「致命异常 」分开------普通错误当返回值,
if err != nil显式处理,啰嗦但清晰;真正的灾难才panic。刚开始你会嫌if err != nil烦,但习惯了会爱上它的「所见即所得」。
十、并发:goroutine + channel(Go 的王牌)
这是 Go 最值钱的部分,也是它能在云原生时代封神的核心原因。
10.1 goroutine:一个 go 关键字就能并发
启动一个 goroutine(协程),只需要在函数调用前加一个 go:
go
func say(s string) {
for i := 0; i < 3; i++ {
fmt.Println(s)
}
}
func main() {
go say("world") // 开一个协程,并发执行
say("hello") // 主协程同时执行
}
没有线程池,没有复杂的配置,go 一个关键字搞定。
goroutine 为什么「轻」?
goroutine 是「用户态协程」,由 Go 运行时调度,而不是直接对应操作系统线程。它通过 GMP 调度模型,把成千上万个协程复用到少量系统线程上:
#mermaid-svg-JCTTRrmRCl5yuNk2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JCTTRrmRCl5yuNk2 .error-icon{fill:#552222;}#mermaid-svg-JCTTRrmRCl5yuNk2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JCTTRrmRCl5yuNk2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .marker.cross{stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JCTTRrmRCl5yuNk2 p{margin:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label text{fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label span{color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label span p{background-color:transparent;}#mermaid-svg-JCTTRrmRCl5yuNk2 .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 span{fill:#333;color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node rect,#mermaid-svg-JCTTRrmRCl5yuNk2 .node circle,#mermaid-svg-JCTTRrmRCl5yuNk2 .node ellipse,#mermaid-svg-JCTTRrmRCl5yuNk2 .node polygon,#mermaid-svg-JCTTRrmRCl5yuNk2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .rough-node .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .rough-node .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label{text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node.clickable{cursor:pointer;}#mermaid-svg-JCTTRrmRCl5yuNk2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .arrowheadPath{fill:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster text{fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster span{color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JCTTRrmRCl5yuNk2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape p,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label rect,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JCTTRrmRCl5yuNk2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go 运行时调度
G(goroutine 协程)
成千上万个
P(调度器)
M(系统线程)
少量几条
操作系统
所以 Go 能轻松跑百万级并发,而传统线程通常几千个就到头了。
10.2 channel:协程之间通信的「管道」
多个 goroutine 之间怎么交换数据?Go 给出的答案是 channel(通道),而不是「共享内存 + 锁」。
Go 的座右铭是那句著名的话:
不要通过共享内存来通信,而要通过通信来共享内存。
go
// 创建一个能传 int 的通道
ch := make(chan int)
// 开一个协程,往通道里发数据
go func() {
ch <- 42 // 发送 42(<- 表示「送入」)
}()
// 主协程从通道里收数据
value := <-ch // 接收(<- 表示「取出」)
fmt.Println(value) // 42
#mermaid-svg-uDarzcezLEZQWfSO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uDarzcezLEZQWfSO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uDarzcezLEZQWfSO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uDarzcezLEZQWfSO .error-icon{fill:#552222;}#mermaid-svg-uDarzcezLEZQWfSO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uDarzcezLEZQWfSO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .marker.cross{stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uDarzcezLEZQWfSO p{margin:0;}#mermaid-svg-uDarzcezLEZQWfSO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label text{fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label span{color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label span p{background-color:transparent;}#mermaid-svg-uDarzcezLEZQWfSO .label text,#mermaid-svg-uDarzcezLEZQWfSO span{fill:#333;color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .node rect,#mermaid-svg-uDarzcezLEZQWfSO .node circle,#mermaid-svg-uDarzcezLEZQWfSO .node ellipse,#mermaid-svg-uDarzcezLEZQWfSO .node polygon,#mermaid-svg-uDarzcezLEZQWfSO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .rough-node .label text,#mermaid-svg-uDarzcezLEZQWfSO .node .label text,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label,#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label{text-anchor:middle;}#mermaid-svg-uDarzcezLEZQWfSO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .rough-node .label,#mermaid-svg-uDarzcezLEZQWfSO .node .label,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label,#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label{text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .node.clickable{cursor:pointer;}#mermaid-svg-uDarzcezLEZQWfSO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .arrowheadPath{fill:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uDarzcezLEZQWfSO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uDarzcezLEZQWfSO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .cluster text{fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster span{color:#333;}#mermaid-svg-uDarzcezLEZQWfSO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uDarzcezLEZQWfSO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO rect.text{fill:none;stroke-width:0;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape,#mermaid-svg-uDarzcezLEZQWfSO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape p,#mermaid-svg-uDarzcezLEZQWfSO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label rect,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uDarzcezLEZQWfSO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uDarzcezLEZQWfSO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ch <- 42 发送
<-ch 接收
goroutine 1
channel
通道
goroutine 2
数据顺着管道流动
天然避免「抢数据」的竞争
有缓冲 vs 无缓冲:
go
ch1 := make(chan int) // 无缓冲:发送方要等接收方(同步)
ch2 := make(chan int, 5) // 有缓冲:能先塞 5 个,满了才阻塞
关闭与 range:
go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch) // 关闭通道
for v := range ch { // range 会一直读,直到通道关闭
fmt.Println(v)
}
10.3 select:同时监听多个通道
select 让一个 goroutine 能「同时等待」多个通道,哪个先有数据就先处理哪个:
go
ch1 := make(chan string)
ch2 := make(chan string)
go func() { ch1 <- "来自 1" }()
go func() { ch2 <- "来自 2" }()
select {
case msg := <-ch1:
fmt.Println("收到:", msg)
case msg := <-ch2:
fmt.Println("收到:", msg)
}
10.4 sync 包:WaitGroup 与 Mutex
虽然「通信共享内存」是主流,但偶尔也需要传统工具:
go
import "sync"
// ① WaitGroup:等待一组 goroutine 全部完成
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1) // 计数 +1
go func(n int) {
defer wg.Done() // 完成后计数 -1
fmt.Println(n)
}(i)
}
wg.Wait() // 等所有 goroutine 完成
// ② Mutex:互斥锁,保护共享变量
var mu sync.Mutex
var count int
func inc() {
mu.Lock()
count++
mu.Unlock()
}
| 工具 | 作用 | 一句话 |
|---|---|---|
go |
启动协程 | 一个关键字并发 |
channel |
协程通信 | 通过通信共享内存 |
select |
多路复用 | 哪个先到先处理哪个 |
sync.WaitGroup |
等待协程完成 | 计数等待 |
sync.Mutex |
互斥锁 | 保护共享变量 |
一句话 :Go 的并发就两个核心------goroutine(轻量协程)负责「跑」,channel(通道)负责「通信」。理解了「通过通信共享内存」这句座右铭,你就理解了 Go 最值钱的那一半。
十一、包与工程:go mod 与工具链
最后,聊聊怎么「组织一个 Go 项目」。
11.1 包与导入
Go 用「包(package)」组织代码,一个目录通常就是一个包:
go
// 声明包
package service
// 导入
import (
"fmt" // 标准库
"example.com/myproj/internal/util" // 自己项目内的包
"github.com/gin-gonic/gin" // 第三方包
)
11.2 首字母大小写决定可见性(Go 独特规则)
Go 没有 public / private 关键字,可见性由标识符首字母大小写决定:
go
type User struct {
Name string // 首字母大写 = 导出(其他包能访问)
age int // 首字母小写 = 私有(只有本包能访问)
}
func PublicFunc() {} // 导出
func privateFunc() {} // 私有
| 首字母 | 可见性 | 等价概念 |
|---|---|---|
| 大写 | 导出(exported) | public |
| 小写 | 私有 | private |
11.3 go mod:依赖管理
go
// go.mod 文件示例
module example.com/myproj
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
)
module声明模块路径(就是项目的唯一名字);require声明依赖;go.sum记录依赖的校验和,保证依赖不被篡改。
11.4 工具链:开箱即用
Go 自带一整套工具,不需要额外装任何东西:
| 命令 | 作用 |
|---|---|
go build |
编译 |
go run |
直接运行 |
go test |
跑测试 |
go fmt |
自动格式化(强制统一风格) |
go vet |
静态检查 |
go mod tidy |
自动整理依赖 |
特别值得一提的是
go fmt------它强制所有 Go 代码的格式一致,从此团队里再也没有「代码风格之争」,这是很多老程序员羡慕到不行的设计。
一句话 :Go 的工程哲学是「工具链全内置 + 约定优于配置 」。不需要额外的构建系统,go build、go test、go fmt一条命令搞定;可见性用首字母大小写表达,简单又统一。
写在最后
我们从「Go 到底是什么」出发,一路把它的语法和特性系统过了一遍。回顾这条主线:
- 认识 Go:一门「简单、并发好、编译快」的语言,云原生的基础设施语言;
- 入门 :
package main+func main起步,go run/go build一跑一编译; - 基础语法 :
var/:=声明变量,变量有「零值」兜底,类型转换显式写; - 复合类型 :数组定长少用,切片 slice (指针+len+cap)才是王者,
map用value, ok判存在; - 流程控制 :
for一统循环,switch不用 break,defer 后进先出、参数快照; - 函数:多返回值是招牌,函数是一等公民(闭包、当参数);
- 指针与方法:Go 默认传值,想共享就传指针,值/指针接收者分清;
- 接口 interface:隐式实现(鸭子类型),Go 最惊艳的设计;
- 错误处理 :
error当返回值,panic/recover处理灾难; - 并发 :
goroutine跑 +channel通信,Go 的王牌; - 工程 :
go mod管依赖,首字母大小写定可见性,工具链全内置。