《面試寶典》:檢驗是否爲合格的初中級程序員的面試知識點,你都知道了嗎?查漏補缺

歡迎關注文章系列,一起學習
《提升能力,漲薪可待篇》
《面試知識,工作可待篇》
《實戰演練,拒絕996篇》
也歡迎關注公 衆 號【Ccww技術博客】,原創技術文章第一時間推出
如果此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!

《面試知識,工作可待篇》-Java筆試面試基礎知識大全

前言

是不是感覺找工作面試是那麼難呢?

在找工作面試應在學習的基礎進行總結面試知識點,工作也指日可待,歡迎一起學習【面試知識,工作可待】系列
《面試知識,工作可待篇》

1. Java環境

1. 1 Java 和 C++ 的區別?

  • 都是面向對象的語言,都支持封裝、繼承和多態。
  • Java 不提供指針來直接訪問內存,程序內存更加安全。
  • Java 的類是單繼承的,C++ 支持多重繼承;雖然 Java 的類不可以多繼承,但是接口可以多繼承。

【重要】Java 有自動內存管理機制,不需要程序員手動釋放無用內存。

1.2 JDK、JRE、JVM

1.2.1 JDK

JDK 即爲 Java 開發工具包,包含編寫 Java 程序所必須的編譯、運行等開發工具以及 JRE。開發工具如:

  • 用於編譯 Java 程序的 javac 命令。
  • 用於啓動 JVM 運行 Java 程序的 Java 命令。
  • 用於生成文檔的 Javadoc 命令。
  • 用於打包的 jar 命令等等。

1.2…2 JRE

JRE 即爲 Java 運行環境,提供了運行 Java 應用程序所必須的軟件環境,包含有 Java 虛擬機(JVM)和豐富的系統類庫。系統類庫即爲 Java 提前封裝好的功能類,只需拿來直接使用即可,可以大大的提高開發效率。

1.2…3 JVM

JVM 即爲 Java 虛擬機,提供了字節碼文件(.class)的運行環境支持。

1.2…4 三者關係

  • JDK > JRE > JVM

1.2 爲什麼 Java 被稱作是“平臺無關的編程語言”?

  • Java 虛擬機是一個可以執行 Java 字節碼的虛擬機進程。
  • Java 源文件( .java )被編譯成能被 Java 虛擬機執行的字節碼文件( .class )。
  • Java 被設計成允許應用程序可以運行在任意的平臺,而不需要程序員爲每一個平臺單獨重寫或者是重新編譯。Java 虛擬機讓這個變爲可能,因爲它知道底層硬件平臺的指令長度和其他特性。

1.3 什麼是字節碼?

這個問題,面試官可以衍生提問,Java 是編譯執行的語言,還是解釋執行的語言。
Java 中引入了虛擬機的概念,即在機器和編譯程序之間加入了一層抽象的虛擬的機器。這臺虛擬的機器在任何平臺上都提供給編譯程序一個的共同的接口。

編譯程序只需要面向虛擬機,生成虛擬機能夠理解的代碼,然後由解釋器來將虛擬機代碼轉換爲特定系統的機器碼執行。在 Java 中,這種供虛擬機理解的代碼叫做字節碼(即擴展名爲 .class 的文件),它不面向任何特定的處理器,只面向虛擬機。

每一種平臺的解釋器是不同的,但是實現的虛擬機是相同的。Java 源程序經過編譯器編譯後變成字節碼,字節碼由虛擬機解釋執行,虛擬機將每一條要執行的字節碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,然後在特定的機器上運行。這也就是解釋了 Java 的編譯與解釋並存的特點。

1.4 Java 源代碼

=> 編譯器 => JVM 可執行的 Java 字節碼(即虛擬指令)=> JVM => JVM 中解釋器 => 機器可執行的二進制機器碼 => 程序運行

1.5 採用字節碼的好處?

Java 語言通過字節碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。所以 Java 程序運行時比較高效,而且,由於字節碼並不專對一種特定的機器,因此,Java程序無須重新編譯便可在多種不同的計算機上運行。

解釋型語言:解釋型語言,是在運行的時候將程序翻譯成機器語言。解釋型語言的程序不需要在運行前編譯,在運行程序的時候才翻譯,專門的解釋器負責在每個語句執行的時候解釋程序代碼。這樣解釋型語言每執行一次就要翻譯一次,效率比較低
例如:Python、PHP 。

