【面試寶典】:檢驗(yàn)是否為合格的初中級程序員的面試知識點(diǎn),你都知道了嗎?查漏補(bǔ)缺

歡迎關(guān)注文章系列,一起學(xué)習(xí)
《提升能力,漲薪可待篇》
《面試知識,工作可待篇》
《實(shí)戰(zhàn)演練,拒絕996篇》
如果此文對你有幫助、喜歡的話,那就點(diǎn)個(gè)贊唄,點(diǎn)個(gè)關(guān)注唄!

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

前言

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

在找工作面試應(yīng)在學(xué)習(xí)的基礎(chǔ)進(jìn)行總結(jié)面試知識點(diǎn),工作也指日可待,歡迎一起學(xué)習(xí)【面試知識,工作可待】系列
《面試知識,工作可待篇》

1. Java環(huán)境

1. 1 Java 和 C++ 的區(qū)別?

  • 都是面向?qū)ο蟮恼Z言,都支持封裝、繼承和多態(tài)。
  • Java 不提供指針來直接訪問內(nèi)存,程序內(nèi)存更加安全。
  • Java 的類是單繼承的,C++ 支持多重繼承;雖然 Java 的類不可以多繼承,但是接口可以多繼承。

【重要】Java 有自動內(nèi)存管理機(jī)制,不需要程序員手動釋放無用內(nèi)存。

1.2 JDK、JRE、JVM

1.2.1 JDK

JDK 即為 Java 開發(fā)工具包,包含編寫 Java 程序所必須的編譯、運(yùn)行等開發(fā)工具以及 JRE。開發(fā)工具如:

  • 用于編譯 Java 程序的 javac 命令。
  • 用于啟動 JVM 運(yùn)行 Java 程序的 Java 命令。
  • 用于生成文檔的 Javadoc 命令。
  • 用于打包的 jar 命令等等。

1.2..2 JRE

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

1.2..3 JVM

JVM 即為 Java 虛擬機(jī),提供了字節(jié)碼文件(.class)的運(yùn)行環(huán)境支持。

1.2..4 三者關(guān)系

  • JDK > JRE > JVM

1.2 為什么 Java 被稱作是“平臺無關(guān)的編程語言”?

  • Java 虛擬機(jī)是一個(gè)可以執(zhí)行 Java 字節(jié)碼的虛擬機(jī)進(jìn)程。
  • Java 源文件( .java )被編譯成能被 Java 虛擬機(jī)執(zhí)行的字節(jié)碼文件( .class )。
  • Java 被設(shè)計(jì)成允許應(yīng)用程序可以運(yùn)行在任意的平臺,而不需要程序員為每一個(gè)平臺單獨(dú)重寫或者是重新編譯。Java 虛擬機(jī)讓這個(gè)變?yōu)榭赡?,因?yàn)樗赖讓佑布脚_的指令長度和其他特性。

1.3 什么是字節(jié)碼?

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

編譯程序只需要面向虛擬機(jī),生成虛擬機(jī)能夠理解的代碼,然后由解釋器來將虛擬機(jī)代碼轉(zhuǎn)換為特定系統(tǒng)的機(jī)器碼執(zhí)行。在 Java 中,這種供虛擬機(jī)理解的代碼叫做字節(jié)碼(即擴(kuò)展名為 .class 的文件),它不面向任何特定的處理器,只面向虛擬機(jī)。

每一種平臺的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的。Java 源程序經(jīng)過編譯器編譯后變成字節(jié)碼,字節(jié)碼由虛擬機(jī)解釋執(zhí)行,虛擬機(jī)將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機(jī)器上的機(jī)器碼,然后在特定的機(jī)器上運(yùn)行。這也就是解釋了 Java 的編譯與解釋并存的特點(diǎn)。

1.4 Java 源代碼

=> 編譯器 => JVM 可執(zhí)行的 Java 字節(jié)碼(即虛擬指令)=> JVM => JVM 中解釋器 => 機(jī)器可執(zhí)行的二進(jìn)制機(jī)器碼 => 程序運(yùn)行

1.5 采用字節(jié)碼的好處?

Java 語言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語言執(zhí)行效率低的問題,同時(shí)又保留了解釋型語言可移植的特點(diǎn)。所以 Java 程序運(yùn)行時(shí)比較高效,而且,由于字節(jié)碼并不專對一種特定的機(jī)器,因此,Java程序無須重新編譯便可在多種不同的計(jì)算機(jī)上運(yùn)行。

解釋型語言:解釋型語言,是在運(yùn)行的時(shí)候?qū)⒊绦蚍g成機(jī)器語言。解釋型語言的程序不需要在運(yùn)行前編譯,在運(yùn)行程序的時(shí)候才翻譯,專門的解釋器負(fù)責(zé)在每個(gè)語句執(zhí)行的時(shí)候解釋程序代碼。這樣解釋型語言每執(zhí)行一次就要翻譯一次,效率比較低
例如:Python、PHP 。

2. 面向?qū)ο蠛兔嫦蜻^程

2.1 什么是面向?qū)ο螅?/h3>

面向?qū)ο笫且环N思想,萬事萬物抽象成一個(gè)對象,這里只討論面向?qū)ο缶幊蹋∣OP),Java 是一個(gè)支持并發(fā)、基于類和面向?qū)ο蟮挠?jì)算機(jī)編程語言。

2.1.1 類class

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

2.1.2 對象instance、object

對象是類的具象,是一個(gè)實(shí)體。
對于我們每個(gè)人這個(gè)個(gè)體,都是抽象概念人 類 的不同的 實(shí)體 。

面向?qū)ο筌浖_發(fā)具有以下優(yōu)點(diǎn):

  • 代碼開發(fā)模塊化,更易維護(hù)和修改。
  • 代碼復(fù)用性強(qiáng)。
  • 增強(qiáng)代碼的可靠性和靈活性。
  • 增加代碼的可讀性。

2.2 面向?qū)ο蟮奶卣?/h3>

2.1 封裝

封裝,給對象提供了隱藏內(nèi)部特性和行為的能力。對象提供一些能被其他對象訪問的方法來改變它內(nèi)部的數(shù)據(jù)。

在 Java 當(dāng)中,有 4 種修飾符: default、public、private 和 protected 。每一種修飾符給其他的位于同一個(gè)包或者不同包下面對象賦予了不同的訪問權(quán)限,權(quán)限如下:

訪問權(quán)限 子類 其他包
public
protect ×
default × ×
private × × ×

封裝好處:

  • 通過隱藏對象的屬性來保護(hù)對象內(nèi)部的狀態(tài)。
  • 提高了代碼的可用性和可維護(hù)性,因?yàn)閷ο蟮男袨榭梢员粏为?dú)的改變或者是擴(kuò)展。
  • 禁止對象之間的不良交互提高模塊化。

2.2 繼承

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

繼承屬性:

  • 子類擁有父類非 private 的屬性和方法。
  • 子類可以擁有自己屬性和方法,即子類可以對父類進(jìn)行擴(kuò)展。
  • 子類可以用自己的方式實(shí)現(xiàn)父類的方法

2.3 多態(tài)

多態(tài),程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會指向哪個(gè)類的實(shí)例對象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定。

Java中有兩種形式可以實(shí)現(xiàn)多態(tài):

  • 繼承(多個(gè)子類對同一方法的重寫)
  • 接口(實(shí)現(xiàn)接口并覆蓋接口中同一方法)

2.3 面向?qū)ο蠛兔嫦蜻^程的區(qū)別?

面向過程:就是分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)依次調(diào)用就可以了,

面向?qū)ο?/strong>:是把構(gòu)成問題事務(wù)分解成各個(gè)對象,建立對象的目的不是為了完成一個(gè)步驟,而是為了描敘某個(gè)事物在整個(gè)解決問題的步驟中的行為。

3.1 面向過程

  • 優(yōu)點(diǎn):性能比面向?qū)ο蟾撸驗(yàn)轭愓{(diào)用時(shí)需要實(shí)例化,開銷比較大,比較消耗資源。比如,單片機(jī)、嵌入式開發(fā)、Linux/Unix 等一般采用面向過程開發(fā),性能是最重要的因素。
  • 缺點(diǎn):沒有面向?qū)ο笠拙S護(hù)、易復(fù)用、易擴(kuò)展。

3.2 面向?qū)ο?/h4>
  • 優(yōu)點(diǎn):易維護(hù)、易復(fù)用、易擴(kuò)展,由于面向?qū)ο笥蟹庋b、繼承、多態(tài)性的特性,可以設(shè)計(jì)出低耦合的系統(tǒng),使系統(tǒng)更加靈活、更加易于維護(hù)。
  • 缺點(diǎn):性能比面向過程低。

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

3. 1 基本數(shù)據(jù)類型如下:

  • 整數(shù)值型:byte、short、int、long
  • 字符型:char
  • 浮點(diǎn)類型:float、double
  • 布爾型:boolean

[圖片上傳失敗...(image-c54079-1574752213386)]

整數(shù)型:默認(rèn) int 型,小數(shù)默認(rèn)是 double 型。Float 和 Long 類型的必須加后綴。

比如:float f = 100f 。

