第三天:第1章 編程規約 (1.4 OOP規約)

今天看了一看 1.4 OOP規約,這個內容有一些十分值得記錄並應用到編程中。推薦看 5 6 8 15 17 20

1、【強制】避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,造成無謂增加編譯器解析成本,直接用類名來訪問即可。

理解:這裏具體原因我不知道,但是我有印象說對象直接訪問類屬性,編譯器會將對象轉化爲類,然後去訪問類屬性。這裏有瞭解的同學可以留言評論。


2、【強制】所有的複寫方法,必須加@Override註解

說明:getObject() 與 get0bject()的問題。一個是字母O,一個是數字0,加@Override可以準確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進行修改,其實現類會馬上編譯報錯。

理解:關於重寫重載,記住一個關鍵詞,方法簽名。方法簽名包括 方法名稱及形參。

重載:方法簽名一定要不同,返回參數及修飾符無所謂。

重寫:跟父類一模一樣就好,修飾符可以向上擴展。


3、【強制】相同參數類型,相同業務含義,纔可以使用Java的可變參數,避免使用Object。

說明:可變參數必須放置在參數列表的最後(建議工程師們儘量不用可變參數編程)

理解:

可變參數的機制是通過先創建一個數組,數組的大小爲在調用位置所傳遞的參數數量,然後將參數值傳到數組中,最後將數組傳遞給方法


4、【強制】對外部正在調用或者二方庫依賴的接口,不允許修改方法簽名,以避免對接口調用方產生影響。若接口過時,必須加@Deprecated 註解,並清晰地說明採用的新接口或者新服務是什麼。

理解:主要是說給SDK開發的。配合 5 看。


5、【強制】不能使用過時的類

說明:java.net.URLDecoder中的方法decode(String encodeStr)已經過時,應該使用雙參數decode(String source,String encode)。接口提供方既然明確是過時接口,那麼有義務同時提供新的接口;作爲調用方來說,有義務去考證過時方法的新實現是什麼

理解:接口提供方寫說明,接口調用方看原因。


6、【強制】Object的equals方法容易拋空指針異常,應使用常量或確定有值的對象來調用equals。

正例:"test".equals(object);

反例:object.equals("test");

說明:推薦使用 java.util.Objects#equals(JDK7引入的工具類)。

理解:直接代碼,另外這個工具類需要 最小SDK版本爲 19,也就是API 4.4。



boolean equals = Objects.equals("a", "b");

7、【強制】所有相同類型的包裝類對象之間值的比較,全部使用equals方法。

說明:對於Inerger var = ? 在 -128 ~ 127 範圍內的賦值,Integer對象實在IntegerCache.cache中產生的,會複用已有對象,這個區間內的Integer值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,且不會複用已有對象。這是一個大坑,推薦使用equals方法進行判斷

理解:自動裝箱自動拆箱,記住 -128 ~ 127 即可。同時記住,包裝類對象使用equals。


8、關於基本數據類型與包裝數據類型的使用標準如下:

1)【強制】所有的POJO類屬性必須使用包裝數據類型。

2)【強制】RPC方法的返回值和參數必須使用包裝數據類型。

3)【推薦】所有的局部變量使用基本數據類型。

說明:POJO 類屬性沒有初值,是要提醒使用者在需要使用時,必須自己顯式地進行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。

正例:數據庫的查詢結果可能是null,因爲自動拆箱,所以用基本數據類型接收有NPE風險。

反例:比如顯示成交總額漲跌情況,即正負X%,X爲基本數據類型,調用的RPC服務在調用不成功時,返回的是默認值,頁面顯示爲0%,這是不合理的,因該顯示成中畫線。所以包裝數據類型的null值,能夠表示額外的信息,如:遠程調用失敗,異常退出。

理解:樓主對這一條的理解很是深刻。因爲樓主有個項目是股票類APP,那麼顯示 0% 和 中畫線 (即 “ -- ”) 是很不同的概念。如 漲幅 0% 和 漲幅 -- 。這個意思肯定是不同的。

