AOS編排語言系列教程(八):創建容器集群Cluster
1474
2022-05-30
對軟件開發人員來說,此規范可以保證軟件產品的質量,可以作為和其他程序員溝通的標準,若編碼規則是建立在廣泛的共識之上,更有利于產品的發展。
一、"JAVA基礎語句"編程規范
例外:如果enum類型的switch語句包含該類型的所有可能值的顯示案例,則該類型的switch語句可以省略default語句組。IDE或其他靜態分析工具能夠在缺少時,發出警告。
不包含default語句的代碼:
private void switchMethodNoDefault(int number) {
switch (number) {
case 1:System.out.println("This is branch 1!");break;
case 2:System.out.println("This is branch 2!");break;
}
}? ? ? ? ? 這個switch就包含兩個分支
通過程序執行的結果,可以發現,參數是4的情況,程序沒有任何輸出,也就是說,程序沒有覆蓋到參數是4的情況。這種代碼是不好的,程序是不完整的。
2.switch的條件變量是枚舉類型
enum類型的switch語句如果包含該類型的所有可能值的顯式案例,則該類型的switch語句可以省略該default語句組。
如果沒有break語句,switch語句會從滿足條件的地方(即與switch(表達式)括號中表達式匹配的case)開始執行,直到switch結構結束。
3.如果要結束switch語句怎么辦?
程序在執行完選中的分支后,要跳出整個switch語句,需要使用break
總結:當switch后面表達式的值和case語句后的值相同時,switch結構從該位置開始向下執行,直到遇到break語句或者switch語句塊結束
4. if-else if-else語句: if-else if-else語句是多條件分支語句,即根據多個條件來控制程序執行的流程。
if-else if語句,最后應以else分支作為結尾。
所以建議,多個else if條件組合的判斷邏輯,往往會出現開發人員忽略的分支,需要設置else默認操作(類似于switch-case語句要有default分支)
5.浮點數不能為循環變量,精度問題會導致產生意想不到的結果
一個浮點數由三部分組成︰符號位S、指數部分E(階碼)以及尾數部分M(如下)。
Float ? S--------E-------M ?1位-----8位-----23位(共32位)
Double? ? S--------E-------M ? 1位-----11位----52位(共64位)
其中尾數部分為有效數字
計算機是用二進制來保持數據的, 在java中,浮點數2000000000f == 2000000045等式成立。
2000000000表示為單精度浮點數,求∶其對應的浮點數2000000000.0的二進制表示。
IEEE二進制浮點數算術標準(IEEE 754 )
IEEE754約定單精度指數偏移量為127,所以2000000000使用IEEE754標準表示時,指數為30+127=157 ,即:10011101,
IEEE754約定單精度尾數長度為23,所以2000000000使用IEEE754標準表示時,尾數為:11011100110101100101000
2000000000是正數,所以符號位是0。把符號位,階碼和尾數連起來︰
最后得到32位浮點數在內存中的二進制存儲格式為∶
0 10011101 11011100110101100101000
2000000050和2000000000的二進制相同,所以,2000000000f == 2000000050
總結︰浮點數據在計算機中不是精確的數據,浮點變量具有不確定性,所以用來當循環的條件,很可能得不到預期結果。因此,不建議用浮點數做循環變量!
6. 在Java程序中,不同的基本數據類型的數據之間經常需要進行相互轉換。我們建議,明確地進行類型轉換,不要依賴隱式類型轉換。
自動類型/隱式類型轉換
由系統自動完成的類型轉換。從存儲范圍小的類型到存儲范圍大的類型。
強制類型轉換
從存儲范圍大的類型到存儲范圍小的類型。該類類型轉換很可能存在精度的損失。
需要精確計算時,怎么辦?
需要精確計算時,使用BigDecimal
總結∶明確地進行類型轉換,不要依賴隱式類型轉換,通過這種方式,程序員表明他知道所涉及的不同類型,并且混合是有意的。以免意外地浮點數轉換截取,導致誤差逐步放大。
二、 "類的使用"編程規范
1.使用類名調用靜態方法,而不要使用實例來調用。
package BlueBridge;
public class Dog {
public static void main(String [] args) {
Dog dog = new Dog();
Dog basenjiDog = new Basenji();
dog.run();
basenjiDog.run();
dog.eat();
basenjiDog.eat();
}
public static void eat() {
System.out.println("Animal eat");
}
public void run() {
System.out.println("Animal running");
}
}
class Basenji extends Dog{
public static void eat() {
System.out.println("Basenji eat");
}
public void run() {
System.out.println("Basenji running");
}
}
運行結果:
Animal running
Basenji running
Animal eat
Animal eat
分析一下原因∶使用實例調用靜態方法時,調用的靜態方法是聲明類型的靜態方法,與實例的實際類型無關。當父類和子類有同名靜態方法時,聲明父類變量引用子類實例,調用該靜態方法時調用的是父類的靜態方法,而非子類的靜態方法。
父類和子類有同名非靜態方法時,子類方法將重寫父類方法;
【當子類的創建方式為 父類聲明 子類實現時】
父類和子類有同名 靜態方法時,父類方法將隱藏字類方法。
總結︰使用對象調用類中的靜態方法,可能會導致與預期的結果不一致的情況,明確的使用類名調用靜態方法不容易造成混淆。
2.避免在無關的變量或無關的概念之間重用名字,避免隱藏(hide)、遮蔽(shadow)、和遮掩(obscure)
OOP---面向對象的思想
總結:
override 存在于子類和父類之間。 子類中的【實例方法】可以覆寫 override 其在超類中能夠訪問(非私有)的同簽名的實例方法(非靜態方法)。
一個實例方法可以覆寫( override )在其超類中可訪問到(非private )的具有相同簽名的實例方法(非static ),從而使能了動態分派( dynamic dispatch );換句話說,VM將基于實例的運行期類型來選擇要調用的覆寫方法。覆寫的好處在于子類可以根據需要,定義特定于自己的行為。
重載(overload)存在于類內部
什么是重載?
Object 一組相似行為用相同名命名
具體而言比如一個計算工具類 求和兩個集合的大小 求和兩個數組的大小 求和集合和數組的大小
總結︰
overload存在于某個類之間。 某個類中的【某個方法】可以重載 overload 同類中的另一個具有相同命名和不同簽名的方法。
在某個類中的方法可以重載( overload )另一個方法,只要它們具有相同的名字和不同簽名。由調用所指定的重載方法是在編譯期選定的。重載歧義或混淆的場景∶ ? 可變參數 ? 包裝類型,例如int與Integer,這時應該修改方法名稱,如果是構造方法,則委托到不同名的靜態方法。
不恰當的使用重命名的方式有三種:
1.隱藏(hide)----子類與父類間
說明:一個屬性、靜態方法或內部類可以分別隱藏(hide)在其超類中可訪問到的具有相同名字(對方法而言就是相同的方法簽名)的所有屬性、方法或內部類。上述成員被隱藏后,將阻止其被繼承:
2.遮蔽(shadow)----類內部
一個變量、方法或類可以分別遮蔽(shadow)在類內部具有相同名字的變量、方法或類。如果一個實體被遮蔽了,那么就無法用簡單名引用到它:
3.遮掩(obscure)----類內部
一個變量可以遮掩具有相同名字的一個類,只要它們都在同一個范圍內︰如果這個名字被用于變量與類都被許可的范圍,那么它將引用到變量上。相似地,一個變量或一個類型可以遮掩一個包。遮掩是唯一一種兩個名字位于不同的名字空間的名字重用形式,這些名字空間包括:變量、包、方法或類型。如果一個類或一個包被遮掩了,那么你不能通過其簡單名引用到它,除非是在這樣一個上下文環境中,即語法只允許在其名字空間中出現一種名字。
3.子類覆寫父類方法時,應加上@Override 注釋
什么是方法的覆寫︰
如果父類的方法,不能滿足子類的要求,子類可以覆寫父類繼承的方法,當調用方法的時候會優先調用子類的方法。
語法規則︰
1.返回值類型? ? ?2.方法名
3.參數列表(類型和個數) ? 都要與繼承父類的方法相同,才叫方法的覆寫。
Annotation(注解)是JDK1.5及以后版本引入的。它可以用于創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。
@Override
它的作用是對覆蓋超類中方法的方法進行標記,如果被標記的方法沒有實際覆蓋超類中的方法,則編譯器會發出錯誤警告。
總結︰加上@Override注解的好處是,如果覆寫時因為疏忽,導致子類方法的參數同父類不一致,編譯時會報錯,使問題在編譯期就被發現。如果父類修改了方法的定義造成子類不再覆寫父類方法,也能使問題在編譯期盡早被發現。
4.哈希集合存儲對象的相關規則
將對象存入HashSet,或作為Key存入HashMap(或HashTable)后,必須確保對象的hashcode值不變,避免因為hashcode值變化導致不能從集合內刪除該對象
HashSet簡介
HashSet是set 接口的實現,不允許元素重復? 元素不重復是基于HashMap實現? 非線程安全的
對于Hash集合(HashMap ,HashSet等)而言,對象的hashcode至關重要,在hash集合內查找該對象完全依賴此值。如果一個對象存入Hash集合后,修改了參與計算hashcode有關的字段,那么修改后的hashcode的值就與最初存儲進來的hashcode的值不同了,結果就是無法在集合內找到該對象,進而不能刪除該對象,最終導致內存泄漏。
當一個對象被存儲在Hash集合中后,不要修改與計算hashcode有關的字段。
5.在向下類型轉換前用Instanceof進行判斷
向上類型轉換(自動類型轉換),是從小類型到大類型的轉換
向下類型轉換(強制類型轉換),是從大類型到小類型的轉換
向上(無風險)? ? ?向下(有風險)
總結:沒有判斷直接進行向下類型轉換,可能會因類型不匹配而導致運行期異常,在強制轉換之前使用instanceof進行判斷,確認轉換操作可行,可以避免類型轉換的安全性問題。
6.使用集合的toArray()方法將集合轉為數組
怎樣將集合轉為數組呢?
如果他們之間能夠轉換,應該是把集合中的元素一個一個取出來,再一個一個放到數組中
集合類有沒有方便的方法可以用呢?
toArray
1.public Object[] toArray()? ? ? ?按適當順序(從第一個到最后一個元素)返回包含此列表中所有元素的數組。
Object[] 不可以轉化為String[]; 需要進行每個元素的單獨轉換
toArray
2.public
按適當順序(從第一個到最后一個元素) 返回包含此列表中所有元素的數組; 返回數組的運行時類型是指定數組的運行時類型。如果指定的數組能容納列表,則將該列表返回此處。否則,將分配一個具有指定數組的運行時類型和此列表大小的新數組。
3.get() method
使用toArray方法,代碼更加簡潔。
Objecr[] toArray()方法
運行程序可能拋出異常:java.lang.ClassCastException
T[] toArray(T[] a)方法? 如果a數組的長度小于集合元素的長度,則重新分配空間復制并返回,否則使用a數組復制并返回
使用toArray帶參方法
1.入參分配的數組空間不夠大時,toArray方法內部將重新分配內存空間,并返回新數組地址﹔例如: new String[0]
⒉.如果數組元素大于實際所需,下標為[ list.size()]的數組元素和后面的元素將被置為null,其它數組元素保持原值,這個設置可以使方法調用者從返回的數組中檢測到null時就知道后面已經沒有list元素對象了。所以,最好將方法入參數組大小定義與集合元素個數一致( new String[list.size()] )。
怎樣進行數組復制呢?
第一種方法:使用for循環進行數組復制
使用System.arraycopy()進行數組復制
System.arraycopy()作用
從指定源數組中復制一個數組,復制從指定的位置開始,到目標數組的指定位置結束。
src:源數組;? srcPos:復制源數組的起始位置;
dest:目的數組;? destPos:目的數組放置的起始位置;? ? length:復制的長度。
第二種方法:使用System.arraycopy()進行數組復制
兩種復制方法的對比:
【1】目標數組是個小數組,長度在100以內
【2】目標是個中等數組,長度以千為單位。
【3】目標數組是個大型數組,長度以萬為單位。
總結:
原始數組長度比較小的時候,幾百以內,for循環表現十分優異,并隨著數組長度的增加,效率越來越低,因此,for循環適合于小型數組。
原始數組長度比較大的時候,以千或者以萬為單位,這時候本地方法System.arraycopy ( )方法的優勢體現出來了。
因此,需要根據操作的數組的長度,靈活地選擇數組復制方式,會使得我們的程序得到性能的略微提升。
三、“異常處理”編程規范
1.在異常條件下,保證釋放已持有的鎖
ReentrantLock(重入鎖)
可重入的互斥鎖,是一種遞歸無阻塞的同步機制。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更強大、靈活的鎖機制,可以減少死鎖發生的概率。
ReentrantLock類能夠實現線程之間同步互斥
ReentrantLock類具有完全互斥排他的效果,即同一時間只有一個線程在執行
ReentrantLock.lock()方法后面的任務。
出現異常應該怎么辦?
出現異常也要釋放掉持有的鎖。
總結:一個線程中沒有正確釋放持有的鎖會使其他線程無法獲取該鎖對象,導致阻塞。在發生異常時,要確保程序正確釋放當前持有的鎖。
Lock接口使用的標準形式:
//創建鎖
Lock lock = new ReentranLock();
...
lock.lock();
try{
//進行必要操作
//捕獲異常,并在必要時恢復不變性條件
} finally{
//釋放鎖
lock.unlock();
}
}
2.防止通過異常泄露敏感信息
異常行為
什么樣的信息是敏感的?? ?一般是由系統的安全策略來決定的
傳遞異常時:? 未對敏感信息過濾? 信息泄露? 攻擊者發起攻擊
典型的敏感信息包括? 個人社保號 口令 秘鑰等等
如果在傳遞異常的時候沒有對其中的敏感信息進行過濾,常常會導致信息泄露,而這可能幫助攻擊者嘗試發起進一步的攻擊,比如攻擊者可以通過構造惡意的輸入參數來發掘應用的內部
不管是異常中的文本消息還是異常本身的類型都有可能泄露敏感信息
敏感信息泄露的來源:
文本消息:例如? FileNotFoundException異常會透露文件系統的結構信息
異常類型:通過異常本身的類型,可以得知所請求的文件不存在
解決辦法:對敏感的異常消息和敏感的異常類型進行過濾。
3.在finally塊中不要使finally塊非正常結束
finally是什么?
1.java的一種異常處理機制? ? 2. 對java異常處理模型的最佳補充? ?3.使代碼總會執行,而不管有無異常發生
在finally塊中使用 return? break 和continue 會使finally塊非正常結束
造成的影響是即使在try塊或catch中拋出了異常,也會因為finally非正常結束而導致無法拋出。
1. 感謝老師們的教學與課件
2. 歡迎各位同學一起來交流學習心得^_^
3. 在線課程、沙箱實驗、認證、論壇和直播,其中包含了許多優質的內容,推薦了解與學習。
Java 數據結構
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。