天天看點

Java面試知識點總結(Java基礎 )

文章目錄

  • ​​一、基礎篇​​
  • ​​1.1、Java基礎​​
  • ​​1.1.1、面向對象的特征:繼承、封裝、多态、抽象​​
  • ​​抽象:​​
  • ​​封裝​​
  • ​​繼承​​
  • ​​多态​​
  • ​​1.1.2、final, finally, finalize 的差別​​
  • ​​final​​
  • ​​finally​​
  • ​​finalize​​
  • ​​1.1.3、Exception、Error、運作時異常與一般異常有何異同​​
  • ​​Exception​​
  • ​​Error​​
  • ​​Runtime Exception​​
  • ​​checked exception​​
  • ​​1.1.4、請寫出5種常見到的Runtime Exception​​
  • ​​1.1.5、int 和 Integer 有什麼差別,Integer的值緩存範圍​​
  • ​​1.1.6、包裝類,裝箱和拆箱​​
  • ​​1.1.7、String、StringBuilder、StringBuffer​​
  • ​​1.1.8、重載和重寫的差別​​
  • ​​重寫(Override)​​
  • ​​方法的重寫規則​​
  • ​​Super關鍵字的使用​​
  • ​​重載(Overload)​​
  • ​​重載規則:​​
  • ​​重寫與重載之間的差別​​
  • ​​總結​​
  • ​​1.1.9、抽象類和接口有什麼差別​​
  • ​​抽象類(abstract class):​​
  • ​​接口(interface):​​
  • ​​抽象類和接口的差別:​​
  • ​​預設的方法實作​​
  • ​​實作​​
  • ​​抽象類可以有構造器,而接口不能有構造器​​
  • ​​抽象方法可以有public、protected和default這些修飾符​​
  • ​​接口方法預設修飾符是public。你不可以使用其它修飾符。​​
  • ​​抽象類在java語言中所表示的是一種繼承關系,一個子類隻能存在一個父類,但是可以存在多個接口。​​
  • ​​抽象方法比接口速度要快​​
  • ​​什麼時候使用抽象類和接口​​
  • ​​1.1.10、說說反射的用途及實作​​
  • ​​什麼是Java類中的反射?​​
  • ​​Java反射架構提供以下功能:​​
  • ​​反射的主要用途​​
  • ​​反射的基本運用​​
  • ​​獲得 Class 對象​​
  • ​​判斷是否為某個類的執行個體​​
  • ​​建立執行個體​​
  • ​​1.1.11、說說自定義注解的場景及實作​​
  • ​​元注解​​
  • ​​Java注解基本知識​​
  • ​​如何通路注解​​
  • ​​自定義注解使用場景​​
  • ​​定義注解​​
  • ​​使用注解​​
  • ​​通路注解​​
  • ​​1.1.12、HTTP請求的GET與POST方式的差別​​
  • ​​1.1.13、Session與Cookie差別​​
  • ​​Session與Cookie差別​​
  • ​​1.1.14、列出自己常用的JDK包​​
  • ​​1.1.15、MVC設計思想​​
  • ​​MVC優點​​
  • ​​MVC的不足之處​​
  • ​​1.1.16、equals與==的差別​​
  • ​​一、java當中的資料類型和“==”的含義:​​
  • ​​二、equals()方法介紹:​​
  • ​​總結:​​
  • ​​1.1.17、hashCode和equals方法的差別與聯系​​
  • ​​hashCode() 的作用​​
  • ​​hashCode() 和 equals() 的關系​​
  • ​​第一種 不會建立“類對應的散清單”​​
  • ​​**第二種 會建立“類對應的散清單”**​​
  • ​​1.1.18、什麼是Java序列化和反序列化,如何實作Java序列化?或者請解釋Serializable 接口的作用​​
  • ​​1.1.19、Object類中常見的方法,為什麼wait notify會放在Object裡邊?​​
  • ​​1.1.20、Java的平台無關性如何展現出來的​​
  • ​​1.1.21、JDK和JRE的差別​​
  • ​​Java 開發工具包 (JDK)​​
  • ​​Java虛拟機(JVM)​​
  • ​​Java運作時環境(JRE)​​
  • ​​1.1.22、Java 8有哪些新特性​​

一、基礎篇

1.1、Java基礎

1.1.1、面向對象的特征:繼承、封裝、多态、抽象

抽象:

從具體事物抽出、概括出它們共同的方面、本質屬性與關系等,而将個别的、非本質的方面、屬性與關系舍棄,這種思維過程,稱為抽象。

Object 類位于 java.lang 包中,是所有 Java 類的祖先,是對萬事萬物的抽象。Java 中的每個類都由它擴充而來。

封裝

封裝,就是把客觀事物封裝成抽象的類,并且類可以把自己的資料和方法隻讓可信的類或者對象操作,對不可信的類或對象進行資訊隐藏。一個類就是一個封裝了資料以及操作這些資料的代碼的邏輯實體。在一個對象内部,某些代碼或某些資料可以是私有的,不能被外界通路。通過這種方式,對象對内部資料提供了不同級别的保護,以防止程式中無關的部分意外的改變或錯誤的使用了對象的私有部分。

封裝是保證軟體部件具有優良的子產品性的基礎,封裝的目标就是要實作軟體部件的“高内聚、低耦合”,防止程式互相依賴性而帶來的變動影響。在面向對象的程式設計語言中,對象是封裝的最基本機關,面向對象的封裝比傳統語言的封裝更為清晰、更為有力。

繼承

繼承的成本很低,通過extends關鍵字就可以使用别人的方法,在定義和實作一個類的時候,可以在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的内容作為自己的内容,并可以加入若幹新的内容,或修改原來的方法使之更适合特殊的需要,這就是繼承。繼承是子類自動共享父類資料和方法的機制,這是類之間的一種關系,提高了軟體的可重用性和可擴充性。

但是我們也應該謹慎使用繼承,防止方法污染和方法爆炸

多态

