【基礎回溯2】Java 基礎知識疑難點/易錯點

2022-01-02 JavaGuide

本文已經收錄自 https://github.com/Snailclimb/JavaGuide  (59k+ Star):【Java學習+面試指南】 一份涵蓋大部分Java程式設計師所需要掌握的核心知識。閱讀原文即可獲取最新更新!

ps: 之前發過這篇文章,不錯後面又對內容重新完善和增加了部分內容,而且為了應讀者要求做一個文章分類目錄菜單,為了保證文章的質量和準確性重新發送了一遍。

1. 基礎1.1. 正確使用 equals 方法

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

舉個例子:


String str = null;
if (str.equals("SnailClimb")) {
...
} else {
..
}

運行上面的程序會拋出空指針異常,但是我們把第二行的條件判斷語句改為下面這樣的話,就不會拋出空指針異常,else 語句塊得到執行。:

"SnailClimb".equals(str);

不過更推薦使用 java.util.Objects#equals(JDK7 引入的工具類)。

Objects.equals(null,"SnailClimb");

我們看一下java.util.Objects#equals的源碼就知道原因了。

public static boolean equals(Object a, Object b) {

return (a == b) || (a != null && a.equals(b));
}

注意:

Reference:Java中equals方法造成空指針異常的原因及解決方案

每種原始類型都有默認值一樣,如int默認值為 0,boolean 的默認值為 false,null 是任何引用類型的默認值,不嚴格的說是所有 Object 類型的默認值。可以使用 == 或者 != 操作來比較null值,但是不能使用其他算法或者邏輯操作。在Java中null == null將返回true。不能使用一個值為null的引用類型變量來調用非靜態方法,否則會拋出異常1.2. 整型包裝類值的比較

所有整型包裝類對象值的比較必須使用equals方法。

先看下面這個例子:

Integer x = 3;
Integer y = 3;
System.out.println(x == y);
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);
System.out.println(a.equals(b));

當使用自動裝箱方式創建一個Integer對象時,當數值在-128 ~127時,會將創建的 Integer 對象緩存起來,當下次再出現該數值時,直接從緩存中取出對應的Integer對象。所以上述代碼中,x和y引用的是相同的Integer對象。

注意: 如果你的IDE(IDEA/Eclipse)上安裝了阿里巴巴的p3c插件,這個插件如果檢測到你用 ==的話會報錯提示,推薦安裝一個這個插件,很不錯。

1.3. BigDecimal1.3.1. BigDecimal 的用處

《阿里巴巴Java開發手冊》中提到:浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用 equals 來判斷。 具體原理和浮點數的編碼方式有關,這裡就不多提了,我們下面直接上實例:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);
System.out.println(b);
System.out.println(a == b);

具有基本數學知識的我們很清楚的知道輸出並不是我們想要的結果(精度丟失),我們如何解決這個問題呢?一種很常用的方法是:使用使用 BigDecimal 來定義浮點數的值,再進行浮點數的運算操作。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
System.out.println(x.equals(y));

1.3.2. BigDecimal 的大小比較

a.compareTo(b) : 返回 -1 表示小於,0 表示 等於, 1表示 大於。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));

1.3.3. BigDecimal 保留幾位小數

通過 setScale方法設置保留幾位小數以及保留規則。保留規則有挺多種,不需要記,IDEA會提示。

BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);

1.3.4. BigDecimal 的使用注意事項

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

《阿里巴巴Java開發手冊》對這部分BigDecimal的描述1.3.5. 總結

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

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

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

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

【強制】所有的 POJO 類屬性必須使用包裝數據類型。【強制】RPC 方法的返回值和參數必須使用包裝數據類型。

比如我們如果自定義了一個Student類,其中有一個屬性是成績score,如果用Integer而不用int定義,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0,這兩個表達的狀態明顯不一樣.

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

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

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

2. 集合2.1. Arrays.asList()使用指南

最近使用Arrays.asList()遇到了一些坑,然後在網上看到這篇文章:Java Array to List Examples 感覺挺不錯的,但是還不是特別全面。所以,自己對於這塊小知識點進行了簡單的總結。

2.1.1. 簡介

Arrays.asList()在平時開發中還是比較常見的,我們可以使用它將一個數組轉換為一個List集合。

String[] myArray = { "Apple", "Banana", "Orange" };
List<String> myList = Arrays.asList(myArray);

List<String> myList = Arrays.asList("Apple","Banana", "Orange");

JDK 源碼對於這個方法的說明:


public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}

2.1.2. 《阿里巴巴Java 開發手冊》對其的描述

Arrays.asList()將數組轉換為集合後,底層其實還是數組,《阿里巴巴Java 開發手冊》對於這個方法有如下描述:

阿里巴巴Java開發手-Arrays.asList()方法2.1.3. 使用時的注意事項總結

傳遞的數組必須是對象數組,而不是基本類型。

Arrays.asList()是泛型方法,傳入的對象必須是對象數組。

int[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());
System.out.println(myList.get(0));
System.out.println(myList.get(1));
int [] array=(int[]) myList.get(0);
System.out.println(array[0]);

當傳入一個原生數據類型數組時,Arrays.asList() 的真正得到的參數就不是數組中的元素,而是數組對象本身!此時List 的唯一元素就是這個數組,這也就解釋了上面的代碼。

我們使用包裝類型數組就可以解決這個問題。

Integer[] myArray = { 1, 2, 3 };

使用集合的修改方法:add()、remove()、clear()會拋出異常。

List myList = Arrays.asList(1, 2, 3);
myList.add(4);
myList.remove(1);
myList.clear();

Arrays.asList() 方法返回的並不是 java.util.ArrayList ,而是 java.util.Arrays 的一個內部類,這個內部類並沒有實現集合的修改方法或者說並沒有重寫這些方法。

List myList = Arrays.asList(1, 2, 3);
System.out.println(myList.getClass());

下圖是java.util.Arrays$ArrayList的簡易源碼,我們可以看到這個類重寫的方法有哪些。

private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
...

@Override
public E get(int index) {
...
}

@Override
public E set(int index, E element) {
...
}

@Override
public int indexOf(Object o) {
...
}

@Override
public boolean contains(Object o) {
...
}

@Override
public void forEach(Consumer<? super E> action) {
...
}

@Override
public void replaceAll(UnaryOperator<E> operator) {
...
}

@Override
public void sort(Comparator<? super E> c) {
...
}
}

我們再看一下java.util.AbstractList的remove()方法,這樣我們就明白為啥會拋出UnsupportedOperationException。

public E remove(int index) {
throw new UnsupportedOperationException();
}

2.1.4. 如何正確的將數組轉換為ArrayList?

stackoverflow:https://dwz.cn/vcBkTiTW

1. 自己動手實現(教育目的)


static <T> List<T> arrayToList(final T[] array) {
final List<T> l = new ArrayList<T>(array.length);

for (final T s : array) {
l.add(s);
}
return (l);
}

Integer [] myArray = { 1, 2, 3 };
System.out.println(arrayToList(myArray).getClass());

2. 最簡便的方法(推薦)

List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

3. 使用 Java8 的Stream(推薦)

Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());

int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

4. 使用 Guava(推薦)

對於不可變集合,你可以使用ImmutableList類及其of()與copyOf()工廠方法:(參數不能為空)

List<String> il = ImmutableList.of("string", "elements");
List<String> il = ImmutableList.copyOf(aStringArray);

對於可變集合,你可以使用Lists類及其newArrayList()工廠方法:

List<String> l1 = Lists.newArrayList(anotherListOrCollection);
List<String> l2 = Lists.newArrayList(aStringArray);
List<String> l3 = Lists.newArrayList("or", "string", "elements");

5. 使用 Apache Commons Collections

List<String> list = new ArrayList<String>();
CollectionUtils.addAll(list, str);

2.2. Collection.toArray()方法使用的坑&如何反轉數組

該方法是一個泛型方法:<T> T[] toArray(T[] a); 如果toArray方法中沒有傳遞任何參數的話返回的是Object類型數組。

String [] s= new String[]{
"dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A"
};
List<String> list = Arrays.asList(s);
Collections.reverse(list);
s=list.toArray(new String[0]);

由於JVM優化,new String[0]作為Collection.toArray()方法的參數現在使用更好,new String[0]就是起一個模板的作用,指定了返回數組的類型,0是為了節省空間,因為它只是為了說明返回的類型。詳見:https://shipilev.net/blog/2016/arrays-wisdom-ancients/

2.3. 不要在 foreach 循環裡進行元素的 remove/add 操作

如果要進行remove操作,可以調用迭代器的 remove方法而不是集合類的 remove 方法。因為如果列表在任何時間從結構上修改創建迭代器之後,以任何方式除非通過迭代器自身remove/add方法,迭代器都將拋出一個ConcurrentModificationException,這就是單線程狀態下產生的 fail-fast 機制