2. 面向對象和麪向過程

2.1 什麼是面向對象?

面向對象是一種思想,萬事萬物抽象成一個對象,這裏只討論面向對象編程(OOP),Java 是一個支持併發、基於類和麪向對象的計算機編程語言。

2.1.1 類class

類是抽象的概念,是萬事萬物的抽象,是一類事物的共同特徵的集合。
用計算機語言來描述,是屬性方法的集合。

2.1.2 對象instance、object

對象是類的具象,是一個實體。
對於我們每個人這個個體,都是抽象概念人 類 的不同的 實體 。

面向對象軟件開發具有以下優點:

  • 代碼開發模塊化,更易維護和修改。
  • 代碼複用性強。
  • 增強代碼的可靠性和靈活性。
  • 增加代碼的可讀性。

2.2 面向對象的特徵

2.1 封裝

封裝,給對象提供了隱藏內部特性和行爲的能力。對象提供一些能被其他對象訪問的方法來改變它內部的數據。

在 Java 當中,有 4 種修飾符: default、public、private 和 protected 。每一種修飾符給其他的位於同一個包或者不同包下面對象賦予了不同的訪問權限,權限如下:

訪問權限 子類 其他包
public
protect ×
default × ×
private × × ×

封裝好處:

  • 通過隱藏對象的屬性來保護對象內部的狀態。
  • 提高了代碼的可用性和可維護性,因爲對象的行爲可以被單獨的改變或者是擴展。
  • 禁止對象之間的不良交互提高模塊化。

2.2 繼承

繼承,使對象基於基類字段和方法,新增自定義的的方法和屬性。繼承提供了代碼的重用行,也可以在不修改類的情況下給現存的類添加新特性。

繼承屬性:

  • 子類擁有父類非 private 的屬性和方法。
  • 子類可以擁有自己屬性和方法,即子類可以對父類進行擴展。
  • 子類可以用自己的方式實現父類的方法

2.3 多態

多態,程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量到底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。

Java中有兩種形式可以實現多態:

  • 繼承(多個子類對同一方法的重寫)
  • 接口(實現接口並覆蓋接口中同一方法)

2.3 面向對象和麪向過程的區別?

面向過程:就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了,

面向對象:是把構成問題事務分解成各個對象,建立對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。

3.1 面向過程

  • 優點:性能比面向對象高,因爲類調用時需要實例化,開銷比較大,比較消耗資源。比如,單片機、嵌入式開發、Linux/Unix 等一般採用面向過程開發,性能是最重要的因素。
  • 缺點:沒有面向對象易維護、易複用、易擴展。

3.2 面向對象

  • 優點:易維護、易複用、易擴展,由於面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護。
  • 缺點:性能比面向過程低。

3. Java數據類型(基本數據類型和引用類型)

3. 1 基本數據類型如下:

  • 整數值型:byte、short、int、long
  • 字符型:char
  • 浮點類型:float、double
  • 布爾型:boolean

img

整數型:默認 int 型,小數默認是 double 型。Float 和 Long 類型的必須加後綴。

比如:float f = 100f 。

引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中。

3.2 引用類型

引用類型指向一個對象,不是原始值,指向對象的變量是引用變量

在java裏面除去基本數據類型的其他類型都是引用類型,自己定義的class類都是引用類型,可以像基本類型一樣使用。

引用類型常見的有:String、StringBuffer、ArrayList、HashSet、HashMap等

特別注意,String 是引用類型不是基本類型。

3.3 引用類型簡介

引用類型 對象是否可引用 回收時間 使用場景
強引用 可以 從不回收 普遍對象的狀態
軟引用 可以 內存不足時 內存敏感的高速緩存
弱引用 可以 下一次GC 對象緩存
虛引用 不可以 下一次GC 一般用於追蹤垃圾收集器的回收動作

3. 4 什麼是值傳遞和引用傳遞?

  • 值傳遞,是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量。

  • 引用傳遞,一般是對於對象型變量而言的,傳遞的是該對象地址的一個副本,並不是原對象本身。

    一般認爲,Java 內的傳遞都是值傳遞,Java 中實例對象的傳遞是引用傳遞。

