java常見筆試,面試題目深度解析
最近找工作,參加了幾個筆試面試,發現總結了常見的筆試面試知識點無非是以下這些:
1字串相關
2 異常相關
3 反射相關
4執行緒相關(Thread)
5 多型相關(Polymorphism)
6 陣列相關(Array)
7 集合相關(Collection)
8 設計模式相關(Design Pattern)
9 相等性相關(equals hashCode)
10 方法重寫相關(Override)
11 jsp相關(jsp)
12 servlet相關(servlet)
13 資料庫相關(sql)
14 三大框架相關(ssh)
15 others
1 字串相關
首先看看看段程式:tr
上圖三個結果都是
false
程式碼
public class StringTest { public static void main(String[] args) { String s0 = new String("abc");//此語句產生幾個String object String s1 = "abc";//此語句產生幾個String object String s2 = new String("abc");//此語句產生幾個String object String s3 = "abc"; System.out.println(s0 == s1);//輸出
Java堆和棧的區別(String類)
堆與棧 Java的堆是一個執行時資料區,物件從中分配空間。這些物件通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程式程式碼來顯式地釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶體的,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在執行時動態分配記憶體,存取速度較慢。 Java程式碼 int a = 3; int b = 3; 編譯器先處理int a = 3;首先它會在棧中建立一個變數為a的引用,然後查詢棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在建立完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。 字串 示例: Java程式碼 String s0="kvill"; String s1="kvill"; String s2="kv" + "ill"; System.out.println(s0==s1); System.out.println(s0==s2); 結果為: true 示例: Java程式碼 String s0="kvill"; String s1=new String("kvill"); String s2="kv" + new String("ill"); System.out.println(s0==s1); System.out.println(s0==s2); System.out.println(s1==s2); 結果為: Java程式碼 String s0= "kvill";String s1=new String("kvill"); String s2=new String("kvill");System.out.println(s0==s1); System.out.println("**********"); s1.intern();s2=s2.intern(); System.out.println(s0==s1); System.out.println(s0==s1.intern()); System.out.println(s0==s2); 結果為: **********" Java程式碼 final StringBuffer a = new StringBuffer("111"); final StringBuffer b = new StringBuffer("222"); a=b;//此句編譯不通過 final StringBuffer a = new StringBuffer("111"); a.append("222");//編譯通過 可見,final只對引用的"值"(即記憶體地址)有效,它迫使引用只能指向初始指向的那個物件,改變它的指向會導致編譯器錯誤。至於它所指向的物件的變化,final是不負責的。 |
2 異常相關(Exception)
Java異常處理機制
對於可能出現異常的程式碼,有兩種處理辦法:
第一、在方法中用try...catch語句捕獲並處理異常,catach語句可以有多個,用來匹配多個異常。例如:
public void p(int x){ |
第二、對於處理不了的異常或者要轉型的異常,在方法的宣告處通過throws語句丟擲異常。例如:
public void test1() throws MyException{ |
如果每個方法都是簡單的丟擲異常,那麼在方法呼叫方法的多層巢狀呼叫中,Java虛擬機器會從出現異常的方法程式碼塊中往回找,直到找到處理該異常的程式碼塊為止。然後將異常交給相應的catch語句處理。如果Java虛擬機器追溯到方法呼叫棧最底部main()方法時,如果仍然沒有找到處理異常的程式碼塊,將按照下面的步驟處理:
第一、呼叫異常的物件的printStackTrace()方法,列印方法呼叫棧的異常資訊。
第二、如果出現異常的執行緒為主執行緒,則整個程式執行終止;如果非主執行緒,則終止該執行緒,其他執行緒繼續執行。
通過分析思考可以看出,越早處理異常消耗的資源和時間越小,產生影響的範圍也越小。因此,不要把自己能處理的異常也拋給呼叫者。
還有一點,不可忽視:finally語句在任何情況下都必須執行的程式碼,這樣可以保證一些在任何情況下都必須執行程式碼的可靠性。比如,在資料庫查詢異常的時候,應該釋放JDBC連線等等。finally語句先於return語句執行,而不論其先後位置,也不管是否try塊出現異常。finally語句唯一不被執行的情況是方法執行了System.exit()方法。System.exit()的作用是終止當前正在執行的Java虛擬機器。finally語句塊中不能通過給變數賦新值來改變return的返回值,也建議不要在finally塊中使用return語句,沒有意義還容易導致錯誤。
最後還應該注意一下異常處理的語法規則:
第一、try語句不能單獨存在,可以和catch、finally組成try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。
第二、try、catch、finally三個程式碼塊中變數的作用域分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。
第三、多個catch塊時候,Java虛擬機器會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。
第四、throw語句後不允許有緊跟其他語句,因為這些沒有機會執行。
第五、如果一個方法呼叫了另外一個宣告丟擲異常的方法,那麼這個方法要麼處理異常,要麼宣告丟擲。
那怎麼判斷一個方法可能會出現異常呢?一般來說,方法宣告的時候用了throws語句,方法中有throw語句,方法呼叫的方法宣告有throws關鍵字。
throw和throws關鍵字的區別
throw用來丟擲一個異常,在方法體內。語法格式為:throw異常物件。
throws用來宣告方法可能會丟擲什麼異常,在方法名後,語法格式為:throws異常型別1,異常型別2...異常型別n。
四、如何定義和使用異常類
1、使用已有的異常類,假如為IOException、SQLException。
try{ |
2、自定義異常類
建立Exception或者RuntimeException的子類即可得到一個自定義的異常類。例如:
public class MyException extends Exception{ |
3、使用自定義的異常
用throws宣告方法可能丟擲自定義的異常,並用throw語句在適當的地方丟擲自定義的異常。例如:
在某種條件丟擲異常
public void test1() throws MyException{ |
將異常轉型(也叫轉譯),使得異常更易讀易於理解
public void test2() throws MyException{ |
還有一個程式碼,很有意思:
public void test2() throws MyException{ |
這段程式碼實際上捕獲了異常,然後又和盤托出,沒有一點意義,如果這樣還有什麼好處理的,不處理就行了,直接在方法前用throws宣告丟擲不就得了。異常的捕獲就要做一些有意義的處理。
五、執行時異常和受檢查異常
Exception類可以分為兩種:執行時異常和受檢查異常。
1、執行時異常
RuntimeException類及其子類都被稱為執行時異常,這種異常的特點是Java編譯器不去檢查它,也就是說,當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句宣告丟擲它,還是會編譯通過。例如,當除數為零時,就會丟擲java.lang.ArithmeticException異常。
2、受檢查異常
除了RuntimeException類及其子類外,其他的Exception類及其子類都屬於受檢查異常,這種異常的特點是要麼用try...catch捕獲處理,要麼用throws語句宣告丟擲,否則編譯不會通過。
3、兩者的區別
執行時異常表示無法讓程式恢復執行的異常,導致這種異常的原因通常是由於執行了錯誤的操作。一旦出現錯誤,建議讓程式終止。
受檢查異常表示程式可以處理的異常。如果丟擲異常的方法本身不處理或者不能處理它,那麼方法的呼叫者就必須去處理該異常,否則呼叫會出錯,連編譯也無法通過。當然,這兩種異常都是可以通過程式來捕獲並處理的,比如除數為零的執行時異常:
public class HelloWorld { |
4、執行時錯誤
Error類及其子類表示執行時錯誤,通常是由Java虛擬機器丟擲的,JDK中與定義了一些錯誤類,比如VirtualMachineError
和OutOfMemoryError,程式本身無法修復這些錯誤.一般不去擴充套件Error類來建立使用者自定義的錯誤類。而RuntimeException類表示程式程式碼中的錯誤,是可擴充套件的,使用者可以建立特定執行時異常類。
Error(執行時錯誤)和執行時異常的相同之處是:Java編譯器都不去檢查它們,當程式執行時出現它們,都會終止執行。
5、最佳解決方案
對於執行時異常,我們不要用try...catch來捕獲處理,而是在程式開發除錯階段,儘量去避免這種異常,一旦發現該異常,正確的做法就會改程序序設計的程式碼和實現方式,修改程式中的錯誤,從而避免這種異常。捕獲並處理執行時異常是好的解決辦法,因為可以通過改進程式碼實現來避免該種異常的發生。
對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws丟擲!
對於Error(執行時錯誤),不需要在程式中做任何處理,出現問題後,應該在程式在外的地方找問題,然後解決。
六、異常轉型和異常鏈
異常轉型在上面已經提到過了,實際上就是捕獲到異常後,將異常以新的型別的異常再丟擲,這樣做一般為了異常的資訊更直觀!比如:
public void run() throws MyException{ |
異常鏈,在JDK1.4以後版本中,Throwable類支援異常鏈機制。Throwable包含了其執行緒建立時執行緒執行堆疊的快照。它還包含了給出有關錯誤更多資訊的訊息字串。最後,它還可以包含cause(原因):另一個導致此throwable丟擲的throwable。它也稱為異常鏈設施,因為cause自身也會有cause,依此類推,就形成了異常鏈,每個異常都是由另一個異常引起的。
通俗的說,異常鏈就是把原始的異常包裝為新的異常類,並在新的異常類中封裝了原始異常類,這樣做的目的在於找到異常的根本原因。
通過Throwable的兩個構造方法可以建立自定義的包含異常原因的異常型別:
Throwable(String message, Throwable cause)
構造一個帶指定詳細訊息和cause的新throwable。
Throwable(Throwable cause)
構造一個帶指定cause和(cause==null ? null :cause.toString())(它通常包含類和cause的詳細訊息)的詳細訊息的新throwable。
getCause()
返回此throwable的cause;如果cause不存在或未知,則返回null。
initCause(Throwable cause)
將此throwable的cause初始化為指定值。
在Throwable的子類Exception中,也有類似的指定異常原因的構造方法:
Exception(String message, Throwable cause)
構造帶指定詳細訊息和原因的新異常。
Exception(Throwable cause)
根據指定的原因和(cause==null ? null : cause.toString())的詳細訊息構造新異常(它通常包含cause的類和詳細訊息)。
因此,可以通過擴充套件Exception類來構造帶有異常原因的新的異常類。
七、Java異常處理的原則和技巧
1、避免過大的try塊,不要把不會出現異常的程式碼放到try塊裡面,儘量保持一個try塊對應一個或多個異常。
2、細化異常的型別,不要不管什麼型別的異常都寫成Excetpion。
3、catch塊儘量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新丟擲新型別的異常。
4、不要把自己能處理的異常拋給別人。
5、不要用try...catch參與控制程式流程,異常控制的根本目的是處理程式的非正常情況。
throws和throw的區別與作用(當不想處理異常時,將其丟擲,在方法中使用throws丟擲可能的系統異常,不需要顯示地在方法內部使用throw異常;對於自定義異常,除了方法名使用throws,還要在方法內部顯示地用throw丟擲異常物件。)
throws 是用來宣告一個方法可能丟擲的所有異常資訊,注意,是可能的,所有的異常資訊,在Java 裡面,大家很熟悉什麼是類和物件,在這裡,throws 要丟擲的就是一個異常類,因為它並不知道具體會出現哪種異常,所以就簡單丟擲一個異常類,包含這個異常類下面所有可能的異常。throws 通常不用顯示的捕獲異常,而是由系統自動將所有捕獲到的異常資訊拋給上級方法來處理,舉個簡單的例子來說:A類宣告丟擲了一個異常類,如果系統捕獲到該異常類下面某個具體的異常物件,則上交給A的父類B來處理,假如B不做處理,那麼B繼續上拋,上拋給B的父類C來處理,以此類推,直至找到可以處理該異常的方法為止。
throw 如上面所說,throws是用來宣告一個方法可能丟擲的所有異常資訊,丟擲的是異常類,那麼throw就是如Java面向物件思想裡面的類和物件的關係,因為throw要丟擲的是一個具體的異常物件,而不是異常類。throw 則需要使用者自己捕獲相關的異常,而後再對其進行相關包裝,最後再將包裝後的異常資訊丟擲。通常在一個方法(類)的宣告處通過 throws 宣告方法(類)可能丟擲的異常資訊,而在方法(類)內部通過 throw 宣告一個具體的異常資訊。throws throw的用法如果在方法中會有異常被丟擲而你又不希望在這個方法體內對此異常進行處理,可以使用throws在宣告方法的時候同時宣告他可能會丟擲的異常。比如:
public void go() throws SQLException |
本來ds.getConnection()將可能會丟擲SQLException,但現在使用throws在方法名處聲明瞭,所以在方法體中就不需要try/catch塊來處理SQLException了throws用來標明一個成員函式可能丟擲的各種"異常"。對大多數Exception子類來說,Java編譯器會強迫你宣告在一個成員函式中丟擲的"異常"的型別。如果"異常"的型別是Error或RuntimeException,或它們的子類,這個規則不起作用,因為這在程式的正常部分中是不期待出現的。如果你想明確地丟擲一個RuntimeException,你必須用throws語句來宣告它的型別。這就重新定義了成員函數的定義語法:type method-name(arg-list) throws exception-list { }下面是一段程式,它丟擲了一個"異常",但既沒有捕捉它,也沒有用throws來宣告。這在編譯時將不會通過。
class ThrowsDemo1 { static void procedure( ) { System.out.println("inside procedure"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { procedure( ); } } |
為了讓這個例子編譯過去,我們需要宣告成員函式procedure丟擲了IllegalAccessException,並且在呼叫它的成員函式main裡捕捉它。下面是正確的例子:
class ThrowsDemo{ static void procedure( ) throws IllegalAccessException{ System.out.println("inside procedure"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { try { procedure( ); }catch (IllegalAccessException e) { System.out.println("caught " + e); } } } |
下面是輸出結果:C:\>java ThrowsDemo inside procedure caught java.lang.IllegalAccessException: demo
1、兩者位置不同。throws在宣告方法的同時宣告要丟擲一個異常類,而throw是在方法內部實現,丟擲一個具體的異常物件
2、對異常處理方式不同。throws對異常不處理,誰呼叫誰處理,throws的Exception的取值範圍要大於方法內部異常的最大範圍,而cathch的範圍又要大於throws的Exception的範圍;throw主動丟擲自定義異常類物件。
自定義異常通常是這樣的:
public class MyFirstException extends Exception {
public MyFirstException() {
super(); //預設構造方法,會打印出系統自帶的異常路徑訊息。
}
public MyFirstException(String msg) {
super(msg); //自定義構造方法,可以帶入一些自定義資訊。
}
public MyFirstException(String msg, Throwable cause) {
super(msg, cause); //要傳入一個系統異常,通常這種構造方法用於包裝系統異常,使異常更清楚:
/* try{
..................
} catch(Exception e){
throw new MyException("發生了異常",e);
}
*/
}
public MyFirstException(Throwable cause) {
super(cause);
}
3 反射相關(Reflection)
1、什麼是反射機制?怎樣用反射機制?
什麼是JAVA的反射機制
JDK中提供的Reflection API
JAVA反射機制提供了什麼功能
獲取類的Class物件
獲取類的Fields
獲取類的Method
獲取類的Constructor
新建類的例項Class<T>的函式newInstance通過Constructor物件的方法newInstance
呼叫類的函式呼叫private函式
設定/獲取類的屬性值 private屬性
動態建立代理類動態代理原始碼分析
JAVA反射Class<T>型別原始碼分析
JAVA反射原理分析 Class檔案結構 JVM載入類物件,對反射的支援
JAVA反射的應用
一、什麼是JAVA的反射機制
Java反射是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields和methods的所有資訊,並可於執行時改變fields內容或喚起methods。
Java反射機制容許程式在執行時載入、探知、使用編譯期間完全未知的classes。
換言之,Java可以載入一個執行時才得知名稱的class,獲得其完整結構。
二、JDK中提供的Reflection API
Java反射相關的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下圖:
Member介面 |
該介面可以獲取有關類成員(域或者方法)後者建構函式的資訊。 |
AccessibleObject類 |
該類是域(field)物件、方法(method)物件、建構函式(constructor)物件的基礎類。它提供了將反射的物件標記為在使用時取消預設Java 語言訪問控制檢查的能力。 |
Array類 |
該類提供動態地生成和訪問JAVA陣列的方法。 |
Constructor類 |
提供一個類的建構函式的資訊以及訪問類的建構函式的介面。 |
Field類 |
提供一個類的域的資訊以及訪問類的域的介面。 |
Method類 |
提供一個類的方法的資訊以及訪問類的方法的介面。 |
Modifier類 |
提供了 static 方法和常量,對類和成員訪問修飾符進行解碼。 |
Proxy類 |
提供動態地生成代理類和類例項的靜態方法。 |
三、JAVA反射機制提供了什麼功能
Java反射機制提供如下功能:
在執行時判斷任意一個物件所屬的類
在執行時構造任意一個類的物件
在執行時判段任意一個類所具有的成員變數和方法
在執行時呼叫任一個物件的方法
在執行時建立新類物件
在使用Java的反射功能時,基本首先都要獲取類的Class物件,再通過Class物件獲取其他的物件。
這裡首先定義用於測試的類:
01 |
class Type{ |
|
02 |
public int pubIntField; |
03 |
public String pubStringField; |
|
04 |
private int prvIntField; |
05 |
||
06 |
public Type(){ |
07 |
Log("Default Constructor"); |
|
08 |
} |
09 |
||
10 |
Type(int arg1, String arg2){ |
11 |
pubIntField = arg1; |
|
12 |
pubStringField = arg2; |
13 |
||
14 |
Log("Cons |