多态是指程式中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在程式設計時并不确定,而是在程式運作期間才确定,即一個引用變量倒底會指向哪個類的執行個體對象,該引用變量發出的方法調用到底是哪個類中實作的方法,必須在由程式運作期間才能決定。因為在程式運作時才确定具體的類,這樣,不用修改源程式代碼,就可以讓引用變量綁定到各種不同的類實作上,進而導緻該引用調用的具體方法随之改變,即不修改程式代碼就可以改變程式運作時所綁定的具體代碼,讓程式可以選擇多個運作狀态,這就是多态性。多态性增強了軟體的靈活性和擴充性。

1.1.2、final, finally, finalize 的差別

final

用于聲明屬性,方法和類, 分别表示屬性不可變, 方法不可覆寫, 類不可繼承。

finally

異常處理語句結構的一部分,表示總是執行。finally關鍵字是對Java異常處理模型的最佳補充。finally結構使代碼總會執行,不關有無異常發生。使得finally可以維護對象的内部狀态,并可以清理非記憶體資源,比如關閉檔案的讀寫操作,或者是關閉資料庫的連接配接等等。

finalize

Object類的一個方法,這個方法是由垃圾收集器在确定這個對象沒有被引用是對這個對象調用的。它是Object類中定義的,是以,所有的類都繼承了它。finalize()方法是在垃圾收集器删除對象之前對這個對象調用的。可以覆寫此方法提供垃圾收集時的其他資源回收,例如關閉檔案等。 JVM不保證此方法總被調用。

1.1.3、Exception、Error、運作時異常與一般異常有何異同

Exception

Exception :表示可恢複的例外,可補足的。

Error

Error:表示由JVM 所偵測到的無法預期的錯誤,由于這是屬于 JVM 層次的嚴重錯誤,導緻 JVM 無法繼續執行,是以,這是不可捕捉的,無法采取任何恢複的操作,頂多隻能顯示錯誤資訊。

Runtime Exception

Runtime Exception,也叫運作時異常,我們可以不處理。當出現這樣的異常時,總是由虛拟機接管。比如:我們從來沒有人去處理過 NullPointerException 異常,它就是運作時異常,并且,這種異常還是最常見的異常之一。

處理 RuntimeException 的原則是:假如出現 RuntimeException,那麼一定是程式員的錯誤,例如,可以通過檢查數組小标和數組邊界來避免越界通路異常。

出現運作時異常後,系統會把異常一直往上抛,一直遇到處理代碼。如果沒有處理塊,到最上層,如果是多線程就由 Thread.run() 抛出,如果是單線程就被 main() 抛出。抛出之後,如果是線程,這個線程也就退出了。如果主程式抛出的異常,那麼這個程式也就退出了。運作時異常是 Exception 的子類,也就是一般異常的特點,也可以被catch 塊處理的。隻不夠往往我們不處理罷了。也就是說,我如果不對運作時異常進行處理,那麼出現運作時異常之後,要麼線程終止,要麼主程式終止。

如果不想終止,則必須捕捉所有的運作時異常,決不讓這個線程退出。隊列裡面出現異常資料了,正常的處理應該是把異常資料舍棄,然後記錄日志。不應該由于異常資料而影響下面對正常資料的處理。在這個場景這樣的處理可能是一個比較好的應用,但并不代表在所有的場景都應該如此。如果在其它場景,遇到了一些錯誤,如果退出程式比較好,這時你就可以不太理會運作時異常,或者是通過對異常的處理顯示的控制程式退出。

異常處理的目标之一就是為了把程式從異常中恢複出來。

checked exception

checked 異常也就是經常遇到的 IO 異常以及 SQL 異常都是這種異常。對于這種異常,Java 編譯器強制要求我們必須對出現的這些異常進行 catch。是以,面對這種異常不管我們是否願意,隻能自己去寫一大堆 catch 塊去處理可能的異常。這類異常一般是外部錯誤,例如試圖從檔案尾後讀取資料錯誤,這并不是程式本身的錯誤,而是在應用環境中出現的外部錯誤。

在定義方法時必須聲明所有可能會抛出的checked exception;在調用這個方法時,必須捕獲它的checked exception,不然就得把它的exception傳遞下去;checked exception是從java.lang.Exception類衍生出來的。

1.1.4、請寫出5種常見到的Runtime Exception

ClassCastException 類型強制轉換異常

Object x = new Integer(0);
System.out.println((String)x);      

當試圖将對象強制轉換為不是執行個體的子類時,抛出該異常

ArithmeticException 算術異常類

int a=5/0;      

一個整數“除以零”時,抛出異常

NullPointerException 空指針異常類

String s=null;
int size=s.size();      

當應用程式試圖在需要對象的地方使用 null 時,抛出異常

StringIndexOutOfBoundsException

"hello".indexOf(-1);      

訓示索引或者為負,或者超出字元串的大小,抛出異常

NegativeArraySizeException 數組負下标異常

數組大小為負值異常。當使用負數大小值建立數組時抛出該異常

String[] ss=new String[-1];      

如應用程式試圖建立大小為負的數組,則抛出異常

IllegalArgumentException 參數異常

抛出的異常表明向方法傳遞了一個不合法或不正确的參數

NumberFormatException 數字格式異常

當試圖将一個String轉換為指定的數字類型,而該字元串确不滿足數字類型要求的格式時,抛出該異常

ArrayIndexOutOfBoundsException 數組下标越界異常

當使用的數組下标超出數組允許範圍時,抛出該異常

ClassNotFoundException 找不到類異常

當應用試圖根據字元串形式的類名構造類,而在周遊CLASSPAH之後找不到對應名稱的class檔案時,抛出該異常

ArrayStoreException 數組存儲異常

當向數組中存放非數組聲明類型對象時抛出

IOException 輸入輸出異常

NoSuchMethodException 方法未找到異常

FileNotFoundException 檔案未找到異常

1.1.5、int 和 Integer 有什麼差別,Integer的值緩存範圍

  • Integer是int的包裝類;int是基本資料類型;
  • Integer變量必須執行個體化後才能使用;int變量不需要;
  • Integer實際是對象的引用,指向此new的Integer對象;int是直接存儲資料值 ;
  • Integer的預設值是null;int的預設值是0。

Integer的值緩存範圍為 -128~127

1.1.6、包裝類,裝箱和拆箱

