【Java转Go】如何理解Go中的值类型、引用类型、nil

397 阅读3分钟

Java的赋值和参数传递都是值拷贝,Golang也是如此。

Java的值类型只有8种:long、int、short、byte、float、double、char、boolean。其他所有类型均为引用类型。

Golang的引用类型只有6种:map、pointers、slice、channel、interface、function。其他所有类型,包括struct,均为值类型。

数据类型本质是固定内存大小的别名

数据就是那些内存中代码段,不是0就是1,只不过不同的读取与解释方式让它有了不同的意义(数据类型亦或指令类型) 。这不同的读取与解释的方式,便是类型的本质。

当操作值类型时,是以某一值类型的方式去解释某段内存数据。

当读引用类型时,是以引用的方式解释某段内存空间,再以引用类型的方式解释引用指向的内存空间。

参数传递是一种赋值,将传递的变量赋值给参数变量。

在Java和Golang中赋值都是值拷贝,对拷贝的值操作不会影响原来的值。

    public static void main(String[] args) {
        int i = 1;
        int j = i;
        i = 2;
        //2
        System.out.println(i);
        //1
        System.out.println(j);
    }
func main() {
   i := 1
   j := i
   i = 2
   //2
   println(i)
   //1
   println(j)
}

当类型为引用类型时,拷贝的是引用,两个引用的值相同,指向同一块内存区域,当操作对应数据时,操作的是同一块数据。

Java不能显式的操作引用,值类型外的都是引用。

    public static void main(String[] args) {
        MyInt myIntI = new MyInt();
        myIntI.num = 1;
        MyInt myIntJ = myIntI;
        myIntI.num = 2;

        //2
        System.out.println(myIntI.num);
        //2
        System.out.println(myIntJ.num);
    }

这里特殊提一下,Java的包装类(Integer等),会自动拆装箱,意味着Integer本身虽然是引用类型,但赋值操作时会拆箱为基本的int类型,再包装成一个新的Integer传递。

    public static void main(String[] args) {
        Integer i = 1;
        Integer j = i;
        i = 2;
        //2
        System.out.println(i);
        //1
        System.out.println(j);
    }

Java的String类型则是遵循不可变原则,里面有一些魔法。当你赋值/修改字符串时可能会赋值一个现成缓存的引用,或新创建空间的引用,但永远不会在原有空间上做修改。

Golang可以通过&取到引用,通过对引用*取到所指的数据。

func main() {
   //int类型
   i := 1
   //*int类型
   j := &i
   i = 2
   //2
   println(i)
   //0xc000061f60
   println(&i)
   //2
   println(*j)
   //0xc000061f60
   println(j)
}

需要强调的是,Golang结构体拷贝,是值拷贝,其中的引用类型拷贝的是引用地址,也就是浅拷贝,这里容易踩坑。

type MyStruct struct {
   np *int
   n  int
}
func main() {
   i := 1
   myStructI := MyStruct{np: &i, n: 2}
   myStructJ := myStructI

   //0xc000061f60
   println(&myStructI)
   //0xc000061f38
   println(myStructI.np)
   //0xc000061f50
   println(&myStructJ)
   //0xc000061f38
   println(myStructJ.np)
   //0xc000061f68
   println(&myStructI.n)
   //0xc000061f58
   println(&myStructJ.n)
   *myStructJ.np = 3
   myStructJ.n = 4
   //3
   println(*myStructI.np)
   //2
   println(myStructI.n)
}

空指针,是用引用的方式解释某段值为0的空间。

只有引用类型可能出现空指针。类型声明意味着开辟一块固定大小的内存空间并写满0,任何值类型的全0都是合法默认值(如int的0,bool的false),只有引用的全0是空的意思。