Java面試手冊:核心基礎-2

1.寫clone()方法時,通常都有一行代碼,是什麼

  • clone 有缺省行爲,super.clone();因爲首先要把父類中的成員複製到位,然後纔是複製自己的成員。
  • 面向對象的特徵有哪些方面
  • 計算機軟件系統是現實生活中的業務在計算機中的映射,而現實生活中的業務其實就是一個個對象協作的過程。面向對象編程就是按現實業務一樣的方式將程序代碼按一個個對象進行組織和編寫,讓計算機系統能夠識別和理解用對象方式組織和編寫的程序代碼,這樣就可以把現實生活中的業務對象映射到計算機系統中。
  • ==封裝==:將對象封裝成獨立的,高度自治的,相對封閉的模塊,實現“高內聚,低耦合”,降低相互的依賴性,這個對象狀態(屬性)由這個對象自己的行爲(方法)來讀取和改變,將屬性(變量)定位private,只有這個類自己的方法纔可以訪問到這些成員變量,一個原則是:讓方法和它待在一起。
  • 舉例1:人要在黑板上畫圓,這一共涉及三個對象:人、黑板、圓,畫圓的方法要分配給哪個對象呢?由於畫圓需要使用到圓心和半徑,圓心和半徑顯然是圓的屬性,如果將它們在類中定義成了私有的成員變量,那麼,畫圓的方法必須分配給圓,它才能訪問到圓心和半徑這兩個屬性,人以後只是調用圓的畫圓方法、表示給圓發給消息而已,畫圓這個方法不應該分配在人這個對象上。
  • 舉例2: 司機將火車剎住了,剎車的動作是分配給司機,還是分配給火車,顯然,應該分配給火車,因爲司機自身是不可能有那麼大的力氣將一個火車給停下來的,只有火車自己才能完成這一動作,火車需要調用內部的離合器和剎車片等多個器件協作才能完成剎車這個動作,司機剎車的過程只是給火車發了一個消息,通知火車要執行剎車動作而已。
  • 面向對象的封裝性,即將對象封裝成一個高度自治和相對封閉的個體,對象狀態(屬性)由這個對象自己的行爲(方法)來讀取和改變。
  • ==繼承==:表示的是類與類的關係,是子類共享父類所有數據和方法的一種機制,可提高軟件的可重用性和擴展性,缺點是加強了耦合性。
  • 構造方法和private修飾的方法不可以被繼承。
  • 具體做法是:在創建一個類的時候,講一個已經存在的類所定義的內容變成自己的(複製)內容,並加入一些新的內容或者修改原來的內容以適應新的特殊要求。
  • ==多態==:程序運行時的多種狀態,即編程的時候我們並不知道程序的“引用變量的具體類型,以及該變量發出的方法調用”。只有在運行期間才能確定,因此我們就可以在不用修改源代碼,就可以實現將不同的引用變量綁定到不同的類實現上,讓其具體的方法也改變(即在不修改程序代碼,就可以實現修改運行時,所綁定的具體代碼)。
  • 多態性:增強了軟件的靈活性和擴展性。
  • ==抽象==: 將事物的相似和共性之處,拿出來,然後將這些事物歸爲一個類,忽略與當前主題和目標無關的那些方面,將注意力集中在與當前目標有關的方面,抽象包括行爲抽象和狀態抽象兩個方面。
  • 把握一個原則:當前系統需要什麼就只考慮什麼。

2.java中實現多態的機制是什麼

靠的是父類或接口定義的引用變量可以指向子類或具體實現類的實例對象,而程序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。

3.abstract class和interface有什麼區別?