自動拆裝箱是Java1.5的一個新特性,自動裝箱/拆箱大大友善了基本類型資料和它們包裝類的使用。

自動裝箱:基本類型自動轉為包裝類(int —> Integer)

自動拆箱:包裝類自動轉為基本類型(Integer —> int)

java對于自動裝箱與拆箱的設計,是一種模式:叫享元模式(flyweight)

包裝類是針對于原生資料類型的包裝。因為有8個原生資料類型,是以對應有8個包裝類。所有的包裝類(8個)都位于java.lang下。

Java面試知識點總結(Java基礎 )

每個包裝類的對象可以封裝一個相應的基本類型的資料,并提供了其它一些有用的方法。包裝類對象一經建立,其内容(所封裝的基本類型資料值)不可改變。

基本類型和對應的包裝類可以互相裝換:

  • 由基本類型向對應的包裝類轉換稱為裝箱,例如把 int 包裝成 Integer 類的對象;
  • 包裝類向對應的基本類型轉換稱為拆箱,例如把 Integer 類的對象重新簡化為 int。

1.1.7、String、StringBuilder、StringBuffer

String 字元串常量

StringBuffer 字元串變量(線程安全)

StringBuilder 字元串變量(非線程安全)

簡要的說, String 類型和 StringBuffer 類型的主要性能差別其實在于 String 是不可變的對象, 是以在每次對 String 類型進行改變的時候其實都等同于生成了一個新的 String 對象,然後将指針指向新的 String 對象,是以經常改變内容的字元串最好不要用 String ,因為每次生成對象都會對系統性能産生影響,特别當記憶體中無引用對象多了以後, JVM 的 GC 就會開始工作,那速度是一定會相當慢的。

而如果是使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象,再改變對象引用。是以在一般情況下我們推薦使用 StringBuffer ,特别是字元串對象經常改變的情況下。而在某些特别情況下, String 對象的字元串拼接其實是被 JVM 解釋成了 StringBuffer 對象的拼接,是以這些時候 String 對象的速度并不會比 StringBuffer 對象慢,而特别是以下的字元串對象生成中, String 效率是遠要比 StringBuffer 快的:

String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“simple”).append(“ test”);      

你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不占優勢。其實這是 JVM 的一個把戲,在 JVM 眼裡,這個

​​

​String S1 = “This is only a” + “ simple” + “test”;​

​​其實就是:

​​

​String S1 = “This is only a simple test”;​

​是以當然不需要太多的時間了。但大家這裡要注意的是,如果你的字元串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:

String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;      

這時候 JVM 會規規矩矩的按照原來的方式去做

在大部分情況下StringBuilder >StringBuffer > String

StringBuilde

​​

​java.lang.StringBuilder​

​可變的字元序列是Java1.5新增的。此類提供一個與 StringBuffer 相容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字元串緩沖區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先采用該類,因為在大多數實作中,它比 StringBuffer 要快。兩者的方法基本相同。

1.1.8、重載和重寫的差別

重寫(Override)

重寫是子類對父類的允許通路的方法的實作過程進行重新編寫, 傳回值和形參都不能改變。即外殼不變,核心重寫!

重寫的好處在于子類可以根據需要,定義特定于自己的行為。 也就是說子類能夠根據需要實作父類的方法。

重寫方法不能抛出新的檢查異常或者比被重寫方法申明更加寬泛的異常。例如: 父類的一個方法申明了一個檢查異常 IOException,但是在重寫這個方法的時候不能抛出 Exception 異常,因為 Exception 是 IOException 的父類,隻能抛出 IOException 的子類異常。

在面向對象原則裡,重寫意味着可以重寫任何現有方法。執行個體如下:

class Animal{
   public void move(){
      System.out.println("動物可以移動");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 對象
      Animal b = new Dog(); // Dog 對象
 
      a.move();// 執行 Animal 類的方法
 
      b.move();//執行 Dog 類的方法
   }
}      

以上執行個體編譯運作結果如下:

動物可以移動
狗可以跑和走      

在上面的例子中可以看到,盡管b屬于Animal類型,但是它運作的是Dog類的move方法。

這是由于在編譯階段,隻是檢查參數的引用類型。

然而在運作時,Java虛拟機(JVM)指定對象的類型并且運作該對象的方法。

是以在上面的例子中,之是以能編譯成功,是因為Animal類中存在move方法,然而運作時,運作的是特定對象的方法。

思考以下例子:

class Animal{
   public void move(){
      System.out.println("動物可以移動");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
   public void bark(){
      System.out.println("狗可以吠叫");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 對象
      Animal b = new Dog(); // Dog 對象
 
      a.move();// 執行 Animal 類的方法
      b.move();//執行 Dog 類的方法
      b.bark();
   }
}      

以上執行個體編譯運作結果如下:

TestDog.java:30: cannot find symbol
symbol  : method bark()
location: class Animal
                b.bark();
                 ^      

該程式将抛出一個編譯錯誤,因為b的引用類型Animal沒有bark方法。

方法的重寫規則

  • 參數清單必須完全與被重寫方法的相同;
  • 傳回類型必須完全與被重寫方法的傳回類型相同;
  • 通路權限不能比父類中被重寫的方法的通路權限更低。例如:如果父類的一個方法被聲明為public,那麼在子類中重寫該方法就不能聲明為protected。
  • 父類的成員方法隻能被它的子類重寫。
  • 聲明為final的方法不能被重寫。
  • 聲明為static的方法不能被重寫,但是能夠被再次聲明。
  • 子類和父類在同一個包中,那麼子類可以重寫父類所有方法,除了聲明為private和final的方法。
  • 子類和父類不在同一個包中,那麼子類隻能夠重寫父類的聲明為public和protected的非final方法。
  • 重寫的方法能夠抛出任何非強制異常,無論被重寫的方法是否抛出異常。但是,重寫的方法不能抛出新的強制性異常,或者比被重寫方法聲明的更廣泛的強制性異常,反之則可以。
  • 構造方法不能被重寫。
  • 如果不能繼承一個方法,則不能重寫這個方法。

Super關鍵字的使用

當需要在子類中調用父類的被重寫方法時,要使用super關鍵字。

class Animal{
   public void move(){
      System.out.println("動物可以移動");
   }
}
 
class Dog extends Animal{
   public void move(){
      super.move(); // 應用super類的方法
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
 
      Animal b = new Dog(); // Dog 對象
      b.move(); //執行 Dog類的方法
 
   }
}      

以上執行個體編譯運作結果如下:

動物可以移動
狗可以跑和走      

重載(Overload)

重載(overloading) 是在一個類裡面,方法名字相同,而參數不同。傳回類型可以相同也可以不同。

每個重載的方法(或者構造函數)都必須有一個獨一無二的參數類型清單。

最常用的地方就是構造器的重載。

重載規則:

