Java转Go面试速记:Go基础22问,一篇理清高频易错点一篇理清高频易错点
Java转Go最难的不是语法,而是思维切换。
很多面试题看起来简单,比如make和new、slice和数组、interface和nil,但一旦用Java思维去答,就很容易踩坑。
这篇文章整理Go基础高频22问,用一句话答案+Java对比+记忆口诀快速过一遍,适合面试前速记。
本篇基于小林coding整理,更详解跳转至小林即可。
1.与其他语言相比,使用Go有什么好处?
Go最大的优势是:简单、并发强、编译快、部署轻。
Java偏重生态和框架,Go偏重工程效率和并发服务。
记忆口诀:
Go=语法简单+goroutine并发+二进制部署。
2.什么是协程?
协程是比线程更轻量的执行单元。Go里的协程叫goroutine。
Java常用线程池,Go直接用go关键字启动goroutine。
记忆口诀:
线程归OS管,goroutine归Go运行时管。
3.协程、线程、进程的区别?
进程是资源分配单位 。
线程是CPU调度单位 。
协程是用户态轻量任务。
记忆口诀:
进程最重,线程其次,协程最轻。
4.make和new的区别?
new只分配内存,返回指针。
make只用于初始化slice、map、channel。
go
p := new(int) // *int
s := make([]int, 0) // []int
m := make(map[string]int)
ch := make(chan int)
记忆口诀:
new给指针,make给可用容器。
5.数组和切片的区别?
数组长度固定,长度是类型的一部分。
切片长度可变,本质是对底层数组的描述。
go
var a [3]int
var s []int
[3]int和[4]int是不同类型,[]int才是常用的动态结构。
记忆口诀:
数组存数据,切片看数组。
6.for range时变量地址会变化吗?
range里的v通常是元素副本,不是原元素本身。
错误写法:
go
for _, v := range nums {
ptrs = append(ptrs, &v)
}
正确写法:
go
for i := range nums {
ptrs = append(ptrs, &nums[i])
}
记忆口诀:
要元素地址,用&numsi,别用&v。
7.如何高效拼接字符串?
少量拼接用+。
循环大量拼接用strings.Builder。
Java对比:
Go的strings.Builder类似Java的StringBuilder。
记忆口诀:
少量用+,大量用Builder。
8.defer的执行顺序和作用?
defer在函数结束前执行,多个defer按后进先出执行。
go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// 输出:3 2 1
常用于关闭文件、释放锁、恢复异常。
记忆口诀:
defer像finally,执行像栈。
9.什么是rune类型?
rune本质是int32,表示一个Unicode字符。
byte看字节,rune看字符。
记忆口诀:
处理英文看byte,处理中文看rune。
10.Go语言tag有什么用?
tag是结构体字段上的元信息,常用于JSON、ORM、参数校验。
go
type User struct {
Name string `json:"name"`
}
Java对比:
Go的tag类似Java注解,但本质只是字符串,需要反射读取。
记忆口诀:
Java用注解,Go用tag。
11.%v、%+v、%#v的区别?
%v打印值。
%+v打印结构体字段名和值。
%#v打印Go语法形式。
记忆口诀:
v看值,+v看字段,#v看源码。
12.空struct{}占用空间吗?
struct{}大小为0,不存任何数据。
go
unsafe.Sizeof(struct{}{}) // 0
记忆口诀:
空结构体=零大小占位符。
13.空struct{}有什么用?
用于只关心"有没有",不关心"值是什么"的场景。
常见用途:
go
set := make(map[string]struct{})
也常用于channel通知:
go
done := make(chan struct{})
记忆口诀:
只占位,不存值,用struct{}。
14.init函数什么时候执行?
init()在main()之前自动执行。
执行顺序是:
text
导入包变量初始化 → 导入包init → 当前包变量初始化 → 当前包init → main
记忆口诀:
init先于main,自动执行一次。
15.两个interface可以比较吗?
可以,但要看里面装的值能不能比较。
如果interface里面装的是int、string这类可比较类型,可以比较。
如果装的是slice、map这类不可比较类型,会panic。
记忆口诀:
interface能不能比,看里面装的是谁。
16.两个nil可能不相等吗?
可能。
interface是否为nil,要看动态类型 和动态值是否都为空。
go
var p *User = nil
var i interface{} = p
fmt.Println(p == nil) // true
fmt.Println(i == nil) // false
因为i里面其实是:
text
(*User, nil)
它有类型,所以不是nil。
记忆口诀:
interface为nil,必须类型和值都为空。
17.Go函数传参是值传递还是引用传递?
Go只有值传递。
slice、map、channel看起来像引用传递,是因为它们的值内部保存了底层数据的引用。
Java对比:
Java也是值传递,只是传的是对象引用的副本。Go同理。
记忆口诀:
Go没有引用传递,只有值里带引用。
18.如何知道对象分配在栈上还是堆上?
看逃逸分析。
bash
go build -gcflags="-m" main.go
看到escapes to heap或moved to heap,说明分配到堆上。
记忆口诀:
堆栈别靠猜,逃逸分析看。
19.Go多返回值如何实现?
Go语言天然支持多个返回值,最常见的是:
go
value, err := doSomething()
Go通常不用异常控制业务流程,而是显式返回error。
Java对比:
Java常用异常或包装对象,Go直接返回多个值。
记忆口诀:
Go多返回,结果和错误一起回。
20.Go语言中_的作用?
_叫空白标识符,用来接收但忽略某个值。
常见用途:
go
value, _ := strconv.Atoi("123")
for _, v := range nums {}
import _ "github.com/go-sql-driver/mysql"
记忆口诀:
_表示:我知道有你,但我不用你。
21.普通指针和unsafe.Pointer的区别?
普通指针有类型,安全。
unsafe.Pointer无具体类型,可以绕过类型系统。
go
var x int
p := &x // *int
up := unsafe.Pointer(p) // unsafe.Pointer
记忆口诀:
普通指针安全,unsafe指针危险但灵活。
22.unsafe.Pointer和uintptr的区别?
unsafe.Pointer是指针,GC能识别。
uintptr是整数,GC不会把它当指针。
最关键区别:
text
unsafe.Pointer:地址,但还是指针
uintptr:地址数字,本质是整数
记忆口诀:
Pointer给GC看,uintptr只是数字。
最后只背这8句就够了
1.Go的优势
简单、并发强、编译快、部署轻。
2.goroutine
goroutine不是线程,是Go运行时调度的轻量任务。
3.make和new
new返回指针,make初始化slice、map、channel。
4.数组和切片
数组长度固定,切片是底层数组的视图。
5.range
range里的v是副本,要元素地址用&numsi。
6.interface和nil
interface为nil,必须动态类型和动态值都为空。
7.传参
Go只有值传递,没有引用传递。
8.unsafe
unsafe.Pointer是指针,uintptr只是整数。