引用類型聲明的變量是指該變量在內(nèi)存中實(shí)際存儲的是一個(gè)引用地址,實(shí)體在堆中。

3.2 引用類型

引用類型指向一個(gè)對象,不是原始值,指向?qū)ο蟮淖兞渴且米兞?/p>

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

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

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

3.3 引用類型簡介

引用類型 對象是否可引用 回收時(shí)間 使用場景
強(qiáng)引用 可以 從不回收 普遍對象的狀態(tài)
軟引用 可以 內(nèi)存不足時(shí) 內(nèi)存敏感的高速緩存
弱引用 可以 下一次GC 對象緩存
虛引用 不可以 下一次GC 一般用于追蹤垃圾收集器的回收動作

?

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

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

  • 引用傳遞,一般是對于對象型變量而言的,傳遞的是該對象地址的一個(gè)副本,并不是原對象本身。

一般認(rèn)為,Java 內(nèi)的傳遞都是值傳遞,Java 中實(shí)例對象的傳遞是引用傳遞。

3. 5 char 型變量中能不能存貯一個(gè)中文漢字?為什么?

  • C 語言中,char 類型占 1 個(gè)字節(jié),而漢字占 2 個(gè)字節(jié),所以不能存儲。
  • Java 語言中,char 類型占 2 個(gè)字節(jié),而且 Java 默認(rèn)采用 Unicode 編碼,一個(gè) Unicode 碼是 16 位,所以一個(gè) Unicode 碼占兩個(gè)字節(jié),Java 中無論漢字還是英文字母,都是用 Unicode 編碼來表示的。所以,在 Java 中,char 類型變量可以存儲一個(gè)中文漢字。

3.6 equals 與 == 的區(qū)別?

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

例如 String 類,兩個(gè)引用所指向的 String 都是 "abc" ,但可能出現(xiàn)他們實(shí)際對應(yīng)的對象并不是同一個(gè)(和 JVM 實(shí)現(xiàn)方式有關(guān)),因此用 == 判斷他們可能不相等,但用 equals 方法判斷一定是相等的。

4. Java類Class

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

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

  • 使用 new 關(guān)鍵字創(chuàng)建對象。
  • 使用 Class 類的 newInstance 方法(反射機(jī)制)。
  • 使用 Constructor 類的 newInstance 方法(反射機(jī)制)。
  • 使用 clone 方法創(chuàng)建對象。
  • 使用(反)序列化機(jī)制創(chuàng)建對象。

4.2 抽象類與接口

4.2.1 抽象類

從面向?qū)ο蟮慕嵌葋碇v,我們知道所有的對象都是通過類來描繪的,但是反過來卻不是這樣,并不是 所有的類都是用來描繪對象的,如果一個(gè)類中沒有包含足夠的信息來描繪一個(gè)具體的對象,這樣的類就可以認(rèn)為是抽象類

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

4.2.2 接口

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

4.2.3 抽象類與接口的對比

參數(shù) 抽象類 接口
默認(rèn)的方法實(shí)現(xiàn) 它可以有默認(rèn)的方法實(shí)現(xiàn) 接口完全是抽象的。它根本不存在方法的實(shí)現(xiàn)
實(shí)現(xiàn) 子類使用extends關(guān)鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)。 子類使用關(guān)鍵字implements來實(shí)現(xiàn)接口。它需要提供接口中所有聲明的方法的實(shí)現(xiàn)
構(gòu)造器 抽象類可以有構(gòu)造器 接口不能有構(gòu)造器
與正常Java類的區(qū)別 除了你不能實(shí)例化抽象類之外,它和普通Java類沒有任何區(qū)別 接口是完全不同的類型
訪問修飾符 抽象方法可以有public、protecteddefault這些修飾符 接口方法默認(rèn)修飾符是public。你不可以使用其它修飾符。
main方法 抽象方法可以有main方法并且我們可以運(yùn)行它 接口沒有main方法,因此我們不能運(yùn)行它。(java8以后接口可以有default和static方法,所以可以運(yùn)行main方法)
多繼承 抽象方法可以繼承一個(gè)類和實(shí)現(xiàn)多個(gè)接口 接口只可以繼承一個(gè)或多個(gè)其它接口
速度 它比接口速度要快 接口是稍微有點(diǎn)慢的,因?yàn)樗枰獣r(shí)間去尋找在類中實(shí)現(xiàn)的方法。
添加新方法 如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類。

4.3 講講類的實(shí)例化順序

初始化順序如下:

->父類靜態(tài)變量
->父類靜態(tài)代碼塊
->子類靜態(tài)變量、
->子類靜態(tài)代碼塊
->父類非靜態(tài)變量(父類實(shí)例成員變量)
->父類構(gòu)造函數(shù)
->子類非靜態(tài)變量(子類實(shí)例成員變量)
->子類構(gòu)造函數(shù)

