1. 程式人生 > >【經典】《Java170道面試筆試題全面含答案》涉及java/資料庫/Spring框架/JVM/資料結構演算法/設計模式相關

【經典】《Java170道面試筆試題全面含答案》涉及java/資料庫/Spring框架/JVM/資料結構演算法/設計模式相關

《Java170道面試筆試題全集》 -更新版-8.30

2018/4/7 日常修復

2017/12/28 更新文章

1、新增二級目錄

2、對部分問題進行了補充

9/24緊急修改以下問題(存在嚴重錯誤)
問題3;
完善
問題10、11

問題目錄:

1、面向物件的特徵有哪些方面?
2、訪問修飾符public,private,protected,以及不寫(預設)時的區別?
3、String 是最基本的資料型別嗎?
4、float f=3.4;是否正確?
5、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?
6、Java有沒有goto?
7、int和Integer有什麼區別?
8、&和&&的區別?
9、解釋記憶體中的棧(stack)、堆(heap)和靜態區(static area)的用法。
10、Math.round(11.5) 等於多少?Math.round(-11.5)等於多少?
11、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
12、用最有效率的方法計算2乘以8?
13、陣列有沒有length()方法?String有沒有length()方法?
14、在Java中,如何跳出當前的多重巢狀迴圈?
15、構造器(constructor)是否可被重寫(override)?
16、兩個物件值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
17、是否可以繼承String類?
18、當一個物件被當作引數傳遞到一個方法後,此方法可改變這個物件的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?
19、String和StringBuilder、StringBuffer的區別?
20、過載(Overload)和重寫(Override)的區別。過載的方法能否根據返回型別進行區分?
21、描述一下JVM載入class檔案的原理機制?
22、char 型變數中能不能存貯一箇中文漢字,為什麼?
23、抽象類(abstract class)和介面(interface)有什麼異同?
24、靜態巢狀類(Static Nested Class)和內部類(Inner Class)的不同?
25、Java 中會存在記憶體洩漏嗎,請簡單描述。
26、抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被synchronized修飾?
27、闡述靜態變數和例項變數的區別。
28、是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的呼叫?
29、如何實現物件克隆?
31、String s = new String("xyz");建立了幾個字串物件?
32、介面是否可繼承(extends)介面?抽象類是否可實現(implements)介面?抽象類是否可繼承具體類(concrete class)?
33、一個".java"原始檔中是否可以包含多個類(不是內部類)?有什麼限制?
34、Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現介面?
35、內部類可以引用它的包含類(外部類)的成員嗎?有沒有什麼限制?

36、Java 中的final關鍵字有哪些用法?

37、執行結果題

