文本已收錄至個人GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客java
前面寫過一篇基礎面試的git
Object的equals方法容易拋空指針異常,應使用常量或肯定有值的對象來調用 equals。github
舉個例子:web
null.equals("小六六")
複製代碼
這種確定是會報NEP的,因此咱們應該把不會用空的放在前面來避免空指針異常。面試
還有一個推薦算法
Objects.equals(null,"小六六");// false
複製代碼
咱們看一下java.util.Objects#equals的源碼就知道緣由了。數據庫
編程
public static boolean equals(Object a, Object b) { // 能夠避免空指針異常。若是a==null的話此時a.equals(b)就不會獲得執行,避免出現空指針異常。 return (a == b) || (a != null && a.equals(b)); } 複製代碼複製代碼
幾點注意的點數組
Integer x = 3;
Integer y = 3;
System.out.println(x == y);// true
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true
複製代碼
當使用自動裝箱方式建立一個Integer對象時,當數值在-128 ~127時,會將建立的 Integer 對象緩存起來,當下次再出現該數值時,直接從緩存中取出對應的Integer對象。因此上述代碼中,x和y引用的是相同的Integer對象。緩存
《阿里巴巴Java開發手冊》中提到:浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用 equals 來判斷。 具體原理和浮點數的編碼方式有關,這裏就很少提了,咱們下面直接上實例:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
複製代碼
具備基本數學知識的咱們很清楚的知道輸出並非咱們想要的結果(精度丟失),咱們如何解決這個問題呢?一種很經常使用的方法是:使用使用 BigDecimal 來定義浮點數的值,再進行浮點數的運算操做。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true
複製代碼
a.compareTo(b) : 返回 -1 表示小於,0 表示 等於, 1表示 大於。 其實你能夠把他看成是 a-b 實際上是一個意思
BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); System.out.println(a.compareTo(b));// 1 複製代碼複製代碼
BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.255 複製代碼複製代碼
注意:咱們在使用BigDecimal時,爲了防止精度丟失,推薦使用它的 BigDecimal(String) 構造方法來建立對象。《阿里巴巴Java開發手冊》對這部份內容也有提到以下圖所示。
BigDecimal 主要用來操做(大)浮點數,BigInteger 主要用來操做大整數(超過 long 類型)。
BigDecimal 的實現利用到了 BigInteger, 所不一樣的是 BigDecimal 加入了小數位的概念
Reference:《阿里巴巴Java開發手冊》
說明 :POJO 類屬性沒有初值是提醒使用者在須要使用時,必須本身顯式地進行賦值,任何 NPE 問題,或者入庫檢查,都由使用者來保證。
正例 : 數據庫的查詢結果多是 null,由於自動拆箱,用基本數據類型接收有 NPE 風險。
Arrays.asList()在平時開發中仍是比較常見的,咱們可使用它將一個數組轉換爲一個List集合。
String[] myArray = { "Apple", "Banana", "Orange" };
List<String> myList = Arrays.asList(myArray);
//上面兩個語句等價於下面一條語句
List<String> myList = Arrays.asList("Apple","Banana", "Orange");
複製代碼
JDK 源碼對於這個方法的說明:
/** *返回由指定數組支持的固定大小的列表。此方法做爲基於數組和基於集合的API之間的橋樑,與 Collection.toArray()結合使用。返回的List是可序列化並實現RandomAccess接口。 */ public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } 複製代碼複製代碼
《阿里巴巴Java 開發手冊》對其的描述
Arrays.asList()將數組轉換爲集合後,底層其實仍是數組,《阿里巴巴Java 開發手冊》對於這個方法有以下描述:
Arrays.asList() 方法返回的並非 java.util.ArrayList ,而是 java.util.Arrays 的一個內部類,這個內部類並無實現集合的修改方法或者說並無重寫這些方法。
List myList = Arrays.asList(1, 2, 3); System.out.println(myList.getClass());//class java.util.Arrays$ArrayList 複製代碼複製代碼
下圖是java.util.Arrays$ArrayList的簡易源碼,咱們能夠看到這個類重寫的方法有哪些。
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
複製代碼
Integer [] myArray = { 1, 2, 3 }; List myList = Arrays.stream(myArray).collect(Collectors.toList()); //基本類型也能夠實現轉換(依賴boxed的裝箱操做) int [] myArray2 = { 1, 2, 3 }; List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList()); 複製代碼複製代碼
若是要進行remove操做,能夠調用迭代器的 remove方法而不是集合類的 remove 方法。由於若是列表在任什麼時候間從結構上修改建立迭代器以後,以任何方式除非經過迭代器自身remove/add方法,迭代器都將拋出一個ConcurrentModificationException,這就是單線程狀態下產生的 fail-fast 機制。
在用迭代器遍歷一個集合對象時,若是遍歷過程當中對集合對象的內容進行了修改(增長、刪除、修改),則會拋出Concurrent Modification Exception。
原理:迭代器在遍歷時直接訪問集合中的內容,而且在遍歷過程當中使用一個 modCount 變量。集合在被遍歷期間若是內容發生變化,就會改變modCount的值。每當迭代器使用hashNext()/next()遍歷下一個元素以前,都會檢測modCount變量是否爲expectedmodCount值,是的話就返回遍歷;不然拋出異常,終止遍歷。
注意:這裏異常的拋出條件是檢測到 modCount!=expectedmodCount 這個條件。若是集合發生變化時修改modCount值恰好又設置爲了expectedmodCount值,則異常不會拋出。所以,不能依賴於這個異常是否拋出而進行併發操做的編程,這個異常只建議用於檢測併發修改的bug。
場景:java.util包下的集合類都是快速失敗的,不能在多線程下發生併發修改(迭代過程當中被修改)。
從上面咱們能夠看出,只要是涉及了改變ArrayList元素的個數的方法都會致使modCount的改變。因此咱們這裏能夠初步判斷因爲expectedModCount 與modCount的改變不一樣步,致使二者之間不等,從而產生fail-fast機制。
那麼日常咱們如何去規避這種狀況呢?這裏有兩種解決方案:
使用CopyOnWriteArrayList來替換ArrayList。 CopyOnWriteArrayList爲何能解決這個問題呢?CopyOnWrite容器即寫時複製的容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。CopyOnWriteArrayList中add/remove等寫方法是須要加鎖的,目的是爲了不Copy出N個副本出來,致使併發寫。可是。CopyOnWriteArrayList中的讀方法是沒有加鎖的。
咱們只須要記住一句話,那就是CopyOnWriteArrayList是線程安全的,因此咱們在多線程的環境下面須要去使用這個就能夠了。關於CopyOnWriteArrayList更加深刻的用法,會在之後的章節中去解釋說明。
文章出自 https://snailclimb.gitee.io/ 寫的很不錯哦
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉。
創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見
六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !