The Java IAQ: Infrequently Answered Questions (有关Java的不经常被问到的问题)
有关Java的不经常被问到的问题
作者 Peter Norvig
问题:什么是不经常被问到的问题?
一个不经常被问到的问题,有可能是因为只有很少的人知道答案或者因为这个问题比较含糊(但是有时这个问题对你解决问题很重要)。我认为是我先发明这个词的,但是它在信息量很大的一个网站上也出现过(About.com Urban Legends)。这里有很多Java FAQs(有关Java的经常被问到的问题),但是只有一个Java IAQ(有关Java的不经常被问到的问题,以后用 Java IAQ来代替)。(在网上只存在很少的IAQ列表,比如包含对C有讽刺意味的列表)
问题:finally中的语句永远会被执行到,对不对?
好吧,通常来说是的。但是下面例子中finally中的语句永远不会被执行,不管布尔变量choice是true还是false:
try {
if (choice) {
while (true) ;
} else {
System.exit(1);
}
} finally {
code.to.cleanup();
}
问题:在类C中的方法m中, this.getClass()是不是总是返回C?
No. It's possible that for some object x that is an instance of some subclass C1 of C either there is no C1.m() method, or some method on x called super.m(). In either case, this.getClass() is C1, not C within the body of C.m(). If C is final, then you're ok.
不是的。如果有一个类C1是C的子类,在C1中没有m方法,或者在C1中调用了super.m()。这两种情况下this.getClass()返回的是C1。 但是如果C是final的,则返回C
问题:我定义了equals方法,但是Hashtable忽略了它.为什么?
equals方法很容易被用错。如果你发现equals方法有问题,请看看下面的一些地方是不是有问题:
你定义了错误的equals方法,例如:
public class C {
public boolean equals(C that) { return id(this) == id(that); }
}
但是要让table.get(c)工作的话,你需要将C中的equals方法的参数改成Object。
public class C {
public boolean equals(Object that) {
return (that instanceof C) && id(this) == id((C)that);
}
}
为什么? Hashtable的get方法看起来像这样:
public class Hashtable {
public Object get(Object key) {
Object entry;
...
if (entry.equals(key)) ...
}
}
entry.equals(key)会调用equals方法,根据entry的运行时类型和key的编译时类型。所以当用户调用table.get(new C(...))时,看起来就像在类C中调用了参数是Object的equals方法。如果你将equals的参数定义成C,这是不对的。系统会忽略这个方法,并寻找方法签名是equals(Object)的方法,最后它会找打Object.equals(Object)。如果你想重载这个方法,你的方法签名必须和Object中的一样。在有些情况下,你可能需要这两个方法,这样你就可以不用进行类型转换当你知道正确的类型时:
Public class C {
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof C) && this.equals((C)that));
}
public boolean equals(C that) {
return id(this) == id(that); // Or whatever is appropriate for class C
}
}
你实现的equals方法没有遵守equals预定:对称,传递,自反。对称就是说如果a.equals(b)成立,那么b.equals(a)也成立(这是很多人会搞乱的地方)。传递就是说如果a.equals(b) 和 b.equals(c)成立,那么 a.equals(c)也成立。自反就是说a.equals(a)必须成立,这也是为什么上面的例子要使用(this == that)的原因。(这样做的好处有二,一效率提高,二有时可以打断相互引用)
你忘记了hashCode方法。只要你定义了equals方法,你应该定义hashCode方法。你必须保证两个相等的对象必须有一样的hashCode,如果你希望hashtable有更好的性能,你应该让大部分不想等的对象有不同的hashCode。一些类会将hashCode缓存起来,所以hashCode应该只计算一次。如果你使用if (this.hashSlot != that.hashSlot) return false,就就可以节省一些时间。
你没有处理好继承。首先,两个不同的类可以相等。在你说“不能,当然不能”之前。假设有一个Rectangle类,有两个属性width, height。和一个Box类,有三个属性,width,height,depth。是不是当Box中的depth==0然后就好Rectangle就相等了?你也许会说是的。 如果你在和一个non-final类打交道,这是很有可能你自己的类是子类,你又希望你自己的子类不很别人相染。在特殊的情况下,你可能会允许继承你的类,然后调用你的类的equals方法:
public class C2 extends C {
int newField = 0;
public boolean equals(Object that) {
if (this == that) return true;
else if (!(that instanceof C2)) return false;
else return this.newField == ((C2)that).newField && super.equals(that);
}
}
如果想让super.euqals工作的话,你必须仔细的定义和实现在C中的equals方法。例如,使用instanceof C,而不是that.getClass() == C.class。只有在你很确认连个对象是同一个类时才使用that.getClass() == C.class。
你没有很好的处理循环引用,例如:
public class LinkedList {
Object contents;
LinkedList next = null;
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof LinkedList) && this.equals((LinkedList)that));
}
public boolean equals(LinkedList that) { // Buggy!
return Util.equals(this.contents, that.contents) &&
Util.equals(this.next, that.next);
}
}
这里我i假设Util方法是:
public static boolean equals(Object x, Object y) {
return (x == y) || (x != null && x.equals(y));
}
我希望这个方法传入的是Object,如果不是的话,你可以在测试的时候抛出null。然而,LinkedList.equals方法不会返回,如果参与比较的连个LinkedList有循环引用(一个LinkedList的中元素指向另外一个元素)。可以参见Common Lisp函数的list-length,是如何使用两个word的额外存储空间来解决这个问题的。(我之所以不给出答案,是想让你自己先想一想)
Q:为什么有时调用父类的方易做图失败?
例如:
/** A version of Hashtable that lets you do
* table.put("dog", "canine");, and then have
* table.get("dogs") return "canine". **/
public class HashtableWithPlurals extends Hashtable {
/** Make the table map both key and key + "s" to value. **/
public Object put(Object key, Object value) {
super.put(key + "s", value);
return super.put(key, value);
}
}
当你在调用父类的方法的时候,你必须对父类非常了解。这这个例子中,Hashtable.put的约定是:它会维护key和value的关系。但是,如果hashtable快满的时候,Hashtable.put方法将会分配一个更多的数组,将原来的copy到新数组中去,然后重新递归调用table.put(key, value)方法。
因为Java是根据运行时类型来解析方法的,在我们的例子中Hashtable的递归调用会调用HashtableWithPlurals.put(key,value),最后的结果是偶然的(当在错误的时间hashtable上溢时),你将得到”dogss”,”dogs”,”dog”指向同一个value。是不是所有的的put都存在这样的情况的?不是,这时你需要查看JDK的code了。
注:可以在调用put之前,将s去掉或者使用封装类,而不是继承已有的类
http://stackoverflow.com/questions/6323923/i-tried-to-forward-a-method-to-super-but-it-occasionally-doesnt-work-why
问题:当我调用get时,为什么我的Properties对象忽略了默认值?
你不应该调用get方法,应该使用getProperty方法。很多人假设getProperty方法只是返回一个String类型,get方法返回Object类型。但是实际上,两者之间区别很多:getProperty会使用default,get方法是从Hashtable继承来的,它忽略了default。get方法的具体行为已经在Hashtable的文档中说明了,但是可能不是你想要的。其它从Hashtable继承而来的方法也会忽略default(例如i
补充:web前端 , JavaScript ,