38、資料型別之間的轉換:
39、如何實現字串的反轉及替換?
40、怎樣將smxfl.cn編碼的字串轉換為lrzqb.cn編碼的字串?
41、日期和時間:
42、列印昨天的當前時刻。
43、比較一下Java和JavaSciprt。
44、什麼時候用斷言(assert)?
45、Error和Exception有什麼區別?
46、try{}裡有一個return語句,那麼緊跟在這個try後的finally{}裡的程式碼會不會被執行,什麼時候被執行,在return前還是後?
47、Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?
48、執行時異常與受檢異常有何異同?
49、列出一些你常見的執行時異常?
50、闡述final、finally、finalize的區別。
51、類ExampleA繼承Exception,類ExampleB繼承ExampleA。
請問執行此段程式碼的輸出是什麼?
52、List、Set、Map是否繼承自Collection介面?
53、闡述ArrayList、Vector、LinkedList的儲存效能和特性。
54、Collection和Collections的區別?
55、List、Map、Set三個介面存取元素時,各有什麼特點?
56、TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素?
57、Thread類的sleep()方法和物件的wait()方法都可以讓執行緒暫停執行,它們有什麼區別?
58、執行緒的sleep()方法和yield()方法有什麼區別?
59、當一個執行緒進入一個物件的synchronized方法A之後,其它執行緒是否可進入此物件的synchronized方法B?
60、請說出與執行緒同步以及執行緒排程相關的方法。
61、編寫多執行緒程式有幾種實現方式?
62、synchronized關鍵字的用法?
63、舉例說明同步和非同步。
64、啟動一個執行緒是呼叫run()還是start()方法?
65、什麼是執行緒池?
66、執行緒的基本狀態以及狀態之間的關係?
67、簡述synchronized 和java.util.concurrent.locks.Lock的異同?
68、Java中如何實現序列化,有什麼意義?
69、Java中有幾種型別的流?
70、寫一個方法,輸入一個檔名和一個字串,統計這個字串在這個檔案中出現的次數。
71、如何用Java程式碼列出一個目錄下所有的檔案?
72、用Java的套接字程式設計實現一個多執行緒的回顯(tqkdz.cn)伺服器。
73、XML文件定義有幾種形式?它們之間有何本質區別?解析XML文件有哪幾種方式?
74、你在專案中哪些地方用到了XML?
75、闡述JDBC操作資料庫的步驟。
76、Statement和PreparedStatement有什麼區別?哪個效能更好?
77、使用JDBC操作資料庫時,如何提升讀取資料的效能?如何提升更新資料的效能?
78、在進行資料庫程式設計時,連線池有什麼作用?
79、什麼是DAO模式?
80、事務的ACID是指什麼?
81、JDBC中如何進行事務處理?
82、JDBC能否處理Blob和bqcrg.cn
83、簡述正則表示式及其用途。
84、Java中是如何支援正則表示式操作的?
85、獲得一個類的類物件有哪些方式?
86、如何通過反射建立物件?
87、如何通過反射獲取和設定物件私有欄位的值?
88、如何通過反射呼叫物件的方法?
89、簡述一下面向物件的"六原則一法則"。
90、簡述一下你瞭解的設計模式。
91、用Java寫一個單例類。
92、什麼是UML?
93、UML中有哪些常用的圖?
94、用Java寫一個氣泡排序。
95、用Java寫一個折半查詢。
96、闡述Servlet和CGI的區別?
97、Servlet介面中有哪些方法?
98、轉發和重定向的區別?
99、JSP有哪些內建物件?作用分別是什麼?
100、get和post請求的區別?
101、常用的Web伺服器有哪些?
102、JSP和Servlet是什麼關係?
103、講解JSP中的四種作用域。
104、如何實現JSP或Servlet的單執行緒模式?
105、實現會話跟蹤的技術有哪些?
106、過濾器有哪些作用和用法?
107、監聽器有哪些作用和用法?
108、web.xml檔案中可以配置哪些內容?
109、你的專案中使用過哪些JSTL標籤?
110、使用標籤庫有什麼好處?如何自定義JSP標籤?
111、說一下表達式語言(EL)的隱式物件及其作用。
112、表示式語言(EL)支援哪些運算子?
113、Java Web開發的Model 1和Model 2分別指的是什麼?
114、Servlet 3中的非同步處理指的是什麼?
115、如何在基於Java的Web專案中實現檔案上傳和下載?
116、伺服器收到使用者提交的表單資料,到底是呼叫Servlet的doGet()還是doPost()方法?
117、JSP中的靜態包含和動態包含有什麼區別?
118、Servlet中如何獲取使用者提交的查詢引數或表單資料?
119、Servlet中如何獲取使用者配置的初始化引數以及伺服器上下文引數?
120、如何設定請求的編碼以及響應內容的型別?
121、解釋一下網路應用的模式及其特點。
122、什麼是Web Service(Web服務)?
123、概念解釋:SOAP、WSDL、UDDI。
124、Java規範中和Web Service相關的規範有哪些?
125、介紹一下你瞭解的Java領域的Web Service框架。
126、什麼是ORM?
127、持久層設計要考慮的問題有哪些?你用過的持久層框架有哪些?
128、Hibernate中SessionFactory是執行緒安全的嗎?Session是執行緒安全的嗎(兩個執行緒能夠共享同一個Session嗎)?
129、Hibernate中Session的load和get方法的區別是什麼?
130、fyrqh.cn的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分別是做什麼的?有什麼區別?
131、闡述Session載入實體物件的過程。
132、Query介面的list方法和iterate方法有什麼區別?
133、Hibernate如何實現分頁查詢?
134、鎖機制有什麼用?簡述Hibernate的悲觀鎖和樂觀鎖機制。
135、闡述實體物件的三種狀態以及轉換關係。
136、如何理解Hibernate的延遲載入機制?在實際應用中,延遲載入與Session關閉的矛盾是如何處理的?
137、舉一個多對多關聯的例子,並說明如何實現多對多關聯對映。
138、談一下你對繼承對映的理解。
139、簡述Hibernate常見優化策略。
140、談一談Hibernate的一級快取、二級快取和查詢快取。
141、Hibernate中DetachedCriteria類是做什麼的?
142、@OneToMany註解的mappedBy屬性有什麼作用?
143、MyBatis中使用#和$書寫佔位符有什麼區別?
144、解釋一下MyBatis中名稱空間(namespace)的作用。
145、MyBatis中的動態SQL是什麼意思?
146、什麼是IoC和DI?DI是如何實現的?
147、Spring中Bean的作用域有哪些?
148、解釋一下什麼叫AOP(面向切面程式設計)?
149、你是如何理解"橫切關注"這個概念的?
150、你如何理解AOP中的連線點(Joinpoint)、切點(Pointcut)、增強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?
151、Spring中自動裝配的方式有哪些?
152、Spring中如何使用註解來配置Bean?有哪些相關的註解?
153、Spring支援的事務管理型別有哪些?你在專案中使用哪種方式?
154、如何在Web專案中配置Spring的IoC容器?
155、如何在Web專案中配置Spring MVC?
156、Spring MVC的工作原理是怎樣的?
157、如何在容器中配置資料來源?
158、如何配置配置事務增強?
159、選擇使用Spring框架的原因(Spring框架為企業級開發帶來的好處有哪些)?
160、Spring IoC容器配置Bean的方式?
161、闡述Spring框架中Bean的生命週期?
162、依賴注入時如何注入集合屬性?
163、Spring中的自動裝配有哪些限制?
164、在Web專案中如何獲得Spring的IoC容器?
165. 大型網站在架構上應當考慮哪些問題?
166、你用過的網站前端優化的技術有哪些?
167、你使用過的應用伺服器優化技術有哪些?
168、什麼是攻擊?什麼是SQL注入攻擊?什麼是CSRF攻擊?
169. 什麼是領域模型?貧血模型(anaemic domain model)和充血模型(rich domain model)有什麼區別?
170. 談一談測試驅動開發(TDD)的好處以及你的理解。



對以上問題的解答:



1、面向物件的特徵有哪些方面? 

答:面向物件的特徵主要有以下幾(四)個方面: 
- 抽象:抽象是將一類物件的共同特徵總結出來構造類的過程,包括資料抽象和行為抽象兩方面。抽象只關注物件有哪些屬性和行為,並不關注這些行為的細節是什麼。 

