为了便于理解,从三个常用的demo code来解释,java中各种传值方式。
一、map中存储
StringBuffer sb = new StringBuffer(); Mapmap = new HashMap<>(); sb.append("1"); map.put("a", sb); sb.append("2"); map.put("b", sb); sb.append("3"); map.put("c", sb.substring(0,2)); sb.append("xyz"); map.put("d", sb); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey()+" = "+entry.getValue()); }
二、传递对象参数
public class Example{ private StringBuffer context; private Calendar calendar; private DateTime dateTime; public String toString(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(this.calendar.getTime())+" \t+this.context.toString()+"\t"+this.dateTime.toString("yyyy-MM-dd HH:mm:ss")} } public class Demo{ public Example test(Example ex){ Calendar c = example.getCalendar(); c.add(Calendar.DAY_OF_MONTH, 2); DateTime dt = example.getDateTime(); dt.plusDays(3); StringBuffer sb = example.getContext(); sb.append("test"); } public static void main(String[] args) { Example ex = new Example(); ex.setCalendar(Calendar.getInstance()); ex.setContext(new StringBuffer("abc")); ex.setDateTime(new DateTime()); Demo demo = new Demo(); System.out.println(ex.toString()); demo.test(ex); System.out.println(ex.toString()); } }
三、传final参数
public Example test(final Example ex){ Calendar c = example.getCalendar(); c.add(Calendar.DAY_OF_MONTH, 2); DateTime dt = example.getDateTime(); dt.plusDays(3); StringBuffer sb = example.getContext(); sb.append("test"); }
以下是三段代码打印信息
一、map存储 d = 123xyz b = 123xyz c = 12 a = 123xyz 二、传递对象 2013-04-28 00:17:24 abc 2013-04-28 00:17:24 2013-04-30 00:17:24 abctest 2013-04-28 00:17:24 三、传final参数 2013-04-28 00:17:24 abc 2013-04-28 00:17:24 2013-04-30 00:17:24 abctest 2013-04-28 00:17:24
打印出来的消息与直观看上去有点出入,为什么?首先要从java语言最基本的概念说起:对象、引用。
java中一切皆对象,但操纵的标识符实际上是对象的一个引用。
引用存储在堆栈中(堆栈能快速有效的分配存储,堆栈指针下移分配新的内存,上移释放内存),对象存储在堆中(堆分配存储有很大的灵活性,若需要新对象,执行new ,就会自动在堆里进行存储分配)。引用是指向对象存储的地址。
代码String s;只是创建s的引用,并不是对象,若执行s.length()编译器提示The local variable s may not have been initialized。因此,一种安全做法是创建引用的当时初始化。
当new创建一个对象--特别是小的、简单的变量,往往不是很有效。因此,java中有一些特殊处理的类型--基本类型(boolean,char,int,long...)。基本类型不用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更高效。
所以当把Java的基本数据类型作为入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对这个拷贝的操作,函数执行结束后,这个局部变量也就完成了它的使命,它影响不到作为输入参数的变量。这种方式的参数传递被称为"值传递"。 而在Java中用对象的作为入口参数的传递则缺省为"引用传递",也就是说仅仅传递了对象的一个"引用"。当函数体内部对输入变量改变时,实质上就是在对这个对象的直接操作。除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"。
第一种:为什么打印出来a = 123xyz? 因为put是个StringBuffer对象的引用而不是重新new StringBuffer对象,StringBuffer是易变的(String是不可变的),所以a,b,d都共用sb的值。c与其它值不一样,是因为StringBuffer substring()方法返回是个new String()。
第二种:传参(Example ex)Calendar c与ex属性共同指向同一个地址值,然Calendar是易变性,当操作c时与会相应修改ex属性值。DateTime是不可变性,因此它此刻作为局部变量来操作。Calendar遇到的情况,Map,Collection,JSONObject也同样会出现。使用java Object定义clone():生成并返回一个对象的拷贝,大部分类都重写过此方法。
第三种:final表示无法被改变。使用的地方在属性、方法和类。think in java:"对于基本类型,final使数值恒定不变,而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径。"。test(final Example ex)当对ex赋值时,编译器提示:cannot be assigned.