4.4 內(nèi)部類

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

4.4.1 內(nèi)部類的作用是什么?

  • 內(nèi)部類可以很好的實(shí)現(xiàn)隱藏(一般的非內(nèi)部類,是不允許有 private 與protected權(quán)限的,但內(nèi)部類可以)

  • 內(nèi)部類擁有外圍類的所有元素的訪問權(quán)限

  • 可是實(shí)現(xiàn)多重繼承

  • 可以避免修改接口而實(shí)現(xiàn)同一個(gè)類中兩種同名方法的調(diào)用。

4.5 Anonymous Inner Class(匿名內(nèi)部類)是否可以繼承其它類?是否可以實(shí)現(xiàn)接口?

可以繼承其他類或?qū)崿F(xiàn)其他接口,在 Java 集合的流式操作中,我們常常這么干。

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

一個(gè)內(nèi)部類對象可以訪問創(chuàng)建它的外部類對象的成員,包括私有成員。

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

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

4.7 構(gòu)造方法、構(gòu)造方法重載

4.7.1 構(gòu)造方法

當(dāng)新對象被創(chuàng)建的時(shí)候,構(gòu)造方法會被調(diào)用。每一個(gè)類都有構(gòu)造方法。在程序員沒有給類提供構(gòu)造方法的情況下,Java 編譯器會為這個(gè)類創(chuàng)建一個(gè)默認(rèn)的構(gòu)造方法。

4.7.2 構(gòu)造方法重載

Java 中構(gòu)造方法重載和方法重載很相似??梢詾橐粋€(gè)類創(chuàng)建多個(gè)構(gòu)造方法。每一個(gè)構(gòu)造方法必須有它自己唯一的參數(shù)列表。

4. 8 重載和重寫的區(qū)別?

4.8.1 重寫 override

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

4.8.2 重載 overload

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

4.9 hashCode() 以及equals()

4.9.1 為什么需要子類實(shí)現(xiàn)這兩個(gè)方法?

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

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

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

4.9.2 說一說你對 java.lang.Object 對象中 hashCode 和 equals 方法的理解。在什么場景下需要重新實(shí)現(xiàn)這兩個(gè)方法?

理解答案與4.8.1差不多,

equals 方法,用于比較對象的內(nèi)容是否相等。

當(dāng)覆蓋了 equals 方法時(shí),比較對象是否相等將通過覆蓋后的 equals 方法進(jìn)行比較(判斷對象的內(nèi)容是否相等)。
hashCode 方法,大多在集合中用到。

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

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

可能會發(fā)生,這個(gè)被稱為哈希碰撞。當(dāng)然,相等的對象,即我們重寫了 equals 方法,一定也要重寫 hashCode 方法,否則將出現(xiàn)我們在 HashMap 中,相等的對象作為 key ,將找不到對應(yīng)的 value 。

4.9.4 equals 和 hashCode 的關(guān)系

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

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

    一般來說,hashCode 方法的重寫,可以看看 《科普:為什么 String hashCode 方法選擇數(shù)字31作為乘子》 方法。

5. 常用類

5.1 String、StringBuffer、StringBuilder

5.1.1 String、StringBuffer、StringBuilder 的區(qū)別?

可變性:

String 類中使用 final 關(guān)鍵字字符數(shù)組保存字符串,代碼:

private?final?char?value[],

所以string對象是不可變的。

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

char[]value

這兩種對象都是可變的。

線程安全性:

  • String中的對象是不可變的,也就可以理解為常量,線程安全。
  • StringBuffer對方法加了同步鎖或者對調(diào)用的方法加了同步鎖,所以是線程安全的。
  • StringBuilder并沒有對方法進(jìn)行加同步鎖,所以是非線程安全的。

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

性能:
每次對String 類型進(jìn)行改變的時(shí)候,都會生成一個(gè)新的String對象,然后將指針指向新的String 對象。

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

5.1.2對于三者使用的總結(jié)

  • 操作少量的數(shù)據(jù)用 = String
  • 單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder,甚至有時(shí),我們?yōu)榱吮苊饷總€(gè)線程重復(fù)創(chuàng)建 StringBuilder 對象,會通過 ThreadLocal + StringBuilder 的方式,進(jìn)行對 StringBuilder 的重用
  • 多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer

5.1.3 String s = new String("xyz") 會創(chuàng)建幾個(gè)對象?

