Java面試寶典(2020版)

来源:https://www.cnblogs.com/aishangJava/archive/2019/11/24/11922550.html
-Advertisement-
Play Games

一、Java 基礎 1. JDK 和 JRE 有什麼區別? JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。 JRE:Java Runtime Environment 的簡稱,java 運行環境,為 java 的運行提供了所需環 ...


一、Java 基礎

1. JDK 和 JRE 有什麼區別?

  • JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。
  • JRE:Java Runtime Environment 的簡稱,java 運行環境,為 java 的運行提供了所需環境。

具體來說 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了很多 java 程式調試和分析的工具。簡單來說:如果你需要運行 java 程式,只需安裝 JRE 就可以了,如果你需要編寫 java 程式,需要安裝 JDK。

2. == 和 equals 的區別是什麼?

== 解讀

對於基本類型和引用類型 == 的作用效果是不同的,如下所示:

  • 基本類型:比較的是值是否相同;
  • 引用類型:比較的是引用是否相同;

代碼示例:

String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代碼解讀:因為 x 和 y 指向的是同一個引用,所以 == 也是 true,而 new String()方法則重寫開闢了記憶體空間,所以 == 結果為 false,而 equals 比較的一直是值,所以結果都為 true。

equals 解讀

equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下麵的代碼就明白了。

首先來看預設情況下 equals 比較一個有相同值的對象,代碼如下:

