基礎知識
String 是最基本的資料類型嗎?
答:不是。Java中的基本資料類型隻有8個:byte、short、int、long、float、double、char、boolean;除了基本類型(primitive type),剩下的都是引用類型(reference type),Java 5以後引入的枚舉類型也算是一種比較特殊的引用類型。
名稱 | 包裝類 | 位元組 | bit | 取值範圍 |
---|---|---|---|---|
byte | Byte | 1 | 8 | -128~127 |
short | Short | 2 | 16 | -2^15 ~2^15-1 |
int | Integer | 4 | 32 | -2^31~2^32-1 |
long | Long | 64 | -2^63~2^63-1 | |
float | Float | -2^128 ~ 2^128 | ||
double | Double | -2^1024 ~ 2^1024 | ||
char | Character | 0~65526 | ||
boolean | Boolean | 編譯後 JVM int 表示 = 4位元組 32bit / 資料 編譯後 JVM byte數組 = 1位元組 8bit |
通路修飾符public,private,protected,以及不寫(預設)時的差別?
答:
修飾符 | 目前類 | 同包 | 子 類 | 其他包 |
---|---|---|---|---|
public | √ | |||
protected | × | |||
default | ||||
private |
類的成員不寫通路修飾時預設為default。預設對于同一個包中的其他類相當于公開(public),對于不是同一個包中的其他類相當于私有(private)。受保護(protected)對子類相當于公開,對不是同一包中的沒有父子關系的類相當于私有。Java中,外部類的修飾符隻能是public或預設,類的成員(包括内部類)的修飾符可以是以上四種。
switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
答:在Java 5以前,switch(expr)中,expr隻能是byte、short、char、int。從Java 5開始,Java中引入了枚舉類型,expr也可以是enum類型,從Java 7開始,expr還可以是字元串(String),但是長整型(long)在目前所有的版本中都是不可以的。
構造器(constructor)是否可被重寫(override)?
答:構造器不能被繼承,是以不能被重寫,但可以被重載。
兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
答:不對,如果兩個對象x和y滿足x.equals(y) == true,它們的哈希碼(hash code)應當相同。Java對于eqauls方法和hashCode方法是這樣規定的:(1)如果兩個對象相同(equals方法傳回true),那麼它們的hashCode值一定要相同;(2)如果兩個對象的hashCode相同,它們并不一定相同。當然,你未必要按照要求去做,但是如果你違背了上述原則就會發現在使用容器時,相同的對象可以出現在Set集合中,同時增加新元素的效率會大大下降(對于使用哈希存儲的系統,如果哈希碼頻繁的沖突将會造成存取性能急劇下降)。
補充:關于equals和hashCode方法,很多Java程式都知道,但很多人也就是僅僅知道而已,在Joshua Bloch的大作《Effective Java》(很多軟體公司,《Effective Java》、《Java程式設計思想》以及《重構:改善既有代碼品質》是Java程式員必看書籍,如果你還沒看過,那就趕緊去亞馬遜買一本吧)中是這樣介紹equals方法的:首先equals方法必須滿足自反性(x.equals(x)必須傳回true)、對稱性(x.equals(y)傳回true時,y.equals(x)也必須傳回true)、傳遞性(x.equals(y)和y.equals(z)都傳回true時,x.equals(z)也必須傳回true)和一緻性(當x和y引用的對象資訊沒有被修改時,多次調用x.equals(y)應該得到同樣的傳回值),而且對于任何非null值的引用x,x.equals(null)必須傳回false。實作高品質的equals方法的訣竅包括:1. 使用==操作符檢查"參數是否為這個對象的引用";2. 使用instanceof操作符檢查"參數是否為正确的類型";3. 對于類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相比對;4. 編寫完equals方法後,問自己它是否滿足對稱性、傳遞性、一緻性;5. 重寫equals時總是要重寫hashCode;6. 不要将equals方法參數中的Object對象替換為其他的類型,在重寫時不要忘掉@Override注解。
是否可以繼承String類?
答:String 類是final類,不可以被繼承。
補充:繼承String本身就是一個錯誤的行為,對String類型最好的重用方式是關聯關系(Has-A)和依賴關系(Use-A)而不是繼承關系(Is-A)。
當一個對象被當作參數傳遞到一個方法後,此方法可改變這個對象的屬性,并可傳回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?
答:是值傳遞。Java語言的方法調用隻支援參數的值傳遞。
Java中沒有傳引用實在是非常的不友善,這一點在Java 8中仍然沒有得到改進,正是如此在Java編寫的代碼中才會出現大量的Wrapper類(将需要通過方法調用修改的引用置于一個Wrapper類中,再将Wrapper對象傳入方法),這樣的做法隻會讓代碼變得臃腫,尤其是讓從C和C++轉型為Java程式員的開發者無法容忍。
String和StringBuilder、StringBuffer的差別?
答:Java平台提供了兩種類型的字元串:String和StringBuffer/StringBuilder,它們可以儲存和操作字元串。其中String是隻讀字元串,也就意味着String引用的字元串内容是不能被改變的。而StringBuffer/StringBuilder類表示的字元串對象可以直接進行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,差別在于它是在單線程環境下使用的,因為它的所有方面都沒有被synchronized修飾,是以它的效率也比StringBuffer要高。
面試題:華為的面試題中曾經問過這樣一個問題 - "為什麼不能根據傳回類型來區分重載",因為重載是編譯時的多态,相同方法,調用無法知道傳回值類型是個啥。但是,在Class檔案可以共存,Java檔案不行,這是Java虛拟機規範和Java語言規範的不同。
描述一下JVM加載class檔案的原理機制?
答:JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實作的,Java中的類加載器是一個重要的Java運作時系統元件,它負責在運作時查找和裝入類檔案中的類。
由于Java的跨平台性,經過編譯的Java源程式并不是一個可執行程式,而是一個或多個類檔案。當Java程式需要使用某個類時,JVM會確定這個類已經被加載、連接配接(驗證、準備和解析)和初始化。類的加載是指把類的.class檔案中的資料讀入到記憶體中,通常是建立一個位元組數組讀入.class檔案,然後産生與所加載類對應的Class對象。加載完成後,Class對象還不完整,是以此時的類還不可用。當類被加載後就進入連接配接階段,這一階段包括驗證、準備(為靜态變量配置設定記憶體并設定預設的初始值)和解析(将符号引用替換為直接引用)三個步驟。最後JVM對類進行初始化,包括:1)如果類存在直接的父類并且這個類還沒有被初始化,那麼就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。
類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴充加載器(Extension)、系統加載器(System)和使用者自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程采取了父親委托機制(PDM)。PDM更好的保證了Java平台的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。JVM不會向Java程式提供對Bootstrap的引用。下面是關于幾個類加載器的說明:
Bootstrap:一般用本地代碼實作,負責加載JVM基礎核心類庫(rt.jar);
Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;
System:又叫應用類加載器,其父類是Extension。它是應用最廣泛的類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中記載類,是使用者自定義加載器的預設父加載
抽象類(abstract class)和接口(interface)有什麼異同?
答:抽象類和接口都不能夠執行個體化,但可以定義抽象類和接口類型的引用。一個類如果繼承了某個抽象類或者實作了某個接口都需要對其中的抽象方法全部進行實作,否則該類仍然需要被聲明為抽象類。接口比抽象類更加抽象,因為抽象類中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法。抽象類中的成員可以是private、預設、protected、public的,而接口中的成員全都是public的。抽象類中可以定義成員變量,而接口中定義的成員變量實際上都是常量。有抽象方法的類必須被聲明為抽象類,而抽象類未必要有抽象方法。
靜态嵌套類(Static Nested Class)和内部類(Inner Class)的不同?
答:Static Nested Class是被聲明為靜态(static)的内部類,它可以不依賴于外部類執行個體被執行個體化。而通常的内部類需要在外部類執行個體化後才能執行個體化
Java 中會存在記憶體洩漏嗎,請簡單描述。
答:理論上Java因為有垃圾回收機制(GC)不會存在記憶體洩露問題(這也是Java被廣泛使用于伺服器端程式設計的一個重要原因);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC回收,是以也會導緻記憶體洩露的發生。例如Hibernate的Session(一級緩存)中的對象屬于持久态,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,如果不及時關閉(close)或清空(flush)一級緩存就可能導緻記憶體洩露
抽象的(abstract)方法是否可同時是靜态的(static),是否可同時是本地方法(native),是否可同時被synchronized修飾?
答:都不能。抽象方法需要子類重寫,而靜态的方法是無法被重寫的,是以二者是沖突的。本地方法是由本地代碼(如C代碼)實作的方法,而抽象方法是沒有實作的,也是沖突的。synchronized和方法的實作細節有關,抽象方法不涉及實作細節,是以也是互相沖突的。
闡述靜态變量和執行個體變量的差別。
答:靜态變量是被static修飾符修飾的變量,也稱為類變量,它屬于類,不屬于類的任何一個對象,一個類不管建立多少個對象,靜态變量在記憶體中有且僅有一個拷貝;執行個體變量必須依存于某一執行個體,需要先建立對象然後通過對象才能通路到它。靜态變量可以實作讓多個對象共享記憶體。
是否可以從一個靜态(static)方法内部發出對非靜态(non-static)方法的調用?
答:不可以,靜态方法隻能通路靜态成員,因為非靜态方法的調用要先建立對象,在調用靜态方法時可能對象并沒有被初始化。
如何實作對象克隆?
答:有兩種方式:
1). 實作Cloneable接口并重寫Object類中的clone()方法;
2). 實作Serializable接口,通過對象的序列化和反序列化實作克隆,可以實作真正的深度克隆
GC是什麼?為什麼要有GC?
答:GC是垃圾收集的意思,記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導緻程式或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域進而達到自動回收記憶體的目的,Java語言沒有提供釋放已配置設定記憶體的顯示操作方法。Java程式員不用擔心記憶體管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉顯示的垃圾回收調用。
垃圾回收可以有效的防止記憶體洩露,有效的使用可以使用的記憶體。垃圾回收器通常是作為一個單獨的低優先級的線程運作,不可預知的情況下對記憶體堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程式員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。在Java誕生初期,垃圾回收是Java最大的亮點之一,因為伺服器端的程式設計需要有效的防止記憶體洩露問題,然而時過境遷,如今Java的垃圾回收機制已經成為被诟病的東西。移動智能終端使用者通常覺得iOS的系統比Android系統有更好的使用者體驗,其中一個深層次的原因就在于Android系統中垃圾回收的不可預知性。
補充:垃圾回收機制有很多種,包括:分代複制垃圾回收、标記垃圾回收、增量垃圾回收等方式。标準的Java程序既有棧又有堆。棧儲存了原始型局部變量,堆儲存了要建立的對象。Java平台對堆記憶體回收和再利用的基本算法被稱為标記和清除,但是Java對其進行了改進,采用“分代式垃圾收集”。這種方法會跟Java對象的生命周期将堆記憶體劃分為不同的區域,在垃圾收集過程中,可能會将對象移動到不同區域:
- 伊甸園(Eden):這是對象最初誕生的區域,并且對大多數對象來說,這裡是它們唯一存在過的區域。
- 幸存者樂園(Survivor):從伊甸園幸存下來的對象會被挪到這裡。
- 終身頤養園(Tenured):這是足夠老的幸存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次完全收集(Major-GC),這裡可能還會牽扯到壓縮,以便為大對象騰出足夠的空間。
與垃圾回收相關的JVM參數:
-Xms / -Xmx — 堆的初始大小 / 堆的最大大小
-Xmn — 堆中年輕代的大小
-XX:-DisableExplicitGC — 讓System.gc()不産生任何作用
-XX:+PrintGCDetails — 列印GC的細節
-XX:+PrintGCDateStamps — 列印GC操作的時間戳
-XX:NewSize / XX:MaxNewSize — 設定新生代大小/新生代最大大小
-XX:NewRatio — 可以設定老生代和新生代的比例
-XX:PrintTenuringDistribution — 設定每次新生代GC後輸出幸存者樂園中對象年齡的分布
-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:設定老年代閥值的初始值和最大值
-XX:TargetSurvivorRatio:設定幸存區的目标使用率
一個".java"源檔案中是否可以包含多個類(不是内部類)?有什麼限制?
答:可以,但一個源檔案中最多隻能有一個公開類(public class)而且檔案名必須和公開類的類名完全保持一緻
Java 中的final關鍵字有哪些用法?
(1)修飾類:表示該類不能被繼承;
(2)修飾方法:表示方法不能被重寫;
(3)修飾變量:表示變量隻能一次指派以後值不能被修改(常量)。
Error和Exception有什麼差別?
答:Error表示系統級的錯誤和程式不必處理的異常,是恢複不是不可能但很困難的情況下的一種嚴重問題;比如記憶體溢出,不可能指望程式能處理這樣的情況;Exception表示需要捕捉或者需要程式進行處理的異常,是一種設計或實作問題;也就是說,它表示如果程式運作正常,從不會發生的情況。
Java使用異常來表示錯誤,并通過try ... catch捕獲異常;
Java的異常是class,并且從Throwable繼承;
Error是無需捕獲的嚴重錯誤,Exception是應該捕獲的可處理的錯誤;RuntimeException無需強制捕獲,非RuntimeException(Checked Exception)需強制捕獲,或者用throws聲明;
不推薦捕獲了異常但不進行任何處理。
參考try{}裡有一個return語句,那麼緊跟在這個try後的finally{}裡的代碼會不會被執行,什麼時候被執行,在return前還是後?
答:會執行,在finally塊中改變return的值對傳回值沒有任何影響,而對引用類型的資料會有影響。
列出一些你常見的運作時異常?
- ArithmeticException(算術異常)
- ClassCastException (類轉換異常)
- IllegalArgumentException (非法參數異常)
- IndexOutOfBoundsException (下标越界異常)
- NullPointerException (空指針異常)
- SecurityException (安全異常)
- ClassNotFoundException(指定類不存在)
- ArrayIndexOutOfBoundsException(資料下标越界)
List、Set、Map是否繼承自Collection接口?
答:List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的差別,而Set存儲的零散的元素且不允許有重複元素(數學中的集合也是如此),List是線性結構的容器,适用于按數值索引通路元素的情形。
Collection和Collections的差別?
答:Collection是一個接口,它是Set、List等容器的父接口;Collections是個一個工具類,提供了一系列的靜态方法來輔助容器操作,這些方法包括對容器的搜尋、排序、線程安全化等等。
Thread類的sleep()方法和對象的wait()方法都可以讓線程暫停執行,它們有什麼差別?
答:sleep()方法(休眠)是線程類(Thread)的靜态方法,調用此方法會讓目前線程暫停執行指定的時間,将執行機會(CPU)讓給其他線程,但是對象的鎖依然保持,是以休眠時間結束後會自動恢複(線程回到就緒狀态,請參考第66題中的線程狀态轉換圖)。wait()是Object類的方法,調用對象的wait()方法導緻目前線程放棄對象的鎖(線程暫停執行),進入對象的等待池(wait pool),隻有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool),如果線程重新獲得對象的鎖就可以進入就緒狀态。
線程的sleep()方法和yield()方法有什麼差別?
答:
① sleep()方法給其他線程運作機會時不考慮線程的優先級,是以會給低優先級的線程以運作的機會;yield()方法隻會給相同優先級或更高優先級的線程以運作的機會;
② 線程執行sleep()方法後轉入阻塞(blocked)狀态,而執行yield()方法後轉入就緒(ready)狀态;
③ sleep()方法聲明抛出InterruptedException,而yield()方法沒有聲明任何異常;
④ sleep()方法比yield()方法(跟作業系統CPU排程相關)具有更好的可移植性。
簡述synchronized 和java.util.concurrent.locks.Lock的異同?
答:Lock是Java 5以後引入的新的API,和關鍵字synchronized相比主要相同點:Lock 能完成synchronized所實作的所有功能;主要不同點:Lock有比synchronized更精确的線程語義和更好的性能,而且不強制性的要求一定要獲得鎖。synchronized會自動釋放鎖,而Lock一定要求程式員手工釋放,并且最好在finally 塊中釋放(這是釋放外部資源的最好的地方)。
Java中有幾種類型的流?
答:位元組流和字元流。位元組流繼承于InputStream、OutputStream,字元流繼承于Reader、Writer
事務的ACID是指什麼?
- 原子性(Atomic):事務中各項操作,要麼全做要麼全不做,任何一項操作的失敗都會導緻整個事務的失敗;
- 一緻性(Consistent):事務結束後系統狀态是一緻的;
- 隔離性(Isolated):并發執行的事務彼此無法看到對方的中間狀态;
- 持久性(Durable):事務完成後所做的改動都會被持久化,即使發生災難性的失敗。通過日志和同步備份可以在故障發生後重建資料。
補充:關于事務,在面試中被問到的機率是很高的,可以問的問題也是很多的。首先需要知道的是,隻有存在并發資料通路時才需要事務。當多個事務通路同一資料時,可能會存在5類問題,包括3類資料讀取問題(髒讀、不可重複讀和幻讀)和2類資料更新問題(第1類丢失更新和第2類丢失更新)。
獲得一個類的類對象有哪些方式?
- 方法1:類型.class,例如:String.class
- 方法2:對象.getClass(),例如:"hello".getClass()
- 方法3:Class.forName(),例如:Class.forName("java.lang.String")
如何通過反射建立對象?
- 方法1:通過類對象調用newInstance()方法,例如:String.class.newInstance()
- 方法2:通過類對象的getConstructor()或getDeclaredConstructor()方法獲得構造器(Constructor)對象并調用其newInstance()方法建立對象,例如:String.class.getConstructor(String.class).newInstance("Hello"
什麼是UML?
答:UML是統一模組化語言(Unified Modeling Language)的縮寫,它發表于1997年,綜合了當時已經存在的面向對象的模組化語言、方法和過程,是一個支援模型化和軟體系統開發的圖形化語言,為軟體開發的所有階段提供模型化和可視化支援。使用UML可以幫助溝通與交流,輔助應用設計和文檔的生成,還能夠闡釋系統的結構和行為。
UML中有哪些常用的圖?
答:UML定義了多種圖形化的符号來描述軟體系統部分或全部的靜态結構和動态結構,包括:用例圖(use case diagram)、類圖(class diagram)、時序圖(sequence diagram)、協作圖(collaboration diagram)、狀态圖(statechart diagram)、活動圖(activity diagram)、構件圖(component diagram)、部署圖(deployment diagram)等。在這些圖形化符号中,有三種圖最為重要,分别是:用例圖(用來捕獲需求,描述系統的功能,通過該圖可以迅速的了解系統的功能子產品及其關系)、類圖(描述類以及類與類之間的關系,通過該圖可以快速了解系統)、時序圖(描述執行特定任務時對象之間的互動關系以及執行順序,通過該圖可以了解對象能接收的消息也就是說對象能夠向外界提供的服務)。
Java Web方面
闡述Servlet和CGI的差別?
答:Servlet與CGI的差別在于Servlet處于伺服器程序中,它通過多線程方式運作其service()方法,一個執行個體可以服務于多個請求,并且其執行個體一般不會銷毀,而CGI對每個請求都産生新的程序,服務完成後就銷毀,是以效率上低于Servlet。
補充:Sun Microsystems公司在1996年釋出Servlet技術就是為了和CGI進行競争,Servlet是一個特殊的Java程式,一個基于Java的Web應用通常包含一個或多個Servlet類。Servlet不能夠自行建立并執行,它是在Servlet容器中運作的,容器将使用者的請求傳遞給Servlet程式,并将Servlet的響應回傳給使用者。通常一個Servlet會關聯一個或多個JSP頁面。以前CGI經常因為性能開銷上的問題被诟病,然而Fast CGI早就已經解決了CGI效率上的問題,是以面試的時候大可不必信口開河的诟病CGI,事實上有很多你熟悉的網站都使用了CGI技術。
Servlet接口中有哪些方法?
答:Servlet接口定義了5個方法,其中前三個方法與Servlet生命周期相關:
- void init(ServletConfig config) throws ServletException
- void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
- void destory()
- java.lang.String getServletInfo()
- ServletConfig getServletConfig()
Web容器加載Servlet并将其執行個體化後,Servlet生命周期開始,容器運作其init()方法進行Servlet的初始化;請求到達時調用Servlet的service()方法,service()方法會根據需要調用與請求對應的doGet或doPost等方法;當伺服器關閉或項目被解除安裝時伺服器會将Servlet執行個體銷毀,此時會調用Servlet的destroy()方法
轉發(forward)和重定向(redirect)的差別?
答:forward是容器中控制權的轉向,是伺服器請求資源,伺服器直接通路目标位址的URL,把那個URL 的響應内容讀取過來,然後把這些内容再發給浏覽器,浏覽器根本不知道伺服器發送的内容是從哪兒來的,是以它的位址欄中還是原來的位址。redirect就是伺服器端根據邏輯,發送一個狀态碼,告訴浏覽器重新去請求那個位址,是以從浏覽器的位址欄中可以看到跳轉後的連結位址,很明顯redirect無法通路到伺服器保護起來資源,但是可以從一個網站redirect到其他網站。forward更加高效,是以在滿足需要時盡量使用forward(通過調用RequestDispatcher對象的forward()方法,該對象可以通過ServletRequest對象的getRequestDispatcher()方法獲得),并且這樣也有助于隐藏實際的連結;在有些情況下,比如需要通路一個其它伺服器上的資源,則必須使用重定向(通過HttpServletResponse對象調用其sendRedirect()方法實作)。
get和post請求的差別?
①get請求用來從伺服器上獲得資源,而post是用來向伺服器送出資料;
②get将表單中資料按照name=value的形式,添加到action 所指向的URL 後面,并且兩者使用"?"連接配接,而各個變量之間使用"&"連接配接;post是将表單中的資料放在HTTP協定的請求頭或消息體中,傳遞到action所指向URL;
③get傳輸的資料要受到URL長度限制(1024位元組);而post可以傳輸大量的資料,上傳檔案通常要使用post方式;
④使用get時參數會顯示在位址欄上,如果這些資料不是敏感資料,那麼可以使用get;對于敏感資料還是應用使用post;
⑤get使用MIME類型application/x-www-form-urlencoded的URL編碼(也叫百分号編碼)文本的格式傳遞參數,保證被傳送的參數由遵循規範的文本組成,例如一個空格的編碼是"%20"。
什麼是Web Service(Web服務)?
答:從表面上看,Web Service就是一個應用程式,它向外界暴露出一個能夠通過Web進行調用的API。這就是說,你能夠用程式設計的方法透明的調用這個應用程式,不需要了解它的任何細節,跟你使用的程式設計語言也沒有關系。例如可以建立一個提供天氣預報的Web Service,那麼無論你用哪種程式設計語言開發的應用都可以通過調用它的API并傳入城市資訊來獲得該城市的天氣預報。之是以稱之為Web Service,是因為它基于HTTP協定傳輸資料,這使得運作在不同機器上的不同應用無須借助附加的、專門的第三方軟體或硬體,就可互相交換資料或內建。
補充:這裡必須要提及的一個概念是SOA(Service-Oriented Architecture,面向服務的架構),SOA是一種思想,它将應用程式的不同功能單元通過中立的契約聯系起來,獨立于硬體平台、作業系統和程式設計語言,使得各種形式的功能單元能夠更好的內建。顯然,Web Service是SOA的一種較好的解決方案,它更多的是一種标準,而不是一種具體的技術。
JVM 底層 與 GC(Garbage Collection) 的面試問題
Java中的引用類型
中文 | 英文 | 描述 |
---|---|---|
強引用 | StrongReference | Java的預設引用實作 |
弱引用 | WeakReference | |
虛引用 | PhantomReference | |
軟引用 | SoftReference |
JRE JDK JVM JIT的差別
名字 | |
---|---|
JRE | Java run-time,運作環境 |
JDK | Java development kit java開發工具,包含JRE |
JVM | Java virtual machine,java虛拟機 |
JIT | Just in time conpilation,即時編譯,當代碼執行的次數超過一定的門檻值時,會将java位元組碼轉換為本地代碼,提高性能 |
記憶體的的面試問題和答案
final、finalize 和 finally 的不同之處?
final | 修飾符,可以修飾變量,方法,類。初始化後不能改變 |
finalize | 垃圾收集器将對象從記憶體中清楚出去之前必做的清理工作 |
finally | 關鍵字,和try-catch使用,一定被執行 |
Java 中的編譯期常量是什麼?使用它又什麼風險?
公共靜态不可變(public static final )變量也就是我們所說的編譯期常量,這裡的 public 可選的。
Java 集合架構的面試題
List、Set、Map 和 Queue 之間的差別
繼承 | 實作類 | ||
---|---|---|---|
List | 有序,可重複,可空 | Collection | ArrayList,LinkList |
Set | 無需,不可重複,可空 | HashSet,LinkHashSet,TreeSet | |
Map | Object | ||
Queue |
線程安全 | ||
---|---|---|
ArrayList | 由數組實作,允許随機通路,插入删除慢,查詢快,動态數組 | |
LinkList | 雙向連結清單,适合頻繁更改,查詢慢,連結清單數組也是動态擴充的 |
HashSet | 快速查詢,是hashMap的包裝類,可以為null,不允許重複key | |
LinkHashSet | 是HashSet的子類,保證插入順序 | |
TreeSet | TreeMap的包裝類,不可空,保證有序性,底層為紅黑樹,存入 TreeSet 的元素必須實作 Comparable 接口 |
LinkedList | 保證了按照元素的插入順序進行操作 | |
PriorityQueue | 按照優先級進行插入抽取操作,保證最高或者最低優先級的的元素總是在隊列頭部,元素可以通過實作 Comparable 接口來保證優先順序 |
HashTable | 不可空,比HashMap慢,是同步的 | 安全 |
HashMap | 散列清單實作,可空 | 不 |
LinkedHashMap | 類似 HashMap,其鍵和值都可以為 null,其有序性為插入順序或者最近最少使用的次序(LRU 算法的核心就是這個),之是以能有序,是因為每個元素還加入到了一個雙向連結清單中 | |
TreeMap | 紅黑樹算法實作的,檢視鍵值對時會被排序,存入的元素必須實作 Comparable 接口,但是不允許鍵為 null,值可以為 null |
從資料結構角度看集合的差別有如下:
動态數組:ArrayList 内部是動态數組,HashMap 内部的連結清單數組也是動态擴充的,ArrayDeque 和 PriorityQueue 内部也都是動态擴充的數組。
連結清單:LinkedList 是用雙向連結清單實作的,HashMap 中映射到同一個連結清單數組的鍵值對是通過單向連結清單連結起來的,LinkedHashMap 中每個元素還加入到了一個雙向連結清單中以維護插入或通路順序。
哈希表:HashMap 是用哈希表實作的,HashSet, LinkedHashSet 和 LinkedHashMap 基于 HashMap,内部當然也是哈希表。
排序二叉樹:TreeMap 是用紅黑樹(基于排序二叉樹)實作的,TreeSet 内部使用 TreeMap,當然也是紅黑樹,紅黑樹能保持元素的順序且綜合性能很高。
堆:PriorityQueue 是用堆實作的,堆邏輯上是樹,實體上是動态數組,堆可以高效地解決一些其他資料結構難以解決的問題。
循環數組:ArrayDeque 是用循環數組實作的,通過對頭尾變量的維護,實作了高效的隊列操作。
位向量:EnumSet 是用位向量實作的,對于隻有兩種狀态且需要進行集合運算的資料使用位向量進行表示、位運算進行處理,精簡且高效
Java 中怎麼列印數組?(answer答案)
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法來列印數組。由于數組沒有實作 toString() 方法,是以如果将數組傳遞給 System.out.println() 方法,将無法列印出數組的内容,但是 Arrays.toString() 可以列印每個元素。
ArrayList 和 HashMap 的預設大小是多數?
在 Java 7 中,ArrayList 的預設大小是 10 個元素,HashMap 的預設大小是16個元素(必須是2的幂)。這就是 Java 7 中 ArrayList 和 HashMap 類的代碼片段:
// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;
//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
Java 中,Comparator 與 Comparable 有什麼不同?
Comparable 接口用于定義對象的自然順序,而 comparator 通常用于定義使用者定制的順序。Comparable 總是隻有一個,但是可以有多個 comparator 來定義對象的順序。
Java IO 和 NIO 的面試題
Date、Time 及 Calendar 的面試題
Java 中 java.util.Date 與 java.sql.Date 有什麼差別?
java.sql.Date是針對SQL語句使用的,它隻包含日期而沒有時間部分,它們都有getTime方法傳回毫秒數,自然就可以直接建構。java.util.Date 是 java.sql.Date 的父類,前者是常用的表示時間的類,我們通常格式化或者得到目前時間都是用他,後者之後在讀寫資料庫的時候用他,因為PreparedStament的setDate()的第2參數和ResultSet的getDate()方法的第2個參數都是java.sql.Date。
單元測試 JUnit 面試題
如何測試靜态方法?
可以使用 PowerMock 庫來測試靜态方法。
關于 OOP 和設計模式的面試題
接口是什麼?為什麼要使用接口而不是直接使用具體類?
接口用于定義 API。它定義了類必須得遵循的規則。同時,它提供了一種抽象,因為用戶端隻使用接口,這樣可以有多重實作,如 List 接口,你可以使用可随機通路的 ArrayList,也可以使用友善插入和删除的 LinkedList。接口中不允許寫代碼,以此來保證抽象,但是 Java 8 中你可以在接口聲明靜态的預設方法,這種方法是具體的。
Java 中,抽象類與接口之間有什麼不同?
Java 中,抽象類和接口有很多不同之處,但是最重要的一個是 Java 中限制一個類隻能繼承一個類,但是可以實作多個接口。抽象類可以很好的定義一個家族類的預設行為,而接口能更好的定義類型,有助于後面實作多态機制。
Java中CyclicBarrier 和 CountDownLatch有什麼不同?
CyclicBarrier 和 CountDownLatch 都可以用來讓一組線程等待其它線程。與 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。
如何避免死鎖?
死鎖是指兩個或兩個以上的程序在執行過程中,因争奪資源而造成的一種互相等待的現象,若無外力作用,它們都将無法推進下去。這是一個嚴重的問題,因為死鎖會讓你的程式挂起無法完成任務,死鎖的發生必須滿足以下四個條件:互斥條件:一個資源每次隻能被一個程序使用。請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。循環等待條件:若幹程序之間形成一種頭尾相接的循環等待資源關系。最簡單的方法就是阻止循環等待條件,将系統中所有的資源設定标志位、排序,規定所有的程序申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。這篇教程有代碼示例和避免死鎖的讨論細節。
Java中活鎖和死鎖有什麼差別?
這是上題的擴充,活鎖和死鎖類似,不同之處在于處于活鎖的線程或程序的狀态是不斷改變的,活鎖可以認為是一種特殊的饑餓。一個現實的活鎖例子是兩個人在狹小的走廊碰到,兩個人都試着避讓對方好讓彼此通過,但是因為避讓的方向都一樣導緻最後誰都不能通過走廊。簡單的說就是,活鎖和死鎖的主要差別是前者程序的狀态可以改變但是卻不能繼續執行。
怎麼檢測一個線程是否擁有鎖?
在java.lang.Thread中有一個方法叫holdsLock(),它傳回true如果當且僅當目前線程擁有某個具體對象的鎖
JVM中哪個參數是用來控制線程的棧堆棧小,
-Xss參數用來控制線程的堆棧大小
Java中synchronized 和 ReentrantLock 有什麼不同?
Java在過去很長一段時間隻能通過synchronized關鍵字來實作互斥,它有一些缺點。比如你不能擴充鎖之外的方法或者塊邊界,嘗試擷取鎖時不能中途取消等。Java 5 通過Lock接口提供了更複雜的控制來解決這些問題。 ReentrantLock 類實作了 Lock,它擁有與 synchronized 相同的并發性和記憶體語義且它還具有可擴充性。
Thread類中的yield方法有什麼作用
Yield方法可以暫停目前正在執行的線程對象,讓其它有相同優先級的線程執行。它是一個靜态方法而且隻保證目前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執行yield()的線程有可能在進入到暫停狀态後馬上又被執行。
Java中ConcurrentHashMap的并發度是什麼?
ConcurrentHashMap把實際map劃分成若幹部分來實作它的可擴充性和線程安全。這種劃分是使用并發度獲得的,它是ConcurrentHashMap類構造函數的一個可選參數,預設值為16,這樣在多線程情況下就能避免争用。
架構内容
Hibernate中SessionFactory是線程安全的嗎?Session是線程安全的嗎(兩個線程能夠共享同一個Session嗎)?
答:SessionFactory對應Hibernate的一個資料存儲的概念,它是線程安全的,可以被多個線程并發通路。SessionFactory一般隻會在啟動的時候建構。對于應用程式,最好将SessionFactory通過單例模式進行封裝以便于通路。Session是一個輕量級非線程安全的對象(線程間不能共享session),它表示與資料庫進行互動的一個工作單元。Session是由SessionFactory建立的,在任務完成之後它會被關閉。Session是持久層服務對外提供的主要接口。Session會延遲擷取資料庫連接配接(也就是在需要的時候才會擷取)。為了避免建立太多的session,可以使用ThreadLocal将session和目前線程綁定在一起,這樣可以讓同一個線程獲得的總是同一個session。Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。
什麼是IoC和DI?DI是如何實作的?
答:IoC叫控制反轉,是Inversion of Control的縮寫,DI(Dependency Injection)叫依賴注入,是對IoC更簡單的诠釋。控制反轉是把傳統上由程式代碼直接操控的對象的調用權交給容器,通過容器來實作對象元件的裝配和管理。所謂的"控制反轉"就是對元件對象控制權的轉移,從程式代碼本身轉移到了外部容器,由容器來建立對象并管理對象之間的依賴關系。IoC展現了好萊塢原則 - "Don’t call me, we will call you"。依賴注入的基本原則是應用元件不應該負責查找資源或者其他依賴的協作對象。配置對象的工作應該由容器負責,查找資源的邏輯應該從應用元件的代碼中抽取出來,交給容器來完成。DI是對IoC更準确的描述,即元件之間的依賴關系由容器在運作期決定,形象的來說,即由容器動态的将某種依賴關系注入到元件之中。
Spring中Bean的作用域有哪些?
答:在Spring的早期版本中,僅有兩個作用域:singleton和prototype,前者表示Bean以單例的方式存在;後者表示每次從容器中調用Bean時,都會傳回一個新的執行個體,prototype通常翻譯為原型。
補充:設計模式中的建立型模式中也有一個原型模式,原型模式也是一個常用的模式,例如做一個室内設計軟體,所有的素材都在工具箱中,而每次從工具箱中取出的都是素材對象的一個原型,可以通過對象克隆來實作原型模式。
Spring 2.x中針對WebApplicationContext新增了3個作用域,分别是:request(每次HTTP請求都會建立一個新的Bean)、session(同一個HttpSession共享同一個Bean,不同的HttpSession使用不同的Bean)和globalSession(同一個全局Session共享一個Bean)。
說明:單例模式和原型模式都是重要的設計模式。一般情況下,無狀态或狀态不可變的類适合使用單例模式。在傳統開發中,由于DAO持有Connection這個非線程安全對象因而沒有使用單例模式;但在Spring環境下,所有DAO類對可以采用單例模式,因為Spring利用AOP和Java API中的ThreadLocal對非線程安全的對象進行了特殊處理。
解釋一下什麼叫AOP(面向切面程式設計)?
答:AOP(Aspect-Oriented Programming)指一種程式設計範型,該範型以一種稱為切面(aspect)的語言構造為基礎,切面是一種新的子產品化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscutting concern)。
"橫切關注"是會影響到整個應用程式的關注功能,它跟正常的業務邏輯是正交的,沒有必然的聯系,但是幾乎所有的業務邏輯都會涉及到這些關注功能。通常,事務、日志、安全性等關注就是應用中的橫切關注功能。
你如何了解AOP中的連接配接點(Joinpoint)、切點(Pointcut)、增強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?
- 連接配接點(Joinpoint):程式執行的某個特定位置(如:某個方法調用前、調用後,方法抛出異常後)。一個類或一段程式代碼擁有一些具有邊界性質的特定點,這些代碼中的特定點就是連接配接點。Spring僅支援方法的連接配接點。
- 切點(Pointcut):如果連接配接點相當于資料中的記錄,那麼切點相當于查詢條件,一個切點可以比對多個連接配接點。Spring AOP的規則解析引擎負責解析切點所設定的查詢條件,找到對應的連接配接點。
- 增強(Advice):增強是織入到目标類連接配接點上的一段程式代碼。Spring提供的增強接口都是帶方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多資料上将增強譯為“通知”,這明顯是個詞不達意的翻譯,讓很多程式員困惑了許久。
說明: Advice在國内的很多書面資料中都被翻譯成"通知",但是很顯然這個翻譯無法表達其本質,有少量的讀物上将這個詞翻譯為"增強",這個翻譯是對Advice較為準确的诠釋,我們通過AOP将橫切關注功能加到原有的業務邏輯上,這就是對原有業務邏輯的一種增強,這種增強可以是前置增強、後置增強、傳回後增強、抛異常時增強和包圍型增強。
- 引介(Introduction):引介是一種特殊的增強,它為類添加一些屬性和方法。這樣,即使一個業務類原本沒有實作某個接口,通過引介功能,可以動态的未該業務類添加接口的實作邏輯,讓業務類成為這個接口的實作類。
- 織入(Weaving):織入是将增強添加到目标類具體連接配接點上的過程,AOP有三種織入方式:①編譯期織入:需要特殊的Java編譯期(例如AspectJ的ajc);②裝載期織入:要求使用特殊的類加載器,在裝載類的時候對類進行增強;③運作時織入:在運作時為目标類生成代理實作增強。Spring采用了動态代理的方式實作了運作時織入,而AspectJ采用了編譯期織入和裝載期織入的方式。
- 切面(Aspect):切面是由切點和增強(引介)組成的,它包括了對橫切關注功能的定義,也包括了對連接配接點的定義。
補充:代理模式是GoF提出的23種設計模式中最為經典的模式之一,代理模式是對象的結構模式,它給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。簡單的說,代理對象可以完成比原對象更多的職責,當需要為原對象添加橫切關注功能時,就可以使用原對象的代理對象。我們在打開Office系列的Word文檔時,如果文檔中有插圖,當文檔剛加載時,文檔中的插圖都隻是一個虛框占位符,等使用者真正翻到某頁要檢視該圖檔時,才會真正加載這張圖,這其實就是對代理模式的使用,代替真正圖檔的虛框就是一個虛拟代理;Hibernate的load方法也是傳回一個虛拟代理對象,等使用者真正需要通路對象的屬性時,才向資料庫發出SQL語句獲得真實對象。
Spring MVC的工作原理是怎樣的?
答:Spring MVC的工作原理如下圖所示:
① 用戶端的所有請求都交給前端控制器DispatcherServlet來處理,它會負責調用系統的其他子產品來真正處理使用者的請求。
② DispatcherServlet收到請求後,将根據請求的資訊(包括URL、HTTP協定方法、請求頭、請求參數、Cookie等)以及HandlerMapping的配置找到處理該請求的Handler(任何一個對象都可以作為請求的Handler)。
③在這個地方Spring會通過HandlerAdapter對該處理器進行封裝。
④ HandlerAdapter是一個擴充卡,它用統一的接口對各種Handler中的方法進行調用。
⑤ Handler完成對使用者請求的處理後,會傳回一個ModelAndView對象給DispatcherServlet,ModelAndView顧名思義,包含了資料模型以及相應的視圖的資訊。
⑥ ModelAndView的視圖是邏輯視圖,DispatcherServlet還要借助ViewResolver完成從邏輯視圖到真實視圖對象的解析工作。
⑦ 當得到真正的視圖對象後,DispatcherServlet會利用視圖對象對模型資料進行渲染。
⑧ 用戶端得到響應,可能是一個普通的HTML頁面,也可以是XML或JSON字元串,還可以是一張圖檔或者一個PDF檔案。
闡述Spring架構中Bean的生命周期?
① Spring IoC容器找到關于Bean的定義并執行個體化該Bean。
② Spring IoC容器對Bean進行依賴注入。
③ 如果Bean實作了BeanNameAware接口,則将該Bean的id傳給setBeanName方法。
④ 如果Bean實作了BeanFactoryAware接口,則将BeanFactory對象傳給setBeanFactory方法。
⑤ 如果Bean實作了BeanPostProcessor接口,則調用其postProcessBeforeInitialization方法。
⑥ 如果Bean實作了InitializingBean接口,則調用其afterPropertySet方法。
⑦ 如果有和Bean關聯的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調用。
⑧ 當銷毀Bean執行個體時,如果Bean實作了DisposableBean接口,則調用其destroy方法。
在Web項目中如何獲得Spring的IoC容器?
WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
并發
什麼是線程死鎖?如何避免死鎖?
多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,是以程式不可能正常終止。
如下圖所示,線程 A 持有資源 2,線程 B 持有資源 1,他們同時都想申請對方的資源,是以這兩個線程就會互相等待而進入死鎖狀态。
死鎖必須具備以下四個條件:
- 互斥條件:該資源任意一個時刻隻由一個線程占用。
- 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件 : 線程已獲得的資源在末使用完之前不能被其他線程強行剝奪,隻有自己使用完畢後才釋放資源。
- 循環等待條件 : 若幹程序之間形成一種頭尾相接的循環等待資源關系。
說說 JDK1.6 之後的synchronized 關鍵字底層做了哪些優化,可以詳細介紹一下這些優化嗎
JDK1.6 對鎖的實作引入了大量的優化,如偏向鎖、輕量級鎖、自旋鎖、适應性自旋鎖、鎖消除、鎖粗化等技術來減少鎖操作的開銷。鎖主要存在四種狀态,依次是:無鎖狀态、偏向鎖狀态、輕量級鎖狀态、重量級鎖狀态,他們會随着競争的激烈而逐漸更新。注意鎖可以更新不可降級,這種政策是為了提高獲得鎖和釋放鎖的效率。