繼承:繼承是從已有類得到繼承資訊建立新類的過程。提供繼承資訊的類被稱為父類(超類、基類);得到繼承資訊的類被稱為子類(派生類)。繼承讓變化中的軟體系統有了一定的延續性,同時繼承也是封裝程式中可變因素的重要手段(如果不能理解請閱讀閻巨集博士的《Java與模式》或《設計模式精解》中關於橋樑模式的部分)。is-a
 - 
封裝:通常認為封裝是把資料和操作資料的方法繫結起來,對資料的訪問只能通過已定義的介面。面向物件的本質就是將現實世界描繪成一系列完全自治、封閉的物件。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對資料和資料操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的程式設計介面(可以想想普通洗衣機和全自動洗衣機的差別,明顯全自動洗衣機封裝更好因此操作起來更簡單;我們現在使用的智慧手機也是封裝得足夠好的,因為幾個按鍵就搞定了所有的事情)。
 - 
多型性:多型性是指允許不同子型別的物件對同一訊息作出不同的響應。簡單的說就是用同樣的物件引用呼叫同樣的方法但是做了不同的事情。多型性分為編譯時的多型性和執行時的多型性。如果將物件的方法視為物件向外界提供的服務,那麼執行時的多型性可以解釋為:當A系統訪問B系統提供的服務時,B系統有多種提供服務的方式,但一切對A系統來說都是透明的(就像電動剃鬚刀是A系統,它的供電系統是B系統,B系統可以使用電池供電或者用交流電,甚至還有可能是太陽能,A系統只會通過B類物件呼叫供電的方法,但並不知道供電系統的底層實現是什麼,究竟通過何種方式獲得了動力)。方法過載(overload)實現的是編譯時的多型性(也稱為前繫結),而方法重寫(override)實現的是執行時的多型性(也稱為後繫結)。執行時的多型是面向物件最精髓的東西,要實現多型需要做兩件事:
1).創方法重寫(子類繼承父類並重寫父類中已有的或抽象的方法);
2).物件造型(過載,用父型別引用引用子型別物件,這樣同樣的引用呼叫同樣的方法就會根據子類物件的不同而表現出不同的行為)。
 

2、訪問修飾符public,private,protected,以及不寫(預設)時的區別? 

答:

修飾符當前類同 包 子 類其他包
public√ √ √ √ 
protected√ √ √ 
default√ √ 
private√ 

類的成員不寫訪問修飾時預設為default。預設對於同一個包中的其他類相當於公開(public),對於不是同一個包中的其他類相當於私有(private)。受保護(protected)對子類相當於公開,對不是同一包中的沒有父子關係的類相當於私有。Java中,外部類的修飾符只能是public或預設,類的成員(包括內部類)的修飾符可以是以上四種


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

答:不是。Java中的基本資料型別只有8個:byte、short、int、long、float、double、char、boolean;除了基本型別(primitive提type),剩下的都是引用型別(reference type)(包含列舉型別(enumerationtype))。

4、float f=3.4;是否正確? 

答: 不正確。3.4是雙精度數,將雙精度型(double)賦值給浮點型(float)屬於下轉型(down-casting,也稱為窄化)會造成精度損失,因此需要強制型別轉換float的
f =(float)3.4; 或者寫成float f =3.4F;。
 

5、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎? 

答:對於short s1 = 1; s1 = s1 +1;由於1是int型別,因此s1+1運算結果也是int型,需要強制轉換型別才能賦值給short型。而short s1 = 1; s1 +=1;可以正確編譯,因為s1+= 1;相當於s1 = (short)(s1 + 1);其中有隱含的強制型別轉換。
 

6、Java有沒有goto/const? 

答:goto ,const是Java中的保留字,在目前版本的Java中沒有使用。(根據James Gosling(Java之父)編寫的《The Java 
ProgrammingLanguage》一書的附錄中給出了一個Java關鍵字列表,其中有goto和const,但是這兩個是目前無法使用的關鍵字,因此有些地方將其稱之為保留字,其實保留字這個詞應該有更廣泛的意義,因為熟悉C語言的程式設計師都知道,在系統類庫中使用過的有特殊意義的單詞或單詞的組合都被視為保留字)
 

7、int和Integer有什麼區別? 

答:Java是一個近乎純潔的面向物件程式語言,但是為了程式設計的方便還是引入了基本資料型別,但是為了能夠將這些基本資料型別當成物件操作,Java為每一個基本資料型別都引入了對應的包裝型別(wrapper一class),int的包裝類就是Integer,從java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。 
Java 為每個原始型別提供了包裝型別: 
- 原始型別: boolean,char,byte,short,int,long,float,double 
- 包裝型別:Boolean,Character,Byte,Short,Integer,Long,Float,Double
 class AutoUnboxingTest {


    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;                  // 將3自動裝箱成Integer型別
        int c = 3;
        System.out.println(a == b);     // false 兩個引用沒有引用同一物件
        System.out.println(a == c);     // true a自動拆箱成int型別再和c比較
    }
} 


執行結果:
false
true


最近還遇到一個面試題,也是和自動裝箱和拆箱有點關係的,程式碼如下所示:
 public class Test03 {


    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100,f3 = 128, f4 = 128;


        System.out.println(f1 == f2);
        System.out.println(f3 == f4);
    }
} 


執行結果:
true
false


如果不明就裡很容易認為兩個輸出要麼都是true要麼都是false。首先需要注意的是f1、f2、f3、f4四個變數都是Integer物件引用,所以下面的==運算比較的不是值而是引用。裝箱的本質是什麼呢?當我們給一個Integer物件賦一個int值的時候,會呼叫Integer類的靜態方法valueOf,如果看看valueOf的原始碼就知道發生了什麼。
     public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

