feat: add 10 Golang interview questions

Added comprehensive Golang interview preparation materials:
- 基础语法(值类型、切片、map、defer、接口、struct、new/make)
- Goroutine 和并发模型(与线程对比、调度模型、内存模型)
- 错误处理和测试(error、panic/recover、单元测试、Benchmark)
- 并发编程进阶(Mutex、RWMutex、WaitGroup、atomic、数据竞争)
- HTTP 和 Web 开发(Client、Server、中间件模式)
- 内存模型和垃圾回收(内存分配、逃逸分析、GC)
- 性能优化(pprof、内存优化、CPU优化、并发优化)
- 反射和 unsafe(反射性能、unsafe 使用场景)
- 接口和类型系统(类型断言、interface{}、类型嵌入、泛型)
- 数据库操作(database/sql、GORM、事务、SQL 注入防护)
- 项目结构和工程化(标准项目结构、Go Module、CI/CD)

Each file includes:
- Detailed questions and comprehensive answers
- Code examples and best practices
- Alibaba P7 level requirements

Total: 60 interview questions (50 backend + 10 Golang)

Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
yasinshaw
2026-03-01 00:16:36 +08:00
parent 0e46a367c4
commit ab3a99f131
10 changed files with 2854 additions and 0 deletions

View File

@@ -0,0 +1,453 @@
# Golang 基础语法
## 问题
1. Go 的值类型和引用类型有哪些?
2. Go 的切片和数组有什么区别?
3. Go 的 map 是线程安全的吗?如何实现线程安全?
4. Go 的 defer 执行顺序和作用是什么?
5. Go 的接口interface是如何实现的鸭子类型是什么
6. Go 的 struct 可以比较吗?什么时候可以比较,什么时候不可以?
7. Go 的 new 和 make 有什么区别?
---
## 标准答案
### 1. 值类型 vs 引用类型
#### **值类型**
- 基本类型:`int``float``bool``string`
- 数组:`[n]int`
- 结构体:`struct`
**特点**:直接存储值,拷贝时复制整个值
#### **引用类型**
- 切片:`[]T`
- 映射:`map[K]V`
- 通道:`chan T`
- 接口:`interface{}`
- 指针:`*T`
**特点**:存储引用地址,拷贝时只复制引用
**示例**
```go
// 值类型
a := 1
b := a // 拷贝值
b = 2
fmt.Println(a) // 1不受影响
// 引用类型
s1 := []int{1, 2, 3}
s2 := s1 // 拷贝引用(指向同一个底层数组)
s2[0] = 999
fmt.Println(s1) // [999 2 3](受影响)
```
---
### 2. 切片 vs 数组
#### **数组Array**
```go
// 数组:固定长度
var arr [5]int // 长度为 5 的数组
arr := [3]int{1, 2, 3}
arr2 := [...]int{1, 2, 3} // 自动推导长度
// 数组是值类型
arr3 := arr
arr3[0] = 999
fmt.Println(arr) // [1 2 3](不受影响)
```
**特点**
- 长度固定(类型的一部分)
- 值类型(拷贝时复制整个数组)
- 很少直接使用
---
#### **切片Slice**
```go
// 切片:动态长度
s := []int{1, 2, 3}
s = append(s, 4) // 动态追加
// 切片的三部分
// 1. 指针:指向底层数组
// 2. 长度:当前元素个数
// 3. 容量:底层数组的总大小
// 切片的底层结构
type slice struct {
array unsafe.Pointer // 指向底层数组
len int // 长度
cap int // 容量
}
```
**切片扩容机制**
```go
s := make([]int, 0, 3) // len=0, cap=3
s = append(s, 1) // len=1, cap=3
s = append(s, 2) // len=2, cap=3
s = append(s, 3) // len=3, cap=3
s = append(s, 4) // len=4, cap=6扩容容量翻倍
```
**扩容策略**
- 容量 < 1024翻倍
- 容量 ≥ 1024增长 1.25 倍
---
### 3. Map 的线程安全问题
#### **Go 的 map 不是线程安全的**
```go
var m = make(map[int]int)
// ❌ 并发写入会 panic
go func() {
m[1] = 1
}()
go func() {
m[2] = 2
}()
// panic: concurrent map writes
```
---
#### **解决方案 1加锁**
```go
import "sync"
var (
m = make(map[int]int)
mu sync.Mutex
)
func write(key, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
func read(key int) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
```
---
#### **解决方案 2sync.Map**
**适用场景**
- 读多写少
- Key 的集合稳定(少量增删)
```go
var m sync.Map
// 写入
m.Store(1, "one")
m.Store(2, "two")
// 读取
if val, ok := m.Load(1); ok {
fmt.Println(val.(string))
}
// 删除
m.Delete(1)
// 遍历
m.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true // 继续遍历
})
```
**sync.Map 底层实现**
- `read`:只读 map无锁atomic
- `dirty`:读写 map加锁
- `misses`:统计 read 未命中次数,达到阈值时提升 dirty 到 read
---
### 4. defer 的执行顺序
#### **特点**
1. **defer 后进先出LIFO**
2. **在函数返回前执行**
3. **可以修改返回值**
---
#### **示例 1执行顺序**
```go
func example() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
fmt.Println("main")
}
// 输出:
// main
// defer 3
// defer 2
// defer 1
```
---
#### **示例 2修改返回值**
```go
func add(a, b int) (result int) {
defer func() {
result += 10 // 修改返回值
}()
result = a + b
return result
}
func main() {
r := add(1, 2)
fmt.Println(r) // 131 + 2 + 10
}
```
---
#### **示例 3defer 与 panic**
```go
func example() {
defer func() {
if err := recover(); err != nil {
fmt.Println("recover:", err)
}
}()
defer fmt.Println("defer 1")
panic("error")
defer fmt.Println("defer 2") // 不会执行
}
// 输出:
// defer 1
// recover: error
```
---
### 5. 接口Interface
#### **接口的定义**
```go
type Speaker interface {
Speak() string
}
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return "汪汪"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return "喵喵"
}
```
---
#### **鸭子类型Duck Typing**
```go
// Go 不需要显式声明实现了接口
// 只要实现了接口的所有方法,就自动实现了该接口
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{Name: "旺财"}
cat := Cat{Name: "咪咪"}
MakeSound(dog) // 汪汪
MakeSound(cat) // 喵喵
}
```
---
#### **接口的零值**
```go
var s Speaker
fmt.Println(s == nil) // true
// 接口内部存储:(type, value)
// type = nil, value = nil 时,接口才等于 nil
```
---
#### **类型断言**
```go
func checkType(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
case Dog:
fmt.Println("Dog:", v.Name)
default:
fmt.Println("unknown type")
}
}
```
---
### 6. Struct 的比较
#### **可比较的 Struct**
```go
type Point struct {
X int
Y int
}
p1 := Point{X: 1, Y: 2}
p2 := Point{X: 1, Y: 2}
fmt.Println(p1 == p2) // true
// 条件:
// 1. 所有字段都是可比较的
// 2. 字段类型相同
// 3. 字段值相同
```
---
#### **不可比较的 Struct**
```go
type Foo struct {
m map[string]int // map 不可比较
}
f1 := Foo{m: make(map[string]int)}
f2 := Foo{m: make(map[string]int)}
// f1 == f2 // 编译错误map 不可比较
// 其他不可比较类型:
// - slice
// - map
// - function
// - 包含这些类型的 struct
```
---
### 7. new vs make
#### **new**
```go
// new: 分配内存,返回指针
p1 := new(int)
fmt.Println(p1) // 0xc0000140a0地址
fmt.Println(*p1) // 0零值
// 等价于
var p2 int
fmt.Println(&p2) // 0xc0000140b0
```
**适用**:所有类型(基本类型、结构体)
---
#### **make**
```go
// make: 只用于创建 slice、map、chan
// 返回初始化后的值(非指针)
// 切片
s := make([]int, 3, 5) // len=3, cap=5
// map
m := make(map[string]int)
// channel
ch := make(chan int, 10)
```
**适用**
- `slice`
- `map`
- `channel`
---
#### **对比**
| 特性 | new | make |
|------|-----|------|
| **返回类型** | 指针 | 值 |
| **适用类型** | 所有类型 | slice、map、chan |
| **初始化** | 零值 | 初始化后可用 |
---
### 8. 阿里 P7 加分项
**深度理解**
- 理解 slice 的底层实现(数组、指针、长度、容量)
- 理解 map 的扩容机制和哈希冲突解决
- 理解 interface 的动态派发和类型断言
**实战经验**
- 有处理并发 map 问题的经验
- 有 defer 导致的性能问题defer 循环中的大锁)
- 有接口设计的最佳实践
**性能优化**
- 理解如何减少内存分配对象池、sync.Pool
- 理解如何优化 slice 的扩容(预分配容量)
- 理解如何减少锁竞争(分片锁、无锁编程)