Go、Java 的值类型和引用类型对比
- [值类型(Value Types)](#值类型(Value Types))
- [引用类型(Reference Types)](#引用类型(Reference Types))
- 如何判断一个类型是值类型还是引用类型?
值类型(Value Types)
当进行赋值或函数调用时,会复制整个数据。常见的值类型包括:
Go
基本数据类型:如 int、float64、bool、string、complex64 等。
数组(Array):如 [3]int
结构体(Struct):由多个字段组成的复合类型。
这些类型在赋值或传递时会复制整个数据,因此修改副本不会影响原始数据。
Java
基本数据类型:如 int、Integer、String、char 等
Go 和 Java 中的数组区别
Java 中的数组是引用类型
示例代码
java
public class ArrType {
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println("main:" + arr);
addOne(arr);
System.out.println("main:" + arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void addOne(int[] arr) {
System.out.println("addone:" + arr);
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] + 1;
}
}
}
运行程序的结果
bash
E:\code\go\go_java>javac ArrType.java
E:\code\go\go_java>java ArrType
main:[I@15db9742
addone:[I@15db9742
main:[I@15db9742
2
3
4
前3行中打印输出了 [I@15db9742 数组的地址都是一致,说明Java中的数组是引用类型,调用addOne 方法之后,数组的值都增加了1。
Go 中的数组是值类型
示例代码
Go
package main
import "fmt"
func addOne(arr [3]int) {
fmt.Printf("addOne:%p\n", &arr)
for index, value := range arr {
arr[index] = value + 1
}
}
func main() {
arr := [3]int{1, 2, 3}
fmt.Printf("main:%p\n", &arr)
addOne(arr)
fmt.Printf("main:%p\n", &arr)
for _, value := range arr {
println(value)
}
}
运行程序的结果
bash
E:\code\go\go_java>go run arr_type.go
main:0xc00000e120
addOne:0xc00000e150
main:0xc00000e120
1
2
3
addOne 中输出的数组地址和main 中的不一致,函数调用的时候复制了一份数组传递了进去,增加1之后,操作的是复制的数组,主函数的数组的值并没有改变
将数组改成切片就是引用类型了,示例代码
Go
package main
import "fmt"
func addOne(arr []int) {
fmt.Printf("addOne:%p\n", arr)
for index, value := range arr {
arr[index] = value + 1
}
}
func main() {
arr := []int{1, 2, 3}
fmt.Printf("main:%p\n", arr)
addOne(arr)
fmt.Printf("main:%p\n", arr)
for _, value := range arr {
println(value)
}
}
运行程序的结果
bash
E:\code\go\go_java>go run arr_type.go
main:0xc00000e120
addOne:0xc00000e120
main:0xc00000e120
2
3
4
main、addOne 函数输出的地址都是一致的0xc00000e120,调用addOne函数之后,数值增加了1
引用类型(Reference Types)
引用类型的数据存储的是指向底层数据的地址(或描述符),赋值或传递时复制的是这个地址,而不是数据本身。多个变量可以共享同一块底层数据。常见的引用类型包括:
切片(Slice):如 []int。
映射(Map):如 map[string]int。
通道(Channel):如 chan int。
接口(Interface):如 interface{}。
函数(Function):如 func()。
指针(Pointer):如 *int。
这些类型在赋值或传递时,只复制引用(地址),因此对一个变量的修改会影响其他引用该数据的变量。
如何判断一个类型是值类型还是引用类型?
- 看是否发生深拷贝:如果赋值或传递时复制了整个数据结构,它是值类型;如果只复制了引用,则是引用类型。
- 看底层数据是否共享:如果多个变量指向同一块底层数据,那么它们是引用类型。例如,切片(slice)虽然在赋值时只复制了其头部信息(包含指向底层数组的指针、长度和容量),但它仍然指向同一底层数组,因此可以认为是引用类型。