fail-fast 機制 :多個線程對 fail-fast 集合進行修改的時,可能會拋出ConcurrentModificationException,單線程下也會出現這種情況,上面已經提到過。

java.util包下面的所有的集合類都是fail-fast的,而java.util.concurrent包下面的所有的類都是fail-safe的。

不要在 foreach 循環裡進行元素的 remove/add 操作、

相關焦點

  • Java 基礎知識總結(一)之Java 概述
    斷續了文章的更新,今天開始為大家講解Java基礎。希望大家多多支持!寫代碼1,明確需求。我要做什麼?2,分析思路。我要怎麼做?1,2,3。3,確定步驟。每一個思路部分用到哪些語句,方法,和對象。用具體的java 語言代碼把思路體現出來。學習新技術的四點1,該技術是什麼?2,該技術有什麼特點(使用注意):3,該技術怎麼使用。demo4,該技術什麼時候用?test。
  • 程序基礎:Java基礎知識一
    在上一篇文章中我們一起配置了Java的基本開發環境,今天我們就一起來創建一個小工程,並且老梁也和大家一起來回顧一部分Java的基礎知識,這些基礎知識有很多是通用的,就是這些知識基本上是不會因為程式語言的區別而有太大的變化,算是程序的基礎。
  • 《教育理論基礎》思維導圖與易錯點解析(二)
    教育理論基礎是教師招聘考試中的必考項,涉及教育學、心理學、教育心理學等六大部分,那麼我們在招教備考中該如何學習才能更快的掌握這些知識呢?思維導圖與易錯易混點的結合利於考生快速梳理章節知識及避免易混淆點出錯,今天小編又整理了《教育學》的部分章節,希望小夥伴可以更輕鬆的備考招教。
  • 4.Java基礎知識-HelloWorld
    4.1 執行流程4.2 編寫代碼步驟首先定義一個類public class 類名在類定義後加上一對大括號{}在大括號中間添加一個主(main)方法/函數public static voidpublic static void main(String[] args) {System.out.println("HelloWorld");}}運行代碼步驟:在命令行模式中, 切換到文件地址 輸入javac命令對原始碼進行編譯,生成字節碼文件– javac 源文件名.java
  • 小學數學基礎知識易錯點匯總,減少孩子作業出錯率!
    糾錯措施:畫兩幅圖,一幅是從同一個點引出兩條射線,另一幅是從兩個點引兩條射線,使學生進一步理解角的含義。 改正:由兩條射線組成的圖形叫做角。(×) 【易錯6】 平角就是一條直線(√) 【問診】 分析原因:學生把角的概念忘記了,角是由一個點引出兩條射線所組成的角,而直線上沒有點,也就不存頂點。
  • Java基礎知識——第一天
    第一天的表演開始了請大家看好了Java程序基礎結構:包相當於電腦中的磁碟,用於管理規範程序,讓程序不會雜亂無章,方便程序查找管理,特別是程序很大時,包名一定是小寫的,這是java語言的規範2、第二條語句public class MyJavaApp,這是創建類的語句,public是Java的關鍵字,是java中的其中一個訪問權限修飾符,用於修飾類/
  • 10.Java基礎知識-If語句
    >/** if語句格式2:* if(關係表達式) {* 語句體1;* }else {* 語句體2;>importjava.util.Scanner;/** 鍵盤錄入兩個數據,獲取這兩個數據的較大值** 分析:* A:看到鍵盤錄入
  • 給Java新手的一些建議——Java知識點歸納(Java基礎部分)
    JVM相關(包括了各個版本的特性)對於剛剛接觸Java的人來說,JVM相關的知識不一定需要理解很深,對此裡面的概念有一些簡單的了解即可。不過對於一個有著3年以上Java經驗的資深開發者來說,不會JVM幾乎是不可接受的。JVM作為java運行的基礎,很難相信對於JVM一點都不了解的人可以把java語言吃得很透。
  • Java基礎學習心得筆記
    對於很多只會C語言的初學者而言,面對java基礎語法學習,反而感覺很難,其實其中最大的問題不是語法難,而是一種編程思想的轉變。
  • 護理學基礎知識:標本採集易錯點及重點歸納
    一、標本採集易錯點:1.靜脈血採集(1)採集時間:除了血型測定可以選任何時間,其他都有時間要求,一般檢驗都選晨起空腹,但血培養採集要在這三個時間段,抗生素使用及傷口治療前,高熱寒戰期,停用抗生素3天後。
  • 初中數學幾何圖形初步,實例分析易錯點及考點,打下良好幾何基礎
    進入初中之後,幾何圖形的學習也與小學有了明顯的區別,對於七年級的學生來說,幾何圖形初步是進入初中學習數學幾何知識的基礎,因此為了給後面的幾何學習打下良好的基礎,我和同學們一起通過實例的形式,分析幾個圖形初步中的易錯點和考點,希望能夠給同學們帶來幫助。
  • 【編程基礎】Java面向對象基礎知識
    前言:前面一系列文章講了Java的一些語法基礎知識、Java中的數據類型和Java中的運算符,基本上都是學習Java語言的基礎知識,從這一講開始將會逐步介紹Java面向對象編程的一些高級知識。所有提到Java語言特性的文章肯定都會提到Java是面向對象的,在我的一篇文章中也說了Java語言有哪些特性,這一篇文章就給大家講講Java面向對象這一知識,有一個面向對象的概念,為以後學習更深層次的東西打下基礎。面向對象程序設計五個特徵在Java編程思想中提到了面向對象程序設計的五個特徵:萬物皆為對象。
  • 小學數學基礎知識易錯點匯總+錯題集整理方法,快告訴孩子!
    【易錯5】由兩條射線組成的圖形叫做角(√)【問診】分析原因:學生對角的認識還不夠充分。糾錯措施:畫兩幅圖,一幅是從同一個點引出兩條射線,另一幅是從兩個點引兩條射線,使學生進一步理解角的含義。改正:由兩條射線組成的圖形叫做角。
  • 7.Java基礎知識-運算符
    7.1 運算符的概念 運算符對常量和變量進行操作的符號稱為運算符 表達式用運算符把常量或者變量連接起來符號java語法的式子就可以稱為表達式。>/** 運算符:對常量和變量進行操作的符號* 表達式:用運算符連接起來的符合java語法的式子。
  • Java基礎知識總結(絕對經典)
    2,分析思路。我要怎麼做?1,2,3。3,確定步驟。每一個思路部分用到哪些語句,方法,和對象。4,代碼實現。用具體的java語言代碼把思路體現出來。 學習新技術的四點:1,該技術是什麼?2,該技術有什麼特點(使用注意):3,該技術怎麼使用。
  • 小學數學——關於角的知識的易錯點和易混點解析
    今天,筆者就以查缺補漏的方式和大家重新回顧一下有關的部分知識,找出易錯、易混的地方,供有志於數學學習的孩子們參考借鑑。一、角的基礎知識易錯點(1)首先要明確角的組成,角是由有公共端點的兩條射線組成的圖形,記住關鍵詞「公共端點」和「兩條射線」,其中公共端點叫做角的頂點,兩條射線叫做角的兩邊。
  • Java基礎知識筆記四(詳細)
    過程圖解3,鍵盤錄入兩個數據,返回兩個數中的較大值package com.baidu.Demo;import java.util.Scanner;public class methodTest {public static void main(String[] args) { Scanner sc=new Scanner(System.in);
  • 公共基礎知識:垃圾分類
    科技類的知識在公共基礎知識中常見考察客觀題,一般考察直接記憶,理解性題目比較少見。出題人將教材中關鍵詞挖出來設置成選項。或者出判斷題。考察考生是否能準確記憶科技知識中的關鍵詞,如名稱地點意義等。要求考生熟悉理科基礎知識,同時與時俱進了解前沿科技知識。新知識和基礎知識結合的考點往往能成為考試的熱門,如垃圾分類。有同學認為這種考察方式很簡單,其實不然。
  • 初中英語學習,抓住4點,零基礎也能逆襲110+
    比如有些同學除了上課聽講,其他時間幾乎不學習;也有些同學在沒有理解基礎知識的情況下盲目地去做題。這樣的做法都是不科學不合理的。鑑於此,老師建議大家從以下4個方面規劃初中英語學習。劃出重點知識,對文章的具體內容和中心思想做全面把控。③學習後:學完之後,將課文中的重要知識點記到筆記本上,將課文作為自己今後的閱讀材料,為初中英語學習夯實基礎。
  • SQL 基礎知識- 聚合和排序
    序  這是《SQL 基礎知識梳理(二) - 查詢基礎》的下篇。易錯點1  【總結】使用 GROUP BY 子句時,SELECT 子句不能出現聚合鍵之外的列名。  (2)易錯:在 GROUP BY 子句中寫了列的別名