前言
经过几天的折腾,小程序总是有了第一版的测试题功能,虽然目前还不够完善,但是测试的效果已经有了,今天上线之后,到目前有二十多人做过测试题。
但是其中有一道题错误率高的让我有点意外: 题目如下:
下面代码中,输出的是
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("1");
List<String> list2= new ArrayList<>();
list2.add("2");
swap(list1,list2);
System.out.println(list1.toString());
}
private static <E> void swap(E e, E e1) {
E e2 = e;
e = e1;
e1 = e2;
}
答案有三个 ,A:[1] B:[2] C:编译报错。不知道看文章的你会选择什么?
这道题正确答案是A,但是二十多人中只有三位选了A,其他的都选了B。
选B也就是认为两个数组变量指向的堆的地址进行了交换。
首先我个人猜测了一下错误的原因:大家可能是被值传递和引用传递这个概念误导了,我们在经历过的面试的中,应该都会被问到值传递和引用传递的区别,通常我们会会值传递的是数据的拷贝,对拷贝值的操作不会影响到原值,引用传递传递的是指向引用类型的地址,对传递的引用地址进行操作会影响到原值。这句话本身应该是没有问题的,但是引用传递在传递的时候是传递地址还是变量的问题我们应该思考一下,如果这个问题想通了,那么这道题很简单。
在Java中:所有方法参数传递方式是按值传递。
也就是说,Java在 方法中将申明变量(注意是方法中申明的变量) 当成参数给传递到方法的时候传递的都是变量的拷贝,无论是基本数据类型还是引用类型。但是之所以将值传递和引用传递的概念拆分开来说,是因为基本类型(数组除外)的申明的变量名及值是放在栈中,而引用类型所声明的变量是放在方法的栈中,但是值是放在堆中的,所以我们可能会误以为,将引用类型进行传递时,传递的是堆中地址。其实不然,引用传递,我们传递的是引用类型变量的拷贝(值传递),但是拷贝变量指向堆中的地址和原址是一样的,当我们操作拷贝变量而不是拷贝变量指向的地址时,是不会影响原值的。
如果上面的描述你还是不懂,我们来具体解析一下上面的面试题:
首先我们调用 swap(list1,list2) 时候,会将list1和list2拷贝一份,然后传递到swap方法中,而swap方法中,将e和e1进行交换,实际上并不是对list1和list2的操作,自然不会影响到list1和list2的值。
Java中只存在值传递,不存在引用传递。
基本类型变量由于存储在栈中,其值也是直接保存在变量中,所以变量是无法被其他方法,甚至其他线程的同一方法所访问到的。但是引用类型不同,其申明变量虽然存储在栈中,但是其值存储在堆中,且引用类型的变量存储的是堆中地址。如果你对虚拟机有所了解,那么你一定知道堆是线程共享的,而栈是线程私有的,局部变量就是存在方法栈中,也就是说,引用类型的变量对应的堆中数据,是可以被其他线程或者同一方法访问到,当我们通过变量去修改堆中数据,那么影响是全局的,而如果我们堆某一个变量做一些不会影响到堆地址中存储的数据操作,那么其他引用改地址的变量是不会有任何变化的。
希望大家可以去微信小程序:每天学Java,刷刷在校生的选择题,这样后续值得分析的题目也就越来越多了