IntegerCache是Integer的內部類,其程式碼如下所示:
!存在快取!
 /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */


    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];


        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;


            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);


            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }


        private IntegerCache() {}
    }


簡單的說,如果整型字面量的值在-128到127之間(閉區間),cache有快取機制,不會new新的Integer物件,而是直接引用常量池中的Integer物件,所以上面的面試題中f1==f2的結果是true,而f3==f4的結果是false。
java中除了對Integer有快取機制外,其中還有ByteCache,ShortCache,LongCache,CharacterCache分別對其對應的型別進行快取,其中Byte,Short,Long的快取範圍都為-128——127,Character為0——127。特別要注意的是這幾個快取中,只有Integer的快取上限(high)可以設定,其他的都不能進行設定,為固定範圍。
  public static void main(String[] args) {
       Integer f1 = 100, f2 = 100, f3 = 127, f4 = 127;


       System.out.println(f1 == f2);
       System.out.println(f3 == f4);
   }


執行結果:
true
true


提醒:越是貌似簡單的面試題其中的玄機就越多,需要面試者有相當深厚的功力。
 

8、&和&&的區別? 

答:&運算子有兩種用法:(1)按位與;(2)邏輯與。&&運算子是短路與運算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算子左右兩端的布林值都是true整個表示式的值才是true。&&之所以稱為短路運算是因為,如果&&左邊的表示式的值是false,右邊的表示式會被直接短路掉,不會進行運算。很多時候我們可能都需要用&&而不是&,例如在驗證使用者登入時判定使用者名稱不是null而且不是空字串,應當寫為:usernamex!=null&&!username.equals(""),二者的順序不能交換,更不能用&運算子,因為第一個條件如果不成立,根本不能進行字串的equals比較,否則會產生NullPointerException異常。注意:邏輯或運算子(|)和短路或運算子(||)的差別也是如此。
 

補充:如果你熟悉JavaScript,那你可能更能感受到短路運算的強大,想成為JavaScript的高手就先從玩轉短路運算開始吧。
 

9、解釋記憶體中的棧(stack)、堆(heap)和靜態區(static area)的用法。 

答:通常我們定義一個基本資料型別的變數,一個物件的引用,還有就是函式呼叫的現場儲存都使用記憶體中的棧空間;而通過new關鍵字和構造器建立的物件放在堆空間;程式中的字面量(literal)如直接書寫的100、"hello"和常量都是放在靜態區中。棧空間操作起來最快但是棧很小,通常大量的物件都是放在堆空間,理論上整個記憶體沒有被其他程序使用的空間甚至硬碟上的虛擬記憶體都可以被當成堆空間來使用。
 String str = new String("hello"); 
上面的語句中變數str放在棧上,用new創建出來的字串物件放在堆上,而"hello"這個字面量放在靜態區。
--!補充
class abc{
private String a="a";
public void f1(){
String b="b";
final String c="c";
}



請問a,b,c存放在記憶體哪塊?
        堆疊棧
!補充:較新版本的Java(從Java 6的某個更新開始)中使用了一項叫"逃逸分析"的技術,可以將一些不會逃逸到方法外的變數(本來應該放堆),放在棧上以提升物件的操作效能。
 

10、Math.round(11.5) 等於多少?Math.round(-11.5)等於多少? 

答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四捨五入的原理是在引數上加0.5然後進行下取整。

 --補充!

    ceil>=rount>=floor

11、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上? 

答:在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。從Java 
5開始,Java中引入了列舉型別,expr也可以是enum型別,從Java 
7開始,expr還可以是字串(String),但是長整型(long)和float在目前所有的版本中都是不可以的。
 

12、用最有效率的方法計算2乘以16? 

答: 2 << 4(左移4位相當於乘以2的4次方,右移4位相當於除以2的4次方)。
 
--!補充 如何最有效率計算2乘以3/2?
補充:我們為編寫的類重寫hashCode方法時,可能會看到如下所示的程式碼,其實我們不太理解為什麼要使用這樣的乘法運算來產生雜湊碼(雜湊碼),而且為什麼這個數是個素數,為什麼通常選擇31這個數?前兩個問題的答案你可以自己百度一下,選擇31是因為可以用移位和減法運算來代替乘法,從而得到更好的效能。說到這裡你可能已經想到了:31那
* num 等價於(num << 5) - num,左移5位相當於乘以2的5次方再減去自身就相當於乘以31,現在的VM都能自動完成這個優化。
 public class PhoneNumber {
    private int areaCode;
    private String prefix;
    private String lineNumber;


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + areaCode;
        result = prime * result
                + ((lineNumber == null) ? 0 : lineNumber.hashCode());
        result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PhoneNumber other = (PhoneNumber) obj;
        if (areaCode != other.areaCode)
            return false;
        if (lineNumber == null) {
            if (other.lineNumber != null)
                return false;
        } else if (!lineNumber.equals(other.lineNumber))
            return false;
        if (prefix == null) {
            if (other.prefix != null)
                return false;
        } else if (!prefix.equals(other.prefix))
            return false;
        return true;
    }


} 


13、陣列有沒有length()方法?String有沒有length()方法? 

答:陣列沒有length()方法,有length 的屬性。String 
有length()方法。javascript中,獲得字串的長度是通過length屬性得到的,這一點容易和Java混淆。
 

14、在Java中,如何跳出當前的多重巢狀迴圈? 

