feat: rename Golang files to Chinese and supplement root files
Changes: - Renamed all 10 Golang files from English to Chinese names - Created 00-项目概述/项目概述.md with comprehensive project overview - Created 08-算法与数据结构/算法与数据结构学习指南.md with detailed learning guide - Created 12-面试技巧/面试准备进度.md with progress tracking - Added .obsidian configuration for better markdown editing - Updated Claude.MD with Chinese filename rule Generated with [Claude Code](https://claude.ai/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:
453
questions/13-Golang语言/Golang基础语法.md
Normal file
453
questions/13-Golang语言/Golang基础语法.md
Normal 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]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **解决方案 2:sync.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) // 13(1 + 2 + 10)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **示例 3:defer 与 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 的扩容(预分配容量)
|
||||
- 理解如何减少锁竞争(分片锁、无锁编程)
|
||||
Reference in New Issue
Block a user