首先,在 String 池內(nèi)找,找到 "xyz" 字符串,不創(chuàng)建 "xyz" 對應(yīng)的 String 對象,否則創(chuàng)建一個(gè)對象。
然后,遇到 new 關(guān)鍵字,在內(nèi)存上創(chuàng)建 String 對象,并將其返回給 s ,又一個(gè)對象。
所以,總共是 1 個(gè)或者 2 個(gè)對象

5.1.4 StringTokenizer 是什么?

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

示例代碼如下:

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

輸出如下:

Hello
World

5.2 什么是自動拆裝箱?

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

5.2.1 什么要轉(zhuǎn)換?

如果你在 Java5 下進(jìn)行過編程的話,你一定不會陌生這一點(diǎn),你不能直接地向集合( Collection )中放入原始類型值,因?yàn)榧现唤邮諏ο蟆?/p>

5.3 int 和 Integer 有什么區(qū)別?

  • int 是基本數(shù)據(jù)類型。

  • Integer 是其包裝類,注意是一個(gè)類。

    需要注意下 Integer 的緩存策略

5.2.3 理解Java Integer 的緩存策略

6. 關(guān)鍵字

6.1 final、finally、finalize

6.1.1 final

final ,是修飾符關(guān)鍵字。

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

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

    另外,在早期的 Java 實(shí)現(xiàn)版本中,會將 final 方法轉(zhuǎn)為內(nèi)嵌調(diào)用。但是如果方法過于龐大,可能看不到內(nèi)嵌調(diào)用帶來的任何性能提升(現(xiàn)在的 Java 版本已經(jīng)不需要使用 final 方法進(jìn)行這些優(yōu)化了)。類中所有的private 方法都隱式地指定為 final 。

6.1.2 finally

在異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會執(zhí)行,然后控制就會進(jìn)入 finally 塊(如果有的話)。

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

  • 在 finally 語句塊中發(fā)生了異常。

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

  • 程序所在的線程死亡。

  • 關(guān)閉 CPU 。

6.1.3 finalize

finalize ,是方法名。

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

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

finalize() 方法,是在垃圾收集器刪除對象之前對這個(gè)對象調(diào)用的。
一般情況下,我們在業(yè)務(wù)中不會自己實(shí)現(xiàn)這個(gè)方法,更多是在一些框架中使用。

6.1.4 String 類能被繼承嗎,為什么?

不能,因?yàn)?String 是 final 修飾。

6.2 static

6.2.1 static特點(diǎn)

  • static是一個(gè)修飾符,用于修飾成員。(成員變量,成員函數(shù))static修飾的成員變量 稱之為靜態(tài)變量或類變量。
  • static修飾的成員被所有的對象共享。
  • static優(yōu)先于對象存在,因?yàn)閟tatic的成員隨著類的加載就已經(jīng)存在。
  • static修飾的成員多了一種調(diào)用方式,可以直接被類名所調(diào)用,(類名.靜態(tài)成員)。
  • static修飾的數(shù)據(jù)是共享數(shù)據(jù),對象中的存儲的是特有的數(shù)據(jù)

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

static 變量在 Java 中是屬于類的,它在所有的實(shí)例中的值是一樣的。當(dāng)類被 Java 虛擬機(jī)載入的時(shí)候,會對 static 變量進(jìn)行初始化。如果你的代碼嘗試不用實(shí)例來訪問非 static 的變量,編譯器會報(bào)錯(cuò),因?yàn)檫@些變量還沒有被創(chuàng)建出來,還沒有跟任何實(shí)例關(guān)聯(lián)上。

由于靜態(tài)方法可以不通過對象進(jìn)行調(diào)用,因此在靜態(tài)方法里,不能調(diào)用其他非靜態(tài)變量,也不可以訪問非靜態(tài)變量成員。

如果你的代碼嘗試不用實(shí)例來訪問非 static 的變量,編譯器會報(bào)錯(cuò),因?yàn)檫@些變量還沒有被創(chuàng)建出來,還沒有跟任何實(shí)例關(guān)聯(lián)上。

6.2.3 成員變量和靜態(tài)變量的區(qū)別:

  • 生命周期的不同:

    • 成員變量隨著對象的創(chuàng)建而存在隨著對象的回收而釋放。
    • 靜態(tài)變量隨著類的加載而存在隨著類的消失而消失。
  • 調(diào)用方式不同:

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

    • 成員變量也稱為實(shí)例變量。
    • 靜態(tài)變量稱為類變量。
  • 數(shù)據(jù)存儲位置不同:

    • 成員變量數(shù)據(jù)存儲在堆內(nèi)存的對象中,所以也叫對象的特有數(shù)據(jù)。

    • 靜態(tài)變量數(shù)據(jù)存儲在方法區(qū)(共享數(shù)據(jù)區(qū))的靜態(tài)區(qū),所以也叫對象的共享數(shù)據(jù)。

