1、必知概念:
注意:值传递与引用传递的区别并不在于被传递参数的类型,也就是说,并不是传入的参数是基本数据类型就是值传递,同理,并不是传入的参数是引用类型就是引用传递。
2、Java中只有值传递的原因:
public class Test { public static void main(String[] args) { int num = 9; System.out.println("修改前的实参为:" + num); change(num); System.out.println("修改后的实参为:" + num); } private static void change(int temp) { System.out.println("传入的形参为:" + temp); temp++; System.out.println("修改后的形参为:" + temp); } }
可以看出,这个过程虽然改变了形参,但是并没有改变实参的值,所以说是一次值传递过程。接下来就按照Java内存区域进行进一步分析:
当程序启动时,首先执行main方法,此时就会在本地方法栈中创建一个关于main方法的栈帧进行压栈,栈帧中包含局部变量表(目前仅有num)及其他信息,因为在main方法中调用了change方法,所以要创建一个关于change方法的栈帧再次压栈,如下图所示,
小结:修改的值是main中传入的num副本值,所以并不会修改num本身。
public class TestUser { public static void main(String[] args) { Person person = new Person(10, "一诺"); System.out.println("修改前的实参为:" + person); change(person); System.out.println("修改后的实参为:" + person); } private static void change(Person p) { p.setAge(100); p.setName("Cat"); } }
public class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(int age, String name) { this.age = age; this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
Amazing,为什么Person的属性发生了改变,不是值传递吗?不是不改变实参值吗?那么我们就通过Java内存区域来进一步分析:
可以看出,传递的参数并不是Person对象的具体值,而是它的地址值,所以实参、形参指向的是堆内存中的同一个Person对象,这样我们对形参的修改就会体现在实参上。
这样理解是没问题,但是我又想了一下,这样通过对象的地址值改变对象属性,这不是引用传递吗?不是说Java中只有值传递吗?带着这个疑问,我又反复看了好多次值传递、引用传递的概念,值传递每次传递的都是实参的副本啊,那要是顺着这条线想下去,那么change中p的值就是person地址值(0x11)的副本值呗,这样就可以解释了上述这个过程是值传递的过程了(不知道这样理解对不对,不对的话还请大家指正~)。
public class TestString { public static void main(String[] args) { String str = new String("孙行者"); System.out.println("字符串修改前:" + str); change(str); System.out.println("字符串修改后:" + str); } private static void change(String str) { str = "行者孙"; } }
???为什么又和第二种情况不一样???String不是也是引用类型吗?怎么会出现这种情况呢?带着充满问号的脑袋,接着来按照Java内存区域进一步分析吧:
可以看出,确实是将实参的副本传入给方法内,但是在change方法中做了一个 str = "行者孙" 的操作,因为String是不可变类,它一旦初始化就不可以再更改值了,因此这个操作是重新实例化了一个String对象,str也由 0x11 变为了 0x10,而0x11所指向的值没有改变,所以自然main方法中的str值不会变啦~
参考:
总结:
站在巨人的肩膀上才能看的更远,很庆幸能在网上找到前辈们写的文章来解答我的疑惑,同时也希望自己总结的文章能够帮助到别人。而对于这个本文分享的问题,之前我也很不理解为什么Java中只有值传递,但是在了解完Java内存区域后再看这些就很好理解了,在这篇文章我也没有很详细的介绍这部分内容,所以还是推荐对此问题有疑惑的朋友先去了解一下Java内存区域。