3. 5 char 型變量中能不能存貯一箇中文漢字?爲什麼?

  • C 語言中,char 類型佔 1 個字節,而漢字佔 2 個字節,所以不能存儲。
  • Java 語言中,char 類型佔 2 個字節,而且 Java 默認採用 Unicode 編碼,一個 Unicode 碼是 16 位,所以一個 Unicode 碼佔兩個字節,Java 中無論漢字還是英文字母,都是用 Unicode 編碼來表示的。所以,在 Java 中,char 類型變量可以存儲一箇中文漢字。

3.6 equals 與 == 的區別?

  • 值類型(int,char,long,boolean 等)都是用 == 判斷相等性。
  • 對象引用的話
    • == 判斷引用所指的對象是否是同一個。
    • equals 方法,是 Object 的成員函數,有些類會覆蓋(override) 這個方法,用於判斷對象的等價性。

例如 String 類,兩個引用所指向的 String 都是 “abc” ,但可能出現他們實際對應的對象並不是同一個(和 JVM 實現方式有關),因此用 == 判斷他們可能不相等,但用 equals 方法判斷一定是相等的。

4. Java類Class

類是對事物的抽象,抽象類是對類的抽象,接口是對抽象類的抽象。

4.1 Java 對象(Class)創建的方式?

  • 使用 new 關鍵字創建對象。
  • 使用 Class 類的 newInstance 方法(反射機制)。
  • 使用 Constructor 類的 newInstance 方法(反射機制)。
  • 使用 clone 方法創建對象。
  • 使用(反)序列化機制創建對象。

4.2 抽象類與接口

4.2.1 抽象類

從面向對象的角度來講,我們知道所有的對象都是通過類來描繪的,但是反過來卻不是這樣,並不是 所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就可以認爲是抽象類

抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變量、成員方法和構造方法的訪問方式和普通類一樣。由於抽象類不能實例化對象,所以抽象類必須被繼承,才能被使用。

4.2.2 接口

接口,在JAVA編程語言中是一個抽象類型,主要是抽象方法的集合,接口中的變量定義必須爲public static final類型。接口通常以interface來聲明。

4.2.3 抽象類與接口的對比

參數 抽象類 接口
默認的方法實現 它可以有默認的方法實現 接口完全是抽象的。它根本不存在方法的實現
實現 子類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。 子類使用關鍵字implements來實現接口。它需要提供接口中所有聲明的方法的實現
構造器 抽象類可以有構造器 接口不能有構造器
與正常Java類的區別 除了你不能實例化抽象類之外,它和普通Java類沒有任何區別 接口是完全不同的類型
訪問修飾符 抽象方法可以有publicprotecteddefault這些修飾符 接口方法默認修飾符是public。你不可以使用其它修飾符。
main方法 抽象方法可以有main方法並且我們可以運行它 接口沒有main方法,因此我們不能運行它。(java8以後接口可以有default和static方法,所以可以運行main方法)
多繼承 抽象方法可以繼承一個類和實現多個接口 接口只可以繼承一個或多個其它接口
速度 它比接口速度要快 接口是稍微有點慢的,因爲它需要時間去尋找在類中實現的方法。
添加新方法 如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼 如果你往接口中添加方法,那麼你必須改變實現該接口的類。

4.3 講講類的實例化順序

初始化順序如下:

->父類靜態變量
->父類靜態代碼塊
->子類靜態變量、
->子類靜態代碼塊
->父類非靜態變量(父類實例成員變量)
->父類構造函數
->子類非靜態變量(子類實例成員變量)
->子類構造函數

4.4 內部類

簡單的說,就是在一個類、接口或者方法的內部創建另一個類。這樣理解的話,創建內部類的方法就很明確了。當然,詳細的可以看看 《Java 內部類總結(吐血之作)》 文章。

4.4.1 內部類的作用是什麼?

  • 內部類可以很好的實現隱藏(一般的非內部類,是不允許有 private 與protected權限的,但內部類可以)

  • 內部類擁有外圍類的所有元素的訪問權限

  • 可是實現多重繼承

  • 可以避免修改接口而實現同一個類中兩種同名方法的調用。

4.5 Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現接口?

