Files
interview/questions/13-Golang语言/go-basic-syntax.md
yasinshaw ab3a99f131 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>
2026-03-01 00:16:36 +08:00

454 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 的扩容(预分配容量)
- 理解如何减少锁竞争(分片锁、无锁编程)