Practical Java(重点版)之对象与相等性

 以下是学习《Practical Java(重点版)》的笔记。

1.  java提供了两种数据类型:原生态和引用类型。原生态数据类型都有相应的包装类对应。

  如:int I = 5;//原生态类型

  Integer j = new Integer(5);//引用类型

   虽然这两种方式存储的数据都是在stack(栈)中并且都是32bits,但是存储的意义是有区别的:前者是存储的数字;而后者存储的是引用(相当于是某个对象的地址),实际的对象是存储在heap(堆)中。在效率上原生态类型要高点,但是原生态类型不能够调用方法。

2.  “==”和“equals”区别是很大的。

public class Test1 {

    public static void main(String[] args) {

       int a = 10;

       int b = 10;

       System.out.println(a == b);//输出true

      

       Integer ia = new Integer(10);

       Integer ib = new Integer(10);

       System.out.println(ia == ib);//输出false

    }

}

上述结果分析:== 只是浅比较,仅仅是比较两边是否一样。对于原生态类型来说,仅仅是比较值。而对于对象引用来说,比较的是引用的地址是否一致。如果 要比较对象代表的值是否一致,请使用equals()方法。

使用“==”可以用来测试基本类型是否相等或者两个引用是否指向同一个对象。使用“equals”来比较对象的值是否一样。

3. 不要依赖默认的equals()方法,大多时候都是自己覆写该方法,该方法是Object的方法。这是Object的方法:

 public boolean equals(Object obj)

  {

     return this == obj;

}

明显可以看到默认的equals方法比较的是两个对象是否是引用同一对象,如果要确认两个对象的值是否一样,那么就需要自己重写该方法。

String重写的equals()方法:先是比较两个对象是否引用同一对象,再对比其值是否一样。StringBuffer、StringBuilder没有重写equals方法。

String、StringBuffer、StringBuilder都是final类;

StringBuffer是线程安全的,效率低;

StringBuilder是线程非安全的,效率高。

4. 在覆写equals方法时要慎重。当认为Object的equals方法不能够满足要求时,可以覆写equals方法。

5. 覆写equals时,请优先使用getClass。

有这样的一种说法:唯有相同的class产生出来的对象才相等,进一步可以说,如果两个对象的class或者类型,则不可能相等。因此,在覆写equals方法时,首先调用getClass来判断两个对象是否是同一class或者类型。

getClass()会返回某个对象的运行期类(runtime class)。

以下是反映继承的getClass()调用关系:

public class Test1 {

    public static void main(String[] args) {

       A aB = new B();

       A aC = new C();

       System.out.println("aB = " + aB.getClass());//aB = class com.study2.B

       System.out.println("aC = " + aC.getClass());//aC = class com.study2.C

       System.out.println(aB.getClass() == aC.getClass());//false

    }

}

 

class A {

 

}

 

class B extends A {

 

}

 

class C extends A {

 

}

从上面可以看出:尽管getClass是反映的引用的那个对象的类型,看“=”右边的类型。

 当我们要覆写equals时,要考虑:1).父类是否覆写了该方法,并且父类是否满足了相等功能;2).相等是要什么相等。

 覆写equals步骤:1)确定传入的比较对象是否为空;2)比较getClass是否一致;3)将对象向下转换类型;4)比较要确定相等的属性的值。

6.  调用super.equals()来唤醒父类的equals方法。在覆写equals方法时,请检查其父类的是否由equals方法,如果有的话,在子类覆写equals时,请必须调用父类的equals方法,这样才安全。不然的话,会破坏类的继承体系。

7.在equals中慎重使用instanceof方法。

在equals中使用instanceof,当子类和父类进行比较的时候,特别要注意对称相等性:即

subClass.equals(fathterClass);

fatherClass.equals(subClass);这两个的结果有可能是不一致的。要尽量避免。

当我们使用equals时,有必要时知道代码的equals的实现方式,其中判断类的隶属问题到底是instaceof还是getClass。

8. 覆写equals的遵循规则

覆写equals并不简单,需要我们准确地定义为什么要这样设计。

无论你选择使用getClass()或instanceof来实现equals(),下面的规则适用于所有的equals()函数:

a.如果某个class 的两个对象即使占据不同的内存空间,也可被视为「逻辑上相等」的话,那么你得为这个class 提供一个equals()。

b.请检查是否等于this。

c.比较这个class 中的相关属性(值域,fields),以判断两个对象是否相等。

d.如果有java.lang.Object 以外的任何父类实现了equals(), 那么就应该调用super.equals()。

在equals()函数中面对getClass()和instanceof 进行取舍时,你要仔细斟酌以下问题:

a.如果只允许同一个class 所产生的对象被视为相等,则通常使用getClass()。

b.只有在不得不「对子类对象与父类对象进行比较」的场合中,才使用instanceof,而且你应该明白这样做带来的可能问题和复杂性。

c.如果使用instanseof,而且子类和父类都实现有equals(),则一定要知道,这种比较不会展现出所谓的「对称相等性」(symmetricequality)。

发布了42 篇原创文章 · 获赞 0 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章