Java基礎知識(一些須要注意的點)

前言

文本已收錄至個人GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客java

絮叨

前面寫過一篇基礎面試的git

正確使用 equals 方法

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)); } 複製代碼複製代碼

幾點注意的點數組

  • 每種原始類型都有默認值同樣,如int默認值爲 0,boolean 的默認值爲 false,null 是任何引用類型的默認值,不嚴格的說是全部 - Object 類型的默認值。
  • 可使用 == 或者 != 操做來比較null值,可是不能使用其餘算法或者邏輯操做。在Java中null == null將返回true。
  • 不能使用一個值爲null的引用類型變量來調用非靜態方法,不然會拋出異常

整型包裝類值的比較

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對象。緩存

BigDecimal

BigDecimal 的用處

《阿里巴巴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 
複製代碼

BigDecimal 的大小比較

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 保留幾位小數

BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.255 複製代碼複製代碼

BigDecimal 的使用注意事項

注意:咱們在使用BigDecimal時,爲了防止精度丟失,推薦使用它的 BigDecimal(String) 構造方法來建立對象。《阿里巴巴Java開發手冊》對這部份內容也有提到以下圖所示。

總結

BigDecimal 主要用來操做(大)浮點數,BigInteger 主要用來操做大整數(超過 long 類型)。

BigDecimal 的實現利用到了 BigInteger, 所不一樣的是 BigDecimal 加入了小數位的概念

基本數據類型與包裝數據類型的使用標準

Reference:《阿里巴巴Java開發手冊》

  • 【強制】全部的 POJO 類屬性必須使用包裝數據類型。
  • 【強制】RPC 方法的返回值和參數必須使用包裝數據類型。
  • 【推薦】全部的局部變量使用基本數據類型。 好比咱們若是自定義了一個Student類,其中有一個屬性是成績score,若是用Integer而不用int定義,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0,這兩個表達的狀態明顯不同.

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

正例 : 數據庫的查詢結果多是 null,由於自動拆箱,用基本數據類型接收有 NPE 風險。

Arrays.asList()使用指南

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的簡易源碼,咱們能夠看到這個類重寫的方法有哪些。

如何正確的將數組轉換爲ArrayList?

  • 最簡便的方法(推薦)
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
複製代碼
  • 使用 Java8 的Stream(推薦)

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()); 複製代碼複製代碼

不要在 foreach 循環裏進行元素的 remove/add 操做

若是要進行remove操做,能夠調用迭代器的 remove方法而不是集合類的 remove 方法。由於若是列表在任什麼時候間從結構上修改建立迭代器以後,以任何方式除非經過迭代器自身remove/add方法,迭代器都將拋出一個ConcurrentModificationException,這就是單線程狀態下產生的 fail-fast 機制。

聊聊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/ 寫的很不錯哦

平常求贊

好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見

六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !

相關文章
相關標籤/搜索