这是一道京东的面试题哦,考察的是值传递和引用传递的知识点。
先看一个值传递、引用传递的示例:
/**
* 引用传递和值传递
*
* @author wanglingqiang
* @date 2020/7/25 下午3:15
**/
public class Test {
public static void main(String[] args) {
Test test = new Test();
int a = 1;
Obj obj = test.new Obj();
obj.i = 2;
System.out.println("main: " + a + ", " + obj.i); // main: 1, 2
test.ref(a, obj);
System.out.println("main: " + a + ", " + obj.i); //main: 1, 22
}
public void ref(int a, Obj b) {
a = 11;
b.i = 22;
System.out.println("ref: " + a + ", " + b.i); //ref: 11, 22
}
class Obj {
int i;
}
}
运行结果:
main: 1, 2
ref: 11, 22
main: 1, 22
int类型的值没被改变,对象的属性值被改变了。不理解的小伙伴可以看下图分析,理解的可以跳过看下一个String、Integer的示例。
画图分析引用传递:
栈里两个变量的引用都指向了堆里同一个的Obj对象,b.i赋值操作修改了堆的Obj属性,所以obj.i获取值时值发生了变化。
画图分析值传递:
ref()方法调用时,形参a会在栈里拷贝一份,并把局部变量a的值赋值给形参a,ref()方法内修改值时,实际修改的只是副本。
String、Integer包装类型是引用传递吗?
基本数据类型、引用数据类型,请参考:这里
Java中,String类型和八种基本数据类型的包装类型是例外,虽然它们是对象类型,但发生方法调用时其实是值传递。
/**
* String、Integer包装类型的传值方式
*
* @author wanglingqiang
* @date 2020/7/25 下午3:15
**/
public class Test {
public static void main(String[] args) {
Test test = new Test();
Integer a = 1;
String b = "2";
System.out.println("main: " + a + ", " + b); // main: 1, 2
test.ref2(a, b);
System.out.println("main: " + a + ", " + b); //main: 1, 2
}
public void ref2(Integer a, String b) {
a = 11;
b = "22";
System.out.println("ref: " + a + ", " + b); //ref: 11, 22
}
}
运行结果:
main: 1, 2
ref: 11, 22
main: 1, 2
两个Integer值交换的示例:
京东的机试题:
进入正题,京东的机试题如下图:
题目要求,不能修改main()方法,只能在swap()方法内部编码。
常规思路,加一个中间变量temp:
结果未能如愿以偿,没有一个值被修改被替换。
画图分析常规做法的代码过程:
Integer temp = i;
i = j; j = temp;
如上图,常规方式交换,只是引用关系发生了变化,而是a、b的引用依然没变。堆对象里的值也没变化。
利用反射实现值交换:
曾经这道面试题也被我遇到过,我写下了用反射来交换值的过程,然后面试官问我,用反射会遇到什么问题,我一脸懵逼。现在回想起来,这个问题还挺深的,回答反射只算答对了一半。
用反射来交换值的代码如下:
但代码运行时会报错:
java.lang.IllegalAccessException: Class com.xxx.Test can not access a
member of class java.lang.Integer with modifiers “private final” at
sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102) at
java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.set(Field.java:761) at com.crc.smartech.wisdomsite.biz.utils.Test.swap(Test.java:33) at
com.crc.smartech.wisdomsite.biz.utils.Test.main(Test.java:20)
因为value是private final修饰的。
报错的解决办法就是,授权可修改:
运行结果正常,但结果只交换了一个值。
画图分析一下原因:
反射比较强势,直接改了堆内存里对象1的位置值为2,此时temp=1,再看看field.set(j, temp)方法的执行:
它接收的是一个Object对象类型,1传给value,被包装成了Integer类型的1,再通过valueOf()找到1的位置,发现值是2,所以field.set(j, temp)还是2。
原因找到了,那这个问题怎么解决呢?
new Integer(temp)的方式,生成一个新对象,此时运行结果交换成功。大功告成!
彩蛋,另一种解题方法:
听说有人用如下方法,面试官也给通过了。
惊不惊喜意不意外!!!