通过代码说明 Java 引用传递在堆栈上的关系。
可以从JVM的内存空间存放上说明,值传递 和引用传递。
堆(线程共享):对象、对象的全局变量、数组
栈(线程私有):声明为局部变量的 基本数据类型、对象引用(是引用,不是对象本身)、方法参数
public class JavaParam { public Bean bean1 = new Bean("1"); public Bean bean2 = new Bean("1"); public String str1 = new String("str1"); public String str2 = new String("str1"); void setObjA(Bean bean) { bean = new Bean("a"); } void setObjB(Bean bean) { bean.setId("b"); } void setStrA(String str) { str = new String("strA"); } void setStrB(String str) { str.replace("str1", "strA"); // 或者 str = "strA"; // 或者 str = str.concat("strA") 原来的str都不会改变。 } public static void main(String[] args) { JavaParam javaParam = new JavaParam(); System.out.println(javaParam.bean1); // 1 System.out.println(javaParam.bean2); // 1 System.out.println(javaParam.str1); // str1 System.out.println(javaParam.str2); // str1 javaParam.setObjA(javaParam.bean1); javaParam.setObjB(javaParam.bean2); javaParam.setStrA(javaParam.str1); javaParam.setStrB(javaParam.str2); System.out.println(javaParam.bean1); // 1 System.out.println(javaParam.bean2); // b System.out.println(javaParam.str1); // str1 System.out.println(javaParam.str2); // str1 } }
针对 public Bean bean1 = new Bean("1"); 这句话会创建1个对象new Bean("1"),1个引用 bean1。
如果这句话在方法中,则 bean1在栈上,随方法结束出栈而消失。
如果这句话在方法外,则 bean1在堆上。
显然 bean1 、bean2、str1、str2 相关的对象和对象引用都在堆上。
以setObjA方法举例。进入方法后,创建栈帧时,因为是方法参数,所以会创建 bean 这个引用(在栈上)
bean = new Bean("a");这句是对栈上的bean赋值,和外面的bean1没有关系。只是在堆里建了一个new Bean("a")。
随方法结束出栈,bean的引用消失。刚建出来new Bean("a")因为失去所有的 “GC ROOT” 引用而等待垃圾回收。
以setObjB方法举例。进入方法后,创建栈帧时,因为是方法参数,所以会创建 bean 这个引用(在栈上)。(以上都和setObjA一致)
bean.setId("b");这句是对堆上的对象操作,即外面的成员变量new Bean("1");也正是因为堆是线程共享的,才引发的出了各种线程安全问题。
随方法结束出栈,bean的引用消失。new Bean("1");此刻仍然被 bean2 引用,不会被垃圾回收。
另外举例了String的操作。作为final的对象,String对象并不可变。要改String类型的全局变量,请使用this.str1 = "XXX"