1. 变量与常量
1.1 变量声明与初始化
var 关键字的标准声明方式
- 示例:
var name string = "Go"
- 显式类型声明与隐式类型推断的区别
- 批量声明语法:
var (a int; b string)
类型推断机制
- 编译器根据右侧表达式自动推导类型
- 示例:
var x = 42
自动推断为 int 类型 - 实际应用:当变量类型明显时,省略类型声明提高代码简洁性
短变量声明 := 的使用场景与限制
- 只能在函数内部使用的具体原因(避免全局作用域污染)
- 不能用于重复声明的示例场景:
x := 1; x := 2
会导致编译错误 - 多变量同时声明:
x, y := 1, "hello"
常用于函数返回多个值时 - 与 var 声明的性能差异:编译后无实质区别
零值机制
- 数值类型:0(包括所有整数和浮点数)
- 布尔类型:false
- 字符串:"" (空字符串,不是nil)
- 指针:nil
- 复合类型:slice/map/channel 等均为 nil
- 结构体:各字段对应类型的零值
1.2 常量定义
const 关键字的基本用法
- 必须初始化且不能修改的底层实现原理(编译期替换)
- 批量声明:
const (PI = 3.14; E = 2.718)
- 常量表达式限制:只能使用基本类型和常量表达式
iota 枚举器的高级用法
- 基础示例:
const (A = iota; B; C)
生成 0,1,2 - 表达式中的灵活应用:
const (KB = 1 << (10 * iota); MB; GB)
- 跳过特定值:
const (A = iota; _; C)
- 重置规则:每遇到新的 const 关键字 iota 重置为0
- 实际应用:定义错误码、状态机等场景
2. 基本数据类型
2.1 数值类型
整型
- 有符号整型:int8(-128~127)到int64
- 无符号整型:uint8(0~255)到uint64
- 平台相关类型:int/uint 根据系统自动调整(32位或64位)
- 类型转换:必须显式转换,如
int32(x)
- 溢出处理:编译时不检查,运行时按二进制补码处理
浮点型
- float32:约6位十进制精度,适合嵌入式等内存敏感场景
- float64:约15位十进制精度,推荐默认使用
- 科学计数法表示:1.23e4 表示 12300
- 特殊值:+Inf/-Inf/NaN 的判断和处理
复数
- 工业应用:信号处理、图形计算
- 示例:
var c complex64 = 3 + 4i
- 操作函数:real()/imag() 获取实部和虚部
- 性能考虑:complex128 比 complex64 运算慢但精度高
2.2 布尔与字符串
bool 类型
- 严格禁止隐式转换(不同于C/C++)
- 必须显式比较:
if x > 0 {...}
而非if x {...}
- 逻辑运算:&& || ! 的短路特性
字符串特性
- UTF-8编码示例:"你好"占用6字节(每个中文字符3字节)
- 不可变性原理:底层字节数组不可变,修改会创建新字符串
- 原生多行字符串:
msg := `第一行 第二行`
- 常用操作:len()获取字节数,utf8.RuneCountInString获取字符数
- 高效处理:strings.Builder 用于大量字符串拼接
3. 控制结构
3.1 条件语句
if 语句
- 初始化语句示例:
if x := f(); x > 0 {...}
- 作用域限制:初始化变量仅在if块内有效
- 省略括号但强制要求大括号的风格
switch 语句
- 表达式switch:
switch x {...}
支持任意表达式 - 类型switch:
switch x.(type) {...}
用于接口类型判断 - fallthrough 的注意事项:会继续执行下一个case(不判断条件)
- case 表达式:可以是多个值的列表
case 1,3,5:
- 与if-else的性能差异:编译器优化后基本相同
3.2 循环语句
for 循环
- 传统C风格:
for i := 0; i < 10; i++ {...}
- while等效写法:
for x < 100 {...}
- 无限循环:
for {...}
或for true {...}
- 循环控制:break/continue 支持标签跳转
range 遍历
- 数组/切片:
for i, v := range arr {...}
(i为索引,v为值副本) - map:
for k, v := range m {...}
顺序随机 - 字符串:按rune遍历:
for i, r := range "你好" {...}
- 忽略值:
for _, v := range slice {...}
- 性能优化:大数据量时避免在循环内append
4. 函数与包
4.1 函数特性
多返回值
- 典型示例:
value, err := os.Open("file.txt")
- 忽略返回值:
_, err := function()
- 返回值命名:
func f() (x int, y string) {...}
命名返回值
- 优点:提高代码可读性,特别是多个同类型返回值
- 缺点:可能导致变量意外修改(return 不指定返回值时会使用命名变量)
- 最佳实践:简单函数可省略,复杂逻辑建议使用
可变参数
- 示例:
func sum(nums ...int) int {...}
- 展开切片:
sum(nums...)
语法糖 - 限制:必须是最后一个参数,且类型相同
- 底层实现:实际转换为切片传递
4.2 包管理
导出规则
- 大写字母开头的标识符才能被外部包访问
- 小写字母开头的为包内私有
- 例外:_test.go 文件中的测试代码可访问未导出标识符
包初始化
- init() 函数的执行顺序:按导入顺序执行
- 包级变量的初始化时机:在init()之前
- 循环导入问题:编译器会检测并报错
- 最佳实践:避免复杂的init()逻辑
5. 复合数据类型
5.1 数组与切片
数组
- 固定长度示例:
var a [3]int
- 值传递特性:函数参数传递时会复制整个数组
- 初始化方式:
a := [3]int{1,2,3}
或a := [...]int{1,2,3}
- 内存布局:连续存储,类型包含长度信息
切片
- 底层三元结构:ptr(指针)、len(长度)、cap(容量)
- 扩容机制:当cap不足时,通常按2倍扩容(小切片)或1.25倍(大切片)
- 常用操作:
make([]int, 5, 10)
创建长度5容量10的切片append(slice, 1, 2, 3)
追加元素(可能触发扩容)s[1:3]
创建子切片(共享底层数组)
- 陷阱:多个切片共享底层数组时的意外修改
5.2 映射与结构体
map
- 线程安全问题:并发读写会导致panic
- 安全使用方式:sync.Map 或 sync.RWMutex
- 初始化:
m := make(map[string]int)
或m := map[string]int{"a":1}
- 操作:
v,ok := m["key"]
判断存在性 - 内存特性:非连续存储,自动扩容
结构体
- 匿名嵌套示例:
type Person struct { Name string } type Employee struct { Person; ID int }
- 与OOP继承的区别:本质是组合而非继承
- 标签:
json:"name"
等元信息 - 内存对齐:影响结构体大小和性能
- 值语义:赋值和传参是值拷贝
6. 指针与方法
6.1 指针特性
与C指针的区别
- 无指针算术运算:
p++
非法 - 自动解引用:结构体字段访问时自动解引用
- 安全性:不会出现野指针(GC管理内存)
new 与 make
- new(T):返回 *T,分配零值内存
- make(T, args):用于slice、map、channel的初始化
- 内存分配位置:可能分配在栈上(逃逸分析优化)
6.2 方法体系
接收者类型
- 值接收者:
func (v Value) Method() {...}
(操作副本) - 指针接收者:
func (p *Pointer) Method() {...}
(操作原值) - 选择依据:是否需要修改接收者,结构体大小
方法集
- 值类型的方法集包含值接收者方法
- 指针类型的方法集包含所有方法
- 接口实现的影响:值类型只能调用值接收者方法
7. 接口与错误处理
7.1 接口设计
鸭子类型
- 实现原理:隐式接口实现
- 示例:只要实现了Read()和Write()就是io.ReadWriter
- 优点:解耦设计和实现
空接口
- interface{} 作为泛型替代方案
- 类型断言:
if str, ok := x.(string); ok {...}
- 类型switch:
switch x.(type) {...}
- 性能开销:涉及反射会有额外成本
7.2 错误处理
error 接口
- 标准实践:
if err != nil {...}
- 自定义错误:
type MyError struct { Msg string }
- 错误包装:
fmt.Errorf("context: %w", err)
- 错误链:
errors.Is
和errors.As
处理嵌套错误
panic/recover
- 正确使用场景:不可恢复的错误(如程序初始化失败)
- 示例:
defer func() { if r := recover(); r != nil {...} }()
- 与error的区别:panic用于程序无法继续执行的错误
- 性能影响:panic比正常返回慢约1000倍
8. 并发编程
8.1 Goroutine原理
轻量级线程
- 初始栈大小仅2KB(相比线程MB级)
- GMP调度模型:Goroutine、Machine、Processor
- 调度特点:协作式抢占,基于函数调用的抢占点
并发模式
- Worker Pool示例:使用channel分发任务
- 发布订阅模式:使用多个channel实现
- Pipeline模式:串联多个处理阶段
- 超时控制:
select
配合time.After
8.2 Channel高级用法
通道类型
- 无缓冲通道:
ch := make(chan int)
(同步通信) - 有缓冲通道:
ch := make(chan int, 10)
(异步通信) - 单向通道:
<-chan
只读,chan<-
只写
select 语句
- 超时处理:
case <-time.After(1*time.Second):
- 默认分支:
default: ...
(非阻塞操作) - 随机选择:多个case就绪时随机执行一个
9. 标准库精选
9.1 常用包
fmt
- 格式化动词:
%v
通用格式%+v
显示结构体字段名%#v
Go语法表示%T
类型信息
strings
- Builder模式:高效字符串拼接
- 示例:
var b strings.Builder b.WriteString("Hello") b.WriteString(" World") result := b.String()
- 性能优势:避免多次内存分配
9.2 文件与IO
文件操作
os.Open()
与os.Create()
ioutil.ReadFile()
一次性读取小文件- 资源释放:
defer file.Close()
- 文件权限:0644 等八进制表示
缓冲IO
bufio.NewReader()
提高读取效率- 示例:逐行读取文件
scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() ... }
- 缓冲区大小:默认4096字节,可自定义
10. 工程实践
10.1 代码规范
命名规则
- 接口命名:Reader、Writer(-er后缀)
- 包命名:简短小写,避免下划线
- 变量命名:驼峰式,短小但描述性
- 常量命名:全大写加下划线
文档注释
- 示例:
// Add returns the sum of two integers func Add(a, b int) int { return a + b }
- godoc 工具生成文档
- 包注释:放在doc.go文件或包的第一个文件
10.2 开发工具链
go fmt
- 自动格式化代码风格
- 配置:gofmt -s 简化代码
- 与IDE集成:保存时自动运行
go test
- 表格驱动测试示例:
func TestAdd(t *testing.T) { tests := []struct { a, b, want int }{ {1, 2, 3}, {0, 0, 0}, } for _, tt := range tests { if got := Add(tt.a, tt.b); got != tt.want { t.Errorf(...) } } }
- 基准测试:
func BenchmarkXxx(b *testing.B)
- 示例测试:输出验证
11. 进阶指南
11.1 反射机制
reflect 包
- 典型场景:JSON序列化/反序列化
- 示例:获取结构体字段名
v := reflect.ValueOf(obj) for i := 0; i < v.NumField(); i++ { fmt.Println(v.Type().Field(i).Name) }
- 性能警告:反射操作比直接调用慢100倍
- 类型安全:运行时可能panic
11.2 模块化开发
go mod
- 初始化:
go mod init example.com/mymodule
- 依赖管理:go.mod 文件格式
- 版本控制:
require example.com/pkg v1.2.3
- 代理设置:GOPROXY环境变量
- 私有仓库:GOPRIVATE配置
- 版本选择:最小版本选择算法