可以繼承其他類或實現其他接口,在 Java 集合的流式操作中,我們常常這麼幹。

4.6 內部類可以引用它的包含類(外部類)的成員嗎?有沒有什麼限制?

一個內部類對象可以訪問創建它的外部類對象的成員,包括私有成員。

如果你把靜態嵌套類當作內部類的一種特例,那在這種情況下不可以訪問外部類的普通成員變量,而只能訪問外部類中的靜態成員,例如:

class Outer {
    static int x;
    static class Inner {
		void test() {
			syso(x);
		}
    }
}

4.7 構造方法、構造方法重載

4.7.1 構造方法

當新對象被創建的時候,構造方法會被調用。每一個類都有構造方法。在程序員沒有給類提供構造方法的情況下,Java 編譯器會爲這個類創建一個默認的構造方法。

4.7.2 構造方法重載

Java 中構造方法重載和方法重載很相似。可以爲一個類創建多個構造方法。每一個構造方法必須有它自己唯一的參數列表。

4. 8 重載和重寫的區別?

4.8.1 重寫 override

  • 方法名、參數、返回值相同。
  • 子類方法不能縮小父類方法的訪問權限。
  • 子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
  • 存在於父類和子類之間。
  • 方法被定義爲 final 不能被重寫。

4.8.2 重載 overload

  • 參數類型、個數、順序至少有一個不相同。
  • 不能重載只有返回值不同的方法名。
  • 存在於父類和子類、同類中。

4.9 hashCode() 以及equals()

4.9.1 爲什麼需要子類實現這兩個方法?

父類的 equals ,一般情況下是無法滿足子類的 equals 的需求。

比如所有的對象都繼承 Object ,默認使用的是 Object 的 equals 方法,在比較兩個對象的時候,是看他們是否指向同一個地址。但是我們的需求是對象的某個屬性相同,就相等了,而默認的 equals 方法滿足不了當前的需求,所以我們要重寫 equals 方法。

如果重寫了 equals 方法,就必須重寫 hashCode 方法,否則就會降低 Map 等集合的索引速度。

4.9.2 說一說你對 java.lang.Object 對象中 hashCode 和 equals 方法的理解。在什麼場景下需要重新實現這兩個方法?

理解答案與4.8.1差不多,

equals 方法,用於比較對象的內容是否相等。

當覆蓋了 equals 方法時,比較對象是否相等將通過覆蓋後的 equals 方法進行比較(判斷對象的內容是否相等)。
hashCode 方法,大多在集合中用到。

將對象放入到集合中時,首先判斷要放入對象的 hashCode 值與集合中的任意一個元素的 hashCode 值是否相等,如果不相等直接將該對象放入集合中。
如果 hashCode 值相等,然後再通過 equals 方法判斷要放入對象與集合中的任意一個對象是否相等,如果 equals 判斷不相等,直接將該元素放入到集合中,否則不放入。

4.9.3 有沒有可能 2 個不相等的對象有相同的 hashCode?

可能會發生,這個被稱爲哈希碰撞。當然,相等的對象,即我們重寫了 equals 方法,一定也要重寫 hashCode 方法,否則將出現我們在 HashMap 中,相等的對象作爲 key ,將找不到對應的 value 。

4.9.4 equals 和 hashCode 的關係

  • equals 不相等,hashCode 可能相等。

  • equals 相等,請重寫 hashCode 方法,保證 hashCode 相等。

    一般來說,hashCode 方法的重寫,可以看看 《科普:爲什麼 String hashCode 方法選擇數字31作爲乘子》 方法。

5. 常用類

5.1 String、StringBuffer、StringBuilder

5.1.1 String、StringBuffer、StringBuilder 的區別?

可變性:

String 類中使用 final 關鍵字字符數組保存字符串,代碼:

private final char value[],

所以string對象是不可變的。

StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在 AbstractStringBuilder 中也是使用字符數組保存字符串 char[] value ,但是沒有用 final 關鍵字修飾,代碼:

char[]value

這兩種對象都是可變的。

線程安全性:

  • String中的對象是不可變的,也就可以理解爲常量,線程安全。
  • StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的。
  • StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的。

AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

性能:
每次對String 類型進行改變的時候,都會生成一個新的String對象,然後將指針指向新的String 對象。

