java到底是按值传递还是按引用传递?

来源:互联网 发布:淘宝自然流量包括什么 编辑:程序博客网 时间:2024/06/10 13:38
 1   数据类型  
  1.1   PrimitiveType(简单类型)  
  1.2   ReferenceType(引用类型)  
  2.   变量  
  2.1   简单类型变量  
  2.2   引用类型变量  
  3.赋值与传递  
  3.1   对象的赋值  
  3.2   传递  
  3.3   final变量能改变吗?  
  3.4   包装类的赋值与传递  
   
  1   数据类型  
  java的数据类型有两类:  
  PrimitiveType(简单类型)  
  ReferenceType(引用类型)  
   
  1.1   PrimitiveType(简单类型)  
  (参考:langspec-3.0/typesValues.html#4.2)  
   
  PrimitiveType的分类如下所示:  
   
  PrimitiveType:  
          NumericType  
          boolean  
   
  NumericType:  
          IntegralType  
          FloatingPointType  
   
  IntegralType:   one   of  
          byte   short   int   long   char  
   
  FloatingPointType:   one   of  
          float   double  
   
  PrimitiveType是java预定义的类型,并且使用保留字命名。比如int、long、float等。由此看来其包装类不算PrimitiveType。  
  1.2   ReferenceType(引用类型)  
  (参考:langspec-3.0/typesValues.html#4.3)  
  ReferenceType有三种类型:类、接口、和数组。  
   
  2.   变量  
  (参考:langspec-3.0/typesValues.html#4.12)  
  A   variable   is   a   storage   location   and   has   an   associated   type,   sometimes   called   its   compile-time   type,   that   is   either   a   primitive   type   (§4.2)   or   a   reference   type   (§4.3).  
  变量是关联于特定类型的存储单元,所关联的类型有时叫做变量的编译时类型,即,既可以是简单类型也可以是引用类型。  
  2.1   简单类型变量  
  A   variable   of   a   primitive   type   always   holds   a   value   of   that   exact   primitive   type.  
  简单类型的变量总是执持简单类型的值。  
  2.2   引用类型变量  
  A   variable   of   a   class   type   T   can   hold   a   null   reference   or   a   reference   to   an   instance   of   class   T   or   of   any   class   that   is   a   subclass   of   T.   A   variable   of   an   interface   type   can   hold   a   null   reference   or   a   reference   to   any   instance   of   any   class   that   implements   the   interface.  
   
  类型是T的类的变量可以执持null引用,或者类T及其子类的实例引用。接口类型的变量可以执持null引用,或者任何实现该接口的类的实例引用。  
   
  注:与langspec2.0不同的是,3.0引入了泛型的概念,其中有Type   Variable的概念,上面的T就是一个Type   Variable。  
  3.赋值与传递  
  如上所述,可以得出下面结论:  
  1) 对于简单类型变量的赋值是按值传递。就是说直接把数值存放到变量的存储单元里。  
  2) 对于引用类型的变量,赋值是把原对象的引用(可以理解为入口地址),存放在变量的存储单元里。  
  3.1   对象的赋值  
  简单类型的赋值很容易理解,这里仅讨论对象的赋值。所有引用类型的实例就是我们常说的对象。  
  可以这样说,除了null以外,任何变量的初始赋值都是分两步:  
  1) 创建对象实例  
  2) 把对象实例的引用赋值给变量。  
   
  比如:  
  Object   o1   =   new   Object();  
  3.2   传递  
  传递是通过变量之间的赋值实现的。在以前的回贴中我说过这样一句话,单纯从变量的角度看,变量之间的赋值是值传递。现在我解释一下我的观点。  
   
  先举一个例子:  
  //   java中所有的类的基类默认为Object,在此不赘述。  
  class   Object1   {}  
  class   Object2   {}  
   
  Object   o1,   o2;  
   
  o1   =   new   Object1();  
   
  o2   =   o1;  
  o2   =   new   Object2();  
   
  这时候,o1的类型是什么?是Object1还是Object2?正确答案是Object1。  
  再举一个例子:  
  class   Word   {  
  String   word;  
  public   Word(String   word){  
  this.word   =   word;  
  }  
  public   void   print(){  
  System.out.println(word);  
  }  
  }  
   
  Word   o1,   o2;  
   
  o1   =   new   Word("Every   Day");  
   
  o2   =   o1;  
  o2   =   new   Word("Every   Night!");  
   
  w1.print();  
   
  会出现什么结果?"Every   Day"   还是   "Every   Night!"?仍然是"Every   Day"。  
   
  这里面有一个很多人特别是初学者忽视了的观点   ――   变量可以引用对象,但变量不是对象。什么是对象?对象初始化之后,会占用一块内存空间,严格意义上讲,这段内存空间才是对象。对象创建于数据段,而变量存在于代码段;对象的入口地址是不可预知的,所以程序只能通过变量来访问对象。  
   
  回到我们的问题上来,第一句  
  o1   =   new   Word("Every   Day");  
  首先创建一个Word实例,即对象,然后把“引用”赋值给o1。  
  第二句  
  o2   =   o1;  
  o1把对象的引用赋值给o2,注意赋的值是对象的引用而不是o1自身的引用。所以,在的三句  
  o2   =   new   Word("Every   Night!");  
  就是又创建一个新对象,再把新对象的引用赋值给o2。  
   
  因为o1和   o2之间是值传,所以,对o2的改变丝毫不会影响到o1。  
   
  也有一种情况好像是影响到了o1,我们继续上面的例子,给Word增加一个方法  
  class   Word   {  
  String   word;  
  public   Word(String   word){  
  this.word   =   word;  
  }  
  public   void   print(){  
  System.out.println(word);  
  }  
  public   void   setWord(String   word){  
  this.word   =   word;  
  }  
  }  
   
  Word   o1,   o2;  
   
  o1   =   new   Word("Every   Day");  
  o2   =   o1;  
  o2.set   Word("Every   Night!");  
   
  o1.print();  
   
  这时的结果是"Every   Night!"。  
   
  那么,这是改变了o1吗?从严格意义上讲,不是。因为o1只是保存对象的引用,执行之后,o1还是持有该对象的引用。所以,o1没变,变的是o1所引用的对象。  
  3.3   final变量能改变吗?  
  好了,我再出道题目:  
   
  final   Word   o3   =   new   Word("Every   Day!");  
  o3.setWord("Every   Night!");  
   
  能通过编译吗?对于final的定义大家都知道,o3是相当于一个常量,既然是常量,怎么能再改变呢?  
  答案是肯定的,能。道理我想大家也明白,这里不罗嗦了。  
  3.4   包装类的赋值与传递  
  以前看过文章说,对于java基本类型及其包装类采用值传递,对于对象采用引用传递。从langspec看,首先包装类不是PrimitiveType,那就只能是ReferenceType,而ReferenceType的变量保存的是引用。既然保存的是引用,也就无从传递数值。那么,这两个观点矛盾吗?  
   
  首先,肯定是langspec正确。  
  其次,虽然前一观点在原理上有错误,但却不影响正常使用。  
   
  为什么会出现这种情况?这是因为这些包装类具有一个简单类型的特征,即,不可改变。以String为例,看一下API   Specification,不会找到能够改变String对象的方法。任何输出上的改变都是重建新的String对象,而不是在原对象基础上改变。改变的是变量的内容,即,不同对象的引用。