java中的值传递和引用传递
方法参数的值传递和引用传递
准备工作
-
关于java中是值传递还是引用传递,网上各种说法层出不穷,正好最近在研究java内存模型,打算从内存模型角度上来分析一下java中的参数传递问题。
先上代码:
public class TransTest { public static void main(String[] args) { //定义一个基本数据类型的数据 int num = 100; //定义一个引用类型为String类的数据 String str = "mainargs str"; //定义一个引用类型为StringBuilder类的数据 StringBuilder stringBuilder = new StringBuilder("mainargs strBuilder"); //定义一个引用类型为普通对象的数据 Student student = new Student(); student.setName("mainargs-zhangsan"); student.setSex("男"); //transOne(num, str, stringBuilder, student); transTwo(num, str, stringBuilder, student); System.out.println(); System.out.println("main-args方法中的数据为:num = " + num + "; str = " + str + "; stringBuilder = " + stringBuilder + "; student = " + student.toString()); } //第一种情况: 完全支持值传递的例子 //解释:其实引用类型传递过来的是相关对象的引用地址,只不过下面的程序将该地址重新赋值了。但是main方法中的引用地址指向的内容并没有变。 public static void transOne(int num, String str, StringBuilder stringBuilder, Student student) { num = 111; //在常量池中新创建"transOne str"对象,并且将该对象的引用赋值给str,并不影响main方法中的str的引用地址 str = "transOne str"; //新创建StringBuilder对象,并将该对象引用地址赋值给stringBuilder stringBuilder = new StringBuilder("transOne strBuilder"); //新创建Student对象,并将该对象引用地址赋值给student student = new Student(); student.setName("transOne-zjangsan"); student.setSex("女"); System.out.println("trans-One方法中的数据为:num = " + num + "; str = " + str + "; stringBuilder = " + stringBuilder + "; student = " + student.toString()); } //第二种情况 支持普通对象为引用传递的例子 但是String类除外 //解释:引用地址没变,但是下面的程序将该引用地址对应的内容变化了,所以main方法中的引用指向的内容也变化了。 public static void transTwo(int num, String str, StringBuilder stringBuilder, Student student) { num = 122; //在常量池中新创建"transTwo str"对象,并且将该对象的引用赋值给str,并不影响main方法中的str的引用地址 str = "transTwo str"; //相同的引用地址,改变该地址指向的内容,即在原来的基础上添加" append" stringBuilder = stringBuilder.append(" append"); //相同的引用地址,改变该地址指向的内容 student.setName("transTwo-zjangsan"); student.setSex("女"); System.out.println("trans-Two方法中的数据为:num = " + num + "; str = " + str + "; stringBuilder = " + stringBuilder + "; student = " + student.toString()); } }
-
调用第一种情况打印出来的结果:从结果上面看,main方法中定义的几个变量在调用transOne()方法后,值并没有发生变化,于是便有人得出java中是值传递;
-
调用第二种情况打印出来的结果:从结果上面看,main方法中定义的几个变量在调用transTwo()方法后,引用类型中的StringBuilder和Student的值发生了变化,但String类型的值却没有发生变化,于是便有人得出java中基本数据类型是值传递、引用类型是引用传递但是String类型例外的结论。
-
图解分析
-
我们从内存角度详细分析一下这两个例子,如下图:
-
第一种情况如图所示:
- 调用transOne()方法时,基本数据类型传递的是值的拷贝,引用数据类型传递的是引用的地址,执行 num = 111后,num变为111,其他引用类型的地址对应的相关内容目前没有变化。
-
接下来是重点:transOne()方法中接下来的操作都是重新创建了新的对象,并将新的对象的地址赋值给相关的形参,并没有改变main方法中的引用地址对应的内容,所以main方法中的几个值未发生变化。
//新创建StringBuilder对象,并将该对象引用地址赋值给stringBuilder stringBuilder = new StringBuilder("transOne strBuilder"); //新创建Student对象,并将该对象引用地址赋值给student student = new Student();
-
为什么执行transTwo()方法时,main方法中的StringBuilder和Student的值发生了变化而String并没有发生变化呢?
首先String类型的变量是新创建了对象,并将新创建的对象的地址赋值给了变量。
//在常量池中新创建"transTwo str"对象,并且将该对象的引用赋值给str,并不影响main方法中的str的引用地址 str = "transTwo str";
但是StringBuilder和Student并没有新创建对象,而是在改变了原来的引用地址对应的内容,main方法的引用地址还是之前的地址,但是引用地址对应的内容发生了变化,所以main方法中的变量也发生了变化,但这不能得出java是引用传递的结论,传递的是引用地址的拷贝。
//相同的引用地址,改变该地址指向的内容,即在原来的基础上添加" append" stringBuilder = stringBuilder.append(" append"); //相同的引用地址,改变该地址指向的内容 student.setName("transTwo-zjangsan"); student.setSex("女");
结论
-
在写程序验证时,引用数据类型的验证,不应该关注该引用地址所对应内容是否发生了变化而应该关注引用地址在传递过程中,有没有被重新赋值:
- 新赋值的地址对对象作出的任何改变都不会影响main方法中的该对象的值;
- 未重新赋值地址;改变了该地址所对应的对象的话,main方法中的该地址的变量自然会跟着一起变化。
-
java中方法参数传递方式是按值传递。基本数据类型传递的是字面量值的拷贝,引用类型,传递的是该变量的引用对象在堆中地址值的拷贝。
欢迎关注
公众号三筒记简介:分享各种编程知识、excel相关技巧、读书笔记