引用类型的内存分配
在java中值分为两类,值类型与引用类型,值类型包括int、double、byte…等8个基本类型,而引用类型则是除基本类型以外的所有类型,包括class申请的自定义类。下面我们通过一张图来大概描述各种类型在内存中的存储形式。
所有的变量在进入内存都会顺序的存入栈中
在栈中基本类型的变量与数值是存在一起的而引用类型则新在堆中开辟了一片空间栈中存放的是空间地址。
字符串有两片存储值的空间地址
那么对引用变量赋值时是传值还是传引用呢?相信这是很多像我一样初学java的人经常会遇到的问题。
由此便引发了深拷贝浅拷贝问题,我们先来看一段代码
可以看到当A赋值给B时,并没有把值传递过去,而是将A开辟的地址传到了B,这便是浅拷贝传递引用。
这时通过构造方法,把值传递到新的Student对象中,便实现了值的传递,也就是深拷贝。
了解了深浅拷贝之后,我们看下面代码,印证之前理论
public static void main(String[] args){
T1 a = new T1();
a.name = "王五";
T1 b = new T1();
b.name = "赵六";
b = a;
System.out.println(a.name);
b.name = "快乐天使";
System.out.println(a.name);
}
clsss T1{
public String name;
public int age;
}
这段代码的运行结果如下:
可以看到最后的输出是“王五”,跟之前图片描述相符。b最后与a指向的是一片内存空间,所以b更改内容后a也收到影响。
引用类型的实质
通过上面的例子不难看出,提及引用必然离不开内存与地址,引用的类型的实质便是对内存地址的引用,通过地址访问内存,前面的b=a就是将地址赋值,值类型便是真值赋值,“=”的作用本质相同。
下面我们在看一段代码进一步加深对引用内存构成的理解。
T1 a = new T1();
a.name = "王五";
T1 b = new T1();
b.name = "赵六";
tem(a,b);
System.out.println(a.name + " " + b.name);
tem1(a,b);
System.out.println(a.name + " " + b.name);
}
public static void tem(T1 a,T1 b) {
T1 temp = a;
a = b;
b = temp;
}
public static void tem1(T1 a,T1 b) {
String temp = a.name;
a.name = b.name;
b.name = temp;
}
运行结果如下图
可以看到在执行第一个函数后值没有发生改变,执行第二个函数后值发生了改变。
(知识点:形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。当实参传入时,分配新的内存给形参后复制实参的值/地址,效果类似“=”)第一个函数执行时完全是两个临时变量在换来换去,与实参没有任何关系。通过下图形象理解。
那么第二个函数为什么能互换值呢?这便涉及了引用类型内部的结构。所有引用类型都是由基本类型构成实例对象其实就是分配空间后,调用构造函数赋初始值,把地址回传给栈中对应位置。那么这个空间分配多大,一开始程序是不能预知的,所以其实引用空间内存中存的也是地址。
(PS:一开始为引用分配的内存是固定的,若地址的内存总和小于分配内存,便会为引用分配若干基本类型地址,若大于分配内存,便会开辟一块更大的空间用来储存地址)
此片介绍了引用类型的基本构成以及地址指向,若有不明之处欢迎探讨。