java初級面試題(二)

金木君發表於2020-10-24

11、 abstract class和interface有什麼區別?

答:
含有abstract修飾符的class即為抽象類,abstract 類不能建立的例項物件。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。
abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,所以,不能有抽象構造方法或抽象靜態方法。如果的子類沒有實現抽象父類中的所有抽象方法,那麼子類也必須定義為abstract型別。
介面(interface)可以說成是抽象類的一種特例,介面中的所有方法都必須是抽象的。介面中的方法定義預設為public abstract型別,介面中的成員變數型別預設為public static final。
下面比較一下兩者的語法區別:
1.抽象類可以有構造方法,介面中不能有構造方法。
2.抽象類中可以有普通成員變數,介面中沒有普通成員變數
3.抽象類中可以包含非抽象的普通方法,介面中的所有方法必須都是抽象的,不能有非抽象的普通方法。
4. 抽象類中的抽象方法的訪問型別可以是public,protected和(預設型別,雖然
eclipse下不報錯,但應該也不行),但介面中的抽象方法只能是public型別的,並且預設即為public abstract型別。
5. 抽象類中可以包含靜態方法,介面中不能包含靜態方法
6. 抽象類和介面中都可以包含靜態成員變數,抽象類中的靜態成員變數的訪問型別可以任意,但介面中定義的變數只能是public static final型別,並且預設即為public static final型別。
7. 一個類可以實現多個介面,但只能繼承一個抽象類。
下面接著再說說兩者在應用上的區別:
介面更多的是在系統架構設計方法發揮作用,主要用於定義模組之間的通訊契約。而抽象類在程式碼實現方面發揮作用,可以實現程式碼的重用,例如,模板方法設計模式是抽象類的一個典型應用,假設某個專案的所有Servlet類都要用相同的方式進行許可權判斷、記錄訪問日誌和處理異常,那麼就可以定義一個抽象的基類,讓所有的Servlet都繼承這個抽象基類,在抽象基類的service方法中完成許可權判斷、記錄訪問日誌和處理異常的程式碼,在各個子類中只是完成各自的業務邏輯程式碼,虛擬碼如下:
public abstract class BaseServlet extends HttpServlet
{
public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{

記錄訪問日誌

進行許可權判斷
if(具有許可權)

{
try

{

doService(request,response);

}
catch(Excetpion e)

{

記錄異常資訊

}

}

}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException;
//注意訪問許可權定義成protected,顯得既專業,又嚴謹,因為它是專門給子類用的

}
public class MyServlet1 extends BaseServlet
{
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{

本Servlet只處理的具體業務邏輯程式碼

}

}
父類方法中間的某段程式碼不確定,留給子類幹,就用模板方法設計模式。

12、 String是最基本的資料型別嗎?

答:
基本資料型別包括byte、int、char、long、float、double、boolean和short。
java.lang.String類是final型別的,因此不可以繼承這個類、不能修改這個類。為了提高效率節省空間,我們應該用StringBuffer類

13、 String s = “Hello”;s = s + " world!";這兩行程式碼執行後,原始的String物件中的內容到底變了沒有?

答:
沒有。因為String被設計成不可變(immutable)類,所以它的所有物件都是不可變物件。在這段程式碼中,s原先指向一個String物件,內容是 “Hello”,然後我們對s進行了+操作,那麼s所指向的那個物件是否發生了改變呢?答案是沒有。這時,s不指向原來那個物件了,而指向了另一個 String物件,內容為"Hello world!",原來那個物件還存在於記憶體之中,只是s這個引用變數不再指向它了。 通過上面的說明,我們很容易匯出另一個結論,如果經常對字串進行各種各樣的修改,或者說,不可預見的修改,那麼使用String來代表字串的話會引起很大的記憶體開銷。因為 String物件建立之後不能再改變,所以對於每一個不同的字串,都需要一個String物件來表示。這時,應該考慮使用StringBuffer類,它允許修改,而不是每個不同的字串都要生成一個新的物件。並且,這兩種類的物件轉換十分容易。 同時,我們還可以知道,如果要使用內容相同的字串,不必每次都new一個String。例如我們要在構造器中對一個名叫s的String引用變數進行初始化,把它設定為初始值,應當這樣做: public class Demo { private String s; … public Demo { s = “Initial Value”; } … } 而非 s = new String(“Initial Value”);
後者每次都會呼叫構造器,生成新物件,效能低下且記憶體開銷大,並且沒有意義,因為String物件不可改變,所以對於內容相同的字串,只要一個String物件來表示就可以了。也就說,多次呼叫上面的構造器建立多個物件,他們的String型別屬性s都指向同一個物件。 上面的結論還基於這樣一個事實:對於字串常量,如果內容相同,Java認為它們代表同一個String物件。而用關鍵字new呼叫構造器,總是會建立一個新的物件,無論內容是否相同。
至於為什麼要把String類設計成不可變類,是它的用途決定的。其實不只String,很多Java標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是物件導向思想的體現。不可變類有一些優點,比如因為它的物件是隻讀的,所以多執行緒併發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個物件來代表,可能會造成效能上的問題。所以Java標準類庫還提供了一個可變版本,即 StringBuffer。

