【JAVA学习】EffectiveJava的学习笔记--第二章 Object类的通用方法
2014-09-08 16:23
393 查看
Object类就是为了扩展用,其中非final方法(equals hashCode,toString,Clone,finalize)耳熟能详的方法在改写的时候必须要有很多通用约定,否则其他依赖于这些约定的类就无法与这些类正常工作。
1.2 实现改写的通用约定
自反性: x.equals(x) 为true
对称性: x.equals(y) 为true,则 y.equals(x)也为true
下面这段代码coment部分的如果存在的话就违反了对称性,即equals方法中CaseInsensitiveString认识String类因此进行了判断,而反过来String类却人不认识CaseInsensitiveString类。这种不自觉的错误经常不经意就发生。
comment之前的结果是:
comment段后的结果是:
传递性: x.equals(y) 为true y.equals(z)为true, 则 x.equals(z)也为true
这个问题经常发生在继承扩展一个值的类的时候,如果继承扩展的类没有override equals则,调用equals的时候就会出现以下问题。
得到的结果是
但实际上cp1 和cp2是两个颜色不同的点。override equals方法后,虽然可以保证证对称性,但没有保证传递性,即p == p1 p==p2 得到的结果是p1 != p2.
为了解决这个传递性的问题,一般要扩展可实例化的类的同时,既要增加新的特征,又要保留equals的约定,没有办法做到。
另外一种解决方案是 采用【复合】的方式。
即ColorPoint不是继承与Point,而是该类包含了一个Point的成员变量。
非空性: 非空引用值 x x.equals(null) 则返回false,非空不用多说了,就是上面override里面的加上null 的判断。
对于类中每个关键域,检查实参中与当前对象中对应的域是否匹配。
1.3检查equals方法的一些小结:
如果该类的类型是接口,则通过接口访问实参中的关键域
如果该类的类型是类,则访问实参中的关键域。
对于不是float 也不是double则可以直接用==比较,对于对象引用域则可以用递归的equals方法,
对于float则使用Float.floatToIntBits转换成int类型的值,再==比较
对于double则使用Double.doubleToLongBits转换成long型的值,再==比较
对于数组域,则以应用到元素上,有些对象引用域包含null是合法的,所以为了避免null异常,可以通过判断该field是不是null然后再进行常规的equals比较。
1.equals方法
1.1什么时候该改写equals方法?
当一个类有自己的逻辑相等(而非对象身份)的概念,超类也没有改写equals以实现自己需要的行为时候,一般都是都是比较两个指向值对象的引用的时候,希望知道逻辑是否相等,而非指向同一个对象。1.2 实现改写的通用约定
自反性: x.equals(x) 为true对称性: x.equals(y) 为true,则 y.equals(x)也为true
下面这段代码coment部分的如果存在的话就违反了对称性,即equals方法中CaseInsensitiveString认识String类因此进行了判断,而反过来String类却人不认识CaseInsensitiveString类。这种不自觉的错误经常不经意就发生。
public class CaseInsensitiveString { private String s; public CaseInsensitiveString(String s) { if(s == null) { throw new NullPointerException(); } this.s = s; } public boolean equals(Object o) { if(o instanceof CaseInsensitiveString) { return s.equalsIgnoreCase(((CaseInsensitiveString)o).s); } // 这段代码违反了对称性 // if(o instanceof String) { // return s.equalsIgnoreCase((String)o); // } return false; } public static void main(String[] args) { String s = "abc"; CaseInsensitiveString cis = new CaseInsensitiveString("Abc"); CaseInsensitiveString cis2 = new CaseInsensitiveString("ABC"); System.out.println("cis.equals(s)= " +cis.equals(s)); System.out.println("s.equals(cis) = " + s.equals(cis)); System.out.println("cis2.equals(cis) = " + cis2.equals(cis)); } }
comment之前的结果是:
cis.equals(s)= true s.equals(cis) = false cis2.equals(cis) = true
comment段后的结果是:
cis.equals(s)= false s.equals(cis) = false cis2.equals(cis) = true
传递性: x.equals(y) 为true y.equals(z)为true, 则 x.equals(z)也为true
这个问题经常发生在继承扩展一个值的类的时候,如果继承扩展的类没有override equals则,调用equals的时候就会出现以下问题。
import java.awt.Color; import java.awt.Point; public class ColorPoint extends Point{ private static final long serialVersionUID = 1L; private Color color; public ColorPoint(int x, int y, Color color) { super(x,y); this.color = color; } public static void main(String[] args) { // TODO Auto-generated method stub Point p = new Point(1,2); ColorPoint cp1 = new ColorPoint(1, 2, Color.red); ColorPoint cp2 = new ColorPoint(1, 2, Color.black); System.out.println("p.equals(cp1) =" + p.equals(cp1)); System.out.println("p.equals(cp2) =" + p.equals(cp2)); System.out.println("cp1.equals(cp2) =" + cp1.equals(cp2)); } }
得到的结果是
p.equals(cp1) =true p.equals(cp2) =true cp1.equals(cp2) =true
但实际上cp1 和cp2是两个颜色不同的点。override equals方法后,虽然可以保证证对称性,但没有保证传递性,即p == p1 p==p2 得到的结果是p1 != p2.
@Override public boolean equals(Object obj) { // TODO Auto-generated method stub if(!(obj instanceof Point)) { return false; } // if obj is normal point do a color-blind comparasion if(!(obj instanceof ColorPoint)) { return this.equals(obj); } ColorPoint cp = (ColorPoint)obj; return super.equals(obj) && (cp.color == this.color); }得到的结果是:
p.equals(cp1) =true p.equals(cp2) =true cp1.equals(cp2) =false
为了解决这个传递性的问题,一般要扩展可实例化的类的同时,既要增加新的特征,又要保留equals的约定,没有办法做到。
另外一种解决方案是 采用【复合】的方式。
即ColorPoint不是继承与Point,而是该类包含了一个Point的成员变量。
非空性: 非空引用值 x x.equals(null) 则返回false,非空不用多说了,就是上面override里面的加上null 的判断。
对于类中每个关键域,检查实参中与当前对象中对应的域是否匹配。
1.3检查equals方法的一些小结:
如果该类的类型是接口,则通过接口访问实参中的关键域如果该类的类型是类,则访问实参中的关键域。
对于不是float 也不是double则可以直接用==比较,对于对象引用域则可以用递归的equals方法,
对于float则使用Float.floatToIntBits转换成int类型的值,再==比较
对于double则使用Double.doubleToLongBits转换成long型的值,再==比较
对于数组域,则以应用到元素上,有些对象引用域包含null是合法的,所以为了避免null异常,可以通过判断该field是不是null然后再进行常规的equals比较。
1.4 修改equals的时候一定要修改hashcode方法,否则违反了相等的对象必须有相等的散列码。
这个下一篇再记述。相关文章推荐
- 多态Object类toString方法学习笔记ObjectDemo.java
- Java菜鸟学习笔记--面向对象篇(十六):Object类方法
- Java菜鸟学习笔记--面向对象篇(十六):Object类方法
- 多态Object类equals方法学习笔记ObjectDemo.java
- Java学习笔记之一(深入理解Object类中方法)
- java学习笔记《java面向对象编程》——面向对象开发方法概述
- [Java学习笔记] 猜猜看(2) ~ 中断循环语句的一种方法(能够中断所有进行中的循环)
- thinking in java学习笔记1——全面解读方法重载
- Java学习笔记1------面向对象的思维方法
- java学习笔记:声明方法时加入可变参数
- objective-c学习笔记第二章《objective-c中类型,表达式与java中的区别》
- Java finalize() 方法 -Java 学习笔记 (18)
- JNI学习笔记5——本地方法处理java数组/引用问题/缓存jfieldID/jmethodID
- Container.paint(Graphics g) 方法 -Java学习笔记(34)
- JNI学习笔记:(1)开篇(2)本地代码访问Java代码 (3)本地方法取得Java属性/调用java方法 (4)本地代码创建Java对象(包括javaString) (5) 本地方法处理java数组
- 《Java Concurrency in Practice》 学习笔记--第二章:线程安全
- 【Java】学习笔记——类与方法的说明符
- [学习笔记]java Enumeration接口方法的学习
- Java学习笔记---7.面向对象编程02-类的封装性以及构造方法
- Java学习笔记---13.面向对象编程08-Java中继承的概念,子类实例化过程,super()及方法复写