答:在最外層迴圈前加一個標記如A,然後用break 
A;可以跳出多重迴圈。(Java中支援帶標籤的break和continue語句,作用有點類似於C和C++中的goto語句,但是就像要避免使用goto一樣,應該避免使用帶標籤的break和continue,因為它不會讓你的程式變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)
 

15、構造器(constructor)是否可被重寫(override)? 

答:構造器不能被繼承,因此不能被重寫,但可以被過載。
 

16、兩個物件值相同(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註解。

 

17、是否可以繼承String類? 

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


補充:繼承String本身就是一個錯誤的行為,對String型別最好的重用方式是關聯關係(Has-A)和依賴關係(Use-A)而不是繼承關係(Is-A)。
 

18、當一個物件被當作引數傳遞到一個方法後,此方法可改變這個物件的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞? 

答:是值傳遞。Java語言的方法呼叫只支援引數的值傳遞。當一個物件例項作為一個引數被傳遞到方法中時,引數的值就是對該物件的引用。物件的屬性可以在被呼叫過程中被改變,但對物件引用的改變是不會影響到呼叫者的。C++和C#中可以通過傳引用或傳輸出引數來改變傳入的引數的值。在C#中可以編寫如下所示的程式碼,但是在Java中卻做不到。
 using System;


namespace CS01 {


    class Program {
        public static void swap(ref int x, ref int y) {
            int temp = x;
            x = y;
            y = temp;
        }


        public static void Main (string[] args) {
            int a = 5, b = 10;
            swap (ref a, ref b);
            // a = 10, b = 5;
            Console.WriteLine ("a = {0}, b = {1}", a, b);
        }
    }
} 




說明:Java中沒有傳引用實在是非常的不方便,這一點在Java 
8中仍然沒有得到改進,正是如此在Java編寫的程式碼中才會出現大量的Wrapper類(將需要通過方法呼叫修改的引用置於一個Wrapper類中,再將Wrapper物件傳入方法),這樣的做法只會讓程式碼變得臃腫,尤其是讓從C和C++轉型為Java程式設計師的開發者無法容忍。
 

19、String和StringBuilder、StringBuffer的區別? 

答:Java平臺提供了兩種型別的字串:String和StringBuffer/StringBuilder,它們可以儲存和操作字串。其中String是隻讀字串,也就意味著String引用的字串內容是不能被改變的。而StringBuffer/StringBuilder類表示的字串物件可以直接進行修改。StringBuilder是Java#
5中引入的,它和StringBuffer的方法完全相同,區別在於它是在單執行緒環境下使用的,因為它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer要高。
 


面試題1 - 什麼情況下用+運算子進行字串連線比呼叫StringBuffer/StringBuilder物件的append方法連線字串效能更好?(String沒有append方法)
 
面試題2 - 請說出下面程式的輸出。
 class StringEqualTest {


    public static void main(String[] args) {
        String s1 = "Programming";
        String s2 = new String("Programming");
        String s3 = "Program" + "ming";
        System.out.println(s1 == s2);
        System.out.println(s1 == s3);
        System.out.println(s1 == s1.intern());
    }
}


FLASE 
TRUE
TRUE
補充:String物件的intern方法會得到字串物件在常量池中對應的版本的引用(如果常量池中有一個字串與String物件的equals結果是true),如果常量池中沒有對應的字串,則該字串將被新增到常量池中,然後返回常量池中字串的引用。
 

20、過載(Overload)和重寫(Override)的區別。過載的方法能否根據返回型別進行區分? 

答:方法的過載和重寫都是實現多型的方式,區別在於前者實現的是編譯時的多型性,而後者實現的是執行時的多型性。過載發生在一個類中,同名的方法如果有不同的引數列表(引數型別不同、引數個數不同或者二者都不同)則視為過載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回型別,比父類被重寫方法更好訪問,不能比父類被重寫方法宣告更多的異常(里氏代換原則)。過載對返回型別沒有特殊的要求。
 


面試題:華為的面試題中曾經問過這樣一個問題 - "為什麼不能根據返回型別來區分過載",快說出你的答案吧!
 

21、描述一下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所指定的目錄中記載類,是使用者自定義載入器的預設父載入器。
 

22、char 型變數中能不能存貯一箇中文漢字,為什麼? 

答:char型別可以儲存一箇中文漢字,因為Java中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字元在字符集中的編號,這是統一的唯一方法),一個char型別佔2個位元組(16位元),所以放一箇中文是沒問題的。
 
補充:使用Unicode意味著字元在JVM內部和外部有不同的表現形式,在JVM內部都是Unicode,當這個字元被從JVM內部轉移到外部時(例如存入檔案系統中),需要進行編碼轉換。所以Java中有位元組流和字元流,以及在字元流和位元組流之間進行轉換的轉換流,如InputStreamReader和OutputStreamReader,這兩個類是位元組流和字元流之間的介面卡類,承擔了編碼轉換的任務;(對於C程式設計師來說,要完成這樣的編碼轉換恐怕要依賴於union(聯合體/共用體)共享記憶體的特徵來實現了。)
 

23、抽象類(abstract class)和介面(interface)有什麼異同? 

