六、消除过期的对象引用:
- public class Stack {
- private Object[] elements;
- private int size = 0;
- private static final int DEFAULT_INITIAL_CAPACITY = 16;
- public Stack() {
- elements = new Object[DEFAULT_INITIAL_CAPACITY];
- }
- public void push(Object e) {
- ensureCapacity();
- elements[size++] = e;
- }
- public Object pop() {
- if (size == 0)
- throw new EmptyStackException();
- return elements[--size];
- }
- private void ensureCapacity() {
- if (elements.length == size)
- elements = Arrays.copys(elements,2*size+1);
- }
- }
- public Object pop() {
- if (size == 0)
- throw new EmptyStackException();
- Object result = elements[--size];
- elements[size] = null; //手工将数组中的该对象置空
- return result;
- }
- public void test() {
- FileInputStream fin = null;
- try {
- fin = new FileInputStream(filename);
- //do something.
- } finally {
- fin.close();
- }
- }
那么在实际的开发中,利用finalizer又能给我们带来什么样的帮助呢?见下例:
- public class FinalizeTest {
- //@Override
- protected void finalize() throws Throwable {
- try {
- //在调试过程中通过该方法,打印对象在被收集前的各种状态,
- //如判断是否仍有资源未被释放,或者是否有状态不一致的现象存在。
- //推荐将该finalize方法设计成仅在debug状态下可用,而在release
- //下该方法并不存在,以避免其对运行时效率的影响。
- System.out.println("The current status: " + _myStatus);
- } finally {
- //在finally中对超类finalize方法的调用是必须的,这样可以保证整个class继承
- //体系中的finalize链都被执行。
- super.finalize();
- }
- }
- }
- public final class CaseInsensitiveString {
- private final String s;
- public CaseInsensitiveString(String s) {
- this.s = s;
- }
- @Override public boolean equals(Object o) {
- if (o instanceof CaseInsensitiveString)
- return s.equalsIgnoreCase((CaseInsensitiveString)o).s);
- if (o instanceof String) //One-way interoperability
- return s.equalsIgnoreCase((String)o);
- return false;
- }
- }
- public static void main(String[] args) {
- CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
- String s = "polish";
- List<CaseInsensitiveString> l = new ArrayList<CaseInsensitiveString>();
- l.add(cis);
- if (l.contains(s))
- System.out.println("s can be found in the List");
- }
- @Override public boolean equals(Object o) {
- if (o instanceof CaseInsensitiveString)
- return s.equalsIgnoreCase((CaseInsensitiveString)o).s);
- return false;
- }
这样修改之后,cis.equals(s)和s.equals(cis)都将返回false。
- public class Point {
- private final int x;
- private final int y;
- public Point(int x,int y) {
- this.x = x;
- this.y = y;
- }
- @Override public boolean equals(Object o) {
- if (!(o instanceof Point))
- return false;
- Point p = (Point)o;
- return p.x == x && p.y == y;
- }
- }
- public class ColorPoint extends Point {
- private final Color c;
- public ColorPoint(int x,int y,Color c) {
- super(x,y);
- this.c = c;
- }
- @Override public boolean equals(Object o) {
- if (!(o instanceof ColorPoint))
- return false;
- return super.equals(o) && ((ColorPoint)o).c == c;
- }
- }
- public void test() {
- Point p = new Point(1,2);
- ColorPoint cp = new ColorPoint(1,2,Color.RED);
- if (p.equals(cp))
- System.out.println("p.equals(cp) is true");
- if (!cp.equals(p))
- System.out.println("cp.equals(p) is false");
- }
- 从输出结果来看,ColorPoint.equals方法破坏了相等性规则中的对称性,因此需要做如下修改:@Override public boolean equals(Object o) {
- if (!(o instanceof Point))
- return false;
- if (!(o instanceof ColorPoint))
- return o.equals(this);
- return super.equals(o) && ((ColorPoint)o).c == c;
- }
- public void test() {
- ColorPoint p1 = new ColorPoint(1,2,Color.RED);
- Point p2 = new Point(1,2);
- ColorPoint p1 = new ColorPoint(1,2,Color.BLUE);
- if (p1.equals(p2) && p2.equals(p3))
- System.out.println("p1.equals(p2) && p2.equals(p3) is true");
- if (!(p1.equals(p3))
- System.out.println("p1.equals(p3) is false");
- }
再次看输出结果,传递性确实被打破了。如果我们在Point.equals中不使用instanceof而是直接使用getClass呢?
- @Override public boolean equals(Object o) {
- if (o == null || o.getClass() == getClass())
- return false;
- Point p = (Point)o;
- return p.x == x && p.y == y;
- }
- class MyTest {
- private static final Set<Point> unitCircle;
- static {
- unitCircle = new HashSet<Point>();
- unitCircle.add(new Point(1,0));
- unitCircle.add(new Point(0,1));
- unitCircle.add(new Point(-1,0));
- unitCircle.add(new Point(0,-1));
- }
- public static boolean onUnitCircle(Point p) {
- return unitCircle.contains(p);
- }
- }
- public class ColorPoint {
- //包含了Point的代理类
- private final Point p;
- private final Color c;
- public ColorPoint(int x,int y,Color c) {
- if (c == null)
- throw new NullPointerException();
- p = new Point(x,y);
- this.c = c;
- }
- //提供一个视图方法返回内部的Point对象实例。这里Point实例为final对象非常重要,
- //可以避免使用者的误改动。视图方法在Java的集合框架中有着大量的应用。
- public Point asPoint() {
- return p;
- }
- @Override public boolean equals(Object o) {
- if (!(o instanceof ColorPoint))
- return false;
- ColorPoint cp = (ColorPoint)o;
- return cp.p.equals(p) && cp.c.equals(c);
- }
- }
- @Override public boolean equals(Object o) {
- if (o == null)
- return false;
- if (!(o instanceof MyType))
- return false;
- ...
- }
- @Override public boolean equals(Object o) {
- if (!(o instanceof MyType))
- return false;
- ...
- }
- @Override public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof MyType))
- return false;
- MyType myType = (MyType)o;
- return objField.equals(o.objField) && intField == o.intField
- && Double.compare(doubleField,o.doubleField) == 0
- && Arrays.equals(arrayField,o.arrayField);
- }
- public final class PhoneNumber {
- private final short areaCode;
- private final short prefix;
- private final short lineNumber;
- public PhoneNumber(int areaCode,int prefix,int lineNumber) {
- //做一些基于参数范围的检验。
- this.areaCode = areaCode;
- this.prefix = prefix;
- this.lineNumber = lineNumber;
- }
- @Override public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof PhoneNumber))
- return false;
- PhoneNumber pn = (PhoneNumber)o;
- return pn.lineNumber = lineNumber && pn.prefix == prefix && pn.areaCode = areaCode;
- }
- }
- public static void main(String[] args) {
- Map<PhoneNumber,String> m = new HashMap<PhoneNumber,String>();
- PhoneNumber pn1 = new PhoneNumber(707,867,5309);
- m.put(pn1,"Jenny");
- PhoneNumber pn2 = new PhoneNumber(707,867,5309);
- if (m.get(pn) == null)
- System.out.println("Object can't be found in the Map");
- }
从以上示例的输出结果可以看出,新new出来的pn2对象并没有在Map中找到,尽管pn2和pn1的相等性比较将返回true。这样的结果很显然是有悖我们的初衷的。如果想从Map中基于pn2找到pn1,那么我们就需要在PhoneNumber类中覆盖缺省的hashCode方法,见如下代码:
- @Override public int hashCode() {
- int result = 17;
- result = 31 * result + areaCode;
- result = 31 * result + prefix;
- result = 31 * result + lineNumber;
- return result;
- }
在上面的代码中,可以看到参与hashCode计算的域字段也同样参与了PhoneNumber的相等性(equals)比较。对于生成的散列码,推荐不同的对象能够尽可能生成不同的散列,这样可以保证在存入HashMap或HashSet中时,这些对象被分散到不同的散列桶中,从而提高容器的存取效率。对于有些不可变对象,如果需要被频繁的存取于哈希集合,为了提高效率,可以在对象构造的时候就已经计算出其hashCode值,hashCode()方法直接返回该值即可,如:
- public final class PhoneNumber {
- private final short areaCode;
- private final short prefix;
- private final short lineNumber;
- private final int myHashCode;
- public PhoneNumber(int areaCode,int prefix,int lineNumber) {
- //做一些基于参数范围的检验。
- this.areaCode = areaCode;
- this.prefix = prefix;
- this.lineNumber = lineNumber;
- myHashCode = 17;
- myHashCode = 31 * myHashCode + areaCode;
- myHashCode = 31 * myHashCode + prefix;
- myHashCode = 31 * myHashCode + lineNumber;
- }
- @Override public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof PhoneNumber))
- return false;
- PhoneNumber pn = (PhoneNumber)o;
- return pn.lineNumber = lineNumber && pn.prefix == prefix && pn.areaCode = areaCode;
- }
- @Override public int hashCode() {
- return myHashCode;
- }
- }
另外,该条目还建议不要仅仅利用某一域字段的部分信息来计算hashCode,如早期版本的String,为了提高计算哈希值的效率,只是挑选其中16个字符参与hashCode的计算,这样将会导致大量的String对象具有重复的hashCode,从而极大的降低了哈希集合的存取效率。
十、始终要覆盖toString:
- @Override String toString() {
- return String.format("(%03d) %03d-%04d",areaCode,prefix,lineNumber);
- }
对于toString返回字符串中包含的域字段,如本例中的areaCode、prefix和lineNumber,应该在该类(PhoneNumber)的声明中提供这些字段的getter方法,以避免toString的使用者为了获取其中的信息而不得不手工解析该字符串。这样不仅带来不必要的效率损失,而且在今后修改toString的格式时,也会给使用者的代码带来负面影响。提到toString返回字符串的格式,有两个建议,其一是尽量不要固定格式,这样会给今后添加新的字段信息带来一定的束缚,因为必须要考虑到格式的兼容性问题,再者就是推荐可以利用toString返回的字符串作为该类的构造函数参数来实例化该类的对象,如BigDecimal和BigInteger等装箱类。
转载于:https://blog.51cto.com/andra/779696