Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1449960
  • 博文数量: 254
  • 博客积分: 8696
  • 博客等级: 中将
  • 技术积分: 2961
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-03 16:46
文章分类

全部博文(254)

文章存档

2015年(4)

2014年(18)

2013年(16)

2012年(8)

2011年(25)

2010年(2)

2009年(74)

2008年(107)

分类: Java

2008-07-06 16:45:11

Java中的给对象赋值
 
对主数据类型的赋值是非常直接的。由于主类型容纳了实际的值,而且并非指向一个对象的句柄,所以在为其赋值的时候,可将来自一个地方的内容复制到另一个地方。例如,假设为主类型使用“A=B”,那么B处的内容就复制到A。若接着又修改了A,那么B根本不会受这种修改的影响。作为一名程序员,这应成为自己的常识。

但在为对象“赋值”的时候,情况却发生了变化。对一个对象进行操作时,我们真正操作的是它的句柄。所以倘若“从一个对象到另一个对象”赋值,实际就是将句柄从一个地方复制到另一个地方。这意味着假若为对象使用“C=D”,那么C和D最终都会指向最初只有D才指向的那个对象。

下面是例子:

//: Assignment.java
// Assignment with objects is a bit tricky
package c03;

class Number {
int i;
}

public class Assignment {
public static void main(String[] args) {
Number n1 = new Number(); //new
Number n2 = new Number(); //new

n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1 = n2;
System.out.println("2: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +
", n2.i: " + n2.i);
}
} ///:~

Number类非常简单,它的两个实例(n1和n2)是在main()里创建的。每个Number中的i值都赋予了一个不同的值。随后,将n2赋给n1,而且n1发生改变。在许多程序设计语言中,我们都希望n1和n2任何时候都相互独立。但由于我们已赋予了一个句柄,所以下面才是真实的输出:
1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 27
看来改变n1的同时也改变了n2!这是由于无论n1还是n2都包含了相同的句柄,它指向相同的对象(最初的句柄位于n1内部,指向容纳了值9的一个对象。在赋值过程中,那个句柄实际已经丢失;它的对象会由“垃圾收集器”自动清除)。
这种特殊的现象通常也叫作“别名”,是Java操作对象的一种基本方式。但假若不愿意在这种情况下出现别名,又该怎么操作呢?可放弃赋值,并写入下述代码:
n1.i = n2.i;
这样便可保留两个独立的对象,而不是将n1和n2绑定到相同的对象。但您很快就会意识到,这样做会使对象内部的字段处理发生混乱,并与标准的面向对象设计准则相悖。
(上述来自:http://hi.baidu.com/famr4r/blog/item/e7fd8fb1d9de955709230297.html)
 
 
复制的两个必须条件:
  1.一定要将重定义后的clone() 方法定义为公有方法(在Object 类中, 它是受保护的成员, 不能直接使用)

  2.该后代类声明实现接口 Cloneable 接口(当类实现该接口, 其任何子类也会继承该接口), 该接口实际上没有任何

  内容, 只是一个标识, 标志实现该接口的类提供clone() 方法.(这是接口的一种非典型用法)

  public class Fraction implements Cloneable {

  public Object clone() {

  try{

  return super.clone(); //call protected method

  } catch (CloneNotSupportedException e) {

  return null;

  }

  }

  //other methods ...

  }

  b.重写Object.clone()

  例如对 private char[] cb; character buffer 进行复制

  // add in class Cirbuf

  public Object clone() {

  try{

  Cirbuf copy = (Cirbuf)super.clone();

  copy.cb = (char[])cb.clone();

  return copy;

  }catch (CloneNotSupportedException e){

  throw new InternalError(e.toString());

  }

  }

  c.复制数组

  数组是在方法调用重以引用的形式传递的对象. 下述情况下非常适合引用来传递数组:

  *正在接收的方法不修改数组

  *正在调用的方法不必关心是否修改数组

  *正在调用的方法想要得到数组中的修改结果

  否则, 就应该在方法调用中传递数组对象的副本. 只需调用 arrObj.clone() 方法即可完成数组arrObj 的复制操作. 随后将该数组副本强制转换为其正确类型:

  (type[])arrObj.clone();

  System.arraycopy 方法提供一种用于在数组间复制多个元素的有效方式.

  System.arraycopy(source, i, target, j, len)

  3.对象实例的比较

  例如:

  Pupil p1 = new Pupil("99184001", "zhang3", 18);

  Pupil p2 = new Pupil("99184001", "zhang3", 18);

  a. "=="

  if(p1 == p2)...

  此次测试的是对象引用, 其结果肯定是false, 只要两个对象引用不是互为别名就不会相等.

  b. 浅比较 false

  if(p1.getSno() == p2.getSno() && p1.getName() == p2.getName()

  && p1.getAge() == p2.getAge()) ...;

  c. 深比较 true[/code]

  if(p1.getSno().equals(p2.getSno()) && p1.getName().equals(p2.getName())

  && p1.getAge() == p2.getAge()) ...;[/code]

  JAVA API 的跟类Object 也提供了equals() 方法, 但它只是比较两个对象引用, 而非比较两个对象实例.

  不管怎样, 如果需要比较Pupil 类的对象(例如要将它们放入对象容器), 应该为Pupil 类重定义equals() 方法:

  public boolean equals(Object otherobj) {

  //检查otherobj 是否为空

  if(otherobj == null) return false;

  //检查otherobj 是否就是当前对象

  if(otherobj == this) return true;

  //检查otherobj 是否具有正确的类型, 即检查是否可与当前对象比较

  if(!(otherobj instanceof Pupil)) return false;

  //将otherobj 转换为Pupil 类的对象引用

  Pupil tmpObj = (Pupil)otherobj;

  //关于学生是否相等的逻辑检查

  if(sno.equals(tmpObj.sno) && name.equals(tmpObj.name)

  & age == tmpObj.age) return true;

  return false;

  }

  JAVA API 所提供的每个类几乎都提供了采用深比较策略的equals() 方法, 例如String 类equals() 方法. 一般来说, 用户自己定义的类也应当提供合适的equals() 方法, 特别是当程序要将其对象放入JAVA API 所提供的对象容器类的时候.

  按照约定, 任何类所提供的equals() 方法所实现的相等比较应该是等价关系, 即满足自反性, 对称性和传递性. 另外一个类重定义了equals() 方法, 也应该重定义相应hashCode() 方法, 否则将这个类的对象放入映射对象容器时也会发生以外.
(上述来自:)
阅读(9568) | 评论(0) | 转发(0) |
0

上一篇:什么是模板

下一篇:java参数与引用

给主人留下些什么吧!~~