StringBuffer每次都會對StringBuffer對象本身進行操作,而不是生成新的對象並改變對象引用。相同情況下使用StirngBuilder 相比使用StringBuffer 僅能獲得10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。

5.1.2對於三者使用的總結

  • 操作少量的數據用 = String
  • 單線程操作字符串緩衝區 下操作大量數據 = StringBuilder,甚至有時,我們爲了避免每個線程重複創建 StringBuilder 對象,會通過 ThreadLocal + StringBuilder 的方式,進行對 StringBuilder 的重用
  • 多線程操作字符串緩衝區 下操作大量數據 = StringBuffer

5.1.3 String s = new String(“xyz”) 會創建幾個對象?

首先,在 String 池內找,找到 “xyz” 字符串,不創建 “xyz” 對應的 String 對象,否則創建一個對象。
然後,遇到 new 關鍵字,在內存上創建 String 對象,並將其返回給 s ,又一個對象。
所以,總共是 1 個或者 2 個對象

5.1.4 StringTokenizer 是什麼?

StringTokenizer ,是一個用來分割字符串的工具類。

示例代碼如下:

StringTokenizer st = new StringTokenizer(”Hello World”);
while (st.hasMoreTokens()) {
    System.out.println(st.nextToken());
}

輸出如下:

Hello
World

5.2 什麼是自動拆裝箱?

自動裝箱和拆箱,就是基本類型和引用類型之間的轉換。

5.2.1 什麼要轉換?

如果你在 Java5 下進行過編程的話,你一定不會陌生這一點,你不能直接地向集合( Collection )中放入原始類型值,因爲集合只接收對象。

通常這種情況下你的做法是,將這些原始類型的值轉換成對象,然後將這些轉換的對象放入集合中。使用 Integer、Double、Boolean 等這些類,我們可以將原始類型值轉換成對應的對象,但是從某些程度可能使得代碼不是那麼簡潔精煉。

爲了讓代碼簡練,Java5 引入了具有在原始類型和對象類型自動轉換的裝箱和拆箱機制。
但是自動裝箱和拆箱並非完美,在使用時需要有一些注意事項,如果沒有搞明白自動裝箱和拆箱,可能會引起難以察覺的 Bug 。

5.3 int 和 Integer 有什麼區別?

  • int 是基本數據類型。

  • Integer 是其包裝類,注意是一個類。

    需要注意下 Integer 的緩存策略

5.2.3 理解Java Integer 的緩存策略

6. 關鍵字

6.1 final、finally、finalize

6.1.1 final

final ,是修飾符關鍵字。

  • Class 類:如果一個類被聲明爲 final ,意味着它不能再派生出新的子類,不能作爲父類被繼承。因此一個類不能既被聲明爲 abstract 的,又被聲明爲 final 的。

  • 變量或方法聲明爲 final ,可以保證它們在使用中不被改變。被聲明爲 final 的變量必須在聲明時給定初值,而在以後的引用中只能讀取,不可修改。被聲明爲 final 的方法也同樣只能使用,不能重寫。

    另外,在早期的 Java 實現版本中,會將 final 方法轉爲內嵌調用。但是如果方法過於龐大,可能看不到內嵌調用帶來的任何性能提升(現在的 Java 版本已經不需要使用 final 方法進行這些優化了)。類中所有的private 方法都隱式地指定爲 final 。

6.1.2 finally

在異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異常,那麼相匹配的 catch 子句就會執行,然後控制就會進入 finally 塊(如果有的話)。

在以下 4 種特殊情況下,finally塊不會被執行:

  • 在 finally 語句塊中發生了異常。

  • 在前面的代碼中用了 System.exit() 退出程序。

  • 程序所在的線程死亡。

  • 關閉 CPU 。

6.1.3 finalize

finalize ,是方法名。

Java 允許使用 #finalize() 方法,在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。

它是在 Object 類中定義的,因此所有的類都繼承了它。
子類覆蓋 finalize() 方法,以整理系統資源或者執行其他清理工作。

finalize() 方法,是在垃圾收集器刪除對象之前對這個對象調用的。
一般情況下,我們在業務中不會自己實現這個方法,更多是在一些框架中使用。

6.1.4 String 類能被繼承嗎,爲什麼?

