舊時王謝堂前燕,
飛入尋常百姓家。
小編將帶你們一起分析阿里巴巴Java開發手冊!
背景
阿里巴巴Java開發手冊是阿里巴巴集團技術團隊的集體智慧結晶和經驗總結,以Java開發者為中心視角,劃分為程式設計規約、異常日誌、單元測試、安全規約、工程結構、MySQL資料庫六個維度。手冊的願景是碼出高效、碼出質量、效率優先、質量為本。
目的
之所以要寫這個系列的文章,首先是學習與總結,其次是思考與理解,更是分享與交流,手冊中的每一條每一項都有其背後隱藏的原理與經驗,我們看到的只是冰山一角,深入挖掘其背後的知識有益於更深刻的理解,並在自己實際程式設計中提高自己的基本技術素養。
主題
- 【推薦】表達異常的分支時,少用 if-else 方式,這種方式可以改寫成:
if (condition) {
...
return obj;
}
// 接著寫 else 的業務邏輯程式碼;
複製程式碼
說明:如果非得使用 if()…else if()…else…方式表達邏輯,【強制】避免後續程式碼維
護困難,請勿超過 3 層。
正例:超過 3 層的 if-else 的邏輯判斷程式碼可以使用衛語句、策略模式、狀態模式等來實現, 其中衛語句示例如下:
public void today() {
if (isBusy()) {
System.out.println(“change time.”); return;
}
if (isFree()) {
System.out.println(“go to travel.”);
return;
}
System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
return;
}
複製程式碼
我們試圖通過例子來分析下上面的規則,在分析之前,我們先明確在Java裡,預設的equals和hashCode方法的實現,以及把一個元素放入雜湊集合(HashSet、HashMap等)時,雜湊集合對equals和hashCode方法的判定規則。
Java物件中預設的equals(Object obj)方法用來判斷兩個物件是否“相同”,如果“相同”則返回true,否則返回false。hashCode()方法返回一個int數,這個整數值是將該物件的內部地址轉換成一個整數返回的 。
在雜湊集合中儲存一個物件時,先進行hashCode的比較,如果hashCode不相等,則直接放入,否則繼續進行equals的比較,equals不相等才放入,equals相等就直接丟棄了。
理解
- 如果只重寫equals而不重寫hashCode會導致什麼問題?
package com.test;
import java.util.HashSet;
import java.util.Set;
public class OverrideEqualsTest {
public static void main(String[] args) {
Set<Point> set = new HashSet<Point>();
Point p1 = new Point(1, 1);
Point p2 = new Point(1, 1);
System.out.println("p1.equals(p2):" + p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p1);
System.out.println("set.size():" + set.size());
for (Point p : set) {
System.out.println(p);
}
}
static class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Point other = (Point) obj;
if (x != other.x) {
return false;
}
if (y != other.y) {
return false;
}
return true;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
}
複製程式碼
執行結果:
p1.equals(p2):true
set.size():2
Point[x=1,y=1]
Point[x=1,y=1]
分析:由於沒有重寫hashCode方法,p1和p2物件預設的hashCode方法返回的兩個物件地址轉換的整數肯定不同,所以p1和p2都可以放入set中,所以這並不是我們期望的結果。
- 如果只重寫hashCode而不重寫equals又會導致什麼問題?
package com.test;
import java.util.HashSet;
import java.util.Set;
public class OverrideHashCodeTest {
public static void main(String[] args) {
Set<Point> set = new HashSet<Point>();
Point p1 = new Point(1, 1);
Point p2 = new Point(1, 1);
System.out.println("p1.equals(p2):" + p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p1);
System.out.println("set.size():" + set.size());
for (Point p : set) {
System.out.println(p);
}
}
static class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 3L;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
}
複製程式碼
執行結果:
p1.equals(p2):false
set.size():2
Point[x=1,y=1]
Point[x=1,y=1]
分析:由於沒有重寫equals方法,p1和p2物件的預設的equals方法通過“==”來比較,而p1和p2是兩個不同的物件,所以p1和p2都可以放入set中,所以這也不是我們期望的結果。
所以綜上,當我們同時重寫equals和hashCode方法後,才能在雜湊集合操作中得到一致性的結果。
- 物件放入雜湊集合後,又修改了影響hashCode的值,後果?
package com.test;
public class Test {
public static void main(String[] args) {
Point p1 = new Point(1, 1);
Point p2 = new Point(2, 2);
set.add(p1);
set.add(p2);
System.out.println("set.size():" + set.size());
p2.setY(3);
set.remove(p1);
set.remove(p2);
System.out.println("set.size():" + set.size());
}
}
複製程式碼
執行結果:
set.size():2
set.size():1
分析:由於在執行期間,修改了p2物件的y值,導致p2物件的hashCode返回值有變化,所以hashset的remove方法將找不到新的hashCode所對映的物件,導致記憶體洩漏。