答:抽象類和介面都不能夠例項化,但可以定義抽象類和介面型別的引用。
1)一個類如果繼承了某個抽象類或者實現了某個介面都需要對其中的抽象方法全部進行實現,否則該類仍然需要被宣告為抽象類。
2)介面比抽象類更加抽象,因為抽象類中可以定義構造器,可以有抽象方法和具體方法,而介面中不能定義構造器而且其中的方法全部都是抽象方法(只能定義)。
抽象類中的成員可以是private、預設、protected、public的,而介面中的成員全都是public的。
3)抽象類中可以定義成員變數,而介面中定義的成員變數實際上都是常量。
4)有抽象方法的類必須被宣告為抽象類,而抽象類未必要有抽象方法。
5)實現、繼承多個介面,繼承一個類;
6)繼承Is-A,實現Like-A關係;



 

24、靜態巢狀類(Static Nested Class)和內部類(Inner Class)的不同? 

答:Static Nested 
Class是被宣告為靜態(static)的內部類,它可以不依賴於外部類例項被例項化。而通常的內部類需要在外部類例項化後才能例項化,其語法看起來挺詭異的,如下所示。
 
/**
 * 撲克類(一副撲克)
 * @author Yien
 *
 */
public class Poker {
    private static String[] suites = {"黑桃", "紅桃", "草花", "方塊"};
    private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};


    private Card[] cards;


    /**
     * 構造器
     * 
     */
    public Poker() {
        cards = new Card[52];
        for(int i = 0; i < suites.length; i++) {
            for(int j = 0; j < faces.length; j++) {
                cards[i * 13 + j] = new Card(suites[i], faces[j]);
            }
        }
    }


    /**
     * 洗牌 (隨機亂序)
     * 
     */
    public void shuffle() {
        for(int i = 0, len = cards.length; i < len; i++) {
            int index = (int) (Math.random() * len);
            Card temp = cards[index];
            cards[index] = cards[i];
            cards[i] = temp;
        }
    }


    /**
     * 發牌
     * @param index 發牌的位置
     * 
     */
    public Card deal(int index) {
        return cards[index];
    }


    /**
     * 卡片類(一張撲克)
     * [內部類]
     * @author Yien
     *
     */
    public class Card {
        private String suite;   // 花色
        private int face;       // 點數


        public Card(String suite, int face) {
            this.suite = suite;
            this.face = face;
        }


        @Override
        public String toString() {
            String faceStr = "";
            switch(face) {
            case 1: faceStr = "A"; break;
            case 11: faceStr = "J"; break;
            case 12: faceStr = "Q"; break;
            case 13: faceStr = "K"; break;
            default: faceStr = String.valueOf(face);
            }
            return suite + faceStr;
        }
    }
}
測試程式碼:
 class PokerTest {


    public static void main(String[] args) {
        Poker poker = new Poker();
        poker.shuffle();                // 洗牌
        Poker.Card c1 = poker.deal(0);  // 發第一張牌
        // 對於非靜態內部類Card
        // 只有通過其外部類Poker物件才能建立Card物件
        Poker.Card c2 = poker.new Card("紅心", 1);    // 自己建立一張牌


        System.out.println(c1);     // 洗牌後的第一張
        System.out.println(c2);     // 列印: 紅心A
    }
} 



面試題 - 下面的程式碼哪些地方會產生編譯錯誤?
 class Outer {


    class Inner {}


    public static void foo() { new Inner(); }


    public void bar() { new Inner(); }


    public static void main(String[] args) {
        new Inner();
    }
} 



注意:Java中非靜態內部類物件的建立要依賴其外部類物件,上面的面試題中foo和main方法都是靜態方法,靜態方法中沒有this,也就是說沒有所謂的外部類物件,因此無法建立內部類物件,如果要在靜態方法中建立內部類物件,可以這樣做:
     new Outer().new Inner();

25、Java 中會存在記憶體洩漏嗎,請簡單描述。 

答:理論上Java因為有垃圾回收機制(GC)不會存在記憶體洩露問題(這也是Java被廣泛使用於伺服器端程式設計的一個重要原因);然而在實際開發中,可能會存在無用但可達的物件,這些物件不能被GC回收,因此也會導致記憶體洩露的發生。例如hibernate的Session(一級快取)中的物件屬於持久態,垃圾回收器是不會回收這些物件的,然而這些物件中可能存在無用的垃圾物件,如果不及時關閉(close)或清空(flush)一級快取就可能導致記憶體洩露。下面例子中的程式碼也會導致記憶體洩露。


import java.util.Arrays;
import java.util.EmptyStackException;


public class MyStack<T> {
    private T[] elements;
    private int size = 0;


    private static final int INIT_CAPACITY = 16;


    public MyStack() {
        elements = (T[]) new Object[INIT_CAPACITY];
    }


    public void push(T elem) {
        ensureCapacity();
        elements[size++] = elem;
    }


    public T pop() {
        if(size == 0) 
            throw new EmptyStackException();
        return elements[--size];
    }


    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}


上面的程式碼實現了一個棧(先進後出(FILO))結構,乍看之下似乎沒有什麼明顯的問題,它甚至可以通過你編寫的各種單元測試。然而其中的pop方法卻存在記憶體洩露的問題,當我們用pop方法彈出棧中的物件時,該物件不會被當作垃圾回收,即使使用棧的程式不再引用這些物件,因為棧內部維護著對這些物件的過期引用(obsolete些
reference)。在支援垃圾回收的語言中,記憶體洩露是很隱蔽的,這種記憶體洩露其實就是無意識的物件保持。如果一個物件引用被無意識的保留起來了,那麼垃圾回收器不會處理這個物件,也不會處理該物件引用的其他物件,即使這樣的物件只有少數幾個,也可能會導致很多的物件被排除在垃圾回收之外,從而對效能造成重大影響,極端情況下會引發Diska
Paging(實體記憶體與硬碟的虛擬記憶體交換資料),甚至造成OutOfMemoryError。
 

