1基本概念
1.1 Java語言的優(yōu)點有哪些?
- Java是純面向?qū)ο蟮恼Z言。在Java中,萬物皆對象。
- 具有平臺無關(guān)性。(平臺無關(guān)性實現(xiàn)原理請參考這里)
- Java提供了很多內(nèi)置的類庫(如對多線程的支持),簡化了開發(fā)人員的程序設(shè)計工作。
- 具有較好的安全性和健壯性。
安全性保證:Java的安全機(jī)制(數(shù)組的安全邊界檢測和Bytecode 檢驗等)
健壯性保證:Java的強(qiáng)類型機(jī)制、垃圾回收器、異常處理和安全檢查機(jī)制使得Java具有很好的健壯性。
1.2 Java與C/C++的異同點
- 相同點:都是面向?qū)ο蟮恼Z言,都使用了面向?qū)ο笏枷?,都有很好的可重用性?/li>
- 不同點:
(1)Java為解釋性語言,其運行過程為:Java源代碼經(jīng)過Java編譯器編譯成字節(jié)碼,然后由JVM解釋執(zhí)行。而C/C++為編譯型語言,源代碼經(jīng)過編譯和鏈接后生成可執(zhí)行的二進(jìn)制代碼。因此,Java的執(zhí)行速度比C/C++慢。但是Java能夠跨平臺執(zhí)行,而C/C++不能。
(2)與C/C++相比,Java中沒有指針的概念,因此可以避免指針可能引起的安全問題。
(3)Java是純面向?qū)ο蟮恼Z言,多有代碼必須在類中實現(xiàn),除基本數(shù)據(jù)類型外,所有類型都是類。此外,Java語言中不存在全局變量和全局函數(shù)。而C++同時具有面向過程和面向?qū)ο蟮奶攸c,且能設(shè)置全局變量和全局函數(shù)。
(4)Java語言不支持多繼承,但是可以實現(xiàn)多個接口。C++能夠支持多繼承。
(5)在C++ 語言中,需要開發(fā)人員去管理內(nèi)存分配,而Java中提供垃圾回收期來對垃圾進(jìn)行自動回收,不需要顯示的通過程序管理內(nèi)存。
1.3 為什么需要public static void main(String[] args)這個方法呢?
??此方法為Java程序的入口方法。JVM執(zhí)行程序時 ,會首先找到這個方法執(zhí)行。
??注意:在同一個Java文件中,每個類中都可以定義main方法,但是只有與文件名相同的并用public修飾的類中main方法才能作為程序入口。
逐次解析:
public:權(quán)限修飾符,表明任何類或者對象都可以訪問這個方法。
static:表明這是一個靜態(tài)方法,即方法中的代碼是存在靜態(tài)代碼區(qū)的,只要類被加載后,就可以使用該方法而不需要實例化對象來訪問,可以直接根絕類名.main()來訪問。JVM啟動時就會根據(jù)上述方法的簽名(必須有public和static,返回值為void,且方法的參數(shù)為字符串?dāng)?shù)組)來查找方法的入口地址,若能找到,就執(zhí)行,若找不到,就會報錯。
void:該方法沒有返回值。
main:JVM識別的特殊方法名,是程序的入口方法。
args:字符串?dāng)?shù)組,,為開發(fā)人員在命令行狀態(tài)下提與程序交互提供了一種手段。
為什么要用public static修飾main()方法?
因為main方法是程序的入口方法,此時還沒有實例化對象,因此在編寫main方法時就要求需要在不實例化對象的情況下就可以調(diào)用這個方法,因此采用public static修飾。注意:public與static沒有先后順序,main方法也可以用final或者synchronized修飾,但是由于要作為程序入口,不能用abstract修飾。
1.4 作為程序入口的main()是否是程序運行時執(zhí)行的第一個模塊呢?
??答案:不是。在Java中,靜態(tài)塊在類被加載時就會被調(diào)用,因此可以在main()方法執(zhí)行前。利用靜態(tài)塊實現(xiàn)一些輸出的功能。
1.5 Java程序初始化順序是怎樣的?
??在Java中,當(dāng)實例化對象時,對象所在類的所有成員變量首先要進(jìn)行初始化,只有當(dāng)所有類成員變量進(jìn)行初始化之后,才會調(diào)用對象所在類的構(gòu)造函數(shù)創(chuàng)建對象。
Java程序初始化的三個原則:
- 靜態(tài)變量(對象)優(yōu)先于非靜態(tài)變量的初始化,且靜態(tài)對象只初始化一次。
- 父類優(yōu)先于子類進(jìn)行初始化。
- 按照成員變量的順序進(jìn)行初始化。
Java中代碼塊的初始化順序:
??父類靜態(tài)變量->父類靜態(tài)代碼塊->子類靜態(tài)變量->子類靜態(tài)代碼塊->父類非靜態(tài)變量->父類非靜態(tài)代碼塊->父類構(gòu)造函數(shù)->子類非靜態(tài)變量->子類非靜態(tài)代碼塊->子類構(gòu)造函數(shù)。
1.6 關(guān)于Java作用域的那些事兒
在和Java中,作用于是由花括號的位置決定的,它決定了其變量的可見性和生命周期。
Java的變量主要有三種:成員變量、靜態(tài)變量與局部變量。其中被static修飾的成員變量稱之為靜態(tài)變量。
| 變量類型 | 作用范圍與可見性 |
|---|---|
| 局部變量 | 它所處的花括號內(nèi) |
| 成員變量 | 與類的實例化對象作用域相同 |
| 靜態(tài)變量 | 與類同在 |
成員變量還可以分成四種作用域,如下表:
| 作用域與可見性 | 當(dāng)前類 | 同一package | 子類 | 其他package |
|---|---|---|---|---|
| public | √ | √ | √ | √ |
| private | √ | × | × | × |
| protected | √ | √ | √ | × |
| default | √ | √ | × | × |
- public:表明該成員變量或者方法對所有對象或者類都是可見的,所有類或者對象都是可以直接訪問的。
- private:表明該成員變量或者方法是私有的,只有當(dāng)前類有訪問權(quán)限。
- protected:表明該成員變量或者方法對該類自身、與它在同一個包之中的其他類。在其他包中的子類都是可見的。
- default:表明該成員變量或者方法只有自己和與其位于同一包內(nèi)的類可見。若父類與子類位于同一包內(nèi),則子類對父類的default的成員變量或者方法都有訪問權(quán)限;若父類與子類不在同一個包內(nèi),則沒有訪問權(quán)限。
- 注意:這些修飾符只能修飾成員變量,不能用來修飾局部變量。private與protected不能用來修飾類(只有public、abstract或final能用來修飾)。
1.7一個Java文件中是否能定義多個類呢?
??當(dāng)然,一個Java文件中能定義多個類,但是只能有一個類被public修飾,并且這個被public修飾的類的名稱必須與文件的名稱相同。若文件中沒有被public修飾的類,則文件名可以是任意一個類的名稱。此外,在使用javac指令編譯.java文件的時候,會給每一個類生成一個對應(yīng)的.class文件。
1.8 為什么Java中有些接口沒有任何方法?
??在Java中沒有任何方法的接口稱之為“標(biāo)識接口”。標(biāo)識接口對于實現(xiàn)它的的類沒有任何語義要求,這種接口僅僅起到一個標(biāo)識作用。用來表明實現(xiàn)它的類屬于一個特定的類型。
??Java類庫中已經(jīng)存在的標(biāo)識接口有Cloneable和Serializable等。在使用時經(jīng)常會使用instance-of來判斷實例對象的類型是否實現(xiàn)了一個給定的標(biāo)識接口。
1.9 什么是JDK和JRE?
- JDK: Java Development Kit,開發(fā)工具包。提供了編譯運行 Java 程序的各種工具,包括編譯器、JRE 及常用類庫,是 JAVA 核心。
- JRE: Java Runtime Environment,運行時環(huán)境,運行 Java 程序的必要環(huán)境,包括 JVM、核心類庫、核心配置工具。
1.10 什么是值調(diào)用和引用調(diào)用?
- 按值調(diào)用指方法接收調(diào)用者提供的值,按引用調(diào)用指方法接收調(diào)用者提供的變量地址。
- Java 總是按值調(diào)用,方法得到的是參數(shù)的副本,傳遞對象時實際上傳遞的是對象引用的副本。其中,有兩點需要注意:
??(1)方法不能修改基本數(shù)據(jù)類型的參數(shù),例如傳遞了一個 int 值 ,改變 int 值不會影響實參。
??(2)方法可以改變對象參數(shù)的狀態(tài),但不能讓對象參數(shù)引用新的對象。例如傳遞了一個 int 數(shù)組,改變數(shù)組內(nèi)容會影響實參,而改變其引用并不會讓實參引用新的數(shù)組對象。
1.11 什么是淺拷貝和深拷貝?
- 淺拷貝只復(fù)制當(dāng)前對象的基本數(shù)據(jù)類型及引用變量,沒有復(fù)制引用變量指向的實際對象。修改克隆對象可能影響原對象。
- 深拷貝完全拷貝基本數(shù)據(jù)類型和引用數(shù)據(jù)類型,修改克隆對象不會影響原對象。
1.12 什么是反射?
??反射機(jī)制是Java語言中一個非常重要的特性,具體而言,反射機(jī)制提供的功能主要有:得到一個對象所屬的類;獲取一個類的所有成員變量和方法;在運行時創(chuàng)建對象;在運行時調(diào)用對象的方法。
class Father{
public void f(){
System.out.println("Father");
}
}
`class Son extends Father{
public void f(){
System.out.println("Son");
}
}`
public class Test{
try{//使用反射加載類
Class c=Class.forName("Son");
Father b=(Father)c.newInstance();
b.f();
}catch(Exception e){
e.printStackTrace();
}
}```
1.13 反射中使用的Class類是怎么獲取的呢?
?? 在程序運行期間,Java 運行時系統(tǒng)為所有對象維護(hù)一個運行時類型標(biāo)識,這個信息會跟蹤每個對象所屬的類,虛擬機(jī)利用運行時類型信息選擇要執(zhí)行的正確方法,保存這些信息的類就是 Class,這是一個泛型類。
??一共有三種方法可以獲得Class類:
??1. Class.forName("類路徑")
?? 2. 類名.Class();
?? 3. 實例.getClass();
1.14 Java 創(chuàng)建對象的方式有幾種?
- 通過new語句實例化一個對象。
- 通過反射機(jī)制創(chuàng)建一個對象。
- 通過clone()方法創(chuàng)建一個對象。
- 通過反序列化的方式創(chuàng)建一個對象。
1.15 package有什么作用
?? package,即“包”,其宗旨是將.java文件、class文件以及其他resource文件進(jìn)行有條理的組織,以供使用。主要有兩個作用:
- 提供多層命名空間,解決命名沖突,使得處于不同package中的類可以存在相同的名字。
- 對類按功能進(jìn)行分類,使項目的組織更加清晰。
1.16 怎么使用package呢?
??其用法一般如下(源文件目錄為所在目錄):
- 在每個源文件的開頭加上“package
packagename;”,然后源文件所在目錄下創(chuàng)建一個新目錄,名字為“packagename”; - 用javac指令編譯每個sourcename.java源文件,將生成的sourcename.classname文件復(fù)制到packagename目錄。
- 用Java指令運行程序:java packagename.sourceename
2 面向?qū)ο笙嚓P(guān)
2.1面向?qū)ο笈c面向過程的區(qū)別以及優(yōu)缺點?
(1)面向?qū)ο笫前褬?gòu)成問題事務(wù)分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。
??優(yōu)點:易維護(hù)、易復(fù)用、易擴(kuò)展,由于面向?qū)ο笥蟹庋b、繼承、多態(tài)性的特性,可以設(shè)計出低耦合的系統(tǒng),使系統(tǒng) 更加靈活、更加易于維護(hù)
??缺點:性能比面向過程低
(2) 面向過程就是分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實現(xiàn),使用的時候一個一個依次調(diào)用就可以了。
??優(yōu)點:性能比面向?qū)ο蟾撸驗轭愓{(diào)用時需要實例化,開銷比較大,比較消耗資源;比如單片機(jī)、嵌入式開發(fā)、 Linux/Unix等一般采用面向過程開發(fā),性能是最重要的因素。
??缺點:沒有面向?qū)ο笠拙S護(hù)、易復(fù)用、易擴(kuò)展
2.2 面向?qū)ο蟮膸状筇匦裕?多態(tài)怎么理解?
?面向?qū)ο笫且环N思想,可以將復(fù)雜問題簡單化,讓我們從執(zhí)行者變?yōu)榱酥笓]者。面向?qū)ο蟮娜筇匦詾椋悍庋b,繼承與多態(tài)。
<font color=Blue>封裝:</font>將事物封裝成一個類,減少耦合,隱藏細(xì)節(jié)。保留特定的接口與外界聯(lián)系,當(dāng)接口內(nèi)部發(fā)生改變時,不會影響外部調(diào)用方。
<font color=Blue>繼承:</font> 從一個已知的類中派生出一個新的類,新類可以擁有已知類的行為和屬性,并且可以通過覆蓋/重寫來增強(qiáng)已知類的能力。
<font color=Blue>多態(tài):</font> 多態(tài)的本質(zhì)就是一個程序中存在多個同名的不同方法,主要通過三種方式來實現(xiàn):
??通過子類對父類的覆蓋來實現(xiàn)
??通過在一個類中對方法的重載來實現(xiàn)
??通過將子類對象作為父類對象使用來實現(xiàn)
2.3如何通過將子類對象作為父類對象使用來實現(xiàn)多態(tài)?
??把不同的子類對象都當(dāng)作父類對象來看,可以屏蔽不同子類對象之間的差異,寫出通用的代碼,做出通用的編程,以適應(yīng)需求的不斷變化。這樣操作之后,父類的對象就可以根據(jù)當(dāng)前賦值給它的子類對象的特性以不同的方式運作。
??對象的引用型變量具有多態(tài)性,因為一個引用型變量可以指向不同形式的對象
?<font color=Blue>向上轉(zhuǎn)型:</font>
?子類對象轉(zhuǎn)為父類,父類可以是接口。
?公式:Father f = new Son(); Father是父類或接口,Son是子類。
? <font color=Blue>向下轉(zhuǎn)型:</font>
?父類對象轉(zhuǎn)為子類。公式:Son s = (Son) f;
<font color=Blue>【轉(zhuǎn)型總結(jié)】</font>
? <font color=#008000>1.什么時候使用向上轉(zhuǎn)型呢?</font>
? ?提高程序的擴(kuò)展性,不關(guān)注子類類型(子類類型被隱藏)
需要用子類的的特有方法嗎?不需要,向上轉(zhuǎn)型
? <font color=#008000>2.什么時候使用向下轉(zhuǎn)型呢?</font>
? ? 需要使用子類型的特有方法時
? ?但是一定要使用instenceof進(jìn)行類型的判斷。避免發(fā)生ClassCastException
<font color=Blue>【多態(tài)成員調(diào)用的特點】</font>
對于成員變量和靜態(tài)函數(shù),編譯和運行都看左邊
對于成員變量和靜態(tài)函數(shù),編譯和運行都看左邊
對于成員函數(shù)。編譯看左邊,運行看右邊
2.4說一下覆蓋和重載的區(qū)別
- 覆蓋是子類和父類之間的關(guān)系,是垂直關(guān)系,而重載是一個類中各個方法之間的關(guān)系,是水平關(guān)系。
- 覆蓋只能由一個方法或者一對方法產(chǎn)生關(guān)系;重載是多個方法之間的關(guān)系。
- 覆蓋要求參數(shù)列表相同;重載要求參數(shù)列表不同。
- 覆蓋關(guān)系中,調(diào)用方法體是根據(jù)對象的類型(對象對應(yīng)存儲空間的類型)來決定,而重載是根據(jù)調(diào)用時的實參列表與形參表來選擇方法體的。
2.5 接口和抽象類的區(qū)別。(重點)
抽象類和接口的主要區(qū)別可以總結(jié)如下。
- ? ?抽象類中可以沒有抽象方法,也可以抽象方法和非抽象方法共存 .
- ? ?接口中的方法在JDK8之前只能是抽象的,JDK8版本開始提供了接口中方法的default實現(xiàn)
- ? ? 抽象類和類一樣是單繼承的;接口可以實現(xiàn)多個父接口 抽象類中可以存在普通的成員變量;接口中的變量必須是static final類型的,必須被初始化,接口中只有常量,沒有變量。
2.51 抽象類和接口應(yīng)該如何選擇?分別在什么情況下使用呢?
. ? ?根據(jù)抽象類和接口的不同之處,當(dāng)我們僅僅需要定義一些抽象方法而不需要其余額外的具體方法或者變量的時候,我們可以使用接口。反之,則需要使用抽象類,因為抽象類中可以有非抽象方法和變量。
2.52 關(guān)于接口的默認(rèn)方法
? ?當(dāng)一個類實現(xiàn)該接口時,可以繼承到該接口中的默認(rèn)方法。
? ?如果兩個接口中存在同樣的默認(rèn)方法,實現(xiàn)類繼承的是哪一個呢?這個時候,實現(xiàn)類那里會編譯錯誤,大概意思就是說:有兩個相同的方法,編譯器不知道該如何選擇了。
2.53 如果實現(xiàn)的接口中有多個相同的默認(rèn)方法該怎么辦呢?
兩種處理方式,如下所示:
- 重寫多個接口中的相同的默認(rèn)方法
- 在實現(xiàn)類中指定要使用哪個接口中的默認(rèn)方法
2.54 那么JDK8中為什么會出現(xiàn)默認(rèn)方法呢?
? ?使用接口,使得我們可以面向抽象編程,但是其有一個缺點就是當(dāng)接口中有改動的時候,需要修改所有的實現(xiàn)類。在JDK8中,為了<font color=#008000>給已經(jīng)存在的接口增加新的方法并且不影響已有的實現(xiàn)</font>,所以引入了接口中的默認(rèn)方法實現(xiàn)。
? ?默認(rèn)方法允許在不打破現(xiàn)有繼承體系的基礎(chǔ)上改進(jìn)接口,<font color=#008000>解決了接口的修改與現(xiàn)有的實現(xiàn)不兼容的問題</font>。該特性在官方庫中的應(yīng)用是:給java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。在我們實際開發(fā)中,接口的默認(rèn)方法應(yīng)該謹(jǐn)慎使用,因為在復(fù)雜的繼承體系中,默認(rèn)方法可能引起歧義和編譯錯誤。
2.6 this與super的區(qū)別
- 在Java語言中,this用來指向當(dāng)前實例對象,它的一個非常重要的作用就是用來區(qū)分對象的成員變量與方法的形參(當(dāng)一個方法的形參與成員變量的名字相同時,就會覆蓋成員變量)
- super可以用來訪問父類的成員變量或者方法。當(dāng)子類的成員變量揮著方法與父類具有相同的名稱時,也會覆蓋父類的成員變量或者方法。要想訪問父類的成員變量或者方法,只能通過super關(guān)鍵字來訪問。
2.7內(nèi)部類的作用是什么,有哪些分類?
??內(nèi)部類可對同一包中其他類隱藏,內(nèi)部類方法可以訪問定義這個內(nèi)部類的作用域中的數(shù)據(jù),包括 private 數(shù)據(jù)。
??內(nèi)部類是一個編譯器現(xiàn)象,與虛擬機(jī)無關(guān)。編譯器會把內(nèi)部類轉(zhuǎn)換成常規(guī)的類文件,用 $ 分隔外部類名與內(nèi)部類名,其中匿名內(nèi)部類使用數(shù)字編號,虛擬機(jī)對此一無所知。
- 靜態(tài)內(nèi)部類: 屬于外部類,只加載一次。作用域僅在包內(nèi),可通過 外部類名.內(nèi)部類名
直接訪問,類內(nèi)只能訪問外部類所有靜態(tài)屬性和方法。HashMap 的 Node 節(jié)點,ReentrantLock 中的 Sync類,ArrayList 的 SubList 都是靜態(tài)內(nèi)部類。內(nèi)部類中還可以定義內(nèi)部類,如 ThreadLoacl 靜態(tài)內(nèi)部類ThreadLoaclMap 中定義了內(nèi)部類 Entry。 - 成員內(nèi)部類: 屬于外部類的每個對象,隨對象一起加載。不可以定義靜態(tài)成員和方法,可訪問外部類的所有內(nèi)容。
- 局部內(nèi)部類: 定義在方法內(nèi),不能聲明訪問修飾符,只能定義實例成員變量和實例方法,作用范圍僅在聲明類的代碼塊中。
- 匿名內(nèi)部類: 只用一次的沒有名字的類,可以簡化代碼,創(chuàng)建的對象類型相當(dāng)于 new 的類的子類類型。用于實現(xiàn)事件監(jiān)聽和其他回調(diào)。
2.8 Object 類中的方法有哪些?
- equals:檢測對象是否相等,默認(rèn)使用 == 比較對象引用,可以重寫 equals 方法自定義比較規(guī)則。equals方法規(guī)范:自反性、對稱性、傳遞性、一致性、對于任何非空引用 x,x.equals(null) 返回 false。
-
hashCode:散列碼是由對象導(dǎo)出的一個整型值,沒有規(guī)律,每個對象都有默認(rèn)散列碼,值由對象存儲地址得出。字符串散列碼由內(nèi)容導(dǎo)出,值可能相同。為了在集合中正確使用,一般需要同時重寫
equals 和 hashCode,要求 equals 相同 hashCode 必須相同,hashCode 相同 equals未必相同,因此 hashCode 是對象相等的必要不充分條件。
- toString:打印對象時默認(rèn)的方法,如果沒有重寫打印的是表示對象值的一個字符串。
-
clone:clone 方法聲明為 protected,類只能通過該方法克隆它自己的對象,如果希望其他類也能調(diào)用該方法必須定義該方法為
public。如果一個對象的類沒有實現(xiàn) Cloneable 接口,該對象調(diào)用 clone 方***拋出一個 CloneNotSupport 異常。默認(rèn)的 clone 方法是淺拷貝,一般重寫 clone 方法需要實現(xiàn) Cloneable接口并指定訪問修飾符為 public。 - getClass:返回包含對象信息的類對象。
- wait / notify / notifyAll:阻塞或喚醒持有該對象鎖的線程。
- finalize:確定一個對象死亡至少要經(jīng)過兩次標(biāo)記,如果對象在可達(dá)性分析后發(fā)現(xiàn)沒有與 GC Roots連接的引用鏈會被第一次標(biāo)記,隨后進(jìn)行一次篩選,條件是對象是否有必要執(zhí)行 finalize方法。假如對象沒有重寫該方法或方法已被虛擬機(jī)調(diào)用,都視為沒有必要執(zhí)行。如果有必要執(zhí)行,對象會被放置在 F-Queue隊列,由一條低調(diào)度優(yōu)先級的 Finalizer 線程去執(zhí)行。虛擬機(jī)會觸發(fā)該方法但不保證會結(jié)束,這是為了防止某個對象的 finalize方法執(zhí)行緩慢或發(fā)生死循環(huán)。只要對象在 finalize方法中重新與引用鏈上的對象建立關(guān)聯(lián)就會在第二次標(biāo)記時被移出回收集合。由于運行代價高昂且無法保證調(diào)用順序,在 JDK 9被標(biāo)記為過時方法,并不適合釋放資源。
3 關(guān)鍵字
3.1變量命名有哪些規(guī)則?
? ?在Java中,變量名、函數(shù)名、數(shù)組名統(tǒng)稱為標(biāo)識符。,Java語言標(biāo)識符只能由字母(a-z,A_Z)、數(shù)字(0-9)、下劃線(_)以及$組成。并且標(biāo)識符的第一個字符必須是除了數(shù)字的以上之一。
3.2 boolean、break和continue有什么區(qū)別?
- break用于直接跳出當(dāng)前循環(huán),不再執(zhí)行剩余代碼。注意:當(dāng)多層循環(huán)嵌套,并且break語句出現(xiàn)在嵌套循環(huán)的內(nèi)層循環(huán)時,它將僅僅只是終止了內(nèi)層循環(huán)的執(zhí)行,而不影響外層循環(huán)的執(zhí)行。
- continue用于停止當(dāng)次循環(huán),回到循環(huán)起始處,進(jìn)入下一次循環(huán)操作。continue語句之后的語句將不再執(zhí)行而是進(jìn)入下一次循環(huán)。
- return語句是一個跳轉(zhuǎn)語句,用來表示從一個方法返回,可以使程序控制回到調(diào)用該方法的的地方。當(dāng)執(zhí)行main()方法時,return語句可以使程序執(zhí)行返回到Java運行系統(tǒng)。
3.3 final、finally、finalize有什么區(qū)別?
- final用于聲明屬性、方法和類。分別用來表示屬性不可變、方法不可覆蓋,類不可被繼承。
- finally作為異常處理的一部分,它只能用在try/catch語句中,并且附帶一個語句塊,表示這段語句最終一定會被執(zhí)行,經(jīng)常被用在需要解放資源的情況下。
- finalize是object類的一個方法,在垃圾回收器執(zhí)行時會調(diào)用被回收對象的finalize()方法,可以覆蓋此方法來實現(xiàn)對其他資源的回收,例如關(guān)閉文件等。但是,一旦垃圾回收器準(zhǔn)備好釋放對象占用的空間,將首先調(diào)用nalize()方法,并且在下一次垃圾回收的時候,才會真正回收對象占用的內(nèi)存。
3.4 詳細(xì)說說final修飾符
? ?final用于聲明屬性,方法和類。分別表示<font color=Blue>屬性不可變,方法不可覆蓋,類不可繼承</font>。
- . <font color=Blue>final屬性:</font>被final修飾的變量不可變。但是Java中的變量不可變有兩種,一種是值不可變,另外一種是引用不可變。而final變量的不可變則是指的引用不可變,即它只能指向初始化時指向的那個對象,但是不關(guān)心對象內(nèi)容是否變化。不過由于這個原因,final變量必須背初始化。
- . <font color=Blue>final方法:</font>當(dāng)一個方法聲明為final方法時,該方法不允許被任何子類進(jìn)行重寫。但是子類仍然可以使用這個方法。
- . <font color=Blue>final參數(shù):</font>用來表示這個參數(shù)在函數(shù)內(nèi)部不能被修改。
- . <font color=Blue>final類:</font>當(dāng)一個類被聲明為final時,此類不能被繼承。其中的方法不能被重寫。但是里面的成員變量可以被修改。如果不想其被修改,可以將其設(shè)置成final的。注意:一個類,不能既被修飾成abstract的,又被修飾成final的。
3.5 JDK中哪些類是不可被繼承的?
??不能被繼承的類是被final關(guān)鍵字修飾的類。一般比較基本的類型都是用final修飾的,這是為了防止擴(kuò)展類無意間破壞原來方法的的實現(xiàn)。在JDK中,String,StringBuffer,StringBuilder等都是基本類型,都是不可被繼承的。
3.6.1 static關(guān)鍵字的作用是什么?
(1)為某特定數(shù)據(jù)類型揮著對象分配單一的內(nèi)存空間,而與創(chuàng)建的對象個數(shù)無關(guān)。
(2)在不創(chuàng)建對象的情況下也能通過類直接調(diào)用方法還或者使用類的屬性。
3.6.2static關(guān)鍵字的使用情況是什么?
主要有以下四種使用情況:
(1)static成員變量
??Java中主要有兩種變量:用static修飾的靜態(tài)變量和不用static修飾的實例變量。靜態(tài)變量屬于類,在內(nèi)存中只有一個復(fù)制,所有實例都指向同一個內(nèi)存地址。只要靜態(tài)變量所在的類被加載,此靜態(tài)變量就會被分配內(nèi)存空間,因此就可以被使用了。
??實例變量屬于對象,只有對象被實例化之后,才能通過對象調(diào)用實例變量。它在內(nèi)存中存在多個復(fù)制。
(2) static成員方法
??與變量類似,存在靜態(tài)方法和非靜態(tài)方法。靜態(tài)方法不需要對象創(chuàng)建即可被調(diào)用,而非靜態(tài)方法需要在對象被創(chuàng)建之后才能被調(diào)用。
??注意:static方法不能使用this和super關(guān)鍵字,不能調(diào)用非static方法和非static成員變量,只能訪問所屬類的靜態(tài)成員變量和成員方法。因為,當(dāng)static方法被調(diào)用時,這個類的對象可能還沒被創(chuàng)建,即使已經(jīng)被創(chuàng)建了,也無法確定調(diào)用哪個對象的方法。
(3)static代碼塊
??靜態(tài)代碼塊在類中是獨立于成員變量和成員函數(shù)的代碼塊的。它不在任何一個方法體中,JVM在加載類時會執(zhí)行靜態(tài)代碼塊。如果存在多個代碼塊,JVM將會按照順序來執(zhí)行。靜態(tài)代碼塊經(jīng)常被用來初始化靜態(tài)變量。這些靜態(tài)代碼塊只會被執(zhí)行一次。
(4)static內(nèi)部類
??static內(nèi)部類是指被聲明為static的內(nèi)部類,它可以不依賴外部類對象而被實例化。而通常的內(nèi)部類需要在外部類實例化之后才能實例化。
??靜態(tài)內(nèi)部類的名字不能與外部類相同,不能訪問外部類的普通成員變量,只能訪問外部類的靜態(tài)成員變量和靜態(tài)方法。
注意:只有內(nèi)部類才能被定義成static。
3.7 什么是實例變量,局部變量,類變量以及final變量?
- 實例變量:屬于對象,只有在對象實例化之后才能使用實例變量。每當(dāng)實例化一個對象時,就會創(chuàng)建一個副本并初始化,如果沒有顯式初始化,那么會初始化一個默認(rèn)值。各個對象中的實例變量互不影響。
- 局部變量:在方法中定義的變量,在使用前必須進(jìn)行初始化。
- 類變量:用static修飾的屬性。變量,歸類所有,只要類被加載,這變量就可以被使用。所有實例化的對象都可以共享這個類變量。
- final變量:表示這個變量為常量,不可修改。
3.8 說一下switch語句
3.8.1 什么是switch語句以及如何使用?
??switch語句用于多分支選擇,在使用switch(expr),expr只能是枚舉常量(內(nèi)部也是由整型或者字符類型實現(xiàn))或一個整數(shù)表達(dá)式,其中整數(shù)表達(dá)式可以是基本類型int或者其對應(yīng)的包裝類Integer,當(dāng)然也包括不同的長度整型,例如short。因為short、char、byte類型的值都能隱式的轉(zhuǎn)換為int類型,因此這些類型以及其對應(yīng)的包裝類型都能作為switch語句的表達(dá)式。但是long、double、float、string則不能,因為他們不能隱式地轉(zhuǎn)換為int類型。如果一定要使用上述類型作為表達(dá)式,需要將其強(qiáng)轉(zhuǎn)為int類型才行。
??此外,與switch語句對應(yīng)的是case語句,case語句之后可以是直接的常量數(shù)值、常量計算式、final類型的變量,但不能是變量或者帶有變量的表達(dá)式、浮點型數(shù)。
3.8.2 Java7switch語句開始支持String類型的實現(xiàn)原理是怎樣的?
??從本質(zhì)上講,switch語句對String類型的支持,其實是int類型值的匹配。
??它的實現(xiàn)原理主要是:通過對case后面String對象調(diào)用hashcode()方法,得到一個int類型的hash值,然后用這個hash值來唯一表示這個case。那么當(dāng)匹配時,會首先調(diào)用這個字符串的的hashcode()方法,獲取一個hash值(int類型),用這個hash值來匹配所有case,如果沒有成功,說明不存在,如果匹配成功,接著會調(diào)用字符串的equals()方法進(jìn)行匹配。因此String變量不能為null,同時,switch的case字句中使用的字符串也不能為null。
??另外需要注意的是一般必須在case語句后面添加break語句。因為一旦通過switch語句確定了入口點,就會順序執(zhí)行后面的代碼,直到遇到關(guān)鍵字break。否則,會執(zhí)行后面的語句,無論后面的語句是否符合當(dāng)前的case情況。
4 基本類型與運算
4.1 java提供了那些基本類型?
??Java提供了下面八種基本數(shù)據(jù)類型,這些數(shù)據(jù)類型的變量再聲明之后就會立刻在棧上被分配內(nèi)部空間。除了這些基本數(shù)據(jù)類型之外,其他變量都是引用類型變量,在聲明之后并不會立即被分配空間,只是存儲了一個內(nèi)存地址而已。
| 數(shù)據(jù)類型 | 內(nèi)存大小 | 默認(rèn)值 | 取值范圍 |
|---|---|---|---|
| byte | 1 B | (byte)0 | -128 ~ 127 |
| short | 2 B | (short)0 | -215 ~ 215-1 |
| int | 4 B | 0 | -231 ~ 231-1 |
| long | 8 B | 0L或0l | -263 ~ 263-1 |
| float | 4 B | 0.0F 或0.0f | ±3.4E+38(有效位數(shù) 6~7 位) |
| double | 8 B | 0.0D | ±1.7E+308(有效位數(shù) 15 位) |
| char | 英文 1B/ 中文 UTF-8 占 3B/ GBK 占 2B | '\u0000' | '\u0000' ~ '\uFFFF' |
| boolean | 單個變量 4B / 數(shù)組 1B | false | true、false |
??注意:Java中的數(shù)值類型都是有符號的,不存在無符號的數(shù),它們的取值范圍也是固定的,不會隨著硬件環(huán)境或者操作系統(tǒng)的改變而改變。除了以上八種基本數(shù)據(jù)類型外,Java還提供了一種基本類型void。
4.2 自動裝箱/拆箱是什么?
??每個基本數(shù)據(jù)類型都對應(yīng)一個包裝類,除了 int 和 char 對應(yīng) Integer 和 Character 外,其余基本數(shù)據(jù)類型的包裝類都是首字母大寫即可。
- 自動裝箱: 將基本數(shù)據(jù)類型包裝為一個包裝類對象,例如向一個泛型為 Integer 的集合添加 int 元素。
- 自動拆箱: 將一個包裝類對象轉(zhuǎn)換為一個基本數(shù)據(jù)類型,例如將一個包裝類對象賦值給一個基本數(shù)據(jù)類型的變量。
- 比較兩個包裝類數(shù)值要用 equals ,而不能用 == 。
4.2.1包裝類與基本類型的區(qū)別是什么?
??原始數(shù)據(jù)類型在傳遞參數(shù)的時候是按照值傳遞的,封裝類型按照引用傳遞。
??當(dāng)封裝類型和原始類型用作某個類的實例數(shù)據(jù)時,他們所指定的默認(rèn)值不同,。對象引用實例變量的默認(rèn)值為null,而原始類型實例變量的默認(rèn)值與它們的類型相關(guān)。
4.2.2 在Java語言中null值是什么?在內(nèi)存中null是什么?
??null不是一個合法的object實例,因此編譯器沒有為其分配內(nèi)存,它僅僅用于表明該引用目前沒有指向任何對象。其實,與C語言類似,null是將引用變量的值全部置為0.
4.3什么是不可變類?
??不可變類是指創(chuàng)建了這個類的實例之后,就不允許修改它的值了,也就是說,一個對象一旦被創(chuàng)建出來,在其整個生命周期中,它的成員變量,就不能被修改了,有點類似于常量。
??在Java類庫中,所有基本類型的包裝類都是不可變類,此外String也是不可變類。
4.3.1 String 是不可變類為什么值可以修改?
??String 類和其存儲數(shù)據(jù)的成員變量 value 字節(jié)數(shù)組都是 final 修飾的。對一個 String 對象的任何修改實際上都是創(chuàng)建一個新 String 對象,再引用該對象。只是修改 String 變量引用的對象,沒有修改原 String 對象的內(nèi)容。
4.3.2 String a = "a" + new String("b") 創(chuàng)建了幾個對象?
??常量和常量拼接仍是常量,結(jié)果在常量池,只要有變量參與拼接結(jié)果就是變量,存在堆。
??使用字面量時只創(chuàng)建一個常量池中的常量,使用 new 時如果常量池中沒有該值就會在常量池中新創(chuàng)建,再在堆中創(chuàng)建一個對象引用常量池中常量。因此 String a = "a" + new String("b") 會創(chuàng)建四個對象,常量池中的 a 和 b,堆中的 b 和堆中的 ab。
4.4字符串拼接的方式有哪些?
- 直接用 + ,底層用 StringBuilder 實現(xiàn)。只適用小數(shù)量,如果在循環(huán)中使用 + 拼接,相當(dāng)于不斷創(chuàng)建新的
StringBuilder 對象再轉(zhuǎn)換成 String 對象,效率極差。 - 使用 String 的 concat 方法,該方法中使用 Arrays.copyOf 創(chuàng)建一個新的字符數(shù)組 buf 并將當(dāng)前字符串
value 數(shù)組的值拷貝到 buf 中,buf 長度 = 當(dāng)前字符串長度 + 拼接字符串長度。之后調(diào)用 getChars 方法使用
System.arraycopy 將拼接字符串的值也拷貝到 buf 數(shù)組,最后用 buf 作為構(gòu)造參數(shù) new 一個新的 String
對象返回。效率稍高于直接使用 +。 - 使用 StringBuilder 或 StringBuffer,兩者的 append 方法都繼承自
AbstractStringBuilder,該方法首先使用 Arrays.copyOf 確定新的字符數(shù)組容量,再調(diào)用 getChars
方法使用 System.arraycopy 將新的值追加到數(shù)組中。StringBuilder 是 JDK5
引入的,效率高但線程不安全。StringBuffer 使用 synchronized 保證線程安全。