不能,因爲 String 是 final 修飾。

6.2 static

6.2.1 static特點

  • static是一個修飾符,用於修飾成員。(成員變量,成員函數)static修飾的成員變量 稱之爲靜態變量或類變量。
  • static修飾的成員被所有的對象共享。
  • static優先於對象存在,因爲static的成員隨着類的加載就已經存在。
  • static修飾的成員多了一種調用方式,可以直接被類名所調用,(類名.靜態成員)。
  • static修飾的數據是共享數據,對象中的存儲的是特有的數據

6.2.2 是否可以在 static方法中訪問非 static 變量?

static 變量在 Java 中是屬於類的,它在所有的實例中的值是一樣的。當類被 Java 虛擬機載入的時候,會對 static 變量進行初始化。如果你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,因爲這些變量還沒有被創建出來,還沒有跟任何實例關聯上。

由於靜態方法可以不通過對象進行調用,因此在靜態方法裏,不能調用其他非靜態變量,也不可以訪問非靜態變量成員。

如果你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,因爲這些變量還沒有被創建出來,還沒有跟任何實例關聯上。

6.2.3 成員變量和靜態變量的區別:

  • 生命週期的不同:

    • 成員變量隨着對象的創建而存在隨着對象的回收而釋放。
    • 靜態變量隨着類的加載而存在隨着類的消失而消失。
  • 調用方式不同:

    • 成員變量只能被對象調用。
    • 靜態變量可以被對象調用,也可以用類名調用。(推薦用類名調用)
  • 別名不同:

    • 成員變量也稱爲實例變量。
    • 靜態變量稱爲類變量。
  • 數據存儲位置不同:

    • 成員變量數據存儲在堆內存的對象中,所以也叫對象的特有數據。

    • 靜態變量數據存儲在方法區(共享數據區)的靜態區,所以也叫對象的共享數據。

6.2.4 static 關鍵字修飾的加載順序

->父類靜態變量

​ ->父類靜態代碼塊

​ ->子類靜態變量

​ ->子類靜態代碼塊

​ ->父類普通變量

​ ->父類普通代碼塊

​ ->父類構造函數

​ ->子類普通變量

​ ->子類普通代碼塊

​ ->子類構造函數

6.3 transient 關鍵字

transient聲明一個實例變量,當對象存儲時,它的值不需要維持。換句話來說就是,用transient關鍵字標記的成員變量不參與序列化過程,

transient 只能修飾變量,不能修飾類和方法。

6.3.1 Java 序列話中,如果有些字段不想進行序列化怎麼辦?

對於不想進行序列化的變量,使用 transient 關鍵字修飾,

  • 當對象被序列化時,阻止實例中那些用此關鍵字修飾的的變量序列化。
  • 當對象被反序列化時,被 transient 修飾的變量值不會被持久化和恢復。

6.4 volatile關鍵詞

volatile 關鍵字用在多線程同步中,可保證讀取的可見性,JVM只是保證從主內存加載到線程工作內存的值是最新的讀取值,而非 cache 中

6.4.1 volatile關鍵字是否能保證線程安全?

不能 , 多個線程對 volatile 的寫操作,無法保證線程安全。例如假如線程 1,線程 2 在進行 read,load 操作中,發現主內存中 count 的值都是 5,那麼都會加載這個最新的值,在線程 1 堆 count 進行修改之後,會 write 到主內存中,主內存中的 count 變量就會變爲 6;線程 2 由於已經進行 read,load 操作,在進行運算之後,也會更新主內存 count 的變量值爲 6;導致兩個線程及時用 volatile 關鍵字修改之後,還是會存在併發的情況

7. Java IO

7.1 Java IO 相關的類

Java IO 相關的類,在 java.io 包下,具體操作分成面向字節(Byte)和麪向字符(Character)兩種方式。如下圖所示:

img

7.2 什麼是 Java 序列化?

序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。

可以對流化後的對象進行讀寫操作,也可將流化後的對象傳輸於網絡之間。
序列化是爲了解決在對對象流進行讀寫操作時所引發的問題。
反序列化的過程,則是和序列化相反的過程。

我們不能將序列化侷限在 Java 對象轉換成二進制數組,比如,將一個 Java 對象轉換成 JSON 字符串等,這也可以理解爲是序列化。

