java中对象做为参数传递给一个方法,究竟是值传递,仍是引用传递?java
String和int参数传递是按值传递仍是引用传递?面试
一道面试题目,String的传递:编程
public String change(String s){ s = "222"; return s; } public static void main(Stirng[] args){ String s = "111"; change(s); sout(s); }
Java 编程语言只有值传递参数。当一个对象实例做为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容能够在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。 我看到题目愣了一下,原本不假思考的结果是111,但仔细想,String是对象类型的,对象传递的是地址,那么地址传递到方法里面后,将指向修改为222,那么结果应该是222才对。实际偏偏相反。app
java传递参数都是值,若是是对象的话,就是将引用的值复制一份给方法当参数。若是是根据引用把堆里的对象修改了,那么对象真被修改了,不过不是被建立赋值给的那个引用修改的,是方法里的一个复制的引用副本给修改的。换句话说,施瓦星格的媳被施瓦星格的克隆人亲了下。jvm
用实例去理解,其实这个理解也就是根据jdk的结果告诉我本身记住规则是这样的,之后要记住。编程语言
public String change(String s, int i, StringBuffer sb, Person p){ s="123"; i=3; sb.append("woshi"); p.setAge(100); sb = new StringBuffer("sbsb"); p = new Person("bb",44); return s; } @Test public void testChange(){ StringBuffer sb = new StringBuffer("buff"); String s = "aaa"; int i = 1; Person p = new Person("aa",12); i=2; change(s,i,sb,p); // s="222"; System.out.println(s); System.out.println(i); System.out.println(sb.toString()); System.out.println(p); }
这里一共测试了String,int,一个对象StringBuffer,一个对象people。让咱们来仔细看看这些传递都发生了什么。我想有很大一部分人都猜不出打印结果。测试
|
咱们来一个个分析。
1 |
|
这里,jvm建立一个变量引用s,在堆中建立一个对象aaa,将aaa放进常量池。s指向aaa。
而后就到了change方法里。这里这样理解:将s引用的一个拷贝传给方法change。这样change有一个变量s,这个s也是指向aaa的。那么咱们来经过debug来看后来发生了什么。
1.s指向aaa的时候:
2.s运行到change方法里的时候
而后看s再次赋值的时候:
而后咱们运行结束change方法后到主方法里:
到这里s就结束了。那么若是咱们按照传递的是s这个变量的引用,即String s="aaa"中这个s自己,那么,s这个自己是个变量,s指向aaa,在方法change里s又指向了123,回到主方法后s变量的指向被改变了?错!显然s仍旧是aaa,那么只能这样理解:s传递到方法里的时候,复制了s指向的地址给change,change里的s是另外一个s,s指向aaa(@718),而后在change中s又指向了123(@731),因为String是不可变类(final and Immutable),这里只是把副本s的指向修改为731,原地址718里的对象没有发生改变由于String不可变。那么,回到主方法的时候,s变量自己没有任何改变,s仍旧指向地址718,718的内容是aaa。因此最终打印aaa。
int是基本类型,因此int只是将值复制一份给别的方法用,这个你们都知道,就不去测试了。如今看StringBuffer发生的改变。
1.初始化:
2.到change方法中:
3.发生append
4.指向新对象
这里就要说一下了,副本指向了新对象。就比如,施瓦星格的克隆人找了另外一个女的当老婆,而真正的施瓦星格老婆没有变。
5.回到主方法:
到这里,StringBuffer就结束了。咱们必须知道,虽然咱们没有去研究源码是怎样实现的,change方法获得是一个sb的副本,只不过这个副本指向708,在change里对708的对象追加,708的对象就真的改变了。而后又把sb副本指向新地址737。这只是把副本指向的地址修改了,若是你在这里打印sb.toString(),打印的就是737里的内容。当跳出change,回到主方法的时候,原sb仍旧仍是指向708的,最终就是打印708的结果。和String不一样的是,StringBuffer的结果发生了变量,由于StringBuffer是可变的,能够append。而String是不可变的,在change中s=123就是发生两个行为,一个是查找常量池中是否有123,若是没有就在堆中建立123,一个是将s指向123.也就是说这时候是建立了一个新的String对象,而不是把原来的String对象s内容修改。这样,回到主方法的时候,s仍旧是aaa。
1.初始化:
2.p传递到change里的时候
3.p副本设置age
4.p副本从新赋值
这里仍旧要说一下,p副本修改了本身指向,并不影响主方法里的p的指向。主方法里的p的指向没有发生变化,依旧应该仍是720.
5.回到主方法
经过上面对String,StringBuffer,People的研究,应该明白一个道理,重要的话说三遍,重要的规则我都演示了三遍。若是跟着步骤一步步走的,确定牢记住了:
java全部的参数传递都是传递的副本,变量所表明的值的副本!java全部的参数传递都是传递的副本,变量所表明的值的副本!java全部的参数传递都是传递的副本,变量所表明的值的副本!
这里必须记住的就是副本概念。在方法里,运行的时候到这里的线程都会把传过来的参数拷贝副本带本身的工做区中,在工做区中对这个副本的值发生一些改变。最终改变的是副本,若是经过副本的指向修改了指向中的内容,那么那个指向的地址里的内容确实改变了。若是修改了副本的指向,即给副本从新赋值,那么关原来的变量何事?元变量仍旧指向最初的地址。
那么,String传递过去的是副本,修改了副本的指向,打印元string是不会改变的,由于副本没有能力修改final的String类。