26、抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被synchronized修飾? 

答:都不能。抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。本地方法是由原生代碼(如C程式碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。synchronized和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。
 

27、闡述靜態變數和例項變數的區別。 

答:靜態變數是被static修飾符修飾的變數,也稱為類變數,它屬於類,不屬於類的任何一個物件,一個類不管建立多少個物件,靜態變數在記憶體中有且僅有一個拷貝;例項變數必須依存於某一例項,需要先建立物件然後通過物件才能訪問到它。靜態變數可以實現讓多個物件共享記憶體。

補充:在Java開發中,上下文類和工具類中通常會有大量的靜態成員。
 

28、是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的呼叫? 

答:不可以,靜態方法只能訪問靜態成員,因為非靜態方法的呼叫要先建立物件,在呼叫靜態方法時可能物件並沒有被初始化。
 

29、如何實現物件克隆? 

答:有兩種方式: 
1).實現Cloneable介面並重寫Object類中的clone()方法; 


2).實現Serializable介面,通過物件的序列化和反序列化實現克隆,可以實現真正的深度克隆,程式碼如下。


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;


public class MyUtil {


    private MyUtil() {
        throw new AssertionError();
    }


    public static <T> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);


        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();


        // 說明:呼叫ByteArrayInputStream或ByteArrayOutputStream物件的close方法沒有任何意義
        // 這兩個基於記憶體的流只要垃圾回收器清理物件就能夠釋放資源,這一點不同於對外部資源(如檔案流)的釋放
    }
}
下面是測試程式碼:
 import java.io.Serializable;


/**
 * 人類
 * @author Yien
 *
 */
class Person implements Serializable {
    private static final long serialVersionUID = -9102017020286042305L;


    private String name;    // 姓名
    private int age;        // 年齡
    private Car car;        // 座駕


    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }


    public Car getCar() {
        return car;
    }


    public void setCar(Car car) {
        this.car = car;
    }


    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }


}
/**
 * 小汽車類
 * @author Yien
 *
 */
class Car implements Serializable {
    private static final long serialVersionUID = -5713945027627603702L;


    private String brand;       // 品牌
    private int maxSpeed;       // 最高時速


    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }


    public String getBrand() {
        return brand;
    }


    public void setBrand(String brand) {
        this.brand = brand;
    }


    public int getMaxSpeed() {
        return maxSpeed;
    }


    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }


    @Override
    public String toString() {
        return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
    }


}
class CloneTest {