class Cat {
    public Cat(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

輸出結果出乎我們的意料,竟然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼如下:

public boolean equals(Object obj) {
    return (this == obj);
}

原來 equals 本質上就是 ==。

那問題來了,兩個相同值的 String 對象,為什麼返回的是 true?代碼如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同樣的,當我們進入 String 的 equals 方法,找到了答案,代碼如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

原來是 String 重寫了 Object 的 equals 方法,把引用比較改成了值比較。

總結 :== 對於基本類型來說是值比較,對於引用類型來說是比較的是引用;而 equals 預設情況下是引用比較,只是很多類重新了 equals 方法,比如 String、Integer 等把它變成了值比較,所以一般情況下 equals 比較的是值是否相等。

3. 兩個對象的 hashCode()相同,則 equals()也一定為 true,對嗎?

不對,兩個對象的 hashCode()相同,equals()不一定 true。

代碼示例:

String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

執行的結果:

str1:1179395 | str2:1179395

false

代碼解讀:很顯然“通話”和“重地”的 hashCode() 相同,然而 equals() 則為 false,因為在散列表中,hashCode()相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不一定能得出鍵值對相等。

4. final 在 java 中有什麼作用?

  • final 修飾的類叫最終類,該類不能被繼承。
  • final 修飾的方法不能被重寫。
  • final 修飾的變數叫常量,常量必須初始化,初始化之後值就不能被修改。

5. java 中的 Math.round(-1.5) 等於多少?

等於 -1,因為在數軸上取值時,中間值(0.5)向右取整,所以正 0.5 是往上取整,負 0.5 是直接捨棄。

6. String 屬於基礎的數據類型嗎?

String 不屬於基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於對象。

7. java 中操作字元串都有哪些類?它們之間有什麼區別?

操作字元串的類有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的區別在於 String 聲明的是不可變的對象,每次操作都會生成新的 String 對象,然後將指針指向新的 String 對象,而 StringBuffer、StringBuilder 可以在原有對象的基礎上進行操作,所以在經常改變字元串內容的情況下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的區別在於,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,所以在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。

8. String str="i"與 String str=new String("i")一樣嗎?

不一樣,因為記憶體的分配方式不一樣。String str="i"的方式,java 虛擬機會將其分配到常量池中;而 String str=new String("i") 則會被分到堆記憶體中。

9. 如何將字元串反轉?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

示例代碼:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

10. String 類的常用方法都有那些?

  • indexOf():返回指定字元的索引。
  • charAt():返回指定索引處的字元。
  • replace():字元串替換。
  • trim():去除字元串兩端空白。
  • split():分割字元串,返回一個分割後的字元串數組。
  • getBytes():返回字元串的 byte 類型數組。
  • length():返回字元串長度。
  • toLowerCase():將字元串轉成小寫字母。
  • toUpperCase():將字元串轉成大寫字元。
  • substring():截取字元串。
  • equals():字元串比較。

11. 抽象類必須要有抽象方法嗎?

不需要,抽象類不一定非要有抽象方法。

示例代碼:

abstract class Cat {
    public static void sayHi() {
        System.out.println("hi~");
    }
}

上面代碼,抽象類並沒有抽象方法但完全可以正常運行。

12. 普通類和抽象類有哪些區別?

  • 普通類不能包含抽象方法,抽象類可以包含抽象方法。
  • 抽象類不能直接實例化,普通類可以直接實例化。

13. 抽象類能使用 final 修飾嗎?

不能,定義抽象類就是讓其他類繼承的,如果定義為 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象類,如下圖所示,編輯器也會提示錯誤信息:

14. 介面和抽象類有什麼區別?

  • 實現:抽象類的子類使用 extends 來繼承;介面必須使用 implements 來實現介面。
  • 構造函數:抽象類可以有構造函數;介面不能有。
  • main 方法:抽象類可以有 main 方法,並且我們能運行它;介面不能有 main 方法。
  • 實現數量:類可以實現很多個介面;但是只能繼承一個抽象類。
  • 訪問修飾符:介面中的方法預設使用 public 修飾;抽象類中的方法可以是任意訪問修飾符。

15. java 中 IO 流分為幾種?

按功能來分:輸入流(input)、輸出流(output)。

按類型來分:位元組流和字元流。

位元組流和字元流的區別是:位元組流按 8 位傳輸以位元組為單位輸入輸出數據,字元流按 16 位傳輸以字元為單位輸入輸出數據。

16. BIO、NIO、AIO 有什麼區別?

  • BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,併發處理能力低。
  • NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和伺服器端通過 Channel(通道)通訊,實現了多路復用。
  • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了非同步非堵塞 IO ,非同步 IO 的操作基於事件和回調機制。

17. Files的常用方法都有哪些?

  • Files.exists():檢測文件路徑是否存在。
  • Files.createFile():創建文件。
  • Files.createDirectory():創建文件夾。
  • Files.delete():刪除一個文件或目錄。
  • Files.copy():複製文件。
  • Files.move():移動文件。
  • Files.size():查看文件個數。
  • Files.read():讀取文件。
  • Files.write():寫入文件。

二、容器

18. java 容器都有哪些?

常用容器的圖錄:

19. Collection 和 Collections 有什麼區別?

  • java.util.Collection 是一個集合介面(集合類的一個頂級介面)。它提供了對集合對象進行基本操作的通用介面方法。Collection介面在Java 類庫中有很多具體的實現。Collection介面的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承介面有List與Set。
  • Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜索以及線程安全等各種操作。

20. List、Set、Map 之間的區別是什麼?

21. HashMap 和 Hashtable 有什麼區別?

  • hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
  • hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
  • hashMap允許空鍵值,而hashTable不允許。

22. 如何決定使用 HashMap 還是 TreeMap?

對於在Map中插入、刪除和定位元素這類操作,HashMap是最好的選擇。然而,假如你需要對一個有序的key集合進行遍歷,TreeMap是更好的選擇。基於你的collection的大小,也許向HashMap中添加元素會更快,將map換為TreeMap進行有序key的遍歷。

23. 說一下 HashMap 的實現原理?

HashMap概述: HashMap是基於哈希表的Map介面的非同步實現。此實現提供所有可選的映射操作,並允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。 

HashMap的數據結構: 在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。

當我們往Hashmap中put元素時,首先根據key的hashcode重新計算hash值,根絕hash值得到這個元素在數組中的位置(下標),如果該數組在該位置上已經存放了其他元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放入鏈尾.如果數組中該位置沒有元素,就直接將該元素放到數組的該位置上。

需要註意Jdk 1.8中對HashMap的實現做了優化,當鏈表中的節點數據超過八個之後,該鏈表會轉為紅黑樹來提高查詢效率,從原來的O(n)到O(logn)

24. 說一下 HashSet 的實現原理?

  • HashSet底層由HashMap實現
  • HashSet的值存放於HashMap的key上
  • HashMap的value統一為PRESENT

25. ArrayList 和 LinkedList 的區別是什麼?

最明顯的區別是 ArrrayList底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構是雙向迴圈鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間複雜度是 O(1),而 LinkedList 是 O(n)。

26. 如何實現數組和 List 之間的轉換?

  • List轉換成為數組:調用ArrayList的toArray方法。
  • 數組轉換成為List:調用Arrays的asList方法。

27. ArrayList 和 Vector 的區別是什麼?

  • Vector是同步的,而ArrayList不是。然而,如果你尋求在迭代的時候對列表進行改變,你應該使用CopyOnWriteArrayList。 
  • ArrayList比Vector快,它因為有同步,不會過載。 
  • ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表。

28. Array 和 ArrayList 有何區別?

  • Array可以容納基本類型和對象,而ArrayList只能容納對象。 
  • Array是指定大小的,而ArrayList大小是固定的。 
  • Array沒有提供ArrayList那麼多功能,比如addAll、removeAll和iterator等。

29. 在 Queue 中 poll()和 remove()有什麼區別?

poll() 和 remove() 都是從隊列中取出一個元素,但是 poll() 在獲取元素失敗的時候會返回空,但是 remove() 失敗的時候會拋出異常。

30. 哪些集合類是線程安全的?

  • vector:就比arraylist多了個同步化機制(線程安全),因為效率較低,現在已經不太建議使用。在web應用中,特別是前臺頁面,往往效率(頁面響應速度)是優先考慮的。
  • statck:堆棧類,先進後出。
  • hashtable:就比hashmap多了個線程安全。
  • enumeration:枚舉,相當於迭代器。

31. 迭代器 Iterator 是什麼?

迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要瞭解該序列的底層結構。迭代器通常被稱為“輕量級”對象,因為創建它的代價小。

32. Iterator 怎麼使用?有什麼特點?

Java中的Iterator功能比較簡單,並且只能單向移動:

(1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。註意:iterator()方法是java.lang.Iterable介面,被Collection繼承。

(2) 使用next()獲得序列中的下一個元素。

(3) 使用hasNext()檢查序列中是否還有元素。

(4) 使用remove()將迭代器新返回的元素刪除。

Iterator是Java迭代器最簡單的實現,為List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。

33. Iterator 和 ListIterator 有什麼區別?

  • Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。 
  • Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以後向。 
  • ListIterator實現了Iterator介面,並包含其他的功能,比如:增加元素,替換元素,獲取前一個和後一個元素的索引,等等。

 三、多線程

35. 並行和併發有什麼區別?

  • 並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生。
  • 並行是在不同實體上的多個事件,併發是在同一實體上的多個事件。
  • 在一臺處理器上“同時”處理多個任務,在多台處理器上同時處理多個任務。如hadoop分散式集群。

所以併發編程的目標是充分的利用處理器的每一個核,以達到最高的處理性能。

36. 線程和進程的區別?

簡而言之,進程是程式運行和資源分配的基本單位,一個程式至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的記憶體單元,而多個線程共用記憶體資源,減少切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程式更小的能獨立運行的基本單位。同一進程中的多個線程之間可以併發執行。

37. 守護線程是什麼?

守護線程(即daemon thread),是個服務線程,準確地來說就是服務其他的線程。

38. 創建線程有哪幾種方式?

①. 繼承Thread類創建線程類

  • 定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執行體。
  • 創建Thread子類的實例,即創建了線程對象。
  • 調用線程對象的start()方法來啟動該線程。

②. 通過Runnable介面創建線程類

  • 定義runnable介面的實現類,並重寫該介面的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
  • 創建 Runnable實現類的實例,並依此實例作為Thread的target來創建Thread對象,該Thread對象才是真正的線程對象。
  • 調用線程對象的start()方法來啟動該線程。

③. 通過Callable和Future創建線程

  • 創建Callable介面的實現類,並實現call()方法,該call()方法將作為線程執行體,並且有返回值。
  • 創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
  • 使用FutureTask對象作為Thread對象的target創建並啟動新線程。
  • 調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值。

39. 說一下 runnable 和 callable 有什麼區別?

有點深的問題了,也看出一個Java程式員學習知識的廣度。

  • Runnable介面中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;
  • Callable介面中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取非同步執行的結果。

40. 線程有哪些狀態?

線程通常都有五種狀態,創建、就緒、運行、阻塞和死亡。

  • 創建狀態。在生成線程對象,並沒有調用該對象的start方法,這是線程處於創建狀態。
  • 就緒狀態。當調用了線程對象的start方法之後,該線程就進入了就緒狀態,但是此時線程調度程式還沒有把該線程設置為當前線程,此時處於就緒狀態。線上程運行之後,從等待或者睡眠中回來之後,也會處於就緒狀態。
  • 運行狀態。線程調度程式將處於就緒狀態的線程設置為當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
  • 阻塞狀態。線程正在運行的時候,被暫停,通常是為了等待某個時間的發生(比如說某項資源就緒)之後再繼續運行。sleep,suspend,wait等方法都可以導致線程阻塞。
  • 死亡狀態。如果一個線程的run方法執行結束或者調用stop方法後,該線程就會死亡。對於已經死亡的線程,無法再使用start方法令其進入就緒   

41. sleep() 和 wait() 有什麼區別?

sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其他線程,等到休眠時間結束後,線程進入就緒狀態和其他線程一起競爭cpu的執行時間。因為sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,但是對象的機鎖沒有被釋放,其他線程依然無法訪問這個對象。

wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。

42. notify()和 notifyAll()有什麼區別?

  • 如果線程調用了對象的 wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
  • 當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭。
  • 優先順序高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。

43. 線程的 run()和 start()有什麼區別?

每個線程都是通過某個特定Thread對象所對應的方法run()來完成其操作的,方法run()稱為線程體。通過調用Thread類的start()方法來啟動一個線程。

start()方法來啟動一個線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,可以直接繼續執行下麵的代碼; 這時此線程是處於就緒狀態, 並沒有運行。 然後通過此Thread類調用方法run()來完成其運行狀態, 這裡方法run()稱為線程體,它包含了要執行的這個線程的內容, Run方法運行結束, 此線程終止。然後CPU再調度其它線程。

run()方法是在本線程里的,只是線程里的一個函數,而不是多線程的。 如果直接調用run(),其實就相當於是調用了一個普通函數而已,直接待用run()方法必須等待run()方法執行完畢才能執行下麵的代碼,所以執行路徑還是只有一條,根本就沒有線程的特征,所以在多線程執行時要使用start()方法而不是run()方法。

44. 創建線程池有哪幾種方式?

①. newFixedThreadPool(int nThreads)

創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。

②. newCachedThreadPool()

創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閑線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。

③. newSingleThreadExecutor()

這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串列執行。

④. newScheduledThreadPool(int corePoolSize)

創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似於Timer。

45. 線程池都有哪些狀態?

線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。

線程池各個狀態切換框架圖:

46. 線程池中 submit()和 execute()方法有什麼區別?

  • 接收的參數不一樣
  • submit有返回值,而execute沒有
  • submit方便Exception處理

47. 在 java 程式中怎麼保證多線程的運行安全?

線程安全在三個方面體現:

  • 原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
  • 可見性:一個線程對主記憶體的修改可以及時地被其他線程看到,(synchronized,volatile);
  • 有序性:一個線程觀察其他線程中的指令執行順序,由於指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。

48. 多線程鎖的升級原理是什麼?

在Java中,鎖共有4種狀態,級別從低到高依次為:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨著競爭情況逐漸升級。鎖可以升級但不能降級。

鎖升級的圖示過程: 

49. 什麼是死鎖?

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。是操作系統層面的一個錯誤,是進程死鎖的簡稱,最早在 1965 年由 Dijkstra 在研究銀行家演算法時提出的,它是電腦操作系統乃至整個併發程式設計領域最難處理的問題之一。

50. 怎麼防止死鎖?

死鎖的四個必要條件:

  • 互斥條件:進程對所分配到的資源不允許其他進程進行訪問,若其他進程訪問該資源,只能等待,直至占有該資源的進程使用完成後釋放該資源
  • 請求和保持條件:進程獲得一定的資源之後,又對其他資源發出請求,但是該資源可能被其他進程占有,此事請求阻塞,但又對自己獲得的資源保持不放
  • 不可剝奪條件:是指進程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完後自己釋放
  • 環路等待條件:是指進程發生死鎖後,若幹進程之間形成一種頭尾相接的迴圈等待資源關係

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發生死鎖。

理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和 解除死鎖。

所以,在系統設計、進程調度等方面註意如何不讓這四個必要條件成立,如何確 定資源的合理分配演算法,避免進程永久占據系統資源。

此外,也要防止進程在處於等待狀態的情況下占用資源。因此,對資源的分配要給予合理的規劃。

51. ThreadLocal 是什麼?有哪些使用場景?

線程局部變數是局限於線程內部的變數,屬於線程自身所有,不在多個線程間共用。Java提供ThreadLocal類來支持線程局部變數,是一種實現線程安全的方式。但是在管理環境下(如 web 伺服器)使用線程局部變數的時候要特別小心,在這種情況下,工作線程的生命周期比任何應用變數的生命周期都要長。任何線程局部變數一旦在工作完成後沒有釋放,Java 應用就存在記憶體泄露的風險。

52.說一下 synchronized 底層實現原理?

synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共用變數的記憶體可見性。

Java中每一個對象都可以作為鎖,這是synchronized實現同步的基礎:

  • 普通同步方法,鎖是當前實例對象
  • 靜態同步方法,鎖是當前類的class對象
  • 同步方法塊,鎖是括弧裡面的對象

53. synchronized 和 volatile 的區別是什麼?

  • volatile本質是在告訴jvm當前變數在寄存器(工作記憶體)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變數,只有當前線程可以訪問該變數,其他線程被阻塞住。
  • volatile僅能使用在變數級別;synchronized則可以使用在變數、方法、和類級別的。
  • volatile僅能實現變數的修改可見性,不能保證原子性;而synchronized則可以保證變數的修改可見性和原子性。
  • volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
  • volatile標記的變數不會被編譯器優化;synchronized標記的變數可以被編譯器優化。

54. synchronized 和 Lock 有什麼區別?

  • 首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;
  • synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;
  • synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
  • 用synchronized關鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結束了;
  • synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可);
  • Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。

55. synchronized 和 ReentrantLock 區別是什麼?

synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變數,ReentrantLock比synchronized的擴展性體現在幾點上: 

  • ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖 
  • ReentrantLock可以獲取各種鎖的信息
  • ReentrantLock可以靈活地實現多路通知 

另外,二者的鎖機制其實也是不一樣的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word。

56. 說一下 atomic 的原理?

Atomic包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變數進行操作時,具有排他性,即當多個線程同時對該變數的值進行更新時,僅有一個線程能成功,而未成功的線程可以向自旋鎖一樣,繼續嘗試,一直等到執行成功。

Atomic系列的類中的核心方法都會調用unsafe類中的幾個本地方法。我們需要先知道一個東西就是Unsafe類,全名為:sun.misc.Unsafe,這個類包含了大量的對C代碼的操作,包括很多直接記憶體分配以及原子操作的調用,而它之所以標記為非安全的,是告訴你這個裡面大量的方法調用都會存在安全隱患,需要小心使用,否則會導致嚴重的後果,例如在通過unsafe分配記憶體的時候,如果自己指定某些區域可能會導致一些類似C++一樣的指針越界到其他進程的問題。


四、反射

57. 什麼是反射?

反射主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力

Java反射:

在Java運行時環境中,對於任意一個類,能否知道這個類有哪些屬性和方法?對於任意一個對象,能否調用它的任意一個方法

Java反射機制主要提供了以下功能:

  • 在運行時判斷任意一個對象所屬的類。
  • 在運行時構造任意一個類的對象。
  • 在運行時判斷任意一個類所具有的成員變數和方法。
  • 在運行時調用任意一個對象的方法。 

58. 什麼是 java 序列化?什麼情況下需要序列化?

簡單說就是為了保存在記憶體中的各種對象的狀態(也就是實例變數,不是方法),並且可以把保存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保存object states,但是Java給你提供一種應該比你自己好的保存對象狀態的機制,那就是序列化。

什麼情況下需要序列化:

a)當你想把的記憶體中的對象狀態保存到一個文件中或者資料庫中時候;
b)當你想用套接字在網路上傳送對象的時候;
c)當你想通過RMI傳輸對象的時候;