7.2.1如何實現 Java 序列化?

將需要被序列化的類,實現 Serializable 接口,該接口沒有需要實現的方法,implements Serializable 只是爲了標註該對象是可被序列化的。

  • 序列化

    • 首先使用一個輸出流(如:FileOutputStream)來構造一個 ObjectOutputStream(對象流)對象
    • 接着,使用 ObjectOutputStream 對象的 #writeObject(Object obj) 方法,就可以將參數爲 obj 的對象寫出(即保存其狀態)。
  • 反序列化

    • 要恢復的話則用輸入流。

7.3 如何實現對象克隆(淺克隆和深克隆)?

  • 實現 Cloneable 接口,並重寫 Object 類中的 #clone() 方法。可以實現淺克隆,也可以實現深克隆。
  • 實現 Serializable 接口,通過對象的序列化和反序列化實現克隆。可以實現真正的深克隆。

實際場景下,我們使用的克隆比較少,更多是對象之間的屬性克隆。例如說,將 DO 的屬性複製到 DTO 中,又或者將 DTO 的屬性複製到 VO 中。此時,我們一般使用 BeanUtils 工具類。

8.異常

8.1 異常機制的概述

​ 異常機制是指當程序出現錯誤後,程序如何處理。具體來說,異常機制提供了程序退出的安全通道。當出現錯誤後,程序執行的流程發生改變,程序的控制權轉移到異常處理器。

程序錯誤分爲三種:

  • 編譯錯誤:因爲程序沒有遵循語法規則,編譯程序能夠自己發現並且提示我們錯誤的原因和位置,這個也是大家在剛接觸編程語言最常遇到的問題。
  • 運行時錯誤:因爲程序在執行時,運行環境發現了不能執行的操作。
  • 邏輯錯誤:因爲程序沒有按照預期的邏輯順序執行。異常也就是指程序運行時發生錯誤,而異常處理就是對這些錯誤進行處理和控制。

8.2 Throwable

Throwable 類圖

img

Throwable有兩個重要的子類 :

  • Exception(異常)
  • Error(錯誤)

二者都是 Java 異常處理的重要子類,各自都包含大量子類

8.2.1 Exception(異常)和 Error(錯誤)

  • Error(錯誤),表示系統級的錯誤和程序不必處理的異常,是 Java 運行環境中的內部錯誤或者硬件問題。

    • 例如:內存資源不足等。
    • 對於這種錯誤,程序基本無能爲力,除了退出運行外別無選擇,它是由 Java 虛擬機拋出的
  • Exception(異常),表示需要捕捉或者需要程序進行處理的異常,它處理的是因爲程序設計的瑕疵而引起的問題或者在外的輸入等引起的一般性問題,是程序必須處理的。Exception 又分爲運行時異常,受檢查異常。

    • RuntimeException(運行時異常),表示無法讓程序恢復的異常,導致的原因通常是因爲執行了錯誤的操作,建議終止邏輯,因此,編譯器不檢查這些異常。
    • CheckedException(受檢查異常),是表示程序可以處理的異常,也即表示程序可以修復(由程序自己接受異常並且做出處理),所以稱之爲受檢查異常

8.3 error 和 exception 有什麼區別?

  • Error:Error類對象由 Java 虛擬機生成並拋出,大多數錯誤與代碼編寫者所執行的操作無關 。比如:

    • OutOfMemoryError
    • NoClassDefFoundError
    • LinkageError
  • Exception : 在Exception分支中有一個重要的子類RuntimeException(運行時異常),該類型的異常自動爲你所編寫的程序定義 異常,比如:

  • ArrayIndexOutOfBoundsException(數組下標越界)

  • NullPointerException(空指針異常)

  • ArithmeticException(算術異常)

  • MissingResourceException(丟失資源)

  • ClassNotFoundException(找不到類)

  • BufferOverflowException

  • ClassCastException

8.4 CheckedException 和 RuntimeException 有什麼區別?

  • RuntimeException運行異常:表示程序運行過程中可能出現的非正常狀態,運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。
  • CheckedException受檢異常:跟程序運行的上下文環境有關,即使程序設計無誤,仍然可能因使用的問題而引發。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,但是並不要求必須聲明拋出未被捕獲的運行時異常

