浅析 Java 传值(值传递、引用传递) clone final 关键字

为了便于理解,从三个常用的demo code来解释,java中各种传值方式。

一、map中存储

       StringBuffer sb = new StringBuffer();
       Map map = 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.

你可能感兴趣的:(技术,java)