指针vs引用


🧩 总体对比表:Go vs Java(指针 vs 引用)

场景编号 场景描述 Go(是否用指针) Java(是否需要显式指针)
1 修改结构体字段 ✅ 是,结构体传 *Struct ✅ 是,所有对象都是引用
2 缓存中存对象引用 ✅ 是,map[string]*Obj ✅ 是,Map 存对象引用
3 可选字段(判断是否有传参) ✅ 是,字段类型设为指针 ✅ 是,用 boxed 类型判断 null
4 大结构体传参/返回值 ✅ 是,传 *Config 节省复制 ✅ 是,对象就是引用
5 链表/树结构 ✅ 是,结构体内嵌指针字段 ✅ 是,类字段是引用类型
6 组合结构体 ✅ 是,字段类型为指针 ✅ 是,成员变量就是引用

🔍 分场景详解对比


✅ 场景 1:修改结构体字段

Go:

1
2
3
4
5
6
type User struct {
Name string
}
func updateName(u *User) {
u.Name = "Alice"
}

Java:

1
2
3
4
5
6
class User {
String name;
}
void updateName(User u) {
u.name = "Alice";
}

📌 区别:

  • Go 需要用 *User 明确是指针,否则值传递会拷贝。
  • Java 所有对象传参天然是引用,直接修改。

✅ 场景 2:缓存中引用对象

Go:

1
cache := map[string]*Product{}

Java:

1
Map<String, Product> cache = new HashMap<>();

📌 区别:

  • Go 明确用 *Product,节省内存。
  • Java 中 Product 是引用,不需要额外操作。

✅ 场景 3:可选字段(如 API 请求中可能未传)

Go:

1
2
3
type UpdateUserRequest struct {
Name *string // 可判断是否传
}

Java:

1
2
3
class UpdateUserRequest {
String name; // 默认 null 就是“没传”
}

📌 区别:

  • Go 需要用指针来区分“没传” vs “传了默认值”。
  • Java 所有对象默认可能是 null,可以直接用 if (req.name != null) 判断。

✅ 场景 4:返回或传入大型配置对象

Go:

1
2
3
func LoadConfig() *Config {
return &Config{}
}

Java:

1
2
3
Config loadConfig() {
return new Config();
}

📌 区别:

  • Go:返回指针防止复制整个结构体。
  • Java:默认就是返回引用,天然高效。

✅ 场景 5:链表、树等结构体

Go:

1
2
3
4
type Node struct {
Val int
Next *Node
}

Java:

1
2
3
4
class Node {
int val;
Node next;
}

📌 区别:

  • Go 要用 *Node 明确引用关系。
  • Java 中 Node 字段天然就是引用类型。

✅ 场景 6:组合结构体

Go:

1
2
3
type Order struct {
User *User
}

Java:

1
2
3
class Order {
User user;
}

📌 区别:

  • Go 中组合用指针字段可避免复制。
  • Java 所有类字段本身就是引用,默认行为就是组合。

🧠 总结:Go vs Java 指针/引用哲学

方面 Go Java
值 vs 引用 区分明确(值类型 vs 指针) 所有对象默认是引用
是否需要手动加 * ✅ 是,需要指明引用 ❌ 不需要
内存管理 自动垃圾回收(有指针但无手动 free) 自动垃圾回收
安全性 不支持指针运算,较安全 更安全,无裸指针
可选字段建模 通过 *T 判断是否设置 判断 null
性能优化(避免复制) 手动使用指针传参 默认就是引用传递

📌 总结一句话:

Java 和 Go 在实际开发中都大量使用“引用”,区别在于:Go 需要你手动声明使用指针,而 Java 默认一切对象就是引用。