Java_Object的使用

来源:互联网 发布:电脑麦克风软件 编辑:程序博客网 时间:2024/04/30 19:05
Object是Java中最基本的类,也是整个Java类层次的根。虽然Object很简单,但却妙用不穷,如何用好这几个方法是Java的入门功夫。

1. clone方法

clone方法对对象进行一个浅度拷贝(shallowcopy)的操作。首先要注意的是Object类并没有实现Cloneable接口,所以Object的任意子类必须先实现了Cloneable接口后,它的实例才可以调用clone方法。其次,先看个例子,
public class CloneExample implements Cloneable{

    Entityentity;
    
    publicstatic void main(String[] args) {

       try{
          CloneExampleexample1 = new CloneExample();
         example1.entity = new Entity(1);
          CloneExampleexample2 = (CloneExample)example1.clone();
         example2.entity.id = 2;
         System.out.println(example1.entity.id);
         System.out.println(example2.entity.id);
       }catch(Exception ex){
         ex.printStackTrace();
      }
   }
}

class Entity {
    
    intid;
    
    publicEntity(int id) {
       this.id =id;
      
}
在上述例子中,example1的entity属性的id也会变成“2",也就是说clone方法的拷贝是浅度拷贝。这是因为在clone方法的缺省实现中,它先创建了一个新的实例B,然后将旧对象A中的所有属性的值拷贝一份给新实例B的相应属性。而在Java中,对于可变对象(mutableobjects)的传递是通过引用进行的,因此在新实例B中,所有的对象属性与A的对象属性持有的是相同的objectreference,可以简单得理解为它们都指向相同的内存地址,因此B中的对象属性改变了会影响到A中的对象属性。

因此,为了实现深度拷贝(deepcopy),基本有两种方法。其一是在子类中改写clone方法,保证每个对象属性都是通过值传递。其二是采用序列化的方法,但是这种方法会带来性能问题,慎用。这里提供一个序列号简单实现:
public static Object deepClone(Object pObj) {
       try {
           if (pObj != null) {
               ByteArrayOutputStream bout = newByteArrayOutputStream();
               ObjectOutputStream out = newObjectOutputStream(bout);
               out.writeObject(pObj);
               out.close();
               ByteArrayInputStream bin = newByteArrayInputStream(bout.toByteArray());
               ObjectInputStream in = new ObjectInputStream(bin);
               Object ret = in.readObject();
               in.close();
               return ret;
           }
           else
               return null;
       }
       catch(Exception ex) {
           return null;
       }
   }
    
2.equals方法

equals方法在实际编程中我们会经常用来判断两个对象是否相等,需要注意的是这个方法和==的区别。equals方法比较的是两个对象的内容是否相等,而==比较的是两个对象是否为同一个对象,也就是它们的reference是否相等。所以,x.clone().equals(x)通常会是true,但x.clone()!=x却永远成立。

equals的更大威力来自于当我们改写它的时候。在实际经验中,我们有的时候会改写一些类的equals方法来满足业务逻辑的需要,特别是在一些涉及语义比较的时候。简单地打个比方说,某个人叫李一,在他的朋友圈中,有人叫他老李,有人叫他小李,但不管怎么叫,大家都知道指的就是李一。用代码表示,
People p1 = new People();
p1.name = "老李";
People p2 = new People();
p2.name = "小李";
为了使得p1.equals(p2)等于true,我们就需要改写People的equals方法。那么在改写equals方法的时候,主要要满足的是四个特性,也就是自反性,对称性,传递性和一致性,具体可参见JavaAPIReference。通常我们在改写equals方法的时候也需要相应修改hashCode方法,使得当x.equals(y)为true是,x.hashCode()== y.hashCode()也成立

3.hashCode方法
hashCode方法最直接的用途是当用HashMap或HashTable是,用来计算哈希值。当除此之外,这个方法和equals方法的关系密切,当x.equals(y)为真时,x.hashCode()==y.hashCode()也必须为真,但是反过来是不成立的,也就是说x.hashCode()==y.hashCode()为真时,x.equals(y)不一定成立。在Object类的实现中,hashCode是通过将对象的内部地址转换为整数计算出来的,但当我们改写equals方法时,经常需要改写这个方法,此时需要注意的是产生hashCode的方法一定要是一致的,也就是不管调用这个方法多少次,产生的hashCode必须是相同的。一个最简单也常用的方法就是直接给定一个常数。

4.toString方法
这个方法返回对象的字串表示,缺省实现是getClass().getName() + "@" +Integer.toHexString(hashCode()),但实际中,我们总会改写这个方法使之更友好。而合理地运用这个方法,有时还可以有特殊的效果。比如在一些管理系统中,很多数据通常需要有中、英文两个名称,中文为了展现的友好性,英文为了处理和存储的方便,这个时候我们就可以将toString方法改写为返回中文名称,这样在很多的展现控件中可以直接显示出来。

5.wait和notify方法
这两个方法配套用于线程同步的最基本方法。当Java的线程模型中,每个对象都有一个监视器(monitor),并且这个监视器只能同时被一个线程拥有。只有当一个线程拥有这个对象的监视器的时候,它才可以调用这个对象的wait和notify方法。一个线程可以通过对一个对象进行同步(synchronized)来获取这个对象的监视器,当这个线程获得监视器后,可以通过调用wait方法来放弃这个监视器并转入等待状态(这和Thread.sleep()方法是不同的,sleep方法没有放弃监控权),直到notify方法被调用,线程才重新被唤醒,并重新获取控制权。一点值得注意的小地方就是notify和notifyAll的区别。当有多个线程在等待这个对象的时候,调用notify方法只会唤醒其中的一个线程,而这个线程的选择是随意的;调用notifyAll方法则将所有的线程唤醒。
原创粉丝点击