Java 只有引用,Go 的指针 * 到底是什么?内存模型一次讲透

📅 2026/7/19 4:08:44 👁️ 阅读次数 📝 编程学习
Java 只有引用,Go 的指针 * 到底是什么?内存模型一次讲透

本文为《Java工程师转Go实战》连载第 4 篇 / 共 20 篇
上一篇:基础语法一天速通
下一篇:没有 class 和继承,Go 怎么写业务?


类比开场

Java 里你很少写指针,但每天都在用:对象变量本质是引用的拷贝

Usera=newUser("张三");Userb=a;b.setName("李四");// a.getName() 也变成 "李四"

Go 把这件事说得更直白:你可以选择传值还是传指针,而且语法里直接有*&


一、三个符号搞懂 80%

符号读法含义
&x取地址得到 x 的指针
*p解引用通过指针访问指向的值
*T类型「指向 T 的指针」类型
x:=42p:=&x// p 的类型是 *intfmt.Println(*p)// 42*p=100fmt.Println(x)// 100

二、Java 引用 vs Go 指针

Java 对象Go 值类型Go 指针
赋值引用拷贝整份拷贝地址拷贝
修改影响原对象
null零值nil
谁管内存GC栈或堆,编译器决定同左
typeCounterstruct{Nint}funcaddValue(c Counter){c.N++}// 不改外面funcaddPtr(c*Counter){c.N++}// 改外面funcmain(){c:=Counter{N:0}addValue(c)fmt.Println(c.N)// 0addPtr(&c)fmt.Println(c.N)// 1}

三、什么时候用指针?

场景建议
方法要修改 receiver 字段指针接收者func (u *User)
结构体很大指针避免拷贝
需要表示「不存在」*User可为 nil
小结构体、只读值接收者即可

Java 对照:指针接收者 ≈ 在方法里改this的字段;值接收者 ≈ 改的是一个副本。


四、new 和 make(Java 人必混淆)

// new:分配内存,返回指针,置零值p:=new(int)// *int,值为 0// make:只用于 slice、map、channel,返回已初始化的引用类型s:=make([]int,0,10)m:=make(map[string]int)ch:=make(chanint)

Java 里new ArrayList<>()一个概念搞定;Go 把slice/map/chan 的内部结构初始化单独拆给make


五、逃逸分析:栈还是堆?

Java 程序员关心 JVM 堆栈;Go 程序员也要关心,但多数时候编译器帮你决定

funcfoo()*int{x:=42return&x// x 逃逸到堆上——否则函数返回后地址失效}

查看逃逸:

go build-gcflags="-m"./...

面试常考:不是说有&就一定在堆上,逃逸分析才是依据。


六、nil 指针陷阱

varu*User=nil// u.Name // panic: nil pointer dereferenceifu!=nil{fmt.Println(u.Name)}

和 JavaNullPointerException一样,先判空再解引用


📦 面试追问

  1. Go 有引用类型吗?
    slice、map、channel 是引用类型(内部含指针),赋值后共享底层数据;但和 C++ 引用不同。

  2. 值传递还是引用传递?
    Go 永远是值传递。传指针时,拷贝的是指针值(地址),所以能改指向的对象。

  3. 为什么 slice 改元素会影响原 slice?
    slice 头是 {指针, len, cap},赋值拷贝的是 header,底层数组共享。


💡 一句话总结

Java 隐式给你引用,Go 显式让你选:值拷贝还是指针——搞懂&*和接收者,就搞懂了 Go 一半的「对象」行为。


建议标签Golang指针Java内存模型面试