這道題的思路是先從總體解釋抽象類和接口的基本概念,然後再比較兩者的語法細節,最後再說兩者的應用區別。比較兩者語法細節區別的條理是:==先從一個類中的構造方法、普通成員變量和方法(包括抽象方法),靜態變量和方法,繼承性等6個方面逐一去比較回答==,接着從第三者繼承的角度的回答,特別是最後用了一個典型的例子來展現自己深厚的技術功底。

  • 抽象類:含有abstract修飾符的class即爲抽象類,abstract類不能創建的實例對象。
    • 含有abstract方法的類必須定義爲abstract class,abstract class類中的方法不必是抽象的。
    • abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,==所以,不能有抽象構造方法或抽象靜態方法==。
    • 如果的子類沒有實現抽象父類中的所有抽象方法,那麼子類也必須定義爲abstract類型。
  • 接口:(interface)可以說成是抽象類的一種特例,接口中的所有方法都必須是抽象的。
 - 接口中的方法定義默認爲public abstract類型,接口中的成員變量類型默認爲public static final。
  • ==語法區別==:
    • 抽象類可以有構造方法,接口中不能有構造方法。
    • 抽象類中可以有普通成員變量,接口中沒有普通成員變量
    • 抽象類中可以包含非抽象的普通方法,接口中的所有方法必須都是抽象的,不能有非抽象的普通方法。
    • 抽象類中的抽象方法的訪問類型可以是public,protected和(默認類型,雖然eclipse下不報錯,但應該也不行),==但接口中的抽象方法只能是public類型的,並且默認即爲public abstract類型==。
    • 抽象類中可以包含靜態方法,接口中不能包含靜態方法
    • 抽象類和接口中都可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,並且默認即爲public static final類型。
    • 一個類可以實現多個接口,但只能繼承一個抽象類。
  • ==應用上的區別==:
    • 接口更多的是在系統架構設計方法發揮作用,主要用於定義模塊之間的通信契約
    • 而抽象類在代碼實現方面發揮作用,可以實現代碼的重用,例如,模板方法設計模式是抽象類的一個典型應用,假設某個項目的所有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只處理的具體業務邏輯代碼 } }

4.abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?

  • abstract的method 不可以是static的,因爲抽象的方法是要被子類實現的,而static與子類扯不上關係!
  • native方法表示該方法要用另外一種依賴平臺的編程語言實現的,不存在着被子類實現的問題,所以,它也不能是抽象的,不能與abstract混用。
    • 例如,FileOutputSteam類要硬件打交道,底層的實現用的是操作系統相關的api實現,例如,在windows用c語言實現的,所以,查看jdk的源代碼,可以發現FileOutputStream的open方法的定義如下:``` private native void open(String name) throws FileNotFoundException;```
  • 如果我們要用java調用別人寫的c語言函數,我們是無法直接調用的,我們需要按照java的要求寫一個c語言的函數,用我們的這個c語言函數去調用別人的c語言函數。由於我們的c語言函數是按java的要求來寫的,我們這個c語言函數就可以與java對接上,java那邊的對接方式就是定義出與我們這個c函數相對應的方法,java中對應的方法不需要寫具體的代碼,==但需要在前面聲明native==。
  • 關於synchronized與abstract合用的問題,我覺得也不行,synchronized應該是作用在一個具體的方法上纔有意義,而且,方法上的synchronized同步所使用的同步鎖對象是this,而抽象方法上無法確定this是什麼。

5.什麼是內部類?Static Nested Class 和 Inner Class的不同。