59. 動態代理是什麼?有哪些應用?

動態代理:

當想要給實現了某個介面的類中的方法,加一些額外的處理。比如說加日誌,加事務等。可以給這個類創建一個代理,故名思議就是創建一個新的類,這個類不僅包含原來類方法的功能,而且還在原來的基礎上添加了額外處理的新類。這個代理類並不是定義好的,是動態生成的。具有解耦意義,靈活,擴展性強。

動態代理的應用:

  • Spring的AOP
  • 加事務
  • 加許可權
  • 加日誌

60. 怎麼實現動態代理?

首先必須定義一個介面,還要有一個InvocationHandler(將實現介面的類的對象傳遞給它)處理類。再有一個工具類Proxy(習慣性將其稱為代理類,因為調用他的newInstance()可以產生代理對象,其實他只是一個產生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進位碼,利用載入器載入,並將其實例化產生代理對象,最後返回。


五、對象拷貝

61. 為什麼要使用克隆?

想對一個對象進行處理,又想保留原有的數據進行接下來的操作,就需要克隆了,Java語言中克隆針對的是類的實例。

62. 如何實現對象克隆?

有兩種方式:

1). 實現Cloneable介面並重寫Object類中的clone()方法;

2). 實現Serializable介面,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆,代碼如下:


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MyUtil {

    private MyUtil() {
        throw new AssertionError();
    }

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();

        // 說明:調用ByteArrayInputStream或ByteArrayOutputStream對象的close方法沒有任何意義
        // 這兩個基於記憶體的流只要垃圾回收器清理對象就能夠釋放資源,這一點不同於對外部資源(如文件流)的釋放
    }
}

