1.Java 8 的重要特性有哪些?
2014年3月發布的Java8,是Java面試中熱門的問題之一。如果你回答的清晰,表明你對Java最新技術是與時俱進。
Java8是自Java5注解和泛型功能以來變化最大的版本。一些重要的特性諸如:
1. Interface changes with default and static methods
2. Functional interfaces and Lambda Expressions
3. Java Stream API for collection classes
強烈推薦訪問以上鏈接了解每個具體的細節,或者閱讀Java 8 Features。
2.Java的跨平臺特性指的什么?
跨平臺意味著你能在任何操作系統運行同樣的Java程序。例如,你可以將在Windows平臺寫的Java程序放到Max OS系統運行。
3.什么是JVM?它可以跨平臺否?
Java虛擬機是Java程序語言的核心。JVM負責將字節碼機器可讀的代碼。JVM是非跨平臺的,這也就是你需要在不同的操作系統上安裝不同的JVM。我們也可以定制JVM配置,比如設置最小和最大分配內存。它是虛擬的,而且不受底層操作系統依賴。
4.JDK和JVM有什么區別?
Java開發套件(JDK)的目的是為開發服務的,JVM是其中一部分,它旨在運行Java程序。JDK提供所有的開發工具,需要編譯的可執行二進制文件,調試和執行Java應用程序。可執行部分由JVM處理,來提供機器獨立運行。
5.JVM和JRE有什么區別?
Java運行時環境(JRE)是JVM的具體實現。JRE由JVM和Java二進制還有其他classes類組成,以保證正確執行Java程序。JRE不包含任何開發工具,比如Java編譯器,測試工具等。如果你要執行任何Java程序,你必須安裝JRE。
6.Java中所有類的父類是什么?
java.lang.Object是所有Java類的父類,使用的時候不需要繼承它。
7.為什么Java不支持多繼承?
Java不支持類的多繼承是因為存在“鉆石問題”。了解“鉆石問題”的例子可以查看文章Multiple Inheritance in Java。
然后多繼承關系可以通過接口來實現。一個接口可以繼承多個接口,因為他們只聲明了方法,而具體的實現放在實現類里面,所以接口中不會存在“鉆石問題”。
8.為什么Java不是純面向對象語言?
因為Java原始類型,例如:int,byte,short,long等,所以Java不是純面向對象語言。我認為它給我們在編寫代碼時提供了便捷。
顯然Java可以用封裝對象給原始對象提供展示,但他們沒有任何好處。
眾所周知,所有的原始對象都有封裝對象,例如:Integer,Long等,封裝對象中會有額外的一些方法。
9.path和classpath變量有什么區別?
PATH是操作系統使用的環境變量,用于放置執行相關的文件。這就是為什么我們安裝完Java,想讓操作系統找到執行文件,我們需要把Java目錄位置加到PATH環境變量中。
Classpath是Java特定用的,用于Java執行文件放置class文件。我們可以在Java應用運行時提供classpath路徑,可以是個目錄,ZIP文件,JAR文件等。
10.Java中main方法有什么重要性?
main()方法是任何獨立Java應用的入口。main方法的語法如下:
public static void main(String args[])
main方法是public和static修飾的,所以Java不用初始化類就能夠訪問它。方法參數是String數組,我們可以在運行程序時候傳進來,查看這篇文章可以進一步了解how to compile and run java program。
11.Java中方法重載和方法重寫有什么區別?
當我們在一個class類中想使用同一個方法名稱實現多個方法而且參數不同時,這時候就可以用方法重載。
方法重寫的概念來自于繼承的情況,當我們有兩個方法用了同一個方法簽名,一個是在父類,另一個是在子類。我們可以在子類中使用@Override注解重寫方法實現,這樣可以確保父類中方法改變了,子類中也跟著變。
重載體現的類內部的多態性,重寫體現的是父、子類繼承間的多態性。
12.是否可以重載main方法?
是的,我們可以在同一個class類中寫多個名稱是"main"的方法。然后當Java虛擬機運行時候會根據public static void main(String args[])語法來找對應的main方法。
13.是否可以在Java源文件中寫多個public classes?
Java不允許在一個源文件中寫多個public class。一個源文件可以含有多個非public聲明的class。
14.什么是Java的Package,哪些包是默認引入的?
包是Java中有效組織類分組的機制。分組邏輯可以基于設計的功能也可以基于模塊。完全區分一個Java類包括包路徑和類名稱。例如java.lang.Object,完全區分是由類名稱Object加上包路徑java.lang。
java.lang下的包是默認引入的,所以我們的程序里面無需引入該包下的類。
15.什么Java訪問修飾符
Java通過public, private and protected訪問修飾詞提供訪問控制。當沒有任何修飾詞時,屬于默認訪問修飾。
Java類只能包含public或者默認修飾詞。閱讀Java Access Modifiers了解更多細節。
16.什么是final關鍵字?
類上使用final關鍵詞修飾為了確保沒有其他類可以繼承它,例如String類是final修飾,所以我們無法繼承String。
方法上使用final關鍵詞修飾為了確保子類無法重寫該方法。
變量上使用final關鍵詞修飾為了確保其只能分配一次。然后變量的狀態可以改變,例如可以給一個object分配final變量,但是object變量后面還可以修改。
17.什么是static關鍵字?
static關鍵詞可以修飾class 層級的變量,使其變成全局共享變量。
static關鍵詞也可以修飾方法。靜態方法只能訪問靜態變量,同時也只能調用類的靜態方法。
閱讀java static keyword了解更多。
18.Java中finally和finalize的區別?
finally代碼塊用在try-catch中,用以確保改部分代碼始終會執行,即便是任何異常拋出的情況。
finally代碼塊經常用來處理try代碼塊中創建的資源進行回收。
finalize()是Object類中特定的方法,可以在基礎的類中進行重寫。當object準備垃圾回收時,這個方法會被垃圾回收調用。這個方法經常重寫用于在垃圾回收時釋放系統資源。
19.是否可以聲明一個靜態類?
不能聲明頂層類為靜態類,然后一個內部類可以聲明成靜態static。如果內部類被聲明為靜態類,它被叫做靜態嵌套類。內部靜態類和其他頂層類是一樣的,嵌套只是為了打包方便。
閱讀java inner class了解更多。
20.什么是靜態引用?
如果我們使用其他類中的靜態變量或靜態方法,通常我們先引入該類,然后使用該類的方法/變量。
import java.lang.Math;
//inside class?
double test = Math.PI * 5;
當只需要用某個方法或變量時,我們也可以同樣的引入靜態方法或變量
import static java.lang.Math.PI;
//no need to refer class now
double test = PI * 5;
使用靜態引入會產生混淆,所以需要盡量避免這種方式。過渡使用靜態引用會導致你的程序可讀性差和可維護性低。
21.什么是try-with-resources?
Java7的一個特性是try-with-resources聲明,用于動態資源管理。Java7之前沒有動態資源管理,我們需要明確需要關閉的資源。通常會在try-catch聲明的finally代碼塊中實現。當我們忘記關閉資源時這種方法會導致內存不足。
從Java7開始,我們可以在try代碼塊中創建資源和使用它,當try-catch代碼塊結束時,Java會馬上處理好資源的。閱讀Java Automatic Resource Management了解更多。
22.什么是multi-catch塊?
多個異常塊捕獲代碼是Java7的一個改善,我們可以捕獲多個異常在同一個catch塊中。當每一個catch塊中都是相似代碼的時候,這樣的處理會讓代碼變得簡潔。
如果一個catch塊處理多個異常,可以使用管道符"|",這種情況下異常參數是final修飾,所以無法改變它。閱讀Java multi catch block了解更多。
23.什么是靜態塊?
Java靜態代碼塊是通過Java ClassLoader把類加載到內存時候的一組可執行的聲明。它被用來初始化類中的靜態變量。大多數情況當類加載時創建靜態資源。
24.什么是接口?
接口是Java程序語言的核心部分,不僅廣泛用于JDK,而且在Java設計模式中,大多數框架中,工具類中都有使用。接口給Java提供了一種抽象化的途徑,而且用于約定在子類中的實現。
接口有利于定義類型和在我們代碼里創建集成關系的頂層邏輯。由于Java類可以實現接口的多繼承,所以在大多數情況下最好使用超類當接口。閱讀java interface了解更多。
25.什么是抽象類?
抽象類用在Java中為子類們創建一個超類且含有具體的方法實現。一個抽象類可以有不含方法體的抽象方法,也可以有具體實現的抽象方法。
abstract關鍵字用于創建抽象類。抽象類不能被實例化,通常用在提供子類繼承和實現基礎的抽象方法和重寫或者用抽象類中的方法。閱讀java abstract class了解更多。
26.Java中抽象類和接口的區別?
abstract關鍵詞創建抽象類而interface關鍵詞創建的是接口;
抽象類可以有具體的方法實現而接口沒有;
一個類只能繼承一個抽象類,但是可以實現多個接口;
我們可以運行含有main()方法的抽象類,而接口不行;
27.接口是否可以實現或繼承另一個接口?
接口無法實現另一個接口,他們是繼承關系。因為接口沒有方法實現,所以不存在“diamond”問題,這就是為什么會允許多接口繼承,一個接口也可以繼承多個接口。
28.什么是Marker interface?
Java中Marker interface是指一個空的接口沒有任何方法,但在一些功能設計的類里面強制實現。我們熟知的一些marker interfaces例如Serializable、Cloneable。
29.什么是Wrapper classes?
Java的嵌套類是Object對八種原始類型的展示。Java中所有嵌套類都是不可變和final修飾的。Java5中自動裝箱和拆箱允許一種便捷的方式轉換原始類型和它們對應的封裝類型。
閱讀Wrapper classes in Java了解更多。
30.什么是Java中的枚舉?
枚舉是Java1.5中引入的新類型,它的filed是固定的常量Set集合。例如我們可以創建方向的枚舉集合:東、西、南、北。
同類類似,創建枚舉的關鍵詞是enum。枚舉常量是隱形的靜態和final。
閱讀java enum了解更多。
31.什么是Java注解?
Java注解提供關于代碼的信息,它們對注解的代碼塊沒有直接影響。注解是Java5引入的。注解是嵌入在程序中關于程序的元數據。注解的語法解析可以被注解解析工具或者編譯器解析。我們也可以指定注解是編譯時有效或運行時有效。Java內置的注解有@Override, @Deprecated,@SuppressWarnings。
閱讀java annotations了解更多。
32.什么是Java反射API?為何它非常重要?
Java反射API是檢查和修改Java應用運行時行為的一種方式。我們可以檢查類、接口、枚舉,獲取它們的方法和成員變量的細節。反射API是高級技能,我們應該避免在平時編碼時使用。反射API的用法可以打破設計模式,比如單例模式,通過調用私有構造器等可以違反Java里訪問修飾的規則。
即便我們不在普通應用編碼時使用反射API,但它還是有其重要意義。在一些框架中,比如Spring,Hibernate或容器應用例如Tomcat,Jobss里不能沒有反射API。它們通過反射API調用合適的方法和實例化對象,在其他程序處理中也應用廣泛。
閱讀Java Reflection Tutorial深入了解反射API。
33.什么是Java復合Composition?
復合/組合是Java類中實現has-a關系的設計技術,我們可以使用對象組合來實現代碼重用。
我們可以針對其他的對象實例化成變量來實現Java組合,其好處是我們可以控制類中其他對象的可見性,在我們需要的時候重用。
閱讀Java Composition了解復合/組合示例。
34.復合優于繼承的好處有哪些?
Java程序中最佳實踐之一是“復合優于繼承”原則,一些原因如下:
a.任何父類的變化可能都會影響子類,及時我們不會用到的父類方法。例如,我們在子類中有一個test()方法,突然有人在其父類也寫了個test()方法,會導致子類編譯錯誤。復合就絕不會有這種問題,因為我們只使用我們需要的方法。
b.繼承暴露了所有父類的方法和變量給繼承者們,如果我們掌握不好父類的設計,會導致安全漏洞。復合允許我們提供嚴格的方法訪問,因此更加安全。
c.我們可以在運行時進行復合綁定,然后繼承綁定需要在編譯時期。這樣復合提供更加靈活的方法調用。
閱讀java composition vs inheritance了解更多組合優于繼承的好處。
35.如何對customer對象集合排序?
需要實現Comparable接口來支持customer集合的排序。Comparable接口有compareTo(T obj)方法,用來做排序的方法,也提供了它的實現。我們可以使用默認的方式來對集合中customer對象來排序。
36.什么是Java中內部類?
我們可以在類的內部定義一個類,叫做嵌套類。任何非靜態嵌套類叫內部類。內部類和類對象關聯,它可以訪問外層類的所有變量和方法。由于內部類和實例有關聯,所以不能含有靜態變量。我們可以有局部內部類或匿名內部類。
閱讀java inner class了解更多。
37.什么是匿名內部類?
局部內部類如果沒有名稱,就是匿名內部類。一個匿名類的定義和實例化都是一條聲明。匿名內部類通常繼承一個類或者實現一個接口。
由于匿名內部類沒有名字,所以無法定義構造方法。匿名內部類可訪問域是它的定義位置。
38.什么是Classloader?
Java?Classloader是一個我們需要訪問類時加載程序字節碼到內存的程序。我們也可以擴展Classloader類,重寫loadClass(String name)方法來創建自己的classloader。
閱讀java classloader了解更多。
39.有哪些不同類型的Classloader?
Java中內置了三種類型類加載器:
1.Bootstrap Class Loader –?它加載JDK的內部類,典型的有rt.jar和其他核心類;
2.Extensions Class Loader – 它加載JDK擴展目錄的類,通常是$JAVA_HOME/lib/ext目錄;
3.System Class Loader – 它加載當前classpath的類,可以在調用程序是命令行指定-cp或者-classpath命令來設置。
40.什么是三元運算符?
Java條件運算符僅指的條件運算,例如三元運算。它在Java程序應用廣泛,是內部替代if-then-else語句用的。我們可以用條件運算符 if-else語句或者switch條件。
閱讀java ternary operator了解更多。
41.super關鍵字有什么作用?
super關鍵字可以用來訪問父類的方法,當你在子類重寫了該方法時用的。
我們可以在子類構造器通過super關鍵字調用父類構造器但是這種情況需要將第一條聲明寫在構造器方法里。
package com.journaldev.access;
public class SuperClass {
????????public SuperClass(){
?????????}????????public SuperClass(int i){}
????????public void test(){
?????????????System.out.println("super class test method");
?????????}?}
使用super關鍵字的例子可以參考下面子類的實現:
package com.journaldev.access;
public class ChildClass extends SuperClass {
? ?????? public ChildClass(String str){
? ?????????? //access super class constructor with super keyword
? ?????????? super();
? ?????????? //access child class method
? ?????????? test();
? ?????????? //use super to access super class method
? ?????????? super.test();
? ? ? ? }
? ?????? @Override????
? ?????? public void test(){
? ?????????? System.out.println("child class test method"); }
? ? ? ? }
}
42.說明下break和continue聲明的用途?
我們可以使用break語句終止for,while,或者do-while循環。可以在switch語句中退出switch條件。你可以看下break的例子java break。我們可以使用break當作標簽來退出內部循環。
continue語句是在for,while或者do-while循環中跳過當次循環。我們可以使用continue語句當作標簽來跳過當次循環。
43.this關鍵字有什么用?
this關鍵字提供了當前類的引用,它常用來確保一個類里面的變量是被調用,而不是局部同名變量被調用了。
44.什么是默認構造器?
類的無參構造函數就是默認的構造函數。當我們沒有寫無參類,Java編譯器會自動給類創建一個默認的無參構造器。如果有其他的構造器定義了的話,編譯器就不會創建默認的了。
45.是否可以在try中不寫catch代碼塊?
可以,我們可以寫try-finally語句而沒有catch代碼塊。
46.什么是垃圾回收?
垃圾回收是在堆內存中回收沒有被使用的對象的進程。在Java中,分配內存的過程是自動由garbage collector處理的。
我們可以在代碼中寫Runtime.getRuntime().gc()來運行垃圾回收,或者使用System.gc()。查看更多分析堆內存和垃圾回收機制的,閱讀Java Garbage Collection。
47.什么是序列化和反序列化?
我們把Java對象轉換成流的過程叫做序列化。一旦Java對象被轉換成流,它就能被保存到文件或者發送到網絡傳輸或者用到socket連接中。
對象在實現序列化接口后,我們可以用java.io.ObjectOutputStream來講對象寫到文件或者任何OutputStream對象中。閱讀Java Serialization了解更多。
進程通過序列化將流數據轉換成對象的過程叫反序列化。閱讀Java Deserialization了解更多。
48.如何在cmd窗口運行JAR文件
我們如果要在命令行運行JAR文件,它需要有mainfest文件定義Main-Class入口。Main-Class是JAR文件的入口,命令行運行時需要用到。
49.System類有哪些用途?
Java的System類是核心類之一,調試時候最早記錄日志信息就是通過System.out.print()方法來實現的。
System類是final的,所以它不能有子類,也不能通過繼承來重寫。System類不提供任何公共構造器,所以我們無法實例化這個類,這就是為什么它所有的方法都是靜態方法。
System的一些有用的方法有數組復制,獲取當前系統時間,讀取系統環境變量等。
閱讀Java System Class了解更多。
50.instanceof關鍵字有什么作用?
instanceof關鍵字用來檢查一個對象是否是某個類的類型。我們贏盡可能避免使用它。用法示例:
public static void main(String args[]){
? ?????? Object str = new String("abc");
? ?????? if(str instanceof String){
? ?????????? System.out.println("String value:"+str);
? ? ? ? }
? ?????? if(str instanceof Integer){
? ?????????? System.out.println("Integer value:"+str);
? ? ? ? }
}
由于str在運行時是String類型,第一個if語句計算的值是true而第二個計算值是false。
51.switch case中是否可以用String?
Java7的一個特性是改進了switch case允許使用String。所以如果使用Java7或者更高版本,你可以在switch-case語句中使用String。
閱讀Java switch-case String example了解更多。
52.Java是值傳遞還是引用傳遞?
這是個容易混淆的問題,我們知道對象變量包含對象到堆的引用。當我們調用任何方法時,這些變量被傳遞而且存儲在方法棧內存中。我們可以通過簡單的方法交換對所有語言測試到底是引用傳遞還是值傳遞,了解更多請閱讀Java is Pass by Value and Not Pass by Reference。
53.堆和棧內存的區別?
以下是堆和棧內存的主要區別:
?堆內存被應用所有部分使用而棧內存只被一個線程執行時使用;
?無論一個對象是否創建,它總是存儲在堆內存棧內存包含它的引用。棧內存包含局部原始變量,對象的引用變量在堆內存區。
?棧內存管理由LIFO管理完成,然而堆內存管理則更加復雜,因為它是全局的。
通過一個簡單的程序了解更多細節,閱讀Java Heap vs Stack Memory。
54.Java編譯的文件存在JDK,JRE還是JVM?
Java編譯器的任務是將程序轉換為字節碼,我們有javac來執行編譯。所以它一定存在于JDK,我們在JRE不需要而JVM只是規范。
55.下面的程序輸出結果是什么?
1.類的靜態方法
package com.journaldev.util;
public class Test {
? ?????? public static String toString(){
? ?????????? System.out.println("Test toString called");
? ?????? ???? return "";
? ????? }
? ?????? public static void main(String args[]){
? ?????????? System.out.println(toString());
? ? ? ? }
}
答案:代碼編譯會失敗,因為Object類方法不能用static關鍵詞修飾。你會得到一個編譯錯誤“This static method cannot hide the instancemethod from Object”。理由是靜態方法屬于類,所有類的基類是Object,我們不能像在類中一樣在實例里面有相同名稱的方法。
2.靜態方法調用
package com.journaldev.util;?
public class Test {
? ?????? public static String foo(){
? ?????????? System.out.println("Test foo called");
? ?????????? return "";
? ? ? ? }
? ?????? public static void main(String args[]){
? ?????????? Test obj = null;
? ?????????? System.out.println(obj.foo());
? ? ? ? }
}
答案:這是個異常的情形。當我們調用對象的方法為空時會報NullPointerException空指針異常。編譯器會給出“The static method foo() from the type Test should be accessed in a static way”,但是當執行時會打印“Test foo called”。
理想的情況是當對象靜態方法調用時Java API應該給出錯誤而不是警告,但我認為這樣強制的太晚了。大多數異常是即使對象這里為空,但調用靜態方法是還是能運行。我認為這里可以運行是因為Java運行時計算出foo()是一個靜態方法,而且在類加載到內存中調用,而且不會用這個對象,所以沒有空指針異常。