  • 被重載的方法必須改變參數清單(參數個數或類型不一樣);
  • 被重載的方法可以改變傳回類型;
  • 被重載的方法可以改變通路修飾符;
  • 被重載的方法可以聲明新的或更廣的檢查異常;
  • 方法能夠在同一個類中或者在一個子類中被重載。
  • 無法以傳回值類型作為重載函數的區分标準。

重寫與重載之間的差別

差別點 重載方法 重寫方法
參數清單 必須修改 一定不能修改
傳回類型 可以修改 一定不能修改
異常 可以修改 可以減少或删除,一定不能抛出新的或者更廣的異常
通路 可以修改 一定不能做更嚴格的限制(可以降低限制)

總結

方法的重寫(Overriding)和重載(Overloading)是java多态性的不同表現,重寫是父類與子類之間多态性的一種表現,重載可以了解成多态的具體表現形式。

  • 方法重載是一個類中定義了多個方法名相同,而他們的參數的數量不同或數量相同而類型和次序不同,則稱為方法的重載(Overloading)。
  • 方法重寫是在子類存在方法與父類的方法的名字相同,而且參數的個數與類型一樣,傳回值也一樣的方法,就稱為重寫(Overriding)。
  • 方法重載是一個類的多态性表現,而方法重寫是子類與父類的一種多态性表現。
Java面試知識點總結(Java基礎 )
Java面試知識點總結(Java基礎 )

1.1.9、抽象類和接口有什麼差別

抽象類(abstract class):

使用abstract修飾符修飾的類。官方點的定義就是:如果一個類沒有包含足夠多的資訊來描述一個具體的對象,這樣的類就是抽象類。

實際點來說,一個抽象類不能執行個體化,因為“沒有包含足夠多的資訊來描述一個具體的對象”。但終歸屬于類,是以仍然擁有普通類一樣的定義。依然可以在類的實體(直白點就是能在{}裡面)定義成員變量,成員方法,構造方法等。

接口(interface):

官方定義:接口在Java中是一個抽象類型,是抽象方法的集合。一個類通過繼承接口的方式,進而繼承接口的抽象方法。

從定義上看,接口是個集合,并不是類。類描述了屬性和方法,而接口隻包含方法(未實作的方法)。接口和抽象類一樣不能被執行個體化,因為不是類。但是接口可以被實作(使用 implements 關鍵字)。實作某個接口的類必須在類中實作該接口的全部方法。雖然接口内的方法都是抽象的(和抽象方法很像,沒有實作)但是不需要abstract關鍵字。

接口中沒有構造方式(因為接口不是類)

接口中的方法必須是抽象的(不能實作)

接口中除了static、final變量,不能有其他變量

接口支援多繼承(一個類可以實作多個接口)

抽象類和接口的差別:

預設的方法實作

抽象類可以有預設的方法實作完全是抽象的。接口根本不存在方法的實作。

抽象類中可以有已經實作了的方法,也可以有被abstract修飾的方法(抽象方法),因為存在抽象方法,是以該類必須是抽象類。但是接口要求隻能包含抽象方法,抽象方法是指沒有實作的方法。是以就不能像抽象類那麼無賴了,接口就根本不能存在方法的實作。

實作

抽象類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實作。

子類使用關鍵字implements來實作接口。它需要提供接口中所有聲明的方法的實作。

抽象類雖然不能執行個體化來使用,但是可以被繼承,讓子類來具體實作父類的所有抽象方法。有點老子沒完成的夢想交給兒子來完成,但是如果子類将抽象方法沒有全部實作,就必須把自己也修飾成抽象類,交于繼承它的子類來完成實作。就相當于,兒子能力不夠也沒完成老爹的夢想,現在兒子等着再生兒子(被繼承),然後讓孫子去完成。以此類推,知道沒有抽象函數。

接口的實作,通過implements關鍵字。實作該接口的類,必須把接口中的所有方法給實作。不能再推給下一代。和抽象類相比,抽象類是将夢想傳給家族,一代一代去完成。那麼接口就是掌門人找大師兄來完成幫派的鴻星偉業,這時候就隻有一次希望,要麼有能力就實作,沒能力就不要接。

抽象類可以有構造器,而接口不能有構造器

抽象類屬于類,天生享有類的所有特性(但是不能執行個體化),當然包括類的構造方法,也就是構造器。

但是接口是所有抽象方法的集合,注意,是集合,不是類。當然沒有構造方法一說,更别提什麼構造器了。

抽象方法可以有public、protected和default這些修飾符
接口方法預設修飾符是public。你不可以使用其它修飾符。
抽象類在java語言中所表示的是一種繼承關系,一個子類隻能存在一個父類,但是可以存在多個接口。
抽象方法比接口速度要快

接口是稍微有點慢的,因為它需要時間去尋找在類中實作的方法。

如果你往抽象類中添加新的方法,你可以給它提供預設的實作。是以你不需要改變你現在的代碼。

如果你往接口中添加方法,那麼你必須改變實作該接口的類。

什麼時候使用抽象類和接口

