package num;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ArrayTest.java
* @Description 数组测试
* @createTime 2022年03月21日 13:21:00
*/
public class ArrayTest {
public static void main(String[] args) {
String str = "good";
char[] chars = {'a','b','c'};
ArrayTest arrayTest = new ArrayTest();
arrayTest.exchange(str,chars);
System.out.println(str);
System.out.println(chars);
}
void exchange(String str,char[] chars){
str ="ok";
chars[0]='g';
}
}
前几天看到一个有意思的问题,关于上面代码的执行的结果应该是什么呢?
答案是:
我们都知道,String的底层是一个char array,但是为什么array的值改变了,而String没有改变?我们更细致的分析一下:
1.Char[ ] chars为什么变了:
java的方法参数传递方式有两种,按值传递和引用传递
- 按值传递: 参数类型是int,long等基本数据类型(八大基本数据类型),参数传递的过程采用值拷贝的方式
- 引用传递: 参数类型为引用类型,参数传递的过程采用拷贝引用的方式
结论:按值传递,不会改变原来的值,引用传递,会改变引用对象的值
对于例子来说Char[ ] chars是一个数组,很明显是一个引用传递:
我们在生成chars的时候,是在堆内存中开辟一块连续空间生成数组:
当我们调用exchange方法的时候,传入的是chars对应的数组的地址,也就是在exchange中创建一个数组,并且指向了堆内存中的“a,b,c”。
那么也就是说,我对exchange方法中的chars进行操作,也就是间接的改变了堆内存中数组的值。
也就是说main方法中指向的数组的值被改变了。
2.String str 为什么没变?
按照上面所说的,String一样是引用数据类型,那么引用传递会导致值的变化,有为什么没有变呢?
首先我们注意String的底层代码:
String的本质是一个被 final 修饰的CharArray,所以我们在创建的时候编译器会使用该值创建一个 String 对象。
对于String来说,String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上。所以例子中的str就是创建了一个Str,然后指向了公共池中的“good”。
那么,在我们调用exchange方法的时候,方法传参,传入的是公共池中good的地址。也就是相当于重新new 了一个str对象,并且指向了公共池中good的地址:
那么当exchange方法中的str发生变化的时候,因为String被final修饰不能改变,所以String的机制是在池中创建一个新的ok对象,并且让exchange方法中的str指向这个新创建的“OK”。
可以看到,方法中str的变化,并不会影响到main函数中str的指向,所以方法执行完成之后,main中的str不会有任何的变化,依旧是指向了good 。
也就是当底层方法没有改变原对象的值,只是将其引用指向了另一个对象的情况下(Integer,String等),直接改变了栈帧中的地址,指向另一个对象,所以并没有改变原来的值。