簡單舉個例子:

public static Integer num;
可能我們一般都寫成了 int,但是這裏強制寫成 Integer,爲的就是提醒使用者在使用時需要顯式賦值,或者判 null 。

如果寫成了 int,初始化值就成了0,那麼使用者就不知道 0 是正常賦值還是初始化值,導致混亂。


9、【強制】在定義DO/DTO/VO等POJO類時,不要設定任何屬性默認值。

反例:POJO類的gmtCreate默認值爲 new Date();,但是這個屬性在數據提取時並沒有置入具體值,在更新其他字段時又附帶更新了此字段,導致創建時間被修改成當前時間。

理解:比如數據庫裏面,一個字段保存創建時間,那麼初始化時,字段保存時間如 10:00 或者 時間戳 xxxxx,那麼當你在 10:20更新其他字段時,附帶更新了此字段,那麼它的值就會變成10:20 或者 其對應的時間戳。

這裏我個人的其他理解是,不要對值進行初始化賦值。

private List dataList = new ArrayList();
在需要時判空,然後 new 出來。


10、【強制】當序列化類新增屬性時,請不要修改 serialVersionUID字段,以避免反序列失敗;如果完全不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID值。

說明:注意serialVersionUID不一致會拋出序列化運行時異常。

理解:這個一般也沒人去修改。


11、【強制】構造方法裏面禁止加入任何業務邏輯,如果有初始化邏輯,請放在init方法中。

理解:這個很好理解,比如自定義控件會經常用到。initView,initLayout這一類的名稱,傳遞參數即可。


12、【強制】POJO類必須寫toString方法。在使用IDE中的工具 source > generate toString時,如果繼承了另一個POJO類,注意在前面加一下 super.toString。

說明:在方法執行拋出異常時,可以直接調用POJO的toString()方法打印其屬性值,便於排查問題。

理解:這個就很有必要了,可以直接用AS自動生成 toString。快捷鍵 Alt + Ins

public class Teacher {
    private int age;
    private String name;

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}


13、【推薦】當使用索引訪問用String的split方法得到的數組時,需要在最後一個分隔符後做有無內容的檢查, 否則會有拋IndexOutOfBoundsException風險。

說明:

String str = "a,b,c,,";
String[] ary = str.split(",");
// 預期大於3,結果是3
System.out.println(ary.length);

理解:如圖,總感覺開發用到的情況很少,看我標記的地方,看示例

http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

split

public String[] split(String regex,
                      int limit)
根據匹配給定的 正則表達式來拆分此字符串。
此方法返回的數組包含此字符串的子字符串,每個子字符串都由另一個匹配給定表達式的子字符串終止,或者由此字符串末尾終止。數組中的子字符串按它們在此字符串中出現的順序排列。如果表達式不匹配輸入的任何部分,那麼所得數組只具有一個元素,即此字符串。

limit 參數控制模式應用的次數,因此影響所得數組的長度。如果該限制 n 大於 0,則模式將被最多應用 n - 1 次,數組的長度將不會大於 n,而且數組的最後一項將包含所有超出最後匹配的定界符的輸入。如果 n 爲非正,那麼模式將被應用盡可能多的次數,而且數組可以是任何長度。如果 n 爲 0,那麼模式將被應用盡可能多的次數,數組可以是任何長度,並且結尾空字符串將被丟棄。

例如,字符串 "boo:and:foo" 使用這些參數可生成以下結果:

Regex	Limit	結果
:	2	{ "boo", "and:foo" }
:	5	{ "boo", "and", "foo" }
:	-2	{ "boo", "and", "foo" }
o	5	{ "b", "", ":and:f", "", "" }
o	-2	{ "b", "", ":and:f", "", "" }
o	0	{ "b", "", ":and:f" }
調用此方法的 str.split(regex, n) 形式與以下表達式產生的結果完全相同:

Pattern. compile ( regex ). split ( str ,  n )
參數:
regex - 定界正則表達式
limit - 結果閾值,如上所述
返回:
字符串數組,它是根據給定正則表達式的匹配拆分此字符串確定的
拋出:
PatternSyntaxException - 如果正則表達式的語法無效
從以下版本開始:
1.4
另請參見:
Pattern
o -2 { "b", "", ":and:f", "", "" } 主要看這個


14、【推薦】當一個類有多個構造方法,或者多個同名方法時,這些方法應該按順序放置在一起,便於閱讀,此條規則優化與本節第15條規則


15、【推薦】類內方法定義順序是:公有方法或保護方法 > 私有方法 > getter /  setter 方法。

說明:公有方法是類的調用者和維護者最關心的方法,首屏展示最好;保護方法雖然只有子類關心,但也可能是“模板設計模式”下的核心方法;而私有方法外部一般不需要關心,是一個黑盒實現;因爲承載的信息價值較低,所有Service 和DAO的 getter / setter 方法放在類體最後。

理解:非常好的分類歸納。非常有利於代碼邏輯層次,另外一定要注意註釋寫法。形參 返回參數 說明等。


16、【推薦】在setter方法中,參數名稱與類成員變量名稱一致,this.成員名 = 參數名。在 getter / setter 方法中,不要增加業務邏輯,否則會增加排查問題的難度。

反例:

public Integer getData() {
    if (condition) {
        return this.data + 100;
    } else {
        return this.data - 100;
    }
}
理解:開發中誰幹這事誰傻。


17、【推薦】在循環體內,字符串的連接方式使用StringBuilder的append方法進行擴展。

說明:反編譯出的字節碼文件顯示每次循環都會 new 出一個 StringBuilder 對象,然後進行append操作,最後通過 toString方法返回String對象,造成內存資源浪費。

反例:

String str = "start";
for (int i = 0; i < 100; i++) {
    str = str + "hello";
}
理解:這個從說明來看,阿里程序員從反編譯的字節碼看,字符串的拼接實際上經歷了, new StringBuilder,append,toString 三個操作,如果循環 100次,就表示 new 了 100 次StringBuilder 。對於這一點,開發中倒是沒見過,漲知識了。


18、【推薦】final可以聲明類、成員變量、方法及本地變量,下列情況使用final關鍵字:

1)不允許被繼承的類,如:String 類

2)不允許修改引用的域對象,如:POJO類的域變量

3)不允許被重寫的方法,如:POJO類的Setter方法

4)不允許運行過程中重新賦值的局部變量

5)避免上下文重複使用一個變量,使用final描述可以強制重新定義一個變量,方便更好地進行重構

理解:SDK開發用的更多一些。平常開發,一般常量或者局部變量擴大作用域,如匿名內部類使用局部變量。


19、【推薦】慎用Object的clone方法來拷貝對象。

說明:對象的clone方法默認是淺拷貝,若想實現深拷貝,需要重寫clone方法來實現屬性對象的拷貝。


20、【推薦】類成員與方法訪問控制從嚴:

1)如果不允許外部直接通過new來創建對象,那麼構造方法必須是private;

2)工具類不允許有public 或 default 構造方法;

3)類非 static 成員變量並且與子類共享,必須是protected;

4)類非 static 成員變量並且僅在本類使用,必須是private;

5)類 static 成員變量如果僅在本類使用,必須是private;

6)若是 static 成員變量,必須 考慮是否爲 final;

7)類成員方法只供類內部調用,必須是private;

8)類成員方法只對繼承類公開,限制爲protected;

說明:對任何類、方法、參數、變量,嚴控訪問範圍。過於寬泛的訪問範圍,不利於模塊解耦。思考,如果一個private的方法,想刪除就刪除;可如果是一個public的 service方法,或者一個public的成員變量,刪除時手心不得冒點汗嗎?變量像自己的小孩,應儘量讓它在自己的視線範圍內。變量作用域太大,如果任其無限制地到處跑,你會擔心的。

理解:這個沒啥可說的,開發習慣,必須遵守。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章