阿里Java開發手冊思考(一)

史培培發表於2019-03-03
阿里Java開發手冊思考(一)

舊時王謝堂前燕,
飛入尋常百姓家。
小編將帶你們一起分析阿里巴巴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所對映的物件,導致記憶體洩漏。

阿里Java開發手冊思考(一)

微信公眾號:碼上論劍

請關注我的個人技術微信公眾號,訂閱更多內容

相關文章