Java Magic. Part 1: java.net.URL @(Base)[JDK, url, magic, 黑魔法] " 英文原文 " 廢話不多說,首先我們看如下代碼: 代碼的第3行和第5行分別會輸出什麼呢? 當然不會是true, 如果是true的話,這篇文章也就不會有java黑魔法的尾碼了
Java Magic. Part 1: java.net.URL
@(Base)[JDK, url, magic, 黑魔法]
英文原文
轉載請寫明:原文地址
系列文章:
-Java Magic. Part 1: java.net.URL
-Java Magic. Part 2: 0xCAFEBABE
-Java Magic. Part 3: Finally
-Java Magic. Part 4: sun.misc.Unsafe
廢話不多說,首先我們看如下代碼:
HashSet set = new HashSet();
set.add(new URL("http://google.com"));
set.contains(new URL("http://google.com"));
Thread.sleep(60000);
set.contains(new URL("http://google.com"));
代碼的第3行和第5行分別會輸出什麼呢?
當然不會是true, 如果是true的話,這篇文章也就不會有java黑魔法的尾碼了。簡單地說吧,當你有運行的PC有網路鏈接的時候,返回結果是false
。如果你關掉你的網路鏈接,比如wi-if,那麼該段程式的返回值就是true
。
這麼奇葩的直接原因就是URL
類的hashCode()
和equals()
方法的實現。
下麵是hashCode()
方法:
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
值得註意的是,URL是一個immutable的對象。
我們可以顯然看到這個URL的hashCode值是一個私有域。也就是說,只會計算一次。什麼是代碼中提到的handler呢?它其實是URLStreamHandler
的子類,具體的類型和當前的網路協議類型(file,http,ftp)有著密不可分的關係。我們可以看看URL.hashCode()
方法上面的註釋:
The hash code is based upon all the URL components relevant for URL comparison. As such, this operation is a blocking operation.
翻譯過來就是:hash code的計算和URL的所有相關屬性都有關係。例如,這個操作是不是blocking的。
WTF,BLOCKING OPERATION?!
好了這個事情我們暫時放一邊。另外這個hashCode的計算奇葩的地方在於,這個handler竟然會解析ip地址參與計算。我們拿這個google.com
為例子。當host的ip是動態的時候,或者說有一個功能變數名稱解析的負載均衡的時候,這個hashCode()
方法就會針對google.com
計算出兩個完全不同的hashCode值。
其實最讓人無法接受的是URLStreamHandler
會開啟一個URLConnection
,當然這又是另外一個話題啦。
How to avoid this?
- 用
java.net.URI
來替換java.net.URL
。雖然這不算是個非常好的辦法,但至少有一個靠譜hashCode。 - 千萬不要在
collections
中使用java.net.URL
。一個好的選擇是,在collections
中放string對象來表示hostname,然後當需要的時候再使用URL對象。 - 關閉你的網路連接,當計算的hashCode值的時候。雖然只是個玩笑,但是確實很有幫助 :)
- 自行替換
URLStreamHandler
我非常確認java.net.URL
有非常多有用的應用場景。但是至少不是像上面那麼用啦~