Effective Java中對異常的使用給出了以下指導原則 :

不要將異常處理用於正常的控制流(設計良好的API不應該強迫它的調用者爲了正常的控制流而使用異常)
對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常
避免不必要的使用受檢異常(可以通過一些狀態檢測手段來避免異常的發生)
優先使用標準的異常
每個方法拋出的異常都要有文檔
保持異常的原子性
不要在catch中忽略掉捕獲到的異常

8.5 Throwable 類常用方法?

  • getMessage() 方法:返回異常發生時的詳細信息。
  • getCause() 方法:獲得導致當前 Throwable 異常的 Throwable 異常。
  • getStackTrace() 方法:獲得 Throwable 對象封裝的異常信息。
  • printStackTrace() 方法:在控制檯上打印。

8.6 throw 與 throws 的區別 ?

  • throw ,用於在程序中顯式地拋出一個異常。
  • throws ,用於指出在該方法中沒有處理的異常。每個方法必須顯式指明哪些異常沒有處理,以便該方法的調用者可以預防可能發生的異常。最後,多個異常用逗號分隔。

8.7 異常處理中 finally 語句塊的重要性?

不管程序是否發生了異常, finally 語句塊都會被執行,甚至當沒有catch 聲明但拋出了一個異常時, finally 語句塊也會被執行。

finally 語句塊通常用於釋放資源, 如 I/O 緩衝區, 數據庫連接等等。

8.8 UnsupportedOperationException 是什麼?

UnsupportedOperationException ,是用於表明操作不支持的異常。

在 JDK 類中已被大量運用,在集合框架java.util.Collections.UnmodifiableCollection 將會在所有 add 和 remove 操作中拋出這個異常。

9.反射

9.1 反射簡介

當程序運行時,允許改變程序結構或變量類型,這種語言稱爲動態語言。我們認爲 Java 並不是動態語言,但是它卻又一個非常突出的動態相關的機制

9.2 反射的用途及實現?

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

  • 在運行時構造一個類的對象。
  • 判斷一個類所具有的成員變量和方法。
  • 調用一個對象的方法。
  • 生成動態代理。
反射的主要用途, 開發各種通用框架
  • Spring 框架的 IoC 基於反射創建對象和設置依賴屬性。
  • Spring MVC 的請求調用對應方法,也是通過反射。
  • JDBC 的 Class#forName(String className) 方法,也是使用反射。

9.3 反射中,Class.forName 和 ClassLoader 區別?

  • Class#forName(…) 方法,除了將類的 .class 文件加載到JVM 中之外,還會對類進行解釋,執行類中的 static 塊。
  • ClassLoader 只幹一件事情,就是將 .class 文件加載到 JVM 中,不會執行 static 中的內容,只有在 newInstance 纔會去執行 static 塊。
  • Class#forName(name, initialize, loader) 方法,帶參函數也可控制是否加載 static 塊,並且只有調用了newInstance 方法採用調用構造函數,創建類的對象。

9.4 什麼時候用斷言(assert)?

斷言,在軟件開發中是一種常用的調試方式,很多開發語言中都支持這種機制。

一般來說,斷言用於保證程序最基本、關鍵的正確性。斷言檢查通常在開發和測試時開啓。爲了保證程序的執行效率,在軟件發佈後斷言檢查通常是關閉的。
斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲true;如果表達式的值爲 false ,那麼系統會報告一個AssertionError 錯誤。斷言的使用如下面的代碼所示:

assert(a > 0); // throws an AssertionError if a <= 0

斷言可以有兩種形式:

assert Expression1; 。
assert Expression1 : Expression2;

Expression1 應該總是產生一個布爾值。
Expression2 可以是得出一個值的任意表達式;這個值用於生成顯示更多調試信息的字符串消息。

要在運行時啓用斷言,可以在啓動 JVM 時使用 -enableassertions 或者 -ea 標記。要在運行時選擇禁用斷言,可以在啓動 JVM 時使用 -da 或者 -disableassertions 標記。要在系統類中啓用或禁用斷言,可使用 -esa 或 -dsa 標記。還可以在包的基礎上啓用或者禁用斷言。

也歡迎關注公 衆 號【Ccww技術博客】,原創技術文章第一時間推出
如果此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章