以下是学习《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)。