涵盖基础语法、并发、内存管理、接口设计、标准库与工具、性能优化以及实战场景
一、基础语法与类型
1. Go 与 C/Java 的主要区别
- 内存管理:Go 有垃圾回收(GC),不需要手动管理内存。
- 并发模型:Go 内置 goroutine 和 channel,而 C/Java 需要线程库或 Executor。
- 面向对象:Go 不支持继承,用组合 + 接口实现多态。
- 错误处理:Go 通过返回值 error 处理,而不是异常。
- 编译与部署:Go 生成静态二进制,部署简单;Java 需要 JVM。
2. Go 的数据类型
基础类型
布尔类型 :bool
数值类型 :
整型 :int8, int16, int32, int64, uint8, uint16, uint32, uint64, int, uint, uintptr
浮点型 :float32, float64
复数型 :complex64, complex128
字符类型 :
byte :uint8 的别名
rune :int32 的别名,表示一个 Unicode 码点
字符串类型:string
复合类型
数组类型 :[n]T,长度固定的元素序列
切片类型 :[]T,动态长度的元素序列
结构体类型 :struct,字段集合,支持方法和接口
指针类型 :*T,指向类型 T 的指针
函数类型 :func,函数签名
接口类型 :interface,定义方法集的类型
映射类型 :map[K]V,键值对集合
通道类型:chan T,用于 goroutine 之间通信的管道
3. new 与 make 区别
关键点 | new | make |
---|---|---|
用途 | 分配内存 | 初始化 slice、map、chan |
返回值 | 指针 | 对象本身 |
例子 | p := new(int) // *int | s := make([]int,5) // []int |
4. defer 执行时机
- 在函数返回前执行,遵循 后进先出(LIFO)
- 常用于释放资源、解锁、打印日志
go
func f() {
defer fmt.Println("first")
defer fmt.Println("second")
fmt.Println("function body")
}
// 输出:
// function body
// second
// first
5. 指针使用限制
- Go 不允许指针算术运算
- 避免野指针和内存越界
- 只能通过 new/make 或取地址 &var 获取指针
二、控制流与函数
1. 可变参数函数
go
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
2. 闭包(closure)
- 函数可以引用外部变量并返回函数,保持状态
go
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
3. panic、recover 与 error
- panic:运行时错误,触发堆栈展开
- recover:捕获 panic,防止程序崩溃
- error:函数返回错误,不影响堆栈
go
func safeDiv(a,b int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
fmt.Println(a/b) // b=0 会 panic
}
4. 方法与函数区别
- 方法绑定在 receiver 上
- 值接收者 vs 指针接收者影响是否能修改对象状态
go
type Point struct { x, y int }
func (p Point) Move(dx, dy int) { p.x += dx; p.y += dy } // 值接收
func (p *Point) MovePtr(dx, dy int) { p.x += dx; p.y += dy } // 指针接收
三、并发与调度
1. Goroutine
- 轻量级线程,使用
go func() {}
创建
2. runtime.Gosched()
- 让出当前 goroutine 的 CPU 时间片
- 不保证其他 goroutine 立即执行
- 不保证严格交替输出
go
go func() { for i:=0;i<10;i+=2{ fmt.Println(i); runtime.Gosched() } }()
go func() { for i:=1;i<10;i+=2{ fmt.Println(i); runtime.Gosched() } }()
3. Channel
- 无缓冲 channel(同步)
- 有缓冲 channel(异步,满时阻塞)
- select 多路复用
go
ch := make(chan int, 2)
ch <- 1
ch <- 2
select {
case v := <- ch:
fmt.Println(v)
default:
fmt.Println("no value")
}
4. sync.Mutex 与 sync.RWMutex
- Mutex:互斥锁
- RWMutex:读写锁,多个读不阻塞,写独占
5. WaitGroup
go
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// do something
}()
wg.Wait()
6. Goroutine 泄漏排查
- channel 阻塞未关闭
- 无限循环或 select 默认分支未退出
- 使用 pprof/goroutine dump 检查堆栈
四、内存管理
1. 栈 vs 堆
- 栈:局部变量,函数返回自动释放
- 堆:逃逸分析判断需跨函数使用的变量分配到堆,GC 回收
2. slice 扩容原理
- slice 底层结构:ptr, len, cap
- append 超出 cap,会分配新数组并复制原数据
go
s := make([]int,0,2)
s = append(s,1,2,3) // cap 扩容
3. map 底层实现
- 哈希表 + 链表/红黑树冲突处理
- 随机 hash 减少碰撞
4. 指针逃逸分析
- 如果变量被返回或闭包引用,会分配到堆
go build -gcflags '-m'
可查看逃逸信息
五、接口与面向对象
1. 接口
- 定义行为,不定义实现
- 空接口
interface{}
可接收任意类型 - 类型断言/类型开关提取具体类型
go
var i interface{} = "hello"
s := i.(string)
switch v := i.(type) {
case string:
fmt.Println("string:", v)
}
2. 多态实现
- 通过接口实现
go
type Shape interface { Area() float64 }
type Circle struct { r float64 }
func (c Circle) Area() float64 { return 3.14*c.r*c.r }
func PrintArea(s Shape) { fmt.Println(s.Area()) }
六、标准库与工具
1. Go module 与 GOPATH
- Module 是推荐方式,支持版本管理
- GOPATH 已逐渐弱化
2. context.Context
- 用于控制 goroutine 生命周期、超时、取消和传递请求范围值
3. net/http 与 fasthttp
- fasthttp 性能更高,但 API 不兼容 http
4. reflect 包
- 用于运行时类型信息
- 使用过度可能影响性能
5. log 包
- 简单日志打印
- 支持自定义输出、前缀和标志位
七、性能与优化
1. 性能分析工具
- pprof CPU / memory / goroutine profiling
- trace 分析并发阻塞、调度
2. 常见优化点
- 减少对象频繁分配
- 减少锁竞争
- 使用池(sync.Pool)复用对象
- 控制 goroutine 数量,避免泄漏
八、实战类场景题
1. 限流器(Token Bucket)
go
type TokenBucket struct {
tokens int
capacity int
mu sync.Mutex
}
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
if tb.tokens > 0 {
tb.tokens--
return true
}
return false
}
2. 生产者-消费者
go
ch := make(chan int, 10)
go func() {
for i:=0;i<10;i++ { ch <- i }
close(ch)
}()
go func() {
for v := range ch { fmt.Println(v) }
}()
3. 协程池示例
go
tasks := make(chan func(), 100)
for i:=0;i<5;i++ {
go func() {
for task := range tasks { task() }
}()
}
tasks <- func() { fmt.Println("task") }
4. 奇偶交替输出
go
odd := make(chan struct{})
even := make(chan struct{})
go func() {
for i:=1;i<=10;i+=2 {
<- odd
fmt.Println(i)
even <- struct{}{}
}
}()
go func() {
for i:=2;i<=10;i+=2 {
<- even
fmt.Println(i)
odd <- struct{}{}
}
}()
odd <- struct{}{} // 启动奇数