6.2.4 static 關(guān)鍵字修飾的加載順序

->父類靜態(tài)變量

? ->父類靜態(tài)代碼塊

? ->子類靜態(tài)變量

? ->子類靜態(tài)代碼塊

? ->父類普通變量

? ->父類普通代碼塊

? ->父類構(gòu)造函數(shù)

? ->子類普通變量

? ->子類普通代碼塊

? ->子類構(gòu)造函數(shù)

6.3 transient 關(guān)鍵字

transient聲明一個(gè)實(shí)例變量,當(dāng)對象存儲時(shí),它的值不需要維持。換句話來說就是,用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過程,

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

6.3.1 Java 序列話中,如果有些字段不想進(jìn)行序列化怎么辦?

對于不想進(jìn)行序列化的變量,使用 transient 關(guān)鍵字修飾,

  • 當(dāng)對象被序列化時(shí),阻止實(shí)例中那些用此關(guān)鍵字修飾的的變量序列化。
  • 當(dāng)對象被反序列化時(shí),被 transient 修飾的變量值不會被持久化和恢復(fù)。

6.4 volatile關(guān)鍵詞

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

6.4.1 volatile關(guān)鍵字是否能保證線程安全?

不能 , 多個(gè)線程對 volatile 的寫操作,無法保證線程安全。例如假如線程 1,線程 2 在進(jìn)行 read,load 操作中,發(fā)現(xiàn)主內(nèi)存中 count 的值都是 5,那么都會加載這個(gè)最新的值,在線程 1 堆 count 進(jìn)行修改之后,會 write 到主內(nèi)存中,主內(nèi)存中的 count 變量就會變?yōu)?6;線程 2 由于已經(jīng)進(jìn)行 read,load 操作,在進(jìn)行運(yùn)算之后,也會更新主內(nèi)存 count 的變量值為 6;導(dǎo)致兩個(gè)線程及時(shí)用 volatile 關(guān)鍵字修改之后,還是會存在并發(fā)的情況

7. Java IO

7.1 Java IO 相關(guān)的類

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

[圖片上傳失敗...(image-79fb1-1574752213386)]

7.2 什么是 Java 序列化?

序列化就是一種用來處理對象流的機(jī)制,所謂對象流也就是將對象的內(nèi)容進(jìn)行流化。

可以對流化后的對象進(jìn)行讀寫操作,也可將流化后的對象傳輸于網(wǎng)絡(luò)之間。
序列化是為了解決在對對象流進(jìn)行讀寫操作時(shí)所引發(fā)的問題。
反序列化的過程,則是和序列化相反的過程。

我們不能將序列化局限在 Java 對象轉(zhuǎn)換成二進(jìn)制數(shù)組,比如,將一個(gè) Java 對象轉(zhuǎn)換成 JSON 字符串等,這也可以理解為是序列化。

7.2.1如何實(shí)現(xiàn) Java 序列化?

將需要被序列化的類,實(shí)現(xiàn) Serializable 接口,該接口沒有需要實(shí)現(xiàn)的方法,implements Serializable 只是為了標(biāo)注該對象是可被序列化的。

  • 序列化

    • 首先使用一個(gè)輸出流(如:FileOutputStream)來構(gòu)造一個(gè) ObjectOutputStream(對象流)對象
    • 接著,使用 ObjectOutputStream 對象的 #writeObject(Object obj) 方法,就可以將參數(shù)為 obj 的對象寫出(即保存其狀態(tài))。
  • 反序列化

    • 要恢復(fù)的話則用輸入流。

7.3 如何實(shí)現(xiàn)對象克隆(淺克隆和深克隆)?

  • 實(shí)現(xiàn) Cloneable 接口,并重寫 Object 類中的 #clone() 方法。可以實(shí)現(xiàn)淺克隆,也可以實(shí)現(xiàn)深克隆。
  • 實(shí)現(xiàn) Serializable 接口,通過對象的序列化和反序列化實(shí)現(xiàn)克隆??梢詫?shí)現(xiàn)真正的深克隆。

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

8.異常

8.1 異常機(jī)制的概述

? 異常機(jī)制是指當(dāng)程序出現(xiàn)錯(cuò)誤后,程序如何處理。具體來說,異常機(jī)制提供了程序退出的安全通道。當(dāng)出現(xiàn)錯(cuò)誤后,程序執(zhí)行的流程發(fā)生改變,程序的控制權(quán)轉(zhuǎn)移到異常處理器。