  • 如果你擁有一些方法并且想讓它們中的一些有預設實作,那麼使用抽象類吧。
  • 如果你想實作多重繼承,那麼你必須使用接口。由于Java不支援多繼承,子類不能夠繼承多個類,但可以實作多個接口。是以你就可以使用接口來解決它。
  • 如果基本功能在不斷改變,那麼就需要使用抽象類。如果不斷改變基本功能并且使用接口,那麼就需要改變所有實作了該接口的類。

1.1.10、說說反射的用途及實作

什麼是Java類中的反射?

當程式運作時,允許改變程式結構或變量類型,這種語言稱為動态語言。我們認為 Java 并不是動态語言,但是它卻又一個非常突出的動态相關的機制,俗稱:反射。

Reflection 是Java 程式開發語言的特征之一,它允許運作中的 Java 程式擷取自身的資訊,并且可以操作類和對象的内部屬性。

通過反射,我們可以在運作時獲得程式或程式集中每一個類型成員和成員變量的資訊。

程式中一般的對象類型都是在編譯期就确定下來的,而Java 反射機制可以動态的建立對象并調用其屬性,這樣對象的類型在編譯期是未知的。是以我們可以通過反射機制直接建立對象即使這個對象在編譯期是未知的,

反射的核心:是 JVM 在運作時 才動态加載的類或調用方法或屬性,它不需要事先(寫代碼的時候或編譯期)知道運作對象是誰。

Java反射架構提供以下功能:

  1. 在運作時判斷任意一個對象所屬的類
  2. 在運作時構造任意一個類的對象
  3. 在運作時判斷任意一個類所具有的成員變量和方法(通過反射設定可以調用 private)
  4. 在運作時調用人一個對象的方法

反射的主要用途

很多人都認為反射在實際Java中開發應用中并不廣泛,其實不然。

當我們在使用 IDE(如 Eclipse\IDEA)時,當我們輸入一個對象或者類并向調用它的屬性和方法時,一按 (“.”)點号,編譯器就會自動列出她的屬性或方法,這裡就會用到反射。

反射最重要的用途就是開發各種通用架構。

反射的基本運用

以上我們提到了反射可以拟用于判斷任意對象所屬的類,獲得 Class對象,構造人一個對象以及調用一個對象。這裡我們介紹一下基本反射功能的事先(反射相關類一般都在 java.lang.relfect包裡)。

獲得 Class 對象

使用 Class類的 forName() 靜态方法:

public static Class<?> forName(String className)      

在JDBC開發中常用此方法加載資料庫驅動:

Class.forName(driver)

直接擷取某一個對象的 class,比如:

Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;      

調用某個對象的getClass() 方法,比如:

StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();      
判斷是否為某個類的執行個體

一般地,我們用 instanceof關鍵字來判斷是否為某個類的執行個體。同時我們也可以借助反射中Class對象的 isInstance()方法來判斷是否為某個類的執行個體,它是一個 Native 方法:

public native boolean isInstance(Object obj);      
建立執行個體

通過反射來生成對象主要有兩種方式。

使用 Class 對象的 newInstance() 方法來建立對象對應類的執行個體

Class<?> c  = String.calss;
Object str = c.getInstance();      

先通過 Class 對象擷取制定的 Constructor 對象,在調用 Constructor 對象的 newInstance() 方法來建立執行個體

這種方法可以用指定的構造器構造類的執行個體。

//擷取String所對應的Class對象
   Class<?> c = String.class;
  //擷取String類帶一個String參數的構造器
  Constructor constructor = c.getConstructor(String.class);
  //根據構造器建立執行個體
  Object obj = constructor.newInstance("23333");
  System.out.println(obj);      

1.1.11、說說自定義注解的場景及實作

元注解

java中有四種元注解:@Retention、@Inherited、@Documented、@Target

Java注解基本知識

  • 注解是代碼的附屬資訊,不能幹擾代碼的正常執行,無論删除或增加注解,代碼都能夠正常執行
  • 定義注解使用@interface修飾符
  • Java預定義注解被稱為元注解,它們被Java編譯器使用,比如:@Retention注解和@Target注解,前者定義注解的保留期限,後者定義注解的應用目标
  • 注解的成員聲明和接口的方法聲明類似,還可以使用default關鍵字指定成員的預設值
  • 如果注解隻有一個成員,則成員名必須取名為​

    ​value()​

    ​,使用時如果給成員指派可以不寫成員名和指派符号’=’
  • 如果注解有多個成員,在指派時如果隻給​

    ​value()​

    ​成員指派,也可以不寫成員名和指派符号’=’
  • 如果在指派時要同時給多個成員指派,則必須寫成員名和指派符号’=’
  • 所有注解類都隐式繼承與​

    ​java.lang.annotation.Annotation​

    ​,但是注解不允許顯示繼承于其他的接口

如何通路注解

通過Java的反射機制讀取注解的資訊

若要通過反射來讀取注解資訊,那麼被定義的注解的保留期限必須是RententionPolicy.RUNTIME,

隻有該政策下的注解資訊會被保留在目标類代碼的位元組碼中,并且當類加載器加載位元組碼時會将注解資訊加載到JVM中

自定義注解使用場景

  • 類屬性自動指派。
  • 驗證對象屬性完整性。
  • 代替配置檔案功能,像spring基于注解的配置。
  • 可以生成文檔,像Java代碼注釋中的@see,@param等

定義注解

@Retention(RetentionPolicy.RUNTIME) // 定義@NeedTest注解的保留期限,該注解會儲存到目标類的位元組碼中,并且會被類加載器加載到JVM中
@Target(ElementType.METHOD) // 定義@NeedTest注解的應用目标,這是一個方法級别的注解
public @interface NeedTest {
    boolean value() default true; // 單個成員,成員名必須是value(), 預設值是true
}      

使用注解

public class MyService {

    public void saySomething(){
        System.out.println("say something");
    }

    @NeedTest(true) // 成員名value()設定為true
    public void sayHello(String name){
        System.out.println("hello "+ name);
    }