首先內部類的總體方面的特點:例如,在兩個地方可以定義,可以訪問外部類的成員變量,不能定義靜態成員,這是大的特點。然後再說一些細節方面的知識,例如,幾種定義方式的語法區別,靜態內部類,以及匿名內部類。

  • 內部類就是在一個類的內部定義的類,內部類中不能定義靜態成員(靜態成員不是對象的特性,只是爲了找一個容身之處,所以需要放到一個類中而已
  • 內部類可以直接訪問外部類中的成員變量,內部類可以定義在外部類的方法外面,也可以定義在外部類的方法體中 public class Outer{ int out_x = 0; public void method(){ Inner1 inner1 = new Inner1(); public class Inner2 { //在方法體內部定義的內部類 public method() { out_x = 3; } } Inner2 inner2 = new Inner2(); } //在方法體外面定義的內部類 public class Inner1 { } } public class Outer { public void start() { new Thread( new Runable(){public void run(){}; } ).start(); } }
    • 在方法內部定義的內部類前面不能有訪問類型修飾符,就好像方法中定義的局部變量一樣,但這種內部類的前面可以使用final或abstract修飾符。==這種內部類對其他類是不可見的其他類無法引用這種內部類==,但是這種內部類創建的實例對象可以傳遞給其他類訪問。這種內部類必須是先定義,後使用,即內部類的定義代碼必須出現在使用該類之前,這與方法中的局部變量必須先定義後使用的道理也是一樣的。這種內部類可以訪問方法體中的局部變量,但是,該局部變量前必須加final修飾符。
    • 在方法體內部還可以採用如下語法來==創建一種匿名內部類==,即定義某一接口或類的子類的同時,還創建了該子類的實例對象,無需爲該子類定義名稱:
    • 在方法體外面定義的內部類的訪問類型可以是public,protecte,默認的,private等==4種類型==,這就好像類中定義的成員變量有4種訪問類型一樣,==它們決定這個內部類的定義對其他類是否可見==。
    1. 我們可以在外部創建實例對象,但是創建內部類的實例對象前應該先創建外部類,然後用這個外部類的實例對象去創建內部類的實例對象Outer outer = new Outer(); Outer.Inner1 inner1 = outer.new Innner1();
    2. 最後,在方法外部定義的內部類前面可以加上static關鍵字,從而成爲Static Nested Class,它不再具有內部類的特性,所以,從狹義上講,它不是內部類。Static Nested Class與普通類在運行時的行爲和功能上沒有什麼區別,只是在編程引用時的語法上有一些差別,它可以定義成public、protected、默認的、private等多種類型,而普通類只能定義成public和默認的這兩種類型。在外面引用Static Nested Class類的名稱爲“外部類名.內部類名”。在外面不需要創建外部類的實例對象,就可以直接創建Static Nested Class.
    3. 由於static Nested Class不依賴於外部類的實例對象,所以,static Nested Class能訪問外部類的非static成員變量。當在外部類中訪問Static Nested Class時,可以直接使用Static Nested Class的名字,而不需要加上外部類的名字了,在Static Nested Class中也可以直接引用外部類的static的成員變量,不需要加上外部類的名字。
    4. 在靜態方法中定義的內部類也是Static Nested Class,這時候不能在類前面加static關鍵字,靜態方法中的Static Nested Class與普通方法中的內部類的應用方式很相似,它除了可以直接訪問外部類中的static的成員變量,還可以訪問靜態方法中的局部變量,但是,該局部變量前必須加final修飾符。

6.內部類可以引用它的包含類的成員嗎?有沒有什麼限制?

  • 一般而言是可以的。如果不是靜態內部類,那沒有什麼限制!
  • 但是,如果把靜態嵌套類當作內部類的一種特例,那在這種情況下不可以訪問外部類的普通成員變量,而只能訪問外部類中的靜態成員:比如下面的代碼 class Outer{ static int x; static class Inner{ void test(){ syso(x); } } }

7.Anonymous Inner Class (匿名內部類)是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)?

  • 可以繼承其他類或實現其他接口。不僅是可以,而是必須!

8.super.getClass()方法調用

   import java.util.Date;
    public  class Test extends Date{
        public static void main(String[] args) {
            new Test().test();
        }

        public void test(){
            System.out.println(super.getClass().getName());
        }
    } 

結果是Test, 在test方法中,直接調用getClass().getName()方法,返回的是Test類名,由於getClass()在Object類中定義成了final,子類不能覆蓋該方法。 所以,在test方法中調用getClass().getName()方法,其實就是在調用從父類繼承的getClass()方法,等效於調用super.getClass().getName()方法。 所以,super.getClass().getName()方法返回的也應該是Test,如果想得到父類的名稱,應該用如下代碼:getClass().getSuperClass().getName();

9.jdk中哪些類是不能繼承的?

  • 不能繼承的是類是那些用final關鍵字修飾的類。一般比較基本的類型或防止擴展類無意間破壞原來方法的實現的類型都應該是final的,在jdk中比如,System,String,StringBuffer等類型。

10.String是最基本的數據類型嗎?

  • 不是,是屬於引用類型。引用類型還有數組,日期等類型,java.lang.String類是final類型的,因此不可以繼承這個類、不能修改這個類。爲了提高效率節省空間,我們應該用StringBuffer類。
  • 基本數據類型包括byte、int、char、long、float、double、boolean和short。

11.String s = "Hello";s = s + " world!";這兩行代碼執行後,原始的String對象中的內容到底變了沒有?

  • 沒有。因爲String由final修飾,被設計成不可變(immutable)類,所以它的所有對象都是不可變對象。
  • 在這段代碼中,s原先指向一個String對象,內容是 "Hello",然後我們對s進行了+操作,然而s所指向的那個對象是沒有改變的,這時,s不指向原來那個對象了,而指向了另一個 String對象,內容爲"Hello world!",原來那個對象還存在於內存之中,只是s這個引用變量不再指向它了。
  • 由此我們知道,如果經常對字符串進行各種各樣的修改,或者說,不可預見的修改,那麼使用String來代表字符串的話會引起很大的內存開銷。因爲String對象建立之後不能再改變,所以對於每一個不同的字符串,都需要一個String對象來表示。
  • StringBuffer類,他允許修改,而不是每個不同的字符串都要生成一個新的對象。並且,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。

12.是否可以繼承String類?

  • 不能,String類是final類,故不可以繼承。 String支持的方法
  • 檢查序列的單個字符串,
  • 比較字符串
  • 搜索字符串
  • 提取字符串
  • 創建字符串結果
  • 將字符串大小寫進行轉換
  • 字符串轉換時通過tostring()方式轉換的(toString是object對象定義的,我們需要時需要重寫這個方法達到我們的要求)
  • string類的split()方法可以匹配正則表達式拆分字符串,故而可以將字符串拆分成單個字符串的形式。
  • 以及字符串串串聯符號,“+”,通過StringBuffer(StringBuider)和append()方法實現字符串可變,然後再轉換成String類,因爲StringBuffer對象如果存儲到java集合中時會出問題.

13.String s = new String("xyz");創建了幾個String Object? 二者之間有什麼區別?

  • 兩個或者一個對象。
  • ”xyz”對應一個對象,這個對象放在==字符串常量緩衝區==,常量”xyz”不管出現多少遍,都是緩衝區中的那一個。
  • New String每寫一遍,就創建一個新的對象,它依據那個常量”xyz”對象的內容來創建出一個新String對象,如果以前就用過’xyz’,就不會創建”xyz”自己了,直接從緩衝區拿。

14.String 和StringBuffer的區別

  • JAVA平臺提供了兩個類:String和StringBuffer,它們可以儲存和操作字符串,即包含多個字符的字符數據。
  • String覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法,所以,將StringBuffer對象存儲進Java集合類中時會出現問題。
  • String類: 表示內容不可改變的字符串,string重寫了equals()方法,new String(“abc”).equals(new String(“abc”)的結果爲true,
  • StringBuffer類: 表示內容可以被修改的字符串,因此當你知道字符數據要改變的時候你就可以使用==StringBuffer==(比如:你可以使用StringBuffer來動態構造字符數據),StringBuffer並沒有實現equals()方法,故new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的結果爲false。 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的區別。

15.StringBuffer與StringBuilder的區別

  • StringBuffer和StringBuilder類都表示內容可以被修改的字符串
  • StringBuilder是線程不安全的,運行效率高,如果一個字符串變量是在方法裏面定義,這種情況只可能有一個線程訪問它,不存在不安全的因素了,則用StringBuilder。
  • 如果要在類裏面定義成員變量,並且這個類的實例對象會在多線程環境下使用,那麼最好用StringBuffer。

16.如何把一段逗號分割的字符串轉換成一個數組?

  • 用正則表達式,代碼大概爲:==String [] result = orgStr.split(“,”);==
  • 用 StringTokenizer 代碼爲(高逼格): StringTokenizer tokener = StringTokenizer(orgStr,”,”); String [] result = new String[tokener .countTokens()]; int i=0; while(tokener.hasNext(){ result[i++]=toker.nextToken(); }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章