怎样理解java多对多_你对 java 的理解程度有多深, 一道面试题便知!

你对 java 的理解程度有多深, 一道面试题便知!

最近看到一篇文章, 关于一道面试题, 先看一下题目, 如下:publicstaticvoidmain(String[]args){

Integera=1;

Integerb=2;

System.out.printf("a = %s, b = %s",a,b);

swap(a,b);

System.out.printf("a = %s, b = %s",a,b);

}

publicstaticvoidswap(Integera,Integerb){

// TODO 实现

}

有人可能在没经过仔细考虑的情况下, 给出以下的答案// 特别提醒, 这是错误的方式

// 特别提醒, 这是错误的方式

// 特别提醒, 这是错误的方式

publicstaticvoidswap(Integera,Integerb){

// TODO 实现

Integertemp=a;

a=b;

b=temp;

}

很遗憾, 这是错误的. 重要的事注释三遍

那么为什么错误, 原因是什么?

想要搞清楚具体的原因, 在这里你需要搞清楚以下几个概念, 如果这个概念搞清楚了, 你也不会把上面的实现方法写错

形参和实参

参数值传递

自动装箱

所以, 上面的问题先放一边, 先看一下这几个概念

形参和实参

什么是形参? 什么是实参? 概念上的东西, 参考教科书或者 google 去吧, 下面直接代码说明更加明显publicvoidtest(){

intshi_can=0;

testA(shi_can);

}

publicvoidtestA(intxing_can){}

注: 为了清楚的表达意思, 我命名的时候并没有按照 java 的驼峰规则命名, 这里只是为了演示

通过上面的代码很清楚的表达形参和实参的概念, 在调用 testA 时, 传递的就是实参, 而在 testA 方法签名中的参数为形参

从作用域上看, 形参只会在方法内部生效, 方法结束后, 形参也会被释放掉, 所以形参是不会影响方法外的

值传递和引用传递

值传递: 传递的是实际值, 像基本数据类型

引用传递: 将对象的引用作为实参进行传递

java 基本类型数据作为参数是值传递, 对象类型是引用传递

实参是可以传递给形参的, 但是形参却不能影响实参, 所以, 当进行值传递的情况下, 改变的是形参的值, 并没有改变实参, 所以无论是引用传递还是值传递, 只要更改的是形参本身, 那么都无法影响到实参的. 对于引用传递而言, 不同的引用可以指向相同的地址, 通过形参的引用地址, 找到了实际对象分配的空间, 然后进行更改就会对实参指向的对象产生影响

额, 上面表述, 可能有点绕, 看代码// 仅仅是一个 java 对象

publicclassIntType{

privateintvalue;

publicintgetValue(){

returnvalue;

}

publicvoidsetValue(intvalue){

this.value=value;

}

}

// main 方法

publicclassIntTypeSwap{

publicstaticvoidmain(String[]args){

// CODE_1

IntTypetype1=newIntType();

type1.setValue(1);

IntTypetype2=newIntType();

type2.setValue(2);

// CODE_1

swap1(type1,type2);

System.out.printf("type1.value = %s, type2.value = %s",type1.getValue(),type2.getValue());

swap2(type1,type2);

System.out.println();

System.out.printf("type1.value = %s, type2.value = %s",type1.getValue(),type2.getValue());

}

publicstaticvoidswap2(IntTypetype1,IntTypetype2){

inttemp=type1.getValue();

type1.setValue(type2.getValue());

type2.setValue(temp);

}

publicstaticvoidswap1(IntTypetype1,IntTypetype2){

IntTypetype=type1;

type1=type2;

type2=type;

}

}

在 main 方法中, CODE_1 中间的代码为声明了两个对象, 分别设置 value 为 1 和 2, 而 swap1 和 swap2 两个方法的目的是为了交互这两个对象的 value 值

先思考一下, 应该输出的结果是什么...

...

type1.value=1,type2.value=2

type1.value=2,type2.value=1

从输出结果来看 swap1 并没有达到目的, 回头看一下 swap1publicstaticvoidswap1(IntTypetype1,IntTypetype2){

IntTypetype=type1;

type1=type2;

type2=type;

}

从值传递的角度来看, 对象参数传递采用的是引用传递, 那么 type1 和 type2 传递过来的是指向对象的引用, 在方法内部, 直接操作形参, 交换了形参的内容, 这样形参改变, 都是并没有对实参产生任何影响, 也没有改变对象实际的值, 所以, 结果是无法交换

而对于 swap2, 对象引用作为形参传递过来后, 并没有对形参做任何的改变, 而是直接操作了形参所指向的对象实际地址, 那这样, 无论是实参还是其他地方, 只要是指向该对象的所有的引用地址对应的值都会改变

自动装箱

看我上面的那个例子的 swap1, 是不是顿时觉得与上面的面试题的错误做法非常相似了, 是的, 错误的原因是一模一样的, 就是稍微有一点区别, 就是 Integer 不是 new 出来的, 而是自动装箱的一个对象, 那么什么是自动装箱呢? jdk 到底做了什么事?

如果你不想知道为什么, 只想知道结果, 那么我就直说, 自动装箱就是 jdk 调用了 Integer 的 valueOf(int) 的方法, 很简单, 看源码publicstaticIntegervalueOf(inti){

if(i>=IntegerCache.low&&i<=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];

returnnewInteger(i);

}

上面那些如果不想深究可以忽略, 就看最后一句, 是不是明白了什么呢. 没错, 也是 new 出来一个对象, 如果想知道上面的代码做了什么处理, 可以参考 Long==Long 有趣的现象 这篇文章, 里面有介绍类似的

好了, 有人可能会问, 为什么会知道自动装箱调用的是 valueOf 方法, 这里其他人怎么知道的我不清楚, 我是通过查看反编译的字节码指令知道的publicstaticvoidmain(String[]args){

Integera=1;

Integerb=2;

System.out.printf("a = %s, b = %s",a,b);

swap(a,b);

System.out.printf("a = %s, b = %s",a,b);

}

publicstaticvoidswap(Integera,Integerb){

Integertemp=a;

a=b;

b=temp;

}

反编译出来的结果为publicstaticvoidswap(Integera,Integerb){

// TODO 实现

// 无解,

}privatefinalintvalue;...publicInteger(intvalue){

this.value=value;

}publicstaticvoidswap(Integera,Integerb){

inttemp=a.intValue();

try{

Fieldvalue=Integer.class.getDeclaredField("value");

value.setAccessible(true);

value.set(a,b);

value.set(b,temp);

}catch(NoSuchFieldExceptione){

e.printStackTrace();

}catch(IllegalAccessExceptione){

e.printStackTrace();

}

}a=1,b=2

a=2,b=2publicstaticvoidswap(Integera,Integerb){

inttemp=a.intValue();

try{

Fieldvalue=Integer.class.getDeclaredField("value");

value.setAccessible(true);

value.set(a,b);

value.set(b,newInteger(temp));

}catch(NoSuchFieldExceptione){

e.printStackTrace();

}catch(IllegalAccessExceptione){

e.printStackTrace();

}

}

来源: http://www.jianshu.com/p/ecad87363236

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值