    @NeedTest(false) // 成員名value()設定為false
    public void sayHi(String name){
        System.out.println("hi " + name);
    }
}      

通路注解

@Test
public void testCustomAnnotation() {
    Class clazz = MyService.class;
    Method[] methods = clazz.getDeclaredMethods();
    if (methods.length == 0){
        System.out.println("method " + clazz.getName() + " has no declared method");
    }else {
        for (Method method : methods){
            NeedTest annotation = method.getAnnotation(NeedTest.class); // 所有自定義的注解都隐式繼承自java.lang.annotation.Annotation接口,但是不允許顯示繼承其他接口
            if (annotation == null){
                System.out.println("method" + method.getName() + " has not annotated @NeedTest");
            }else {
                boolean value = annotation.value();
                System.out.println(method.getName() + " has annotated @NeedTest and value = " + value);
            }
        }
    }
}      

1.1.12、HTTP請求的GET與POST方式的差別

GET和POST是HTTP請求的兩種基本方法,最直覺的差別就是GET把參數包含在URL中,POST通過request body傳遞參數。get參數有長度限制(受限于url長度,具體的數值取決于浏覽器和伺服器的限制),而post無限制

GET的語義是請求擷取指定的資源。GET方法是安全、幂等、可緩存的(除非有 Cache-ControlHeader的限制),GET方法的封包主體沒有任何語義。

POST的語義是根據請求負荷(封包主體)對指定的資源做出處理,具體的處理方式視資源類型而不同。POST不安全,不幂等,(大部分實作)不可緩存。

1.1.13、Session與Cookie差別

Cookie是用戶端儲存使用者資訊的一種機制,用來記錄使用者的一些資訊。如何識别特定的客戶呢?cookie就可以做到。每次HTTP請求時,用戶端都會發送相應的Cookie資訊到服務端。它的過期時間可以任意設定,如果你不主動清除它,在很長一段時間裡面都可以保留着,即便這之間你把電腦關機了。

Session是在無狀态的HTTP協定下,服務端記錄使用者狀态時用于辨別具體使用者的機制。它是在服務端儲存的用來跟蹤使用者的狀态的資料結構,可以儲存在檔案、資料庫或者叢集中。在浏覽器關閉後這次的Session就消失了,下次打開就不再擁有這個Session。其實并不是Session消失了,而是Session ID變了,伺服器端可能還是存着你上次的Session ID及其Session 資訊,隻是他們是無主狀态,也許一段時間後會被删除。

Session與Cookie差別

  1. cookie資料存放在客戶的浏覽器上,session資料放在伺服器上。
  2. cookie不是很安全,别人可以分析存放在本地的COOKIE并進行COOKIE欺騙,考慮到安全應當使用session。
  3. session會在一定時間内儲存在伺服器上。當通路增多,會比較占用你伺服器的性能,考慮到減輕伺服器性能方面,應當使用COOKIE。
  4. 單個cookie儲存的資料不能超過4K,很多浏覽器都限制一個站點最多儲存20個cookie。

是以個人建議:

将登陸資訊等重要資訊存放為SESSION

其他資訊如果需要保留,可以放在COOKIE中

1.1.14、列出自己常用的JDK包

java.lang: 這個是系統的基礎類,比如String、Math、Integer、System和Thread, 提供常用功能。

​​​java.io​​​: 這裡面是所有輸入輸出有關的類,比如檔案操作等

​​​java.net​​​: 這裡面是與網絡有關的類,比如URL,URLConnection等。

java.util : 這個是系統輔助類,特别是集合類Collection,List,Map等。

java.sql: 這個是資料庫操作的類,Connection, Statememt,ResultSet等

1.1.15、MVC設計思想

MVC模式(Model–view–controller)是軟體工程中的一種軟體架構模式,把軟體系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。

  • 視圖:管理作為位圖展示到螢幕上的圖形和文字輸出;
  • 控制器:翻譯使用者的輸入并依照使用者的輸入操作模型和視圖;
  • 模型:管理應用的行為和資料,響應資料請求(經常來自視圖)和更新狀态的指令(經常來自控制器);
Java面試知識點總結(Java基礎 )

MVC優點

  1. 可以為一個模型在運作時同時建立和使用多個視圖。變化-傳播機制可以確定所有相關的視圖及時得到模型資料變化,進而使所有關聯的視圖和控制器做到行為同步。
  2. 視圖與控制器的可接插性,允許更換視圖和控制器對象,而且可以根據需求動态的打開或關閉、甚至在運作期間進行對象替換。
  3. 模型的可移植性。因為模型是獨立于視圖的,是以可以把一個模型獨立地移植到新的平台工作。需要做的隻是在新平台上對視圖和控制器進行新的修改。
  4. 潛在的架構結構。可以基于此模型建立應用程式架構,不僅僅是用在設計界面的設計中。

MVC的不足之處

  1. 增加了系統結構和實作的複雜性。對于簡單的界面,嚴格遵循MVC,使模型、視圖與控制器分離,會增加結構的複雜性,并可能産生過多的更新操作,降低運作效率。
  2. 視圖與控制器間的過于緊密的連接配接。視圖與控制器是互相分離,但确實聯系緊密的部件,視圖沒有控制器的存在,其應用是很有限的,反之亦然,這樣就妨礙了他們的獨立重用。
  3. 視圖對模型資料的低效率通路。依據模型操作接口的不同,視圖可能需要多次調用才能獲得足夠的顯示資料。對未變化資料的不必要的頻繁通路,也将損害操作性能。

通俗來講

優點:

  1. 視圖控制模型分離, 提高代碼重用性。
  2. 提高開發效率。
  3. 便于後期維護, 降低維護成本。
  4. 友善多開發人員間的分工。

缺點:

  1. 清晰的構架以代碼的複雜性為代價, 對小項目優可能反而降低開發效率。
  2. 運作效率相對較低 (通過系統的緩存機制來減小對性能的影響)
  3. 有時會導緻級聯的修改。這種修改尤其展現在自上而下的方向。如果在表示層中需要增加一個功能,為保證其設計符合分層式結構,可能需要在相應的業務邏輯層和資料通路層中都增加相應的代碼(可通過設計模式優化)
  4. 控制層和表現層有時會過于緊密,導緻沒有真正分離和重用

1.1.16、equals與==的差別

一、java當中的資料類型和“==”的含義:

  • 基本資料類型(也稱原始資料類型) :byte,short,char,int,long,float,double,boolean。他們之間的比較,應用雙等号(==),比較的是他們的值。
  • 引用資料類型:當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放位址(确切的說,是堆記憶體位址)。

注:對于第二種類型,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。因為每new一次,都會重新開辟堆記憶體空間。

二、equals()方法介紹:

JAVA當中所有的類都是繼承于Object這個超類的,在Object類中定義了一個equals的方法,equals的源碼是這樣寫的:

public boolean equals(Object obj) {
    //this - s1
    //obj - s2
    return (this == obj);
}      

可以看到,這個方法的初始預設行為是比較對象的記憶體位址值,一般來說,意義不大。是以,在一些類庫當中這個方法被重寫了,如String、Integer、Date。在這些類當中equals有其自身的實作(一般都是用來比較對象的成員變量值是否相同),而不再是比較類在堆記憶體中的存放位址了。

是以說,對于複合資料類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是記憶體中的存放位置的位址值,跟雙等号(==)的結果相同;如果被複寫,按照複寫的要求來。

總結:

== 的作用:

  基本類型:比較的就是值是否相同

  引用類型:比較的就是位址值是否相同

equals 的作用:

  引用類型:預設情況下,比較的是位址值。

注:不過,我們可以根據情況自己重寫該方法。一般重寫都是自動生成,比較對象的成員變量值是否相同

1.1.17、hashCode和equals方法的差別與聯系

hashCode() 的作用

hashCode() 的作用是擷取哈希碼,也稱為散列碼;它實際上是傳回一個int整數。這個哈希碼的作用是确定該對象在哈希表中的索引位置。

hashCode() 定義在JDK的Object.java中,這就意味着Java中的任何類都包含有hashCode() 函數。

雖然,每個Java類都包含hashCode() 函數。但是,僅僅當建立并某個“類的散清單”(關于“散清單”見下面說明)時,該類的hashCode() 才有用(作用是:确定該類的每一個對象在散清單中的位置;其它情況下(例如,建立類的單個對象,或者建立類的對象數組等等),類的hashCode() 沒有作用。

上面的散清單,指的是:Java集合中本質是散清單的類,如HashMap,Hashtable,HashSet。

也就是說:**hashCode() 在散清單中才有用,在其它情況下沒用。**在散清單中hashCode() 的作用是擷取對象的散列碼,進而确定該對象在散清單中的位置。

我們都知道,散清單存儲的是鍵值對(key-value),它的特點是:能根據“鍵”快速的檢索出對應的“值”。這其中就利用到了散列碼!

散清單的本質是通過數組實作的。當我們要擷取散清單中的某個“值”時,實際上是要擷取數組中的某個位置的元素。而數組的位置,就是通過“鍵”來擷取的;更進一步說,數組的位置,是通過“鍵”對應的散列碼計算得到的。

hashCode() 和 equals() 的關系

以“類的用途”來将“hashCode() 和 equals()的關系”分2種情況來說明。

第一種 不會建立“類對應的散清單”

這裡所說的“不會建立類對應的散清單”是說:我們不會在HashSet, Hashtable, HashMap等等這些本質是散清單的資料結構中,用到該類。例如,不會建立該類的HashSet集合。

在這種情況下,該類的“hashCode() 和 equals() ”沒有半毛錢關系的!

這種情況下,equals() 用來比較該類的兩個對象是否相等。而hashCode() 則根本沒有任何作用,是以,不用理會hashCode()。

下面,我們通過示例檢視類的兩個對象相等 以及 不等時hashCode()的取值。

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比較equals() 傳回true 以及 傳回false時, hashCode()的值。
 *
 */
public class NormalHashCodeTest{

    public static void main(String[] args) {
        // 建立2個相同内容的Person對象,
        // 再用equals比較它們是否相等
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n", p1.equals(p3), p1.hashCode(), p3.hashCode());
    }

    /**
     * @desc Person類。
     */
    private static class Person {
        int age;
        String name;

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

        public String toString() {
            return name + " - " +age;
        }

        /** 
         * @desc 覆寫equals方法 
         */  
        public boolean equals(Object obj){  
            if(obj == null){  
                return false;  
            }  
              
            //如果是同一個對象傳回true,反之傳回false  
            if(this == obj){  
                return true;  
            }  
              
            //判斷是否類型相同  
            if(this.getClass() != obj.getClass()){  
                return false;  
            }  
              
            Person person = (Person)obj;  
            return name.equals(person.name) && age==person.age;  
        } 
    }
}      

運作結果:

p1.equals(p2) : true; p1(1169863946) p2(1901116749)
p1.equals(p3) : false; p1(1169863946) p3(2131949076)      

從結果也可以看出:p1和p2相等的情況下,hashCode()也不一定相等。

第二種 會建立“類對應的散清單”

這裡所說的“會建立類對應的散清單”是說:我們會在HashSet, Hashtable, HashMap等等這些本質是散清單的資料結構中,用到該類。例如,會建立該類的HashSet集合。

在這種情況下,該類的“hashCode() 和 equals() ”是有關系的:

  1. 如果兩個對象相等,那麼它們的hashCode()值一定相同。這裡的相等是指,通過equals()比較兩個對象時傳回true。
  2. 如果兩個對象hashCode()相等,它們并不一定相等。

    因為在散清單中,hashCode()相等,即兩個鍵值對的哈希值相等。然而哈希值相等,并不一定能得出鍵值對相等。補充說一句:“兩個不同的鍵值對,哈希值相等”,這就是哈希沖突。

此外,在這種情況下。若要判斷兩個對象是否相等,除了要覆寫equals()之外,也要覆寫hashCode()函數。否則,equals()無效。

例如,建立Person類的HashSet集合,必須同時覆寫Person類的equals() 和 hashCode()方法。

如果單單隻是覆寫equals()方法。我們會發現,equals()方法沒有達到我們想要的效果。

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比較equals() 傳回true 以及 傳回false時, hashCode()的值。
 *
 */
public class ConflictHashCodeTest1{

    public static void main(String[] args) {
        // 建立Person對象,
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);

        // 建立HashSet對象 
        HashSet set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        // 比較p1 和 p2, 并列印它們的hashCode()
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        // 列印set
        System.out.printf("set:%s\n", set);
    }

    /**
     * @desc Person類。
     */
    private static class Person {
        int age;
        String name;

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

        public String toString() {
            return "("+name + ", " +age+")";
        }

        /** 
         * @desc 覆寫equals方法 
         */  
        @Override
        public boolean equals(Object obj){  
            if(obj == null){  
                return false;  
            }  
              
            //如果是同一個對象傳回true,反之傳回false  
            if(this == obj){  
                return true;  
            }  
              
            //判斷是否類型相同  
            if(this.getClass() != obj.getClass()){  
                return false;  
            }  
              
            Person person = (Person)obj;  
            return name.equals(person.name) && age==person.age;  
        } 
    }
}      

運作結果:

p1.equals(p2) : true; p1(1169863946) p2(1690552137)
set:[(eee, 100), (eee, 100), (aaa, 200)]      

結果分析:

我們重寫了Person的equals()。但是,很奇怪的發現:HashSet中仍然有重複元素:p1 和 p2。為什麼會出現這種情況呢?

這是因為雖然p1 和 p2的内容相等,但是它們的hashCode()不等;是以,HashSet在添加p1和p2的時候,認為它們不相等。

下面,我們同時覆寫equals() 和 hashCode()方法。

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比較equals() 傳回true 以及 傳回false時, hashCode()的值。
 *
 */
public class ConflictHashCodeTest2{

