今日面试遇到golang相关的问题,所以让ai总结整理了 Go 语言常见面试题,涵盖基础、并发、内存管理、标准库等核心知识点。
一、基础语法与特性
1. Go 与 Python/Java 的核心区别
| 特性 | Go | Python | Java |
|---|
| 类型系统 | 静态类型,编译型 | 动态类型,解释型 | 静态类型,编译型 |
| 并发模型 | Goroutine + Channel | 线程/协程(asyncio) | 线程/线程池 |
| 内存管理 | GC,但可手动优化 | GC | GC |
| 编译速度 | 极快 | 无需编译 | 较慢 |
| 部署 | 单二进制文件 | 依赖解释器 | JRE + jar |
| 错误处理 | 显式 error 返回值 | 异常 try/except | 异常 try/catch |
2. 值传递 vs 引用传递
func modifyValue(x int) {
x = 100
}
func modifyPointer(x *int) {
*x = 100
}
func modifySlice(s []int) {
s[0] = 100
s = append(s, 200)
}
func main() {
s := []int{1, 2, 3}
modifySlice(s)
fmt.Println(s)
}
3. defer 的执行顺序和陷阱
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}
func trap() {
i := 0
defer fmt.Println(i)
i = 3
}
func deferAndReturn() (result int) {
defer func() {
result++
}()
return 0
}
func bad() {
for i := 0; i < 10000; i++ {
defer fmt.Println(i)
}
}
二、并发编程(重点)
4. Goroutine 和 Channel
func basicChannel() {
ch := make(chan int, 3)
go func() {
ch <- 1
ch <- 2
ch <- 3
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
}
func bufferedVsUnbuffered() {
unbuf := make(chan int)
go func() { unbuf <- 1 }()
<-unbuf
buf := make(chan int, 1)
buf <- 1
buf <- 2
}
5. select 多路复用
func selectDemo() {
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- 1
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "hello"
}()
select {
case v1 := <-ch1:
fmt.Println("ch1:", v1)
case v2 := <-ch2:
fmt.Println("ch2:", v2)
case <-time.After(3 * time.Second):
fmt.Println("timeout")
default:
fmt.Println("no data")
}
}
6. 并发同步原语
import "sync"
func waitGroupDemo() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait()
}
func mutexDemo() {
var mu sync.Mutex
var count int
for i := 0; i < 1000; i++ {
go func() {
mu.Lock()
count++
mu.Unlock()
}()
}
}
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data[key]
}
func (c *Cache) Set(key, value string) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
var bytePool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func usePool() {
buf := bytePool.Get().([]byte)
bytePool.Put(buf)
}
7. Context 上下文控制
func contextDemo() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
select {
case <-time.After(5 * time.Second):
fmt.Println("work done")
case <-ctx.Done():
fmt.Println("timeout:", ctx.Err())
}
ctx2, cancel2 := context.WithCancel(context.Background())
go func() {
time.Sleep(1 * time.Second)
cancel2()
}()
ctx3 := context.WithValue(context.Background(), "userID", "123")
userID := ctx3.Value("userID").(string)
}
func handleRequest(ctx context.Context, req *Request) {
go queryDatabase(ctx, req.Query)
go callExternalAPI(ctx, req.API)
}
8. 常见并发陷阱
func closureTrap() {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
for i := 0; i < 10; i++ {
go func(id int) {
fmt.Println(id)
}(i)
}
}
func closedChannelTrap() {
ch := make(chan int)
close(ch)
ch <- 1
}
func leakTrap() {
ch := make(chan int)
go func() {
ch <- 1
}()
}
func deadlockTrap() {
ch := make(chan int)
ch <- 1
<-ch
}
三、内存管理与 GC
9. 逃逸分析
func stackAlloc() int {
x := 10
return x
}
func heapAlloc() *int {
x := 10
return &x
}
10. GC 机制
func reduceGCPressure() {
data := make([]int, 0, 10000)
type BigStruct struct {
data [1024 * 1024]int
}
func process(ptr *BigStruct) {
}
}
四、接口与反射
11. 接口实现机制
type Reader interface {
Read(p []byte) (n int, err error)
}
type MyReader struct{}
func (m MyReader) Read(p []byte) (n int, err error) {
return 0, nil
}
func emptyInterface() {
var i interface{} = "hello"
s := i.(string)
s, ok := i.(string)
switch v := i.(type) {
case string:
fmt.Println("string:", v)
case int:
fmt.Println("int:", v)
default:
fmt.Println("unknown:", v)
}
}
type MyError struct{}
func (e *MyError) Error() string { return "error" }
func returnNilError() error {
var e *MyError = nil
return e
}
func main() {
err := returnNilError()
fmt.Println(err == nil)
}
12. 反射
import "reflect"
func reflectDemo() {
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
u := User{Name: "Tom", Age: 20}
t := reflect.TypeOf(u)
v := reflect.ValueOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v (tag: %s)\n", field.Name, value, field.Tag.Get("json"))
}
v2 := reflect.ValueOf(&u).Elem()
v2.FieldByName("Age").SetInt(30)
}
五、标准库与工程实践
13. HTTP 服务
package main
import (
"net/http"
"time"
)
func basicHTTPServer() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func productionHTTPServer() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()
}
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL, time.Since(start))
})
}
14. 测试
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d, want 5", result)
}
}
func TestAddTable(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 5},
{-1, 1, 0},
{0, 0, 0},
}
for _, tt := range tests {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
}
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
type Database interface {
Get(id string) (User, error)
}
type MockDB struct {
data map[string]User
}
func (m *MockDB) Get(id string) (User, error) {
return m.data[id], nil
}
六、高频面试题速查
简答题
| 问题 | 核心要点 |
|---|
| Goroutine 为什么轻量? | 用户态调度,~2KB栈,动态增长,非OS线程 |
| Channel 底层实现? | 环形队列 + 锁 + 发送/接收队列 |
| Go 的 map 是线程安全的吗? | 不是!需用 sync.RWMutex 或 sync.Map |
| 什么情况下会发生内存泄漏? | Goroutine 泄露、未关闭的 Channel、全局变量累积 |
| defer 的用途和陷阱? | 资源清理、参数求值时机、循环内使用 |
| 如何实现单例模式? | sync.Once、init、原子操作 |
| 两个 Goroutine 如何交替打印? | 两个无缓冲 Channel 互相通知 |
代码题
func alternatePrint() {
numCh := make(chan bool)
charCh := make(chan bool)
done := make(chan bool)
go func() {
for i := 1; i <= 4; i++ {
<-numCh
fmt.Print(i)
charCh <- true
}
}()
go func() {
for c := 'A'; c <= 'D'; c++ {
<-charCh
fmt.Printf("%c", c)
numCh <- true
}
done <- true
}()
numCh <- true
<-done
}
type SafeMap struct {
mu sync.RWMutex
data map[string]interface{}
}
func (s *SafeMap) Get(key string) (interface{}, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
val, ok := s.data[key]
return val, ok
}
func (s *SafeMap) Set(key string, val interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
s.data[key] = val
}
func limitedConcurrency(urls []string, maxConcurrent int) {
sem := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
sem <- struct{}{}
go func(u string) {
defer wg.Done()
defer func() { <-sem }()
fetch(u)
}(url)
}
wg.Wait()
}
学习资源推荐
| 资源 | 说明 |
|---|
| 《Go 程序设计语言》 | 经典入门,Kernighan 著 |
| 《Go 语言高级编程》 | 深入 runtime、CGO |
| Go 官方博客 | golang.org/blog,GC、调度器详解 |
| Go 101 | go101.org,深入语言细节 |
| Uber Go Style Guide | 工程实践规范 |