Z
ZHANK
🐹

Go 面试题

7 道题 · 2 个模块

简单 3中等 3困难 1

基础语法

4
Q1.Go 中 defer 的执行顺序和用途?
简单
参考答案
`defer` 延迟执行——在函数 return 前按**后进先出(LIFO)**顺序执行。

go
func main() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    // 输出: 3, 2, 1
}

用途:关闭文件/连接、释放锁、recover panic。
Q2.Go 中数组和切片(slice)的区别?
简单
参考答案
**数组**:长度固定,是值类型——赋值会复制整个数组。`[3]int{1,2,3}`

**切片**:动态长度,是引用类型——指向底层数组的视图。`[]int{1,2,3}`

go
arr := [3]int{1, 2, 3}     // 数组
s := []int{1, 2, 3}        // 切片
s = append(s, 4)           // 切片可追加

// len=长度, cap=容量
s2 := make([]int, 0, 10)   // len=0, cap=10

日常开发中几乎只用切片。
Q3.Go 中的指针和取地址?有指针运算吗?
简单
参考答案
Go 有指针但没有指针运算(更安全)。`*T` 是指针类型,`&` 取地址,`*` 解引用。

go
x := 42
p := &x        // *int
fmt.Println(*p) // 42(解引用)
*p = 100       // 修改 x 的值

// ❌ 没有指针运算
// p++  // 编译错误

Go 的指针比 C 安全——不能做算术运算,有自动逃逸分析。
Q4.Go 的接口(interface)是如何实现的?空接口有什么用?
中等
参考答案
Go 接口是**隐式实现**——不需要显式声明 `implements`。接口底层是 `(type, value)` 对。

go
type Writer interface {
    Write([]byte) (int, error)
}
// 任何有 Write 方法的类型自动实现 Writer

// 空接口 interface{} = any(Go 1.18+)
var x any = "hello"
x = 42
x = []int{1,2,3}

空接口可以接收任意类型——类似 Java Object,但需要类型断言才能使用。

并发编程

3
Q1.goroutine 和线程的区别?
中等
参考答案
| | goroutine | 线程 |
|---|----------|------|
| 内存 | ~2KB 栈(可动态增长) | ~1MB 固定 |
| 调度 | Go 运行时(用户态) | OS 内核 |
| 切换成本 | 极低(~200ns) | 较高(~1-10μs) |
| 数量 | 可轻松创建数十万 | 数千 |
| 通信 | channel 优先 | 共享内存+锁 |

goroutine 是 Go 并发的核心——轻量、多路复用在线程上。
Q2.channel 的缓冲和非缓冲的区别?
中等
参考答案
**非缓冲** `make(chan int)`:发送方阻塞直到接收方准备好——同步通信。

**缓冲** `make(chan int, 10)`:缓冲满之前不阻塞——异步通信。

go
ch := make(chan int)       // 非缓冲
ch := make(chan int, 10)   // 缓冲 10

// select 多路复用
select {
case msg := <-ch1:
    fmt.Println(msg)
case ch2 <- val:
    fmt.Println("sent")
default:
    fmt.Println("no activity")
}
Q3.Go 中如何避免 goroutine 泄漏?
困难
参考答案
goroutine 泄漏 = goroutine 永远阻塞,无法退出。

**常见原因与解决方案**:
1. **channel 永远无人接收** → 用 `context` 或 `done` channel 通知退出
2. **无限循环无退出条件** → 总是有 `ctx.Done()` 分支

go
func worker(ctx context.Context, ch <-chan int) {
    for {
        select {
        case <-ctx.Done():
            return  // 正常退出
        case val := <-ch:
            process(val)
        }
    }
}

用 `runtime.NumGoroutine()` 监控协程数量。