    public static void main(String[] args) {
        // 建立Person對象,
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);
        Person p4 = new Person("EEE", 100);

        // 建立HashSet對象 
        HashSet set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        // 比較p1 和 p2, 并列印它們的hashCode()
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        // 比較p1 和 p4, 并列印它們的hashCode()
        System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", p1.equals(p4), p1.hashCode(), p4.hashCode());
        // 列印set
        System.out.printf("set:%s\n", set);
    }

    /**
     * @desc Person類。
     */
    private static class Person {
        int age;
        String name;

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

        public String toString() {
            return name + " - " +age;
        }

        /** 
         * @desc重寫hashCode 
         */  
        @Override
        public int hashCode(){  
            int nameHash =  name.toUpperCase().hashCode();
            return nameHash ^ age;
        }

        /** 
         * @desc 覆寫equals方法 
         */  
        @Override
        public boolean equals(Object obj){  
            if(obj == null){  
                return false;  
            }  
              
            //如果是同一個對象傳回true,反之傳回false  
            if(this == obj){  
                return true;  
            }  
              
            //判斷是否類型相同  
            if(this.getClass() != obj.getClass()){  
                return false;  
            }  
              
            Person person = (Person)obj;  
            return name.equals(person.name) && age==person.age;  
        } 
    }
}      

運作結果:

p1.equals(p2) : true; p1(68545) p2(68545)
p1.equals(p4) : false; p1(68545) p4(68545)
set:[aaa - 200, eee - 100]      

結果分析:

這下,equals()生效了,HashSet中沒有重複元素。

比較 p1和 p2 ,我們發現:它們的hashCode()相等,通過equals()比較它們也傳回true。是以,p1和 p2 被視為相等。

比較 p1 和 p4 ,我們發現:雖然它們的hashCode()相等;但是,通過equals()比較它們傳回false。是以,p1 和 p4 被視為不相等。

1.1.18、什麼是Java序列化和反序列化,如何實作Java序列化?或者請解釋Serializable 接口的作用

1.1.19、Object類中常見的方法,為什麼wait notify會放在Object裡邊?

wait()暫停的是持有鎖的對象,是以想調用wait()必須為:對象.wait();

notify()喚醒的是等待鎖的對象,調用:對象.notify();

簡單說:因為synchronized中的這把鎖可以是任意對象,是以任意對象都可以調用wait()和notify();是以wait和notify屬于Object。

專業說:因為這些方法在操作同步線程時,都必須要辨別它們操作線程的鎖,隻有同一個鎖上的被等待線程,可以被同一個鎖上的notify喚醒,不可以對不同鎖中的線程進行喚醒。也就是說,等待和喚醒必須是同一個鎖。而鎖可以是任意對象,是以可以被任意對象調用的方法是定義在object類中。

1.1.20、Java的平台無關性如何展現出來的

Java在不同的作業系統之間,提供不同的JVM虛拟機,讓JVM虛拟機實作編譯後的位元組碼檔案到實際作業系統指令集的轉換。Java隻提供統一的開發接口,實作統一的編碼規範

Java正是憑借Java虛拟機來實作其平台無關性的。通過在機器與編譯程式之間加入一層抽象(即JVM)來實作脫離平台這一性質。

其中,注意:

Java虛拟機面向編譯器給其提供相同的接口(即各平台編譯器相同),這就是說,編譯器隻要面向Java虛拟機生成Java虛拟機可以了解的代碼,那麼就可以通過不同平台的 不同解釋器來生成與平台相對應的機器碼來執行Java程式。 虛拟機暴露給編譯器的接口是相同的,而虛拟機的解釋器針對不同的平台而不同。

1.1.21、JDK和JRE的差別

  • JDK是用于開發的而JRE是用于運作Java程式的。
  • JDK和JRE都包含了JVM,進而使得我們可以運作Java程式。
  • JVM是Java程式設計語言的核心并且具有平台獨立性。

補充:

Java 開發工具包 (JDK)

Java開發工具包是Java環境的核心元件,并提供編譯、調試和運作一個Java程式所需的所有工具,可執行檔案和二進制檔案。JDK是一個平台特定的軟體,有針對Windows,Mac和Unix系統的不同的安裝包。可以說JDK是JRE的超集,它包含了JRE的Java編譯器,調試器和核心類。目前JDK的版本号是1.7,也被稱為Java 7。

Java虛拟機(JVM)

JVM是Java程式設計語言的核心。當我們運作一個程式時,JVM負責将位元組碼轉換為特定機器代碼。JVM也是平台特定的,并提供核心的Java方法,例如記憶體管理、垃圾回收和安全機制等。JVM 是可定制化的,我們可以通過Java 選項(java options)定制它,比如配置JVM 記憶體的上下界。JVM之是以被稱為虛拟的是因為它提供了一個不依賴于底層作業系統和機器硬體的接口。這種獨立于硬體和作業系統的特性正是Java程式可以一次編寫多處執行的原因。

Java運作時環境(JRE)

1.1.22、Java 8有哪些新特性