14、 是否可以繼承String類?

答:
String類是final類故不可以繼承。

15、 String和StringBuffer的區別

答:
JAVA平臺提供了兩個類:String和StringBuffer,它們可以儲存和操作字串,即包含多個字元的字元資料。String類表示內容不可改變的字串。而StringBuffer類表示內容可以被修改的字串。當你知道字元資料要改變的時候你就可以使用StringBuffer。典型地,你可以使用StringBuffers來動態構造字元資料。另外,String實現了equals方法,new String(“abc”).equals(new String(“abc”)的結果為true,而StringBuffer沒有實現equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的結果為false。
接著要舉一個具體的例子來說明,我們要把1到100的所有數字拼起來,組成一個串。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++)

{

sbf.append(i);

}
上面的程式碼效率很高,因為只建立了一個StringBuffer物件,而下面的程式碼效率很低,因為建立了101個物件。
String str = new String();
for(int i=0;i<100;i++)

{

str = str + i;

}
在講兩者區別時,應把迴圈的次數搞成10000,然後用endTime-beginTime來比較兩者執行的時間差異,最後還要講講StringBuilder與StringBuffer的區別。
String覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法,所以,將StringBuffer物件儲存進Java集合類中時會出現問題。

16、 StringBuffer與StringBuilder的區別

答:
StringBuffer和StringBuilder類都表示內容可以被修改的字串,StringBuilder是執行緒不安全的,執行效率高,如果一個字串變數是在方法裡面定義,這種情況只可能有一個執行緒訪問它,不存在不安全的因素了,則用StringBuilder。如果要在類裡面定義成員變數,並且這個類的例項物件會在多執行緒環境下使用,那麼最好用StringBuffer。

17、 陣列有沒有length()這個方法? String有沒有length()這個方法?

答:
陣列沒有length()這個方法,有length的屬性。String有有length()這個方法。

18、 try {}裡有一個return語句,那麼緊跟在這個try後的finally {}裡的code會不會被執行,什麼時候被執行,在return前還是後?

答:

public   class   Test {
``
                  /**

                   * **@param** args add by zxx ,Dec 9, 2008

                   */

                    **public** **static** **void** main(String[] args) {

                  // **TODO** Auto-generated method stub

                       System.*out*.println(**new** Test().test());;

                    }
static **int** test()

                    {

                       **int** x = 1;

                       **try**

                       {

                            **return** x;

                       }

                       **finally**

                       {

                          ++x;

                       }

                   }

}

---------執行結果 ---------
執行結果是1,為什麼呢?主函式呼叫子函式並得到結果的過程,好比主函式準備一個空罐子,當子函式要返回結果時,先把結果放在罐子裡,然後再將程式邏輯返回到主函式。所謂返回,就是子函式說,我 不執行了,你主函式繼續執行吧,這沒什麼結果可言,結果是在說這話之前放進罐子裡的。

## 19、 final, finally, finalize的區別

答:
final 用於宣告屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
內部類要訪問區域性變數,區域性變數必須定義成final型別,例如,一段程式碼……
finally是異常處理語句結構的一部分,表示總是執行。
finalize是Object類的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉檔案等。JVM不保證此方法總被呼叫

## 20、 執行時異常與一般異常有何異同?

答:
異常表示程式執行過程中可能出現的非正常狀態,執行時異常表示虛擬機器的通常操作中可能遇到的異常,是一種常見執行錯誤。java編譯器要求方法必須宣告丟擲可能發生的非執行時異常,但是並不要求必須宣告丟擲未被捕獲的執行時異常。

相關文章