    public static void main(String[] args) {
        try {
            Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
            Person p2 = MyUtil.clone(p1);   // 深度克隆
            p2.getCar().setBrand("BYD");
            // 修改克隆的Person物件p2關聯的汽車物件的品牌屬性
            // 原來的Person物件p1關聯的汽車不會受到任何影響
            // 因為在克隆Person物件時其關聯的汽車物件也被克隆了
            System.out.println(p1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} 

注意:基於序列化和反序列化實現的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可以檢查出要克隆的物件是否支援序列化,這項檢查是編譯器完成的,不是在執行時丟擲異常,這種是方案明顯優於使用Object類的clone方法克隆物件。讓問題在編譯的時候暴露出來總是優於把問題留到執行時。
 

30、GC是什麼?為什麼要有GC? 

答:GC是垃圾收集的意思,記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導致程式或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測物件是否超過作用域從而達到自動回收記憶體的目的,Java語言沒有提供釋放已分配記憶體的顯示操作方法。Java程式設計師不用擔心記憶體管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以呼叫下面的方法之一:System.gc()8或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:設定倖存區的目標使用率
 

31、String s = new String("xyz");建立了幾個字串物件? 



答:當靜態區有"xyz"時是一個,若無兩個物件,一個是靜態區的"xyz",一個是用new建立在堆上的物件。
 

32、介面是否可繼承(extends)介面?抽象類是否可實現(implements)介面?抽象類是否可繼承具體類(concrete class)? 

答:介面可以繼承介面,而且支援多重繼承(繼承多介面)。抽象類可以實現(implements)介面,抽象類可繼承具體類也可以繼承抽象類。

!--- 詳細補充

一道java 常見面試題,網上找到的幾乎每個 java 面試筆試題大全或集錦裡都能找到這道題。
題目如下:
 
問: 抽象類是否可繼承實體類 (concrete class)
答: 抽象類是可以繼承實體類,但前提是實體類必須有明確的建構函式
 
答案很明確,可以繼承。其實從Object就是個實體類,java的API文件裡,每個抽象類的條目裡都明確寫著直接或間接繼承自Object,所以這點是沒有疑問的。
 
關鍵在於這答案裡所說的“前提是實體類必須有明確的建構函式”一句,是什麼意思。
 
一般學習者會寫的簡單試驗程式碼:
 
class A{}
abstract class B extends A{}
 
結果完全正常,編譯通過。似乎和“實體類必須有明確的建構函式”完全沒有關係。
 
這個問題涉及到兩個個基礎知識:
1.
所有的class都必須有一個構造方法,如果你沒有在程式碼裡宣告構造方法,系統會自動給你生成一個公有無參的構造方法。而只要你自己聲明瞭一個構造方法,無論有參無參,私有公有,系統就不再幫你生成預設無參構造器了。
2.
所有的子類構造器都要求在第一行程式碼中呼叫父類構造器,如果不寫,系統預設去呼叫父類的無參構造器。
 
 
所以,如果把系統預設配給的方法也算進去,class A{}的程式碼實際上是
class A{
public A(){}
}
 
B繼承 A 的時候,則是
abstract class B extends A{
public B(){
super();
}
}
 
要試驗出這繼承規則的內部情況,也很簡單,在最上面那個簡單試驗程式碼裡,加上個私有構造器,有參無參都行。
class A{
private A(){}
}
這個時候,如基礎知識(1) 中所說,系統不再給你預設無參構造器, B的構造器根據(2)中的規則去呼叫super(),卻找不到A的無參構造器,所以導致abstract class B extends A{} 編譯不能通過。(因為A中沒有任何構造器可供子類呼叫,其實這個時候A只能夠供內部類繼承,我用的Eclipse的3.4版本會建議給B改名,但是這解決不了這個問題。)
 
現在,你應該瞭解了資料給的那句語焉不詳的“實體類必須有明確的建構函式”的含義:
1.沒寫構造器的,那是擁有預設無參公有建構函式的,子類可以什麼都不寫,讓預設構造器去呼叫它。這是最初那兩行程式碼的情況。
2.寫了子類可訪問的無參構造器的,也是一樣,子類裡可以什麼都不寫,用預設機制呼叫。
3.寫了 有參構造器卻沒寫無參構造器的,父類裡沒有子類可訪問的無參構造器,子類必須在子類構造器裡的第一句寫明,呼叫父類有參構造器,並把引數傳進去。
4.宣告為final的以及所有構造器都不在子類訪問許可權之內的類無法繼承
 
其實只要是在類的繼承中,無論抽象還是實體,都需要符合這個規則的。在這個繼承試驗中隨時刪掉或是加上abstract的字首,結果都沒有變化。個人覺得“實體類必須有明確的建構函式”一句實在是無法把這個情況表達清楚,所以廣大求職者還是寫得清楚些好。

33、一個".java"原始檔中是否可以包含多個類(不是內部類)?有什麼限制? 

答:可以,但一個原始檔中最多隻能有一個公開類(public class)而且檔名必須和公開類的類名完全保持一致。
 

34、Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現介面? 

答:可以繼承其他類或實現其他介面,在Swing程式設計和android開發中常用此方式來實現事件監聽和回撥。
 

35、內部類可以引用它的包含類(外部類)的成員嗎?有沒有什麼限制? 

答:一個內部類物件可以訪問建立它的外部類物件的成員,包括私有成員。
 

36、Java 中的final關鍵字有哪些用法? 

答:(1)修飾類:表示該類不能被繼承;(2)修飾方法:表示方法不能被重寫;(3)修飾變數:表示變數只能一次賦值以後值不能被修改(常量)。
 

37、指出下面程式的執行結果。

 class A {


    static {
        System.out.print("1");
    }


    public A() {
        System.out.print("2");
    }
}


class B extends A{


    static {
        System.out.print("a");
    }


    public B() {
        System.out.print("b");
    }
}


public class Hello {


    public static void main(String[] args) {
        A ab = new B();
        ab = new B();//總是先呼叫父類建構函式
    }


} 


答:執行結果:1a2b2b。建立物件時構造器的呼叫順序是:先初始化靜態成員,然後呼叫父類構造器,再初始化非靜態成員,最後呼叫自身構造器。
 


--!提示:如果不能給出此題的正確答案,說明之前第21題Java類載入機制還沒有完全理解,趕緊再看看吧。
 

38、資料型別之間的轉換: 

- 如何將字串轉換為基本資料型別? 
- 如何將基本資料型別轉換為字串? 
答: 
- 呼叫基本資料型別對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本型別; 
- 一種方法是將基本資料型別與空字串("")連線(+)即可獲得其所對應的字串;另一種方法是呼叫String 類中的valueOf()方法返回相應字串
 

39、如何實現字串的反轉及替換? 

答:方法很多,可以自己寫實現也可以使用String或StringBuffer/StringBuilder中的方法。有一道很常見的面試題是用遞迴實現字串反轉,程式碼如下所示:
     public static String reverse(String originStr) {
        if(originStr == null || originStr.length() <= 1) 
            return originStr;
        return reverse(originStr.substring(1)) + originStr.charAt(0);
    } 

40、怎樣將GB2312編碼的字串轉換為ISO-8859-1編碼的字串? 

答:程式碼如下所示:
 String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1"); 

41、日期和時間: 

- 如何取得年月日、小時分鐘秒? 
- 如何取得從1970年1月1日0時0分0秒到現在的毫秒數? 
- 如何取得某月的最後一天? 
- 如何格式化日期? 
答: 
問題1:建立java.util.Calendar 例項,呼叫其get()方法傳入不同的引數即可獲得引數所對應的值。Java 
8中可以使用java.time.LocalDateTimel來獲取,程式碼如下所示。
 public class DateTimeTest {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        System.out.println(cal.get(Calendar.YEAR));
        System.out.println(cal.get(Calendar.MONTH));    // 0 - 11
        System.out.println(cal.get(Calendar.DATE));
        System.out.println(cal.get(Calendar.HOUR_OF_DAY));
        System.out.println(cal.get(Calendar.MINUTE));
        System.out.println(cal.get(Calendar.SECOND));


        // Java 8
        LocalDateTime dt = LocalDateTime.now();
        System.out.println(dt.getYear());
        System.out.println(dt.getMonthValue());     // 1 - 12
        System.out.println(dt.getDayOfMonth());
        System.out.println(dt.getHour());
        System.out.println(dt.getMinute());
        System.out.println(dt.getSecond());
    }
} 


問題2:以下方法均可獲得該毫秒數。
 
 Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis(); // Java 8123123 

問題3:程