下麵是測試代碼:


import java.io.Serializable;

/**
 * 人類
 * @author nnngu
 *
 */
class Person implements Serializable {
    private static final long serialVersionUID = -9102017020286042305L;

    private String name;    // 姓名
    private int age;        // 年齡
    private Car car;        // 座駕

    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }

    public String getName() {
        return name;
    }

    

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • verilog語言簡介 verilog語言是一種語法類似於c的語言,但是與c語言也有不同之處,比如: 1.verilog語言是並行的,每個always塊都是同時執行,而c語言是順序執行的 2.verilog又被稱作硬體描述語言,在用verilog語言編程的時候,不如說是在用verilog描述一段電路 ...
  • [TOC] 靜態文件 預設情況下所有的html文件都是放在templates文件夾內 什麼是靜態文件 網站所使用的提前寫的css、js 第三方的前端模塊、圖片都叫做靜態資源 預設情況下網站使用的靜態資源全部會放到static文件夾下 通常情況下 在static文件夾內部還會再建其他文件夾 這是為了更 ...
  • 目前主流的三種web服務交互方案: REST (Representational State Transfer) 表徵性狀態轉移 SOAP (Simple Object Access Protocol)簡單的對象訪問協議 XML RPC (XML Remote Procedure Call)基於XM ...
  • 一、背景 書接手寫MQ框架(二)-服務端實現 ,前面介紹了服務端的實現。但是具體使用框架過程中,用戶肯定是以客戶端的形式跟服務端打交道的。客戶端的好壞直接影響了框架使用的便利性。 雖然框架目前是通過web的形式提供功能的,但是某的目標其實是通過socket實現,所以不僅需要有客戶端,還要包裝一下,讓 ...
  • 一、起航 本著從無到有,從有到優的原則,所以計劃先通過web實現功能,然後再優化改寫為socket的形式。 1、關於技術選型 web框架使用了之前寫的gmvc框架(手寫MVC框架(一)-再出發),消息存儲採用存在資料庫的方式,使用的框架也是前段時間寫的gdao(手寫DAO框架(一)-從“1”開始 ) ...
  • golang中連接mysql資料庫,需要使用一個第三方類庫github.com/go-sql-driver/mysql,在這個類庫中就實現了mysql的連接池,並且只需要設置兩個參數就可以實現 一般連接mysql首先需要調用sql.Open函數,但是此時並沒有真正的去連接mysql,而是只創建了一個 ...
  • 1.註釋 合理的代碼註釋應該占源代碼的 1/3 左右,Python 語言允許在任何地方插入空字元或註釋,但不能插入到標識符和字元串中間。 在 Python 中,通常包括 3 種類型的註釋,分別是單行註釋、多行註釋和中文編碼聲明註釋: 單行註釋: Python 中使用井號( # )作為單行註釋的符號, ...
  • 我的專業與互聯網沒有太大關係,接觸博客還是工作以後的事情。隨著工作的經驗增加,總想將自己的所思所得記錄下來,畢竟,好記性不如爛筆頭。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...