程序錯(cuò)誤分為三種:

  • 編譯錯(cuò)誤:因?yàn)槌绦驔]有遵循語法規(guī)則,編譯程序能夠自己發(fā)現(xiàn)并且提示我們錯(cuò)誤的原因和位置,這個(gè)也是大家在剛接觸編程語言最常遇到的問題。

  • 運(yùn)行時(shí)錯(cuò)誤:因?yàn)槌绦蛟趫?zhí)行時(shí),運(yùn)行環(huán)境發(fā)現(xiàn)了不能執(zhí)行的操作。

  • 邏輯錯(cuò)誤:因?yàn)槌绦驔]有按照預(yù)期的邏輯順序執(zhí)行。異常也就是指程序運(yùn)行時(shí)發(fā)生錯(cuò)誤,而異常處理就是對這些錯(cuò)誤進(jìn)行處理和控制。

8.2 Throwable

Throwable 類圖

img

Throwable有兩個(gè)重要的子類 :

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

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

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

  • Error(錯(cuò)誤),表示系統(tǒng)級的錯(cuò)誤和程序不必處理的異常,是 Java 運(yùn)行環(huán)境中的內(nèi)部錯(cuò)誤或者硬件問題。

    • 例如:內(nèi)存資源不足等。
    • 對于這種錯(cuò)誤,程序基本無能為力,除了退出運(yùn)行外別無選擇,它是由 Java 虛擬機(jī)拋出的
  • Exception(異常),表示需要捕捉或者需要程序進(jìn)行處理的異常,它處理的是因?yàn)槌绦蛟O(shè)計(jì)的瑕疵而引起的問題或者在外的輸入等引起的一般性問題,是程序必須處理的。Exception 又分為運(yùn)行時(shí)異常,受檢查異常。

    • RuntimeException(運(yùn)行時(shí)異常),表示無法讓程序恢復(fù)的異常,導(dǎo)致的原因通常是因?yàn)閳?zhí)行了錯(cuò)誤的操作,建議終止邏輯,因此,編譯器不檢查這些異常。
    • CheckedException(受檢查異常),是表示程序可以處理的異常,也即表示程序可以修復(fù)(由程序自己接受異常并且做出處理),所以稱之為受檢查異常

8.3 error 和 exception 有什么區(qū)別?

  • Error:Error類對象由 Java 虛擬機(jī)生成并拋出,大多數(shù)錯(cuò)誤與代碼編寫者所執(zhí)行的操作無關(guān) 。比如:

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

    • ArrayIndexOutOfBoundsException(數(shù)組下標(biāo)越界)

    • NullPointerException(空指針異常)

    • ArithmeticException(算術(shù)異常)

    • MissingResourceException(丟失資源)

    • ClassNotFoundException(找不到類)

    • BufferOverflowException

    • ClassCastException

8.4 CheckedException 和 RuntimeException 有什么區(qū)別?

  • RuntimeException運(yùn)行異常:表示程序運(yùn)行過程中可能出現(xiàn)的非正常狀態(tài),運(yùn)行時(shí)異常表示虛擬機(jī)的通常操作中可能遇到的異常,是一種常見運(yùn)行錯(cuò)誤,只要程序設(shè)計(jì)得沒有問題通常就不會發(fā)生。
  • CheckedException受檢異常:跟程序運(yùn)行的上下文環(huán)境有關(guān),即使程序設(shè)計(jì)無誤,仍然可能因使用的問題而引發(fā)。Java編譯器要求方法必須聲明拋出可能發(fā)生的受檢異常,但是并不要求必須聲明拋出未被捕獲的運(yùn)行時(shí)異常

Effective Java中對異常的使用給出了以下指導(dǎo)原則 :

不要將異常處理用于正常的控制流(設(shè)計(jì)良好的API不應(yīng)該強(qiáng)迫它的調(diào)用者為了正常的控制流而使用異常)
對可以恢復(fù)的情況使用受檢異常,對編程錯(cuò)誤使用運(yùn)行時(shí)異常
避免不必要的使用受檢異常(可以通過一些狀態(tài)檢測手段來避免異常的發(fā)生)
優(yōu)先使用標(biāo)準(zhǔn)的異常
每個(gè)方法拋出的異常都要有文檔
保持異常的原子性
不要在catch中忽略掉捕獲到的異常

8.5 Throwable 類常用方法?

  • getMessage() 方法:返回異常發(fā)生時(shí)的詳細(xì)信息。
  • getCause() 方法:獲得導(dǎo)致當(dāng)前 Throwable 異常的 Throwable 異常。
  • getStackTrace() 方法:獲得 Throwable 對象封裝的異常信息。
  • printStackTrace() 方法:在控制臺上打印。

