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

歡迎關注文章系列,一塊兒學習
《提高能力,漲薪可待篇》
《面試知識,工做可待篇》
《實戰演練,拒絕996篇》
也歡迎關注微信公衆號【Ccww筆記】,原創技術文章第一時間推出
若是此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!java

《面試知識,工做可待篇》-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 的編譯與解釋並存的特色。bash

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 面向對象和麪向過程的區別?

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

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

2.3.1 面向過程

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

2.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筆記】,原創技術文章第一時間推出
若是此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!

相關文章
相關標籤/搜索