参考链接(引用数据类型与基本数据类型补充): https://blog.csdn.net/qq_42499188/article/details/88407827
参考链接(基本数据类型): https://www.cnblogs.com/xiohao/p/4296059.html
参考链接: https://blog.csdn.net/weixin_44015669/article/details/89764195
在java中几乎所有的空指针异常都是由引用传递出错(对象没有实例化)而产生的。
JVM运行数据区
jvm运行数据去共有五部分:堆,方法区,栈区,程序计数器,本地方法栈
详情请见:https://blog.csdn.net/weixin_44545800/article/details/102810204
引用数据类型与基本数据类型
引用数据类型
引用数据类型对象本身是存储在堆中(通过new开辟空间),而在栈中保存的是该对象在堆内存中的地址(可以理解为指针),但是在作为类中的属性时,则变量名与变量对象都保存在堆内存中。
eg: Person per=new Person(“张三”,1)[Person类中有两个属性,为name与age,都存储为默认值]
- 在栈中开辟一块栈内存,为null。标识符为per
- 在堆内存中开辟一块空间,存储Person类与name,age属性(不存储方法),都默认设置为默认值
- 将name赋值为"",age赋值为0(执行的是对属性的初始化赋值)
- 执行构造方法,将name赋值为“张三”,age赋值为1
- 将per中存储Person对内存的地址
注:java中关于null的原理,请参考: https://blog.csdn.net/weixin_44015669/article/details/89764195
基本数据类型
被创建时,系统自动在栈区开辟空间,将数值直接存储到栈上
但是并非所有的基本数据类型的值都保存在栈空间中:
- 方法中声明的变量(即局部变量):每当程序调用该方式时,首先通过动态链接在方法区中读取读取的方法代码,执行方法。在执行方法时,系统会为该方法建立一个方法栈,在方法中声明的变量保存在该栈中,当方法结束时,系统会释放该方法栈,相应的变量也会被销毁。这也是局部变量只能在本方法中有效的原因
- 在类中的变量(即全局变量):当变量为基本数据类型时,变量名与变量值都存储在堆内存中。当变量为引用数据类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中
引用传递
eg:
class Person{
String name="";
int age=0;
public void tell() {
System.out.println("姓名"+name+"\t年龄:"+age);
}
}
public class P{
public static void main(String args[]) {
Person perA=new Person();
//首先给perA开辟一个栈内存,存储的是在堆内存开辟的一个Person类(记为person1)的内存的地址,
Person perB=new Person();
//再给perB开辟另一个一个栈内存,存储的是在堆内存开辟的另一个Person类(记为person2)的内存的地址
perA.name="张三";
perA.age=30;
//再通过存储在perA的堆内存地址,将对应的堆内存Person对象属性赋值(将name赋值为张三,age赋值为30),
perB.name="王五";
perB.age=10;
//perB同理。
perB=perA;
//再将栈perA中的堆内存地址复制到perB中,此时perB与perA指向的都为person1。
perB.name="赵六";
//perB再将对应的堆内存的name属性改为赵六
perA.tell();
}
}
输出为:赵六 30
分析:首先给perA开辟一个栈内存,存储的是在堆内存开辟的一个Person类(记为person1)的内存的地址,再给perB开辟另一个一个栈内存,存储的是在堆内存开辟的另一个Person类(记为person2)的内存的地址,再通过存储在perA的堆内存地址,将对应的堆内存Person对象属性赋值(将name赋值为张三,age赋值为30),perB同理。再将栈perA中的堆内存地址复制到perB中,此时perB与perA指向的都为person1。perB再将对应的堆内存的name属性改为赵六
内存分配图:
eg2:
package lianxi;
public class P {
public static void main(String args[]) {
String str="hello";
fun(str); // -->相当于s=str,将内存空间传递给s
System.out.println(str);
}
public static void fun(String s) {
s="word"; //方法结束后,进行释放
}
}
结果:hello
总结
1、new关键字永远将开辟一块堆内存空间,存储的是属性
2、栈内存只能保存一块堆内存地址
3、引用传递的本质为:同一块堆内存可以被不同的栈内存指向
4、在发生引用传递时,如果操作的栈内存空间有堆内存指向,那么改变堆空间就改变了内存指向
5、如果一块堆内存没有任何的栈内存指向,那么此空间将成为垃圾空间,所有的垃圾空间会被GC(垃圾收集器)回收并且释放,垃圾回收时间不确定,所以应尽量少产生垃圾空间。(详情请见jvm)
若有不足,请多多指教。