8.6 throw 與 throws 的區(qū)別 ?

  • throw ,用于在程序中顯式地拋出一個(gè)異常。
  • throws ,用于指出在該方法中沒有處理的異常。每個(gè)方法必須顯式指明哪些異常沒有處理,以便該方法的調(diào)用者可以預(yù)防可能發(fā)生的異常。最后,多個(gè)異常用逗號分隔。

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

不管程序是否發(fā)生了異常, finally 語句塊都會被執(zhí)行,甚至當(dāng)沒有catch 聲明但拋出了一個(gè)異常時(shí), finally 語句塊也會被執(zhí)行。

finally 語句塊通常用于釋放資源, 如 I/O 緩沖區(qū), 數(shù)據(jù)庫連接等等。

8.8 UnsupportedOperationException 是什么?

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

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

9.反射

9.1 反射簡介

當(dāng)程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動態(tài)語言。我們認(rèn)為 Java 并不是動態(tài)語言,但是它卻又一個(gè)非常突出的動態(tài)相關(guān)的機(jī)制

9.2 反射的用途及實(shí)現(xiàn)?

Java 反射機(jī)制主要提供了以下功能:

  • 在運(yùn)行時(shí)構(gòu)造一個(gè)類的對象。
  • 判斷一個(gè)類所具有的成員變量和方法。
  • 調(diào)用一個(gè)對象的方法。
  • 生成動態(tài)代理。
反射的主要用途, 開發(fā)各種通用框架
  • Spring 框架的 IoC 基于反射創(chuàng)建對象和設(shè)置依賴屬性。
  • Spring MVC 的請求調(diào)用對應(yīng)方法,也是通過反射。
  • JDBC 的 Class#forName(String className) 方法,也是使用反射。

9.3 反射中,Class.forName 和 ClassLoader 區(qū)別?

  • Class#forName(...) 方法,除了將類的 .class 文件加載到JVM 中之外,還會對類進(jìn)行解釋,執(zhí)行類中的 static 塊。
  • ClassLoader 只干一件事情,就是將 .class 文件加載到 JVM 中,不會執(zhí)行 static 中的內(nèi)容,只有在 newInstance 才會去執(zhí)行 static 塊。
  • Class#forName(name, initialize, loader) 方法,帶參函數(shù)也可控制是否加載 static 塊,并且只有調(diào)用了newInstance 方法采用調(diào)用構(gòu)造函數(shù),創(chuàng)建類的對象。

9.4 什么時(shí)候用斷言(assert)?

斷言,在軟件開發(fā)中是一種常用的調(diào)試方式,很多開發(fā)語言中都支持這種機(jī)制。

一般來說,斷言用于保證程序最基本、關(guān)鍵的正確性。斷言檢查通常在開發(fā)和測試時(shí)開啟。為了保證程序的執(zhí)行效率,在軟件發(fā)布后斷言檢查通常是關(guān)閉的。
斷言是一個(gè)包含布爾表達(dá)式的語句,在執(zhí)行這個(gè)語句時(shí)假定該表達(dá)式為true;如果表達(dá)式的值為 false ,那么系統(tǒng)會報(bào)告一個(gè)AssertionError 錯(cuò)誤。斷言的使用如下面的代碼所示:

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

斷言可以有兩種形式:

assert Expression1; 。
assert Expression1 : Expression2;

Expression1 應(yīng)該總是產(chǎn)生一個(gè)布爾值。
Expression2 可以是得出一個(gè)值的任意表達(dá)式;這個(gè)值用于生成顯示更多調(diào)試信息的字符串消息。

要在運(yùn)行時(shí)啟用斷言,可以在啟動 JVM 時(shí)使用 -enableassertions 或者 -ea 標(biāo)記。要在運(yùn)行時(shí)選擇禁用斷言,可以在啟動 JVM 時(shí)使用 -da 或者 -disableassertions 標(biāo)記。要在系統(tǒng)類中啟用或禁用斷言,可使用 -esa 或 -dsa 標(biāo)記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。

也歡迎關(guān)注公 眾 號【Ccww筆記】,原創(chuàng)技術(shù)文章第一時(shí)間推出

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,658評論 1 32
  • 一:java概述:1,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境,java的開發(fā)工...
    ZaneInTheSun閱讀 2,812評論 0 11
  • 所有知識點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,710評論 1 4
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 1,272評論 0 5
  • Java基礎(chǔ)面試 Java基礎(chǔ)面試... 1 1. Java基礎(chǔ)知識... 5 1.1. Java源程序的擴(kuò)展名是...
    來著何人閱讀 1,291評論 0 1

友情鏈接更多精彩內(nèi)容