Java 筆試面試 基礎篇 一

1. Java 基礎部分
基礎部分的順序:基本語法, 類相關的語法, 內部類的語法, 繼承相關的語法, 異常的語法, 
線程的語法, 集合的語法, io 的語法, 虛擬機方面的語法。

一、一個".java"源文件中是否能夠包括多個類(不是內部類)?有什麼限制?
能夠有多個類, 但只能有一個public 的類, 而且public 的類名必須與文件名相一致。

二、Java 有沒有goto?
java 中的保留字, 如今沒有在java 中使用。

三、說說&和&&的區別。
&和&&均可以用做邏輯與的運算符, 表示邏輯與(and), 當運算符兩邊的表達式的結果都爲true 時, 
整個運算結果才爲true, 不然, 只要有一方爲false, 則結果爲false。
&&還具備短路的功能, 即若是第一個表達式爲false, 則再也不計算第二個表達式, 例如, 對
於if(str != null&& !str.equals(""))表達式, 當str 爲null 時, 後面的表達式不會執行, 因此不
會出現NullPointerException 若是將&&改成&, 則會拋出NullPointerException 異常。
If(x==33 &++y>0) y 會增加, If(x==33 && ++y>0)不會增加
&還能夠用做位運算符, 當&操做符兩邊的表達式不是boolean 類型時, &表示按位與操做, 
咱們一般使用0x0f 來與一個整數進行&運算, 來獲取該整數的最低4個bit 位, 例如, 0x31 &
0x0f 的結果爲0x01。
備註:這道題先說二者的共同點, 再說出&&和&的特殊之處, 並列舉一些經典的例子來代表本身理解透徹深刻、實際經驗豐富。

4、在JAVA 中如何跳出當前的多重嵌套循環?
在Java 中, 要想跳出多重循環, 能夠在外面的循環語句前定義一個標號, 而後在裏層循環
體的代碼中使用帶有標號的break 語句, 便可跳出外層循環。例如, 
ok:
for(int i=0; i<10; i++) {
    for(int j=0; j<10; j++) {
        System.out.println("i=" + i + ",j=" + j);
        if(j == 5) break ok;
    }
}
另外, 我我的一般並不使用標號這種方式, 而是讓外層的循環條件表達式的結果能夠受到裏
層循環體代碼的控制, 例如, 要在二維數組中查找到某個數字。
int arr[][] = {{1,2,3},{4,5,6,7},{9}};
boolean found = false;
for(int i=0; i<arr.length&& !found; i++) {
    for(int j=0; j<arr[i].length; j++) {
        System.out.println("i=" + i + ",j=" + j);
        if(arr[i][j] ==5) {
            found = true;
            break;
        }
    }
}

五、switch 語句可否做用在byte 上, 可否做用在long 上, 可否做用在String上?
在switch(expr1)中, expr1只能是一個整數表達式或者枚舉常量(更大字體), 整數表達
式能夠是int 基本類型或Integer 包裝類型, 因爲, byte,short,char 均可以隱含轉換爲int, 
因此, 這些類型以及這些類型的包裝類型也是能夠的。顯然, long 和String 類型都不符合
switch 的語法規定, 而且不能被隱式轉換成int 類型, 因此, 它們不能做用於swtich 語句中。
// Java7 String 是能夠的

六、short s1 = 1; s1 = s1 + 1;有什麼錯? short s1 = 1; s1 += 1;有什麼錯?
對於short s1 = 1; s1 = s1 + 1;因爲s1+1運算時會自動提高表達式的類型, 因此結果是int
型, 再賦值給short 類型s1時, 編譯器將報告須要強制轉換類型的錯誤。
對於short s1 = 1; s1 += 1;因爲 +=是java 語言規定的運算符, java 編譯器會對它進行特殊
處理, 所以能夠正確編譯。

七、char 型變量中能不能存貯一箇中文漢字?爲何?
char 型變量是用來存儲Unicode 編碼的字符的, unicode 編碼字符集中包含了漢字, 因此, 
char 型變量中固然能夠存儲漢字啦。不過, 若是某個特殊的漢字沒有被包含在unicode 編
碼字符集中, 那麼, 這個char 型變量中就不能存儲這個特殊漢字。補充說明:unicode 編
碼佔用兩個字節, 因此, char 類型的變量也是佔用兩個字節。
備註:後面一部分回答雖然不是在正面回答題目, 可是, 爲了展示本身的學識和表現本身對
問題理解的透徹深刻, 能夠回答一些相關的知識, 作到知無不言, 言無不盡。

八、用最有效率的方法算出2 乘以8 等於幾?
2 << 3, 
由於將一個數左移n 位, 就至關於乘以了2的n 次方, 那麼, 一個數乘以8只要將其左移3位
便可, 而位運算cpu 直接支持的, 效率最高, 因此, 2乘以8等於幾的最效率的方法是2 << 39、請設計一個一百億的計算器
首先要明白這道題目的考查點是什麼, 一是你們首先要對計算機原理的底層細節要清楚、要
知道加減法的位運算原理和知道計算機中的算術運算會發生越界的狀況, 二是要具有必定的
面向對象的設計思想。
首先, 計算機中用固定數量的幾個字節來存儲的數值, 因此計算機中可以表示的數值是有一
定的範圍的, 爲了便於講解和理解, 咱們先以byte 類型的整數爲例, 它用1個字節進行存儲, 
表示的最大數值範圍爲-128到+127。-1在內存中對應的二進制數據爲11111111, 若是兩個-1
相加, 不考慮Java 運算時的類型提高, 運算後會產生進位, 二進制結果爲1,11111110, 由
於進位後超過了byte 類型的存儲空間, 因此進位部分被捨棄, 即最終的結果爲11111110, 
也就是-2, 這正好利用溢位的方式實現了負數的運算。-128在內存中對應的二進制數據爲
10000000, 若是兩個-128相加, 不考慮Java 運算時的類型提高, 運算後會產生進位, 二
進制結果爲1,00000000, 因爲進位後超過了byte 類型的存儲空間, 因此進位部分被捨棄, 
即最終的結果爲00000000, 也就是0, 這樣的結果顯然不是咱們指望的, 這說明計算機中的
算術運算是會發生越界狀況的, 兩個數值的運算結果不能超過計算機中的該類型的數值範
圍。因爲Java 中涉及表達式運算時的類型自動提高, 咱們沒法用byte 類型來作演示這種問
題和現象的實驗, 你們能夠用下面一個使用整數作實驗的例子程序體驗一下:
int a = Integer.MAX_VALUE;
int b = Integer.MAX_VALUE;
int sum = a + b;
System.out.println("a="+a+",b="+b+",sum="+sum);
先不考慮long 類型, 因爲int 的正數範圍爲2的31次方, 表示的最大數值約等於
2*1000*1000*1000, 也就是20億的大小, 因此, 要實現一個一百億的計算器, 咱們得本身
設計一個類能夠用於表示很大的整數, 而且提供了與另一個整數進行加減乘除的功能, 大
概功能以下:
(1)這個類內部有兩個成員變量, 一個表示符號, 另外一個用字節數組表示數值的二進制數
(2)有一個構造方法, 把一個包含有多位數值的字符串轉換到內部的符號和字節數組中
(3)提供加減乘除的功能
public class BigInteger {
    int sign;
    byte[] val;
    public Biginteger(String val) {
        sign = ;
        val = ;
    }
    public BigInteger add(BigInteger other) {
    }
    public BigInteger subtract(BigInteger other) {
    }
    public BigInteger multiply(BigInteger other) {
    }
    public BigInteger divide(BigInteger other) {
    }
}
備註:要想寫出這個類的完整代碼, 是很是複雜的, 若是有興趣的話, 能夠參看jdk 中自帶
的java.math.BigInteger 類的源碼。面試的人也知道誰都不可能在短期內寫出這個類的完
整代碼的, 他要的是你是否有這方面的概念和意識, 他最重要的仍是考查你的能力, 因此, 
你不要由於本身沒法寫出完整的最終結果就放棄答這道題, 你要作的就是你比別人寫得多, 
證實你比別人強, 你有這方面的思想意識就能夠了, 畢竟別人可能連題目的意思都看不懂, 
什麼都沒寫, 你要勇於答這道題, 即便只答了一部分, 那也與那些什麼都不懂的人區別出來, 
拉開了距離, 算是矮子中的高個, 機會固然就屬於你了。另外, 答案中的框架代碼也很重要, 
體現了一些面向對象設計的功底, 特別是其中的方法命名很專業, 用的英文單詞很精準, 這
也是能力、經驗、專業性、英語水平等多個方面的體現, 會給人留下很好的印象, 在編程能
力和其餘方面條件差很少的狀況下, 英語好除了能夠使你得到更多機會外, 薪水能夠高出一
千元。

10、使用final 關鍵字修飾一個變量時, 是引用不能變, 仍是引用的對象不能變?
使用final 關鍵字修飾一個變量時, 是指引用變量不能變, 引用變量所指向的對象中的內容仍是能夠改變的。
例如, 對於以下語句:
final StringBuffer a=new StringBuffer("immutable");
執行以下語句將報告編譯期錯誤:
a=new StringBuffer("");
可是, 執行以下語句則能夠經過編譯:
a.append(" broken!");
有人在定義方法的參數時, 可能想採用以下形式來阻止方法內部修改傳進來的參數對象:
public void method(final StringBuffer param){
}
實際上, 這是辦不到的, 在該方法內部仍然能夠增長以下代碼來修改參數對象:
param.append("a");

十一、"=="和equals 方法究竟有什麼區別?
(單獨把一個東西說清楚, 而後再說清楚另外一個, 這樣, 它們的區別天然就出來了, 混在一
起說, 則很難說清楚)
==操做符專門用來比較兩個變量的值是否相等, 也就是用於比較變量所對應的內存中所存
儲的數值是否相同, 要比較兩個基本類型的數據或兩個引用變量是否相等, 只能用==操做符。
若是一個變量指向的數據是對象類型的, 那麼, 這時候涉及了兩塊內存, 對象自己佔用一塊
內存(堆內存), 變量也佔用一塊內存, 例如Objet obj = new Object();變量obj 是一個內存, 
new Object()是另外一個內存, 此時, 變量obj 所對應的內存中存儲的數值就是對象佔用的那
塊內存的首地址。對於指向對象類型的變量, 若是要比較兩個變量是否指向同一個對象, 即
要看這兩個變量所對應的內存中的數值是否相等, 這時候就須要用==操做符進行比較。
equals 方法是用於比較兩個獨立對象的內容是否相同, 就比如去比較兩我的的長相是否相
同, 它比較的兩個對象是獨立的。例如, 對於下面的代碼:
String a=new String("foo");
String b=new String("foo");
兩條new 語句建立了兩個對象, 而後用a/b 這兩個變量分別指向了其中一個對象, 這是兩
個不一樣的對象, 它們的首地址是不一樣的, 即a 和b 中存儲的數值是不相同的, 因此, 表達
式a==b 將返回false, 而這兩個對象中的內容是相同的, 因此, 表達式a.equals(b)將返回true。
在實際開發中, 咱們常常要比較傳遞進行來的字符串內容是否等, 例如, String input
= …;input.equals("quit"), 許多人稍不注意就使用==進行比較了, 這是錯誤的, 隨便從網上
找幾個項目實戰的教學視頻看看, 裏面就有大量這樣的錯誤。記住, 字符串的比較基本上都
是使用equals 方法。
若是一個類沒有本身定義equals 方法, 那麼它將繼承Object 類的equals 方法, Object 類
的equals 方法的實現代碼以下:
boolean equals(Object o){
return this==o;
}
這說明, 若是一個類沒有本身定義equals 方法, 它默認的equals 方法(從Object 類繼承
的)就是使用==操做符, 也是在比較兩個變量指向的對象是不是同一對象, 這時候使用
equals 和使用==會獲得一樣的結果, 若是比較的是兩個獨立的對象則總返回false。若是你
編寫的類但願可以比較該類建立的兩個實例對象的內容是否相同, 那麼你必須覆蓋equals
方法, 由你本身寫代碼來決定在什麼狀況便可認爲兩個對象的內容是相同的。

12、靜態變量和實例變量的區別?
在語法定義上的區別:靜態變量前要加static 關鍵字, 而實例變量前則不加。
在程序運行時的區別:實例變量屬於某個對象的屬性, 必須建立了實例對象, 其中的實例變量纔會被分配空間, 才能使用這個實例變量。
靜態變量不屬於某個實例對象, 而是屬於類, 因此也稱爲類變量, 只要程序加載了類的字節碼, 不用建立任何實例對象, 靜態變量就會被分配空間, 靜態變量就能夠被使用了。
總之, 實例變量必須建立對象後才能夠經過這個對象來使用, 靜態變量則能夠直接使用類名來引用。
例如, 對於下面的程序, 不管建立多少個實例對象, 永遠都只分配了一個staticVar 變量, 
而且每建立一個實例對象, 這個staticVar 就會加1;可是, 每建立一個實例對象, 就會分配
一個instanceVar, 便可能分配多個instanceVar, 而且每一個instanceVar 的值都只自加了1
次。
public class VariantTest {
    public static int staticVar = 0;
    public int instanceVar = 0;
    public VariantTest() {
        staticVar++;
        instanceVar++;
        System.out.println("staticVar=" + staticVar + ",instanceVar="+
                           instanceVar);
    }
}
備註:這個解答除了說清楚二者的區別外, 最後還用一個具體的應用例子來講明二者的差別, 
體現了本身有很好的解說問題和設計案例的能力, 思惟敏捷, 超過通常程序員, 有寫做能力!

13、是否能夠從一個static 方法內部發出對非static 方法的調用?
不能夠。由於非static 方法是要與對象關聯在一塊兒的, 必須建立一個對象後, 才能夠在該對
象上進行方法調用, 而static 方法調用時不須要建立對象, 能夠直接調用。也就是說, 當一
個static 方法被調用時, 可能尚未建立任何實例對象, 若是從一個static 方法中發出對非
static 方法的調用, 那個非static 方法是關聯到哪一個對象上的呢?這個邏輯沒法成立, 因此, 
一個static 方法內部發出對非static 方法的調用。

14、Integer 與int 的區別
int 是java 提供的8種原始數據類型之一。Java 爲每一個原始類型提供了封裝類, Integer 是java
爲int 提供的封裝類。int 的默認值爲0, 而Integer 的默認值爲null, 即Integer 能夠區分出
未賦值和值爲0的區別, int 則沒法表達出未賦值的狀況, 例如, 要想表達出沒有參加考試和
考試成績爲0的區別, 則只能使用Integer。在JSP 開發中, Integer 的默認爲null, 因此用
el 表達式在文本框中顯示時, 值爲空白字符串, 而int 默認的默認值爲0, 因此用el 表達式
在文本框中顯示時, 結果爲0, 因此, int 不適合做爲web 層的表單數據的類型。
在Hibernate 中, 若是將OID 定義爲Integer 類型, 那麼Hibernate 就能夠根據其值是否爲
null 而判斷一個對象是不是臨時的, 若是將OID 定義爲了int 類型, 還須要在hbm 映射文
件中設置其unsaved-value 屬性爲0。
另外, Integer 提供了多個與整數相關的操做方法, 例如, 將一個字符串轉換成整數, Integer
中還定義了表示整數的最大值和最小值的常量。

1五、Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math 類中提供了三個與取整有關的方法:ceil、floor、round, 這些方法的做用與它們的英
文名稱的含義相對應, 例如, 
ceil 的英文意義是天花板, 該方法就表示向上取整, 
Math.ceil(11.3)的結果爲12,
Math.ceil(-11.3)的結果是-11;
floor 的英文意義是地板, 該方法就表示向下取整, 
Math.ceil(11.6)的結果爲11,
Math.ceil(-11.6)的結果是-12;最難掌握的是
round 方法, 它表示"四捨五入", 算法爲Math.floor(x+0.5), 即將原來的數字加上0.5後再向下取整, 因此, 
Math.round(11.5)的結果爲12, 
Math.round(-11.5)的結果爲-111六、下面的代碼有什麼不妥之處?
1. if(username.equals("zxx"){}
username 可能爲NULL,會報空指針錯誤;改成"zxx".equals(username)
2. int x = 1;
return x==1?true:false; 這個改爲return x==1;就能夠!

1七、請說出做用域public, private, protected, 以及不寫時的區別
這四個做用域的可見範圍以下表所示。
說明:若是在修飾的元素上面沒有寫任何訪問修飾符, 則表示friendly。
 
備註:只要記住了有4種訪問權限, 4個訪問範圍, 而後將全選和範圍在水平和垂直方向上
分別按排從小到大或從大到小的順序排列, 就很容易畫出上面的圖了。

 做用域       當前類    同一package      子孫類     其餘package
 public        √          √              √            √

 protected     √          √              √            ×

 friendly      √          √              ×            ×

 private       √          ×              ×            ×
1八、Overload 和Override 的區別。Overloaded 的方法是否能夠改變返回值的類型? Overload 是重載的意思, Override 是覆蓋的意思, 也就是重寫。 重載Overload 表示同一個類中能夠有多個名稱相同的方法, 但這些方法的參數列表各不相 同(即參數個數或類型不一樣)。 重寫Override 表示子類中的方法能夠與父類中的某個方法的名稱和參數徹底相同, 經過子 類建立的實例對象調用這個方法時, 將調用子類中的定義方法, 這至關於把父類中定義的那 個徹底相同的方法給覆蓋了, 這也是面向對象編程的多態性的一種表現。子類覆蓋父類的方 法時, 只能比父類拋出更少的異常, 或者是拋出父類拋出的異常的子異常, 由於子類能夠解 決父類的一些問題, 不能比父類有更多的問題。子類方法的訪問權限只能比父類的更大, 不 能更小。若是父類的方法是private 類型, 那麼, 子類則不存在覆蓋的限制, 至關於子類中 增長了一個全新的方法。 至於Overloaded 的方法是否能夠改變返回值的類型這個問題, 要看你倒底想問什麼呢?這 個題目很模糊。若是幾個Overloaded 的方法的參數列表不同, 它們的返回者類型固然也 能夠不同。但我估計你想問的問題是:若是兩個方法的參數列表徹底同樣, 是否可讓它 們的返回值不一樣來實現重載Overload。這是不行的, 咱們能夠用反證法來講明這個問題, 由於咱們有時候調用一個方法時也能夠不定義返回結果變量, 即不要關心其返回結果, 例如, 咱們調用map.remove(key)方法時, 雖然remove 方法有返回值, 可是咱們一般都不會定義 接收返回結果的變量, 這時候假設該類中有兩個名稱和參數列表徹底相同的方法, 僅僅是返 回類型不一樣, java 就沒法肯定編程者倒底是想調用哪一個方法了, 由於它沒法經過返回結果 類型來判斷。 override 能夠翻譯爲覆蓋, 從字面就能夠知道, 它是覆蓋了一個方法而且對其重寫, 以求達 到不一樣的做用。對咱們來講最熟悉的覆蓋就是對接口方法的實現, 在接口中通常只是對方法 進行了聲明, 而咱們在實現時, 就須要實現接口聲明的全部方法。除了這個典型的用法之外, 咱們在繼承中也可能會在子類覆蓋父類中的方法。在覆蓋要注意如下的幾點: 1、覆蓋的方法的標誌必需要和被覆蓋的方法的標誌徹底匹配, 才能達到覆蓋的效果; 2、覆蓋的方法的返回值必須和被覆蓋的方法的返回一致; 3、覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致, 或者是其子類; 4、被覆蓋的方法不能爲private, 不然在其子類中只是新定義了一個方法, 並無對其進行覆蓋。 overload 對咱們來講可能比較熟悉, 能夠翻譯爲重載, 它是指咱們能夠定義一些名稱相同 的方法, 經過定義不一樣的輸入參數來區分這些方法, 而後再調用時, VM 就會根據不一樣的參 數樣式, 來選擇合適的方法執行。在使用重載要注意如下的幾點: 1、在使用重載時只能經過不一樣的參數樣式。例如, 不一樣的參數類型, 不一樣的參數個數, 不 同的參數順序(固然, 同一方法內的幾個參數類型必須不同, 例如能夠是fun(int,float), 可是不能爲fun(int,int)); 2、不能經過訪問權限、返回類型、拋出的異常進行重載; 3、方法的異常類型和數目不會對重載形成影響; 4、對於繼承來講, 若是某一方法在父類中是訪問權限是priavte, 那麼就不能在子類對其進行重載, 若是定義的話, 也只是定義了一個新方法, 而不會達到重載的效果。 1九、構造器Constructor 是否可被override? 構造器Constructor 不能被繼承, 所以不能重寫Override, 但能夠被重載Overload。 20、接口是否可繼承接口?抽象類是否可實現(implements)接口? 抽象類是否可繼承具體類(concrete class)?抽象類中是否能夠有靜態的main 方法? 接口能夠繼承接口。抽象類能夠實現(implements)接口, 抽象類能夠繼承具體類。抽象類中能夠有靜態的main 方法。 備註:只要明白了接口和抽象類的本質和做用, 這些問題都很好回答, 你想一想, 若是你是java 語言的設計者, 你是否會提供這樣的支持, 若是不提供的話, 有什麼理由嗎?若是你沒有道 理不提供, 那答案就是確定的了。 只有記住抽象類與普通類的惟一區別:就是不能建立實例對象和容許有abstract 方法。 21、寫clone()方法時, 一般都有一行代碼, 是什麼? clone 有缺省行爲, super.clone(); 由於首先要把父類中的成員複製到位, 而後纔是複製本身的成員。 22、面向對象的特徵有哪些方面 計算機軟件系統是現實生活中的業務在計算機中的映射, 而現實生活中的業務其實就是一個 個對象協做的過程。面向對象編程就是按現實業務同樣的方式將程序代碼按一個個對象進行 組織和編寫, 讓計算機系統可以識別和理解用對象方式組織和編寫的程序代碼, 這樣就能夠 把現實生活中的業務對象映射到計算機系統中。 面向對象的編程語言有, 嗎等4個主要的特徵。 //封裝: 封裝是保證軟件部件具備優良的模塊性的基礎, 封裝的目標就是要實現軟件部件的"高內聚、 低耦合", 防止程序相互依賴性而帶來的變更影響。在面向對象的編程語言中, 對象是封裝 的最基本單位, 面向對象的封裝比傳統語言的封裝更爲清晰、更爲有力。面向對象的封裝就 是把描述一個對象的屬性和行爲的代碼封裝在一個"模塊"中, 也就是一個類中, 屬性用變量 定義, 行爲用方法進行定義, 方法能夠直接訪問同一個對象中的屬性。一般狀況下, 只要記 住讓變量和訪問這個變量的方法放在一塊兒, 將一個類中的成員變量所有定義成私有的, 只 有這個類本身的方法才能夠訪問到這些成員變量, 這就基本上實現對象的封裝, 就很容易 找出要分配到這個類上的方法了, 就基本上算是會面向對象的編程了。把握一個原則:把 對同一事物進行操做的方法和相關的方法放在同一個類中, 把方法和它操做的數據放在同 一個類中。 例如, 人要在黑板上畫圓, 這一共涉及三個對象:人、黑板、圓, 畫圓的方法要分配給哪一個 對象呢?因爲畫圓須要使用到圓心和半徑, 圓心和半徑顯然是圓的屬性, 若是將它們在類中 定義成了私有的成員變量, 那麼, 畫圓的方法必須分配給圓, 它才能訪問到圓心和半徑這兩 個屬性, 人之後只是調用圓的畫圓方法、表示給圓發給消息而已, 畫圓這個方法不該該分配 在人這個對象上, 這就是面向對象的封裝性, 即將對象封裝成一個高度自治和相對封閉的 個體, 對象狀態(屬性)由這個對象本身的行爲(方法)來讀取和改變。一個更便於理解 的例子就是, 司機將火車剎住了, 剎車的動做是分配給司機, 仍是分配給火車, 顯然, 應該 分配給火車, 由於司機自身是不可能有那麼大的力氣將一個火車給停下來的, 只有火車本身 才能完成這一動做, 火車須要調用內部的離合器和剎車片等多個器件協做才能完成剎車這個 動做, 司機剎車的過程只是給火車發了一個消息, 通知火車要執行剎車動做而已。 //抽象: 抽象就是找出一些事物的類似和共性之處, 而後將這些事物歸爲一個類, 這個類只考慮這些 事物的類似和共性之處, 而且會忽略與當前主題和目標無關的那些方面, 將注意力集中在與 當前目標有關的方面。例如, 看到一隻螞蟻和大象, 你可以想象出它們的相同之處, 那就是 抽象。抽象包括行爲抽象和狀態抽象兩個方面。例如, 定義一個Person 類, 以下: classPerson { String name; int age; } 人原本是很複雜的事物, 有不少方面, 但由於當前系統只須要了解人的姓名和年齡, 因此上 面定義的類中只包含姓名和年齡這兩個屬性, 這就是一種抽像, 使用抽象能夠避免考慮一些 與目標無關的細節。我對抽象的理解就是不要用顯微鏡去看一個事物的全部方面, 這樣涉及 的內容就太多了, 而是要善於劃分問題的邊界, 當前系統須要什麼, 就只考慮什麼。 //繼承: 在定義和實現一個類的時候, 能夠在一個已經存在的類的基礎之上來進行, 把這個已經存在 的類所定義的內容做爲本身的內容, 並能夠加入若干新的內容, 或修改原來的方法使之更適 合特殊的須要, 這就是繼承。繼承是子類自動共享父類數據和方法的機制, 這是類之間的一 種關係, 提升了軟件的可重用性和可擴展性。 //多態: 多態是指程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編 程時並不肯定, 而是在程序運行期間才肯定, 即一個引用變量倒底會指向哪一個類的實例對象, 該引用變量發出的方法調用究竟是哪一個類中實現的方法, 必須在由程序運行期間才能決定。 由於在程序運行時才肯定具體的類, 這樣, 不用修改源程序代碼, 就可讓引用變量綁定到 各類不一樣的類實現上, 從而致使該引用調用的具體方法隨之改變, 即不修改程序代碼就能夠 改變程序運行時所綁定的具體代碼, 讓程序能夠選擇多個運行狀態, 這就是多態性。多態性 加強了軟件的靈活性和擴展性。例如, 下面代碼中的UserDao 是一個接口, 它定義引用變 量userDao 指向的實例對象由daofactory.getDao()在執行的時候返回, 有時候指向的是 UserJdbcDao 這個實現, 有時候指向的是UserHibernateDao 這個實現, 這樣, 不用修改 源代碼, 就能夠改變userDao 指向的具體類實現, 從而致使userDao.insertUser()方法調用 的具體代碼也隨之改變, 即有時候調用的是UserJdbcDao 的insertUser 方法, 有時候調用 的是UserHibernateDao 的insertUser 方法: UserDao userDao =daofactory.getDao(); userDao.insertUser(user); 比喻:人吃飯, 你看到的是左手, 仍是右手? 23、java 中實現多態的機制是什麼? 靠的是父類或接口定義的引用變量能夠指向子類或具體實現類的實例對象, 而程序調用的方 法在運行期才動態綁定, 就是引用變量所指向的具體實例對象的方法, 也就是內存里正在運 行的那個對象的方法, 而不是引用變量的類型中定義的方法。 2四、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 classBase Servlet 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 extendsBaseServlet { protected voiddoService(HttpServletRequest request, HttpServletResponse response) throwsIOExcetion, ServletException { 本Servlet 只處理的具體業務邏輯代碼 } } 父類方法中間的某段代碼不肯定, 留給子類幹, 就用模板方法設計模式。 備註:這道題的思路是先從整體解釋抽象類和接口的基本概念, 而後再比較二者的語法細節, 最後再說二者的應用區別。比較二者語法細節區別的條理是:先從一個類中的構造方法、普 通成員變量和方法(包括抽象方法), 靜態變量和方法, 繼承性等6個方面逐一去比較回答, 接着從第三者繼承的角度的回答, 特別是最後用了一個典型的例子來展示本身深厚的技術功 底。 2五、abstract 的method 是否可同時是static,是否可同時是native, 是否可同時是synchronized? 1 abstract 的method 不能夠是static 的, 由於抽象的方法是要被子類實現的, 而static 與子類扯不上關係! 2 native 方法表示該方法要用另一種依賴平臺的編程語言實現的, 不存在着被子類實現的問題, 因此, 它也不能是抽象的, 不能與abstract 混用。 例如, FileOutputSteam 類要硬件打交道, 底層的實現用的是操做系統相關的api 實現, 例如, 在windows 用c 語言實現的, 因此, 查看jdk 的源代碼, 能夠發現FileOutputStream 的open 方法的定義以下: private native void open(Stringname) throws FileNotFoundException; 若是咱們要用java 調用別人寫的c 語言函數, 咱們是沒法直接調用的, 咱們須要按照java 的要求寫一個c 語言的函數, 又咱們的這個c 語言函數去調用別人的c 語言函數。因爲咱們 的c 語言函數是按java 的要求來寫的, 咱們這個c 語言函數就能夠與java 對接上, java 那 邊的對接方式就是定義出與咱們這個c 函數相對應的方法, java 中對應的方法不須要寫具體的代碼, 但須要在前面聲明native。 3 關於synchronized 與abstract 合用的問題, 我以爲也不行, 由於在我幾年的學習和開發中, 歷來沒見到過這種狀況, 而且我以爲synchronized 應該是做用在一個具體的方法上纔有意 義。並且, 方法上的synchronized 同步所使用的同步鎖對象是this, 而抽象方法上沒法確 定this 是什麼。 26、什麼是內部類?Static Nested Class 和Inner Class 的不一樣。 內部類就是在一個類的內部定義的類, 內部類中不能定義靜態成員(靜態成員不是對象的特 性, 只是爲了找一個容身之處, 因此須要放到一個類中而已, 這麼一點小事, 你還要把它放 到類內部的一個類中, 過度了啊!提供內部類, 不是爲讓你幹這種事情, 無聊, 不讓你幹。 我想多是既然靜態成員相似c 語言的全局變量, 而內部類一般是用於建立內部對象用的, 因此, 把"全局變量"放在內部類中就是毫無心義的事情, 既然是毫無心義的事情, 就應該被 禁止), 內部類能夠直接訪問外部類中的成員變量, 內部類能夠定義在外部類的方法外面, 也能夠定義在外部類的方法體中, 以下所示: 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,protecte,默認的, private 等4種類型, 這就好像類中定義的成員變量有4種訪問類型同樣, 它們決定這個內部類的定義對其餘類是 否可見;對於這種狀況, 咱們也能夠在外面建立內部類的實例對象, 建立內部類的實例對象時, 必定要先建立外部類的實例對象, 而後用這個外部類的實例對象去建立內部類的實例對象, 代碼以下: // Outer outer = new Outer(); // Outer.Inner1 inner1 = outer.new Innner1(); 在方法內部定義的內部類前面不能有訪問類型修飾符, 就好像方法中定義的局部變量同樣, 但這種內部類的前面能夠使用final 或abstract 修飾符。這種內部類對其餘類是不可見的其餘類沒法引用這種內部類, 可是這種內部類建立的實例對象能夠傳遞給其餘類訪問。這種內部類必須是先定義, 後使用, 即內部類的定義代碼必須出如今使用該類以前, 這與方法中的局部變量必須先定義後使用的道理也是同樣的。 // 這種內部類能夠訪問方法體中的局部變量, 可是, 該局部變量前必須加final 修飾符。 對於這些細節, 只要在eclipse 寫代碼試試, 根據開發工具提示的各種錯誤信息就能夠立刻了解到。 // 匿名內部類 在方法體內部還能夠採用以下語法來建立一種匿名內部類, 即定義某一接口或類的子類的同時, 還建立了該子類的實例對象, 無需爲該子類定義名稱: public class Outer { public void start() { new Thread( new Runable() { public void run() {}; } ).start(); } } // 靜態內部類 最後, 在方法外部定義的內部類前面能夠加上static 關鍵字, 從而成爲Static Nested Class, 它再也不具備內部類的特性, 全部, 從狹義上講, 它不是內部類。Static Nested Class 與普通類在運行時的行爲和功能上沒有什麼區別, 只是在編程引用時的語法上有一些差異, 它能夠定義成public、protected、默認的、private 等多種類型, 而普通類只能定義成public 和默認的這兩種類型。在外面引用Static Nested Class 類的名稱爲"外部類名.內部類名"。 在外面不須要建立外部類的實例對象, 就能夠直接建立Static Nested Class, 例如, 假設Inner是定義在Outer 類中的Static Nested Class, 那麼能夠使用以下語句建立Inner 類: // Outer.Inner inner = newOuter.Inner(); 因爲static Nested Class 不依賴於外部類的實例對象, 因此, static Nested Class 能訪問外部類的非static 成員變量(不能直接訪問, 須要建立外部類實例才能訪問非靜態變量)。 當在外部類中訪問Static Nested Class 時, 能夠直接使用Static Nested Class 的名字, 而不須要加上外部類的名字了, 在Static Nested Class 中也能夠直接引用外部類的static 的成員變量, 不須要加上外部類的名字。 // 靜態方法內部類 在靜態方法中定義的內部類也是Static Nested Class, 這時候不能在類前面加static 關鍵字, 靜態方法中的Static Nested Class 與普通方法中的內部類的應用方式很類似, // 它除了能夠直接訪問外部類中的static 的成員變量, 還能夠訪問靜態方法中的局部變量, 可是, 該局部變量前必須加final 修飾符。 備註:首先根據你的印象說出你對內部類的整體方面的特色:例如, 在兩個地方能夠定義, 能夠訪問外部類的成員變量, 不能定義靜態成員, 這是大的特色。而後再說一些細節方面的 知識, 例如, 幾種定義方式的語法區別, 靜態內部類, 以及匿名內部類。 27、內部類能夠引用它的包含類的成員嗎?有沒有什麼限制? 徹底能夠。若是不是靜態內部類, 那沒有什麼限制! 若是你把靜態嵌套類看成內部類的一種特例, 那在這種狀況下不能夠訪問外部類的普通成員變量, 而只能訪問外部類中的靜態成員, 例如, 下面的代碼: class Outer { static int x; static class Inner { void test() { syso(x); } } } 答題時, 也要能察言觀色, 揣摩提問者的心思, 顯然人家但願你說的是靜態內部類不能訪問外部類的成員, 但你一上來就頂牛, 這很差, 要先順着人家, 讓人家滿意, 而後再說特殊狀況, 讓人家吃驚。 28、Anonymous Inner Class (匿名內部類)是否能夠extends(繼承)其它類, 是否能夠implements(實現)interface(接口)? // 能夠繼承其餘類或實現其餘接口。不只是能夠, 而是必須! 2九、super.getClass()方法調用 下面程序的輸出結果是多少? importjava.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 這屬於腦筋急轉彎的題目, 在一個qq 羣有個網友正好問過這個問題, 我以爲挺有趣, 就研究了一下, 沒想到今天還被你面到了, 哈哈。 在test 方法中, 直接調用getClass().getName()方法, 返回的是Test 類名 因爲getClass()在Object 類中定義成了final, 子類不能覆蓋該方法, 因此, 在 test 方法中調用getClass().getName()方法, 其實就是在調用從父類繼承的getClass()方法, 等效於調用super.getClass().getName()方法, 因此, super.getClass().getName()方法返回的也應該是Test。 若是想獲得父類的名稱, 應該用以下代碼: getClass().getSuperClass().getName(); 30、String 是最基本的數據類型嗎? 基本數據類型包括byte、intcharlongfloatdoubleboolean 和short。 java.lang.String 類是final 類型的, 所以不能夠繼承這個類、不能修改這個類。 爲了提升效率節省空間, 咱們應該用StringBuffer 類 3一、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。 3二、是否能夠繼承String 類? String 類是final 類故不能夠繼承。 3三、String s = new String("xyz");建立了幾個String Object?兩者之間有什麼區別? 兩個或一個, "xyz"對應一個對象, 這個對象放在字符串常量緩衝區, 常量"xyz"無論出現多少遍, 都是緩衝區中的那一個。New String 每寫一遍, 就建立一個新的對象, 它一句那個常量"xyz"對象的內容來建立出一個新String 對象。若是之前就用過’xyz’, 這句表明就不會建立"xyz"本身了, 直接從緩衝區拿。 34、String 和StringBuffer 的區別 JAVA 平臺提供了兩個類:String 和StringBuffer, 它們能夠儲存和操做字符串, 即包含多個字符的字符數據。 // 這個String 類提供了數值不可改變的字符串。 // 而這個StringBuffer 類提供的字符串進行修改。 當你知道字符數據要改變的時候你就能夠使用StringBuffer。典型地, 你能夠使用StringBuffers 來動態構造字符數據。另外, String 實現了equals 方法, new String("abc").equals(newString("abc")的結果爲true,而StringBuffer 沒有實現equals 方法, 因此, new StringBuffer("abc").equals(newStringBuffer("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 集合類中時會出現問題。 3五、如何把一段逗號分割的字符串轉換成一個數組? 若是不查jdk api, 我很難寫出來!我能夠說說個人思路: 1 用正則表達式, 代碼大概爲:String [] result = orgStr.split(","); 2 用 StingTokenizer ,代碼爲:StringTokenizer tokener = StringTokenizer(orgStr,","); String [] result =new String[tokener .countTokens()]; Int i=0; while (tokener.hasNext()) { result[i++] = toker.nextToken(); } 3六、數組有沒有length()這個方法? String 有沒有length()這個方法? 數組沒有length()這個方法, 有length 的屬性。String 有有length()這個方法。 3七、下面這條語句一共建立了多少個對象:String s="a"+"b"+"c"+"d"; 答:對於以下代碼: String s1 = "a"; String s2 = s1 + "b"; String s3 = "a" + "b"; System.out.println(s2 == "ab"); System.out.println(s3 == "ab"); 第一條語句打印的結果爲false, 第二條語句打印的結果爲true, 這說明javac 編譯能夠對字符串常量直接相加的表達式進行優化, 沒必要要等到運行期去進行加法運算處理, 而是在編譯時去掉其中的加號, 直接將其編譯成一個這些常量相連的結果。 題目中的第一行代碼被編譯器在編譯時優化後, 至關於直接定義了一個"abcd"的字符串, // 因此, 上面的代碼應該只建立了一個String 對象。 寫以下兩行代碼, String s ="a" + "b" + "c" + "d"; System.out.println(s== "abcd"); 最終打印的結果應該爲true。 3八、try {}裏有一個return 語句, 那麼緊跟在這個try 後的finally{}裏的code會不會被執行, 何時被執行, 在return 前仍是後? 也許你的答案是在return 以前, 但往更細地說, 個人答案是在return 中間執行, 請看下面程序代碼的運行結果: public classTest { /** * @paramargs add by zxx ,Dec 9, 2008 */ public static void main(String[] args) { System.out. println (new Test().test());; } static int test() { int x = 1; try { Return x; } finally { ++x; } } } ---------執行結果 --------- 1 運行結果是1, 爲何呢?主函數調用子函數並獲得結果的過程, 比如主函數準備一個空罐子, 當子函數要返回結果時, 先把結果放在罐子裏, 而後再將程序邏輯返回到主函數。 所謂返回, 就是子函數說, 我不運行了, 你主函數繼續運行吧, 這沒什麼結果可言, 結果是在說這話以前放進罐子裏的。 39、下面的程序代碼輸出的結果是多少? public class smallT { public static void main(String args[]) { smallT t = new smallT(); int b = t.get(); System.out.println(b); } public int get() { try { Return 1 ; } finally { Return 2 ; } } } 返回的結果是2。 我能夠經過下面一個例子程序來幫助我解釋這個答案, 從下面例子的運行結果中能夠發現, try 中的return 語句調用的函數先於finally 中調用的函數執行, 也就是說return 語句先執行, finally 語句後執行, 因此, 返回的結果是2。Return 並非讓函數立刻返回, 而是return 語 句執行後, 將把返回結果放置進函數棧中, 此時函數並非立刻返回, 它要執行finally 語句後才真正開始返回。 在講解答案時能夠用下面的程序來幫助分析: public classTest { /** * @paramargs add by zxx ,Dec 9, 2008 */ public static void main(String[] args) { System.out.println(newTest().test());; } int test() { try { return func1(); } finally { return func2(); } } int func1() { System.out.println("func1"); return 1; } int func2() { System.out.println("func2"); return 2; } } -----------執行結果----------------- func1 func2 2 結論:finally 中的代碼比return 和break 語句後執行 40、final, finally, finalize 的區別。 final 用於聲明屬性, 方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承。 內部類要訪問局部變量, 局部變量必須定義成final 類型, 例如, 一段代碼…… finally 是異常處理語句結構的一部分, 表示老是執行。 finalize 是Object 類的一個方法, 在垃圾收集器執行的時候會調用被回收對象的此方法, 能夠覆蓋此方法提供垃圾收集時的其餘資源回收, 例如關閉文件等。JVM 不保證此方法總被調用 41、運行時異常與通常異常有何異同? 異常表示程序運行過程當中可能出現的非正常狀態, 運行時異常表示虛擬機的一般操做中可能遇到的異常, 是一種常見運行錯誤。java 編譯器要求方法必須聲明拋出可能發生的非運行時異常, 可是並不要求必須聲明拋出未被捕獲的運行時異常。 4二、error 和 exception 有什麼區別? error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。不可能期望程序能處理這樣的狀況。 exception 表示一種設計或實現問題。也就是說, 它表示若是程序運行正常, 從不會發生的狀況。 43、Java 中的異常處理機制的簡單原理和應用。 異常是指java 程序運行時(非編譯)所發生的非正常狀況或錯誤, 與現實生活中的事件很類似, 現實生活中的事件能夠包含事件發生的時間、地點、人物、情節等信息, 能夠用一個對象來表示, Java 使用面向對象的方式來處理異常, 它把程序中發生的每一個異常也都分別封裝到一個對象來表示的, 該對象中包含有異常的信息。Java 對異常進行了分類, 不一樣類型的異常分別用不一樣的Java 類表示, 全部異常的根類爲java.lang.Throwable, Throwable 下面又派生了兩個子類:Error 和Exception, // Error Error 表示應用程序自己沒法克服和恢復的一種嚴重問題, 程序只有死的份了, 例如, 說內存溢出和線程死鎖等系統問題。 // Exception 表示程序還可以克服和恢復的問題, 其中又分爲系統異常和普通異常, // 系統異常 系統異常是軟件自己缺陷所致使的問題, 也就是軟件開發人員考慮不周所致使的問題, 軟件使用者沒法克服和恢復這種問題, 但在這種問題下還可讓軟件系統繼續運行或者讓軟件死掉, 例如, 數組腳本越界(ArrayIndexOutOfBoundsException), 空指針異常(NullPointerException),類轉換異常(ClassCastException); // 普通異常 普通異常是運行環境的變化或異常所致使的問題, 是用戶可以克服的問題, // 例如, 網絡斷線, 硬盤空間不夠, 發生這樣的異常後, 程序不該該死掉。 java 爲系統異常和普通異常提供了不一樣的解決方案, 編譯器強制普通異常必須try..catch 處理或用throws 聲明繼續拋給上層調用方法處理, 因此普通異常也稱爲checked 異常, 而系統異常能夠處理也能夠不處理, 因此, 編譯器不強制用try..catch 處理或用throws 聲明, 因此係統異常也稱爲unchecked 異常。 提示答題者:就按照三個級別去思考:虛擬機必須宕機的錯誤, 程序能夠死掉也能夠不死掉的錯誤, 程序不該該死掉的錯誤; 44、請寫出你最多見到的5 個runtime exception。 這道題主要考你的代碼量到底多大, 若是你長期寫代碼的, 應該常常都看到過一些系統方面的異常, 你不必定真要回答出5個具體的系統異常, 但你要可以說出什麼是系統異常, 以及幾個系統異常就能夠了, 固然, 這些異常徹底用其英文名稱來寫是最好的, 若是實在寫不出, 那就用中文吧, 有總比沒有強! 所謂系統異常, 就是….., 它們都是RuntimeException 的子類, 在jdk doc 中查RuntimeException 類, 就能夠看到其全部的子類列表, 也就是看到了全部的系統異常。 我比較有印象的系統異常有:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。 4五、JAVA 語言如何進行異常處理, 關鍵字:throws,throw,try,catch,finally 分別表明什麼意義?在try 塊中能夠拋出異常嗎? throws 捕獲並向外拋出異常 throw 拋出異常 try catch 是內部捕獲異常並作自定義處理 finally 是不管是否有異常都會被處理的語句, 除非在finally 前存在被執行的 System.exit(int i)時除外 4六、java 中有幾種方法能夠實現一個線程?用什麼關鍵字修飾同步方法? stop()和suspend()方法爲什麼不推薦使用? java5之前, 有以下兩種: // 第一種: new Thread(){}.start(); 這表示調用Thread 子類對象的run 方法, new Thread(){}表示一個Thread 的匿名子類的實例對象, 子類加上run 方法後的代碼以下: new Thread() { public void run() {} }.start(); // 第二種: new Thread(new Runnable() {}).start(); 這表示調用Thread 對象接受的Runnable 對象的run方法, new Runnable() {}表示一個Runnable 的匿名子類的實例對象, runnable 的子類加上run 方法後的代碼以下: new Thread(new Runnable() { public void run() {} }).start(); 從java5開始, 還有以下一些線程池建立多線程的方式: ExecutorService pool = Executors.newFixedThreadPool(3) for (int i = 0; i < 10; i++) { pool.execute(new Runable() { public void run() {} }); } Executors.new CachedThreadPool().execute(new Runable() { public void run() {} }); Executors.new SingleThreadExecutor().execute(new Runable() { public void run() {} }); 有兩種實現方法, 分別使用new Thread()和new Thread(runnable)形式, 第一種直接調用thread 的run 方法, 因此, 咱們每每使用Thread 子類, 即new SubThread()。 第二種調用runnable 的run 方法。 有兩種實現方法, 分別是繼承Thread 類與實現Runnable 接口用synchronized 關鍵字修飾同步方法反對使用stop(), 是由於它不安全。它會解除由線程獲取的全部鎖定, 並且若是對象處於一種不連貫狀態, 那麼其餘線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。 suspend()方法容易發生死鎖。調用suspend()的時候, 目標線程會停下來, 但卻仍然持有在這以前得到的鎖定。 此時, 其餘任何線程都不能訪問鎖定的資源, 除非被"掛起"的線程恢復運行。對任何線程來講, 若是它們想恢復目標線程, 同時又試圖使用任何一個鎖定的資源, 就會形成死鎖。 因此不該該使用suspend(), 而應在本身的Thread 類中置入一個標誌, 指出線程應該活動仍是掛起。 若標誌指出線程應該掛起, 便用wait()命其進入等待狀態。若標誌指出線程應當恢復, 則用一個notify()從新啓動線程。 4七、sleep()和 wait()有什麼區別? (網上的答案:sleep 是線程類(Thread)的方法, 致使此線程暫停執行指定時間, 給執行機會給其餘線程, 可是監控狀態依然保持, 到時後會自動恢復。調用sleep 不會釋放對象鎖。 wait 是Object 類的方法, 對此對象調用wait 方法致使本線程放棄對象鎖, 進入等待此對象的等待鎖定池, 只有針對此對象發出notify 方法(或notifyAll)後本線程才進入對象鎖定池準備得到對象鎖進入運行狀態。) // sleep sleep 就是正在執行的線程主動讓出cpu, cpu 去執行其餘線程, 在sleep 指定的時間事後, cpu 纔會回到這個線程上繼續往下執行, 若是當前線程進入了同步鎖, sleep 方法並不會釋放鎖, 即便當前線程使用sleep 方法讓出了cpu, 但其餘被同步鎖擋住了的線程也沒法獲得執行。 // wait wait 是指在一個已經進入了同步鎖的線程內, 讓本身暫時讓出同步鎖, 以便其餘正在等待此鎖的線程能夠獲得同步鎖並運行, 只有其餘線程調用了notify 方法 (notify 並不釋放鎖, 只是告訴調用過wait 方法的線程能夠去參與得到鎖的競爭了, 但不是立刻獲得鎖, 由於鎖還在別人手裏, 別人還沒釋放。若是notify 方法後面的代碼還有不少, 須要這些代碼執行完後纔會釋放鎖, 能夠在notfiy 方法後增長一個等待和一些代碼, 看看效果), 調用wait方法的線程就會解除wait 狀態和程序能夠再次獲得鎖後繼續向下運行。 對於wait 的講解必定要配合例子代碼來講明, 才顯得本身真明白。 package com.huawei.interview; public class MultiThread { public static void main(String[] args) { new Thread(new Thread1()).start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Thread2()).start(); } private static class Thread1 implements Runnable { @Override public void run() { // 因爲這裏的Thread1和下面的Thread2內部run 方法要用同一對象做爲監視器, // 咱們這裏不能用this, 由於在Thread2裏面的this 和這個Thread1的this 不是同一個對象。 // 由於是靜態類,因此咱們用MultiThread.class 這個字節碼對象, 當前虛擬機裏引用這個變量時, 指向的都是同一個對象。 synchronized (MultiThread.class) { System.out.println("enter thread1..."); System.out.println("thread1 is waiting"); try { // 釋放鎖有兩種方式, 第一種方式是程序天然離開監視器的範圍, 也就是離開了synchronized 關鍵字管轄的代碼範圍, // 另外一種方式就是在synchronized 關鍵字管轄的代碼內部調用監視器對象的wait 方法。這裏, 使用wait 方法釋放鎖。 MultiThread.class.wait(); } catch(InterruptedException e) { e.printStackTrace(); } System.out.println("thread1 is going on..."); System.out.println("thread1 is being over!"); } } } private static class Thread2 implements Runnable { @Override public void run() { synchronized (MultiThread.class) { System.out.println("enter thread2..."); System.out.println("thread2 notify other thread can release wait status.."); //因爲notify 方法並不釋放鎖, 即便thread2調用下面的sleep 方法休息了10毫秒, // 但thread1仍然不會執行, 由於thread2沒有釋放鎖, 因此Thread1沒法得不到鎖。 MultiThread.class.notify(); System.out.println("thread2 is sleeping ten millisecond..."); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread2 is going on..."); System.out.println("thread2 is being over!"); } } } } 48、同步和異步有何異同, 在什麼狀況下分別使用他們?舉例說明。 若是數據將在線程間共享。例如正在寫的數據之後可能被另外一個線程讀到, 或者正在讀的數據可能已經被另外一個線程寫過了, 那麼這些數據就是共享數據, 必須進行同步存取。 當應用程序在對象上調用了一個須要花費很長時間來執行的方法, 而且不但願讓程序等待方法的返回時, 就應該使用異步編程, 在不少狀況下采用異步途徑每每更有效率。 49. 下面兩個方法同步嗎?(本身發明) class Test { Synchronized static void say Hello3() { } Synchronized void getX() {} } 50、多線程有幾種實現方法?同步有幾種實現方法? // 多線程有兩種實現方法, 分別是繼承Thread 類與實現Runnable 接口 // 同步的實現方面有兩種, 分別是 synchronized, wait 與notify wait():使一個線程處於等待狀態, 而且釋放所持有的對象的lock。 sleep():使一個正在運行的線程處於睡眠狀態, 是一個靜態方法, 調用此方法要捕捉InterruptedException(中斷異常)異常。 notify():喚醒一個處於等待狀態的線程, 注意的是在調用此方法的時候, 並不能確切的喚醒某一個等待狀態的線程, 而是由JVM 肯定喚醒哪一個線程, 並且不是按優先級。 notifyAll():喚醒全部處入等待狀態的線程, 注意並非給全部喚醒線程一個對象的鎖, 而是讓它們競爭。 5一、啓動一個線程是用run()仍是start()? . 啓動一個線程是調用start()方法, 使線程就緒狀態, 之後能夠被調度爲運行狀態, 一個線程必須關聯一些具體的執行代碼, run()方法是該線程所關聯的執行代碼。 5二、當一個線程進入一個對象的一個synchronized 方法後, 其它線程是否可進入此對象的其它方法? 分幾種狀況: 1. 其餘方法前是否加了synchronized 關鍵字, 若是沒加, 則能。 2. 若是這個方法內部調用了wait, 則能夠進入其餘synchronized 方法。 3. 若是其餘個方法都加了synchronized 關鍵字, 而且內部沒有調用wait, 則不能。 4. 若是其餘方法是static, 它用的同步鎖是當前類的字節碼, 與非靜態的方法不能同步, 由於非靜態的方法用的是this。 53、線程的基本概念、線程的基本狀態以及狀態之間的關係 一個程序中能夠有多條執行線索同時執行, 一個線程就是程序中的一條執行線索, 每一個線程上都關聯有要執行的代碼, 便可以有多段程序代碼同時運行, 每一個程序至少都有一個線程, 即main 方法執行的那個線程。若是隻是一個cpu, 它怎麼可以同時執行多段程序呢? 這是從宏觀上來看的, cpu 一會執行a 線索, 一會執行b 線索, 切換時間很快, 給人的感受是a,b 在同時執行, 比如你們在同一個辦公室上網, 只有一條連接到外部網線, 其實, 這條網線一會爲a 傳數據, 一會爲b 傳數據, 因爲切換時間很短暫, 因此, 你們感受都在同時上網。 // 狀態:就緒, 運行, synchronize 阻塞, wait 和sleep 掛起, 結束。 wait 必須在synchronized內部調用。 調用線程的start 方法後線程進入就緒狀態, 線程調度系統將就緒狀態的線程轉爲運行狀態, 遇到synchronized 語句時, 由運行狀態轉爲阻塞, 當synchronized 得到鎖後, 由阻塞轉爲運行, 在這種狀況能夠調用wait 方法轉爲掛起狀態, 當線程關聯的代碼執行完後, 線程變爲結束狀態。 54、簡述synchronized 和java.util.concurrent.locks.Lock 的異同? // 主要相同點:Lock 能完成synchronized 所實現的全部功能 // 主要不一樣點:Lock 有比synchronized 更精確的線程語義和更好的性能。 synchronized 會自動釋放鎖, 而Lock 必定要求程序員手工釋放, 而且必須在finally 從句中釋放。 Lock 還有更強大的功能, 例如, 它的tryLock 方法能夠非阻塞方式去拿鎖。 舉例說明(對下面的題用lock 進行了改寫): package com.huawei.interview; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; publicclass ThreadTest { private int j; private Lock lock =new ReentrantLock(); public static void main(String[] args) { ThreadTest tt = new ThreadTest(); for(int i=0; i<2; i++) { new Thread(tt.new Adder()).start(); new Thread(tt.new Subtractor()).start(); } } private class Subtractor implements Runnable { @Override public void run() { while(true) { /*synchronized (ThreadTest.this) { System.out.println("j--="+ j--); //這裏拋異常了, 鎖能釋放嗎? }*/ lock.lock(); try { System.out.println("j--="+ j--); } finally { lock.unlock(); } } } } private class Adder implements Runnable { @Override public void run() { while(true) { /*synchronized (ThreadTest.this) { System.out.println("j++="+ j++); }*/ lock.lock(); try { System.out.println("j++="+ j++); } finally { lock.unlock(); } } } } } 55、設計4 個線程, 其中兩個線程每次對j 增長1, 另外兩個線程對j 每次減小1。寫出程序。 如下程序使用內部類實現線程, 對j 增減的時候沒有考慮順序問題。 public class ThreadTest1 { private int j; public static void main(String args[]) { ThreadTest1 tt=new ThreadTest1(); Inc inc=tt.new Inc(); Dec dec=tt.new Dec(); for(int i = 0; i<2; i++) { Thread t=new Thread(inc); t.start(); t=new Thread(dec); t.start(); } } private synchronized void inc() { j++; System.out.println(Thread.currentThread().getName()+"-inc:"+j); } private synchronized void dec() { j--; System.out.println(Thread.currentThread().getName()+"-dec:"+j); } class Inc implements Runnable { public void run() { for(inti=0; i<100; i++) { inc(); } } } class Dec implements Runnable { public void run() { for(inti=0; i<100; i++) { dec(); } } } } ----------隨手再寫的一個------------- class A { JManger j =new JManager(); main() { new A().call(); } void call { for(int i=0; i<2; i++) { new Thread( new Runnable() { public void run() { while(true) { j.accumulate() } } } ).start(); new Thread(new Runnable() { public void run() { while(true) { j.sub() } } }).start(); } } } class JManager { private j = 0; public synchronized void subtract() { j-- } public synchronized void accumulate() { j++; } } 56、子線程循環10 次, 接着主線程循環100, 接着又回到子線程循環10 次, 接着再回到主線程又循環100, 如此循環50 次, 請寫出程序。 最終的程序代碼以下: public class ThreadTest { public static void main(String[] args) { new ThreadTest().init(); } public void init() { final Business business =new Business(); new Thread( new Runnable() { public void run() { for(inti=0; i<50; i++) { business.SubThread(i); } } } ).start(); for(int i=0; i<50; i++) { business.MainThread(i); } } private class Business { Boolean bShouldSub =true;//這裏至關於定義了控制該誰執行的一個信號燈 public synchronized void MainThread(int i) { if(bShouldSub) try { this.wait(); } catch(InterruptedException e) { e.printStackTrace(); } for(int j=0; j<5; j++) { System.out.println(Thread.currentThread().getName()+ ":i=" + i +",j=" + j); } bShouldSub =true; this.notify(); } public synchronized void SubThread(int i) { if(!bShouldSub) try { this.wait(); } catch (InterruptedExceptione) { e.printStackTrace(); } for(int j=0; j<10; j++) { System.out.println(Thread.currentThread().getName()+ ":i=" + i +",j=" + j); } bShouldSub =false; this.notify(); } } } 備註:不可能一上來就寫出上面的完整代碼, 最初寫出來的代碼以下, 問題在於兩個線程的 代碼要參照同一個變量, 即這兩個線程的代碼要共享數據, 因此, 把這兩個線程的執行代碼 搬到同一個類中去: package com.huawei.interview.lym; publicclass ThreadTest { private static booleanbShouldMain=false; public static void main(String[]args) { /*new Thread(){ public void run() { for(int i=0;i<50;i++) { for(int j=0;j<10;j++) { System.out.println("i="+ i + ",j=" + j); } } } }.start();*/ //final String str = newString(""); new Thread( new Runnable() { public voidrun() { for(inti=0; i<50; i++) { synchronized(ThreadTest.class) { if(bShouldMain) { try { ThreadTest.class.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } for(intj=0; j<10; j++) { System.out.println( Thread.currentThread().getName()+ "i="+ i + ",j=" + j); } bShouldMain= true; ThreadTest.class.notify(); } } } } ).start(); for(int i=0; i<50; i++) { synchronized (ThreadTest.class) { if(!bShouldMain) { try { ThreadTest.class.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } for(intj=0; j<5; j++) { System.out.println( Thread.currentThread().getName()+ "i=" + i +",j=" + j); } bShouldMain =false; ThreadTest.class.notify(); } } } } 下面使用jdk5中的併發庫來實現的: import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; public class ThreadTest { private static Lock lock = new ReentrantLock(); private static Condition subThreadCondition = lock.newCondition(); private static boolean bBhouldSubThread = false; public static void main(String [] args) { ExecutorService threadPool = Executors.newFixedThreadPool(3); threadPool.execute(new Runnable() { public void run() { for(int i=0; i<50; i++) { lock.lock(); try { if(!bBhouldSubThread) subThreadCondition.await(); for(intj=0; j<10; j++) { System.out.println(Thread.currentThread().getName()+ ",j=" + j); } bBhouldSubThread= false; subThreadCondition.signal(); } catch(Exceptione) { } finally { lock.unlock(); } } } }); threadPool.shutdown(); for(inti=0; i<50; i++) { lock.lock(); try { if(bBhouldSubThread) subThreadCondition.await(); for(intj=0; j<10; j++) { System.out.println(Thread.currentThread().getName()+ ",j=" + j); } bBhouldSubThread= true; subThreadCondition.signal(); } catch(Exceptione) { } finally { lock.unlock(); } } } } 57、介紹Collection 框架的結構 答:隨意發揮題, 天南海北誰便談, 只要讓別以爲你知識淵博, 理解透徹便可。 Collection 是單列集合 // List 元素是有序的、可重複 有序的 collection, 能夠對列表中每一個元素的插入位置進行精確地控制。 能夠根據元素的整數索引(在列表中的位置)訪問元素, 並搜索列表中的元素。 可存放重複元素, 元素存取是有序的。 List接口中經常使用類 l Vector: 線程安全, 但速度慢, 已被ArrayList替代。 底層數據結構是數組結構 l ArrayList:線程不安全, 查詢速度快。 底層數據結構是數組結構 l LinkedList:線程不安全。增刪速度快。 底層數據結構是列表結構 // Set(集) 元素無序的、不可重複。 取出元素的方法只有迭代器。不能夠存放重複元素, 元素存取是無序的。 Set接口中經常使用的類 l HashSet:線程不安全, 存取速度快。 它是如何保證元素惟一性的呢?依賴的是元素的hashCode方法和euqals方法。 l TreeSet:線程不安全, 能夠對Set集合中的元素進行排序。 它的排序是如何進行的呢?經過compareTo或者compare方法中的來保證元素的惟一性。元素是以二叉樹的形式存放的。 // Map 是一個雙列集合 |--Hashtable:線程安全, 速度快。底層是哈希表數據結構。是同步的。 不容許null做爲鍵, null做爲值。 |--Properties:用於配置文件的定義和操做, 使用頻率很是高, 同時鍵和值都是字符串。 是集合中能夠和IO技術相結合的對象。(到了IO在學習它的特有和io相關的功能。) |--HashMap:線程不安全, 速度慢。底層也是哈希表數據結構。是不一樣步的。 容許null做爲鍵, null做爲值。替代了Hashtable. |--LinkedHashMap: 能夠保證HashMap集合有序。存入的順序和取出的順序一致。 |--TreeMap:能夠用來對Map集合中的鍵進行排序. Collection 和 Collections的區別 // Collection是集合類的上級接口, 子接口主要有Set 和List、Map。 // Collections是針對集合類的一個幫助類, 提供了操做集合的工具方法: 一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做。 58、Collection 框架中實現比較要實現什麼接口 comparable/comparator 59、ArrayList 和 Vector 的區別 答: 這兩個類都實現了List 接口(List 接口繼承了Collection 接口), 他們都是有序集合, 即存儲在這兩個集合中的元素的位置都是有順序的, 至關於一種動態的數組, 咱們之後能夠按位置索引號取出某個元素, 而且其中的數據是容許重複的, 這是HashSet 之類的集合的最大不一樣處, HashSet 之類的集合不能夠按索引號去檢索其中的元素, 也不容許有重複的元素(原本題目問的與hashset 沒有任何關係, 但爲了說清楚ArrayList 與Vector 的功能, 咱們使用對比方式, 更有利於說明問題)。 接着才說ArrayList 與 Vector 的區別, 這主要包括兩個方面:. // (1)同步性: // Vector 是線程安全的, 也就是說是它的方法之間是線程同步的, 而ArrayList 是線程序不安全的, 它的方法之間是線程不一樣步的。若是隻有一個線程會訪問到集合, 那最好是使用ArrayList, 由於它不考慮線程安全, 效率會高些;若是有多個線程會訪問到集合, 那最好是使用Vector, 由於不須要咱們本身再去考慮和編寫線程安全的代碼。 備註:對於Vector&ArrayList、Hashtable&HashMap, 要記住線程安全的問題, 記住Vector與Hashtable 是舊的, 是java 一誕生就提供了的, 它們是線程安全的, ArrayList 與HashMap是java2時才提供的, 它們是線程不安全的。因此, 咱們講課時先講老的。 // (2)數據增加: ArrayList 與Vector 都有一個初始的容量大小, 當存儲進它們裏面的元素的個數超過了容量時, 就須要增長ArrayList 與Vector 的存儲空間, 每次要增長存儲空間時, 不是隻增長一個存儲單元, 而是增長多個存儲單元, 每次增長的存儲單元的個數在內存空間利用與程序效率之間要取得必定的平衡。 Vector 默認增加爲原來兩倍, 而ArrayList 的增加策略在文檔中沒有明確規定(從源代碼看到的是增加爲原來的1.5倍)。 ArrayList 與Vector 均可以設置初始的空間大小, Vector 還能夠設置增加的空間大小, 而ArrayList 沒有提供設置增加空間的方法。 // 總結:即Vector 增加原來的一倍, ArrayList 增長原來的0.5倍。 60、HashMap 和Hashtable 的區別 (條理上還須要整理, 也是先說相同點, 再說不一樣點) HashMap 是Hashtable 的輕量級實現(非線程安全的實現), 他們都完成了Map 接口, 主要區別在於HashMap 容許空(null)鍵值(key),因爲非線程安全, 在只有一個線程訪問的狀況下, 效率要高於Hashtable。 HashMap 容許將null 做爲一個entry 的key 或者value, 而Hashtable 不容許。 HashMap 把Hashtable 的contains 方法去掉了, 改爲containsvalue 和containsKey。由於contains 方法容易讓人引發誤解。 Hashtable 繼承自Dictionary 類, 而HashMap 是Java1.2引進的Map interface 的一個實現。 最大的不一樣是, Hashtable 的方法是Synchronize 的, 而HashMap 不是, 在多個線程訪問Hashtable 時, 不須要本身爲它的方法實現同步, 而HashMap 就必須爲之提供外同步。 Hashtable 和HashMap 採用的hash/rehash 算法都大概同樣, 因此性能不會有很大的差別。 就HashMap 與HashTable 主要從三方面來講。 一.歷史緣由:Hashtable 是基於陳舊的Dictionary 類的, HashMap 是Java 1.2引進的Map接口的一個實現 二.同步性:Hashtable 是線程安全的, 也就是說是同步的, 而HashMap 是線程序不安全的, 不是同步的 三.值:只有HashMap 可讓你將空值做爲一個表的條目的key 或value 6一、List 和 Map 區別? 一個是存儲單列數據的集合, 另外一個是存儲鍵和值這樣的雙列數據的集合, List 中存儲的數據是有順序, 而且容許重複; Map 中存儲的數據是沒有順序的, 其鍵是不能重複的, 它的值是能夠有重複的。 6二、List, Set, Map 是否繼承自Collection 接口? List, Set 是, Map 不是 63、List、Map、Set 三個接口, 存取元素時, 各有什麼特色? 這樣的題屬於隨意發揮題:這樣的題比較考水平, 兩個方面的水平:一是要真正明白這些內容, 二是要有較強的總結和表述能力。若是你明白, 但表述不清楚, 在別人那裏則等同於不明白。 首先, List 與Set 具備類似性, 它們都是單列元素的集合, 因此, 它們有一個功共同的父接口Collection。 Set 裏面不容許有重複的元素, 所謂重複, 即不能有兩個相等(注意, 不是僅僅是相同)的對象, 即假設Set 集合中有了一個A 對象, 如今我要向Set 集合再存入一個B 對象, 但B 對象與A 對象equals 相等, 則B 對象存儲不進去, 因此, Set 集合的add 方法有一個boolean 的返回值, 當集合中沒有某個元素, 此時add 方法可成功加入該元素時, 則返回true, 當集合含有與某個元素equals 相等的元素時, 此時add 方法沒法加入該元素, 返回結果爲false。 Set 取元素時, 無法說取第幾個, 只能以Iterator 接口取得全部的元素, 再逐一遍歷各個元素。 List 表示有前後順序的集合, 注意, 不是那種按年齡、按大小、按價格之類的排序。 當咱們屢次調用add(Obj e)方法時, 每次加入的對象就像火車站買票有排隊順序同樣, 按先來後到的順序排序。有時候, 也能夠插隊, 即調用add(int index,Obj e)方法, 就能夠指定當前對象在集合中的存放位置。一個對象能夠被反覆存儲進List 中, 每調用一次add 方法, 這個對象就被插入進集合中一次, 其實, 並非把這個對象自己存儲進了集合中, 而是在集合中用一個索引變量指向這個對象, 當這個對象被add 屢次時, 即至關於集合中有多個索 引指向了這個對象, 如圖x 所示。List 除了能夠以Iterator 接口取得全部的元素, 再逐一遍歷各個元素以外, 還能夠調用get(index i)來明確說明取第幾個。 Map 與List 和Set 不一樣, 它是雙列的集合, 其中有put 方法, 定義以下:put(objkey,objvalue), 每次存儲時, 要存儲一對key/value, 不能存儲重複的key, 這個重複的規則也是按equals 比較相等。 取則能夠根據key 得到相應的value, 即get(Object key)返回值爲key所對應的value。 另外, 也能夠得到全部的key 的結合, 還能夠得到全部的value的結合, 還能夠得到key 和value 組合成的Map.Entry 對象的集合。 List 以特定次序來持有元素, 可有重複元素。Set 沒法擁有重複元素,內部排序。 Map 保存key-value 值, value 可多值。 HashSet 按照hashcode 值的某種運算方式進行存儲, 而不是直接按hashCode 值的大小進行存儲。 例如, "abc"---> 78, "def" ---> 62, "xyz" ---> 65在hashSet 中的存儲順序不是62,65,78, 這些問題感謝之前一個叫崔健的學員提出, 最後經過查看源代碼給他解釋清楚, 看本次培訓學員當中有多少能看懂源碼。LinkedHashSet 按插入的順序存儲, 那被存儲對象 的hashcode 方法還有什麼做用呢?學員想一想! hashset 集合比較兩個對象是否相等, 首先看hashcode 方法是否相等, 而後看equals 方法是否相等。 new 兩個Student 插入到HashSet中, 看HashSet 的size, 實現hashcode 和equals 方法後再看size。 同一個對象能夠在Vector 中加入屢次。往集合裏面加元素, 至關於集合裏用一根繩子鏈接到了目標對象。 往HashSet 中卻加不了屢次的。 64、說出ArrayList,Vector, LinkedList 的存儲性能和特性 ArrayList 和Vector 都是使用數組方式存儲數據, 此數組元素數大於實際存儲的數據以便增長和插入元素, 它們都容許直接按序號索引元素, 可是插入元素要涉及數組元素移動等內存操做, 因此索引數據快而插入數據慢, Vector 因爲使用了synchronized 方法(線程安全), 一般性能上較ArrayList 差, 而LinkedList 使用雙向鏈表實現存儲, 按序號索引數據須要進行前向或後向遍歷, 可是插入數據時只須要記錄本項的先後項便可, 因此插入速度較快。 LinkedList 也是線程不安全的, LinkedList 提供了一些方法, 使得LinkedList 能夠被看成堆棧和隊列來使用。 65、去掉一個Vector 集合中重複的元素 Vector newVector = new Vector(); For (int i=0; i<vector.size(); i++) { Object obj = vector.get(i); if(!newVector.contains(obj); newVector.add(obj); } 還有一種簡單的方式, HashSet set = new HashSet(vector); 66、Collection 和 Collections 的區別。 Collection 是集合類的上級接口, 繼承與他的接口主要有Set 和List. Collections 是針對集合類的一個幫助類, 他提供一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做。 6七、Set 裏的元素是不能重複的, 那麼用什麼方法來區分重複與否呢?是用==仍是equals()?它們有何區別? Set 裏的元素是不能重複的, 元素重複與否是使用equals()方法進行判斷的。 equals()和==方法決定引用值是否指向同一對象, equals()在類中被覆蓋, 爲的是當兩個分離的對象的內容和類型相配的話, 返回真值。 68、你所知道的集合類都有哪些?主要方法? 最經常使用的集合類是 List 和 Map。 List 的具體實現包括 ArrayList 和 Vector, 它們是可變大小的列表, 比較適合構建、存儲和操做任何類型對象的元素列表。 List 適用於按數值索引訪問元素的情形。 Map 提供了一個更通用的元素存儲方法。 Map 集合類用於存儲元素對(稱做"鍵"和"值"), 其中每一個鍵映射到一個值。 ArrayList/Vector List Collection HashSet/TreeSet Set Propeties/HashTable Map Treemap/HashMap 我記的不是方法名, 而是思想, 我知道它們都有增刪改查的方法, 但這些方法的具體名稱, 我記得不是很清楚, 對於set, 大概的方法是add,remove, contains;對於map, 大概的方法就是put,remove, contains 等, 由於, 我只要在eclispe 下按點操做符, 很天然的這些方法就出來了。 我記住的一些思想就是List 類會有get(int index)這樣的方法, 由於它能夠按順序取元素, 而set 類中沒有get(int index)這樣的方法。List 和set 均可以迭代出全部元素, 迭代時先要獲得一個iterator 對象, 因此, set 和list 類都有一個iterator 方法, 用於返回那個iterator 對象。 map 能夠返回三個集合, 一個是返回全部的key 的集合, 另一個返回的是全部value 的集合, 再一個返回的key 和value 組合成的EntrySet 對象的集合, map 也有get 方法, 參數是key, 返回值是key 對應的value。 6九、兩個對象值相同(x.equals(y) == true), 但卻可有不一樣的hash code, 這句話對不對? 對。 // 若是對象要保存在HashSet或HashMap中, 它們的equals相等, 那麼, 它們的hashcode值就必須相等。 若是不是要保存在HashSet或HashMap, 則與hashcode沒有什麼關係了, 這時候hashcode不等是能夠的, 例如arrayList 存儲的對象就不用實現hashcode, 固然, 咱們沒有理由不實現, 一般都會去實現的。 70、TreeSet 裏面放對象, 若是同時放入了父類和子類的實例對象, 那比較時使用的是父類的compareTo 方法, 仍是使用的子類的compareTo 方法, 仍是拋異常! (應該是沒有針對問題的確切的答案, 當前的add方法放入的是哪一個對象, 就調用哪一個對象的compareTo方法, 至於這個compareTo 方法怎麼作, 就看當前這個對象的類中是如何編寫這個方法的) 實驗代碼: public class Parent implements Comparable { private int age = 0; public Parent(int age) { this.age = age; } public int compareTo(Object o) { System.out.println("method ofparent"); Parent o1 = (Parent)o; return age>o1.age?1:age<o1.age?-1:0; } } public class Child extends Parent { public Child() { super(3); } public int compareTo(Object o) { System.out.println("methodof child"); // Child o1 = (Child)o; return 1; } } public class TreeSetTest { public static void main(String[] args) { TreeSet set = new TreeSet(); set.add(new Parent(3)); set.add(new Child()); set.add(new Parent(4)); System.out.println(set.size()); } } 71、說出一些經常使用的類, 包, 接口, 請各舉5 個 要讓人家感受你對java ee 開發很熟, 因此, 不能僅僅只列core java 中的那些東西, 要多列你在作ssh 項目中涉及的那些東西。就寫你最近寫的那些程序中涉及的那些類。 經常使用的類:BufferedReader BufferedWriter FileReader FileWirter String Integer java.util.Date, System, Class, List,HashMap 經常使用的包:java.lang java.io java.util java.sql,javax.servlet,org.apache.strtuts.action,org.hibernate 經常使用的接口:Remote List Map Document NodeList,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate)、 Session(Hibernate),HttpSession 72、java 中有幾種類型的流?JDK 爲每種類型的流提供了一些抽象類以供繼承, 請說出他們分別是哪些類? 字節流, 字符流。 字節流繼承於InputStream OutputStream, 字符流繼承於InputStreamReader OutputStreamWriter。 在java.io 包中還有許多其餘的流, 主要是爲了提升性能和使用方便。 73、字節流與字符流的區別 要把一片二進制數據數據逐一輸出到某個設備中, 或者從某個設備中逐一讀取一片二進制數據, 無論輸入輸出設備是什麼, 咱們要用統一的方式來完成這些操做, 用一種抽象的方式進行描述, 這個抽象描述方式起名爲IO 流, 對應的抽象類爲OutputStream 和InputStream, 不一樣的實現類就表明不一樣的輸入和輸出設備, 它們都是針對字節進行操做的。在應用中, 常常要徹底是字符的一段文本輸出去或讀進來, 用字節流能夠嗎? 計算機中的一切最終都是二進制的字節形式存在。對於"中國"這些字符, 首先要獲得其對應的字節, 而後將字節寫入到輸出流。 讀取時, 首先讀到的是字節, 但是咱們要把它顯示爲字符, 咱們須要將字節轉換成字符。因爲這樣的需求很普遍, 人家專門提供了字符流的包裝類。 底層設備永遠只接受字節數據, 有時候要寫字符串到底層設備, 須要將字符串轉成字節再進行寫入。 字符流是字節流的包裝, 字符流則是直接接受字符串, 它內部將串轉成字節, 再寫入底層設備, 這爲咱們向IO 設別寫入或讀取字符串提供了一點點方便。字符向字節轉換時, 要注意編碼的問題, 由於字符串轉成字節數組, 實際上是轉成該字符的某種編碼的字節形式, 讀取也是反之的道理。 講解字節流與字符流關係的代碼案例: import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.InputStreamReader; import java.io.PrintWriter; public class IOTest { public static void main(String[]args) throws Exception { String str = "中國人"; /*FileOutputStream fos = new FileOutputStream("1.txt"); fos.write(str.getBytes("UTF-8")); fos.close();*/ /*FileWriter fw =new FileWriter("1.txt"); fw.write(str); fw.close();*/ PrintWriter pw =new PrintWriter("1.txt","utf-8"); pw.write(str); pw.close(); /*FileReader fr =new FileReader("1.txt"); char[] buf = newchar[1024]; int len =fr.read(buf); String myStr = newString(buf,0,len); System.out.println(myStr);*/ /*FileInputStream fr = new FileInputStream("1.txt"); byte[] buf = new byte[1024]; int len =fr.read(buf); String myStr = newString(buf,0,len,"UTF-8"); System.out.println(myStr);*/ BufferedReader br =new BufferedReader( New InputStreamReader( New FileInputStream("1.txt"),"UTF-8" ) ); String myStr =br.readLine(); br.close(); System.out.println(myStr); } } 74、什麼是java 序列化, 如何實現java 序列化?或者請解釋Serializable 接口的做用。 咱們有時候將一個java 對象變成字節流的形式傳出去或者從一個字節流中恢復成一個java對象, 例如, 要將java 對象存儲到硬盤或者傳送給網絡上的其餘計算機, 這個過程咱們能夠本身寫代碼去把一個java 對象變成某個格式的字節流再傳輸, 可是, jre 自己就提供了這種支持, 咱們能夠調用OutputStream 的writeObject 方法來作, 若是要讓java 幫咱們作, 要被傳輸的對象必須實現serializable 接口, 這樣, javac 編譯時就會進行特殊處理, 編譯的類才能夠被writeObject 方法操做, 這就是所謂的序列化。須要被序列化的類必須實現Serializable 接口, 該接口是一個mini 接口, 其中沒有須要實現的方法, Implements Serializable 只是爲了標註該對象是可被序列化的。 例如, 在web 開發中, 若是對象被保存在了Session 中, tomcat 在重啓時要把Session 對象序列化到硬盤, 這個對象就必須實現Serializable 接口。若是對象要通過分佈式系統進行網絡傳輸或經過rmi 等遠程調用, 這就須要在網絡上傳輸對象, 被傳輸的對象就必須實現Serializable 接口。 7五、描述一下JVM 加載class 文件的原理機制? JVM 中類的裝載是由ClassLoader 和它的子類來實現的,Java ClassLoader 是一個重要的Java 運行時系統組件。 它負責在運行時查找和裝入類文件的類。 76、heap 和 stack 有什麼區別。 java 的內存分爲兩類, 一類是棧內存, 一類是堆內存。棧內存是指程序進入一個方法時, 會爲這個方法單獨分配一塊私屬存儲空間, 用於存儲這個方法內部的局部變量, 當這個方法結束時, 分配給這個方法的棧會釋放, 這個棧中的變量也將隨之釋放。 堆是與棧做用不一樣的內存, 通常用於存放不放在當前方法棧中的那些數據, 例如, 使用new建立的對象都放在堆裏, 因此, 它不會隨方法的結束而消失。 // 方法中的局部變量使用final修飾後, 放在堆中, 而不是棧中。 7七、GC 是什麼?爲何要有GC? GC 是垃圾收集的意思(Gabage Collection),內存處理是編程人員容易出現問題的地方, 忘記或者錯誤的內存回收會致使程序或系統的不穩定甚至崩潰, Java 提供的GC 功能能夠自動監測對象是否超過做用域從而達到自動回收內存的目的, Java 語言沒有提供釋放已分配內存的顯示操做方法。 78、垃圾回收的優勢和原理。並考慮2 種回收機制。 Java 語言中一個顯著的特色就是引入了垃圾回收機制, 使c++程序員最頭疼的內存管理的問題迎刃而解, 它使得Java 程序員在編寫程序的時候再也不須要考慮內存管理。因爲有個垃圾回收機制, Java 中的對象再也不有"做用域"的概念, 只有對象的引用纔有"做用域"。 垃圾回收能夠有效的防止內存泄露, 有效的使用能夠使用的內存。 垃圾回收器一般是做爲一個單獨的低級別的線程運行, 不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清楚和回收, 程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。 回收機制有分代複製垃圾回收和標記垃圾回收, 增量垃圾回收。 79、垃圾回收器的基本原理是什麼?垃圾回收器能夠立刻回收內存嗎?有什麼辦法主動通知虛擬機進行垃圾回收? 對於GC 來講, 當程序員建立對象時, GC 就開始監控這個對象的地址、大小以及使用狀況。 一般, GC 採用有向圖的方式記錄和管理堆(heap)中的全部對象。經過這種方式肯定哪些對象是"可達的", 哪些對象是"不可達的"。當GC 肯定一些對象爲"不可達"時, GC 就有責任回收這些內存空間。 能夠。程序員能夠手動執行System.gc(), 通知GC 運行, 可是Java 語言規範並不保證GC 必定會執行。 80、何時用assert。 assertion(斷言)在軟件開發中是一種經常使用的調試方式, 不少開發語言中都支持這種機制。 在實現中, assertion 就是在程序中的一條語句, 它對一個boolean 表達式進行檢查, 一個正確程序必須保證這個boolean 表達式的值爲true;若是該值爲false, 說明程序已經處於不正確的狀態下, assert 將給出警告或退出。通常來講, assertion 用於保證程序最基本、關鍵的正確性。 assertion 檢查一般在開發和測試時開啓。爲了提升性能, 在軟件發佈後, assertion 檢查一般是關閉的。 package com.huawei.interview; publicclass AssertTest { public static void main(String[] args) { int i = 0; for(i=0; i<5; i++) { System.out.println(i); } //假設程序不當心多了一句--i; --i; assert i==5; } } 81、java 中會存在內存泄漏嗎, 請簡單描述。 所謂內存泄露就是指一個再也不被程序使用的對象或變量一直被佔據在內存中。 java 中有垃圾回收機制, 它能夠保證一對象再也不被引用的時候, 即對象編程了孤兒的時候, 對象將自動被垃圾回收器從內存中清除掉。因爲Java 使用有向圖的方式進行垃圾回收管理, 能夠消除引用循環的問題, 例若有兩個對象, 相互引用, 只要它們和根進程不可達的, 那麼GC 也是能夠回收它們的, 例以下面的代碼能夠看到這種狀況的內存回收: package com.huawei.interview; import java.io.IOException; publicclass GarbageTest { /** * @paramargs * @throwsIOException */ public static void main(String[] args)throws IOException { try { gcTest(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("hasexited gcTest!"); System.in.read(); System.in.read(); System.out.println("out begingc!"); for(int i=0; i<100; i++) { System.gc(); System.in.read(); System.in.read(); } } private static void gcTest()throws IOException { System.in.read(); System.in.read(); Person p1 = new Person(); System.in.read(); System.in.read(); Person p2 = new Person(); p1.setMate(p2); p2.setMate(p1); System.out.println("before exit gctest!"); System.in.read(); System.in.read(); System.gc(); System.out.println("exit gctest!"); } private static class Person { byte[] data =new byte[20000000]; Person mate = null; public void setMate(Person other) { mate = other; } } } java 中的內存泄露的狀況:長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄露, 儘管短生命週期對象已經再也不須要, 可是由於長生命週期對象持有它的引用而致使不能被回收, 這就是java 中內存泄露的發生場景, 通俗地說, 就是程序員可能建立了一個對象, 之後一直再也不使用這個對象, 這個對象卻一直被引用, 即這個對象無用可是卻沒法被垃圾回收器回收的, 這就是java 中可能出現內存泄露的狀況, 例如, 緩存系統, 咱們加載了一個對象放在緩存中(例如放在一個全局map 對象中), 而後一直再也不使用它, 這個對象一直被緩存引用, 但卻再也不被使用。 檢查java 中的內存泄露, 必定要讓程序將各類分支狀況都完整執行到程序結束, 而後看某個對象是否被使用過, 若是沒有, 則才能斷定這個對象屬於內存泄露。 若是一個外部類的實例對象的方法返回了一個內部類的實例對象, 這個內部類對象被長期引用了, 即便那個外部類實例對象再也不被使用, 但因爲內部類持久外部類的實例對象, 這個外部類對象將不會被垃圾回收, 這也會形成內存泄露。 下面內容來自於網上(主要特色就是清空堆棧中的某個元素, 並非完全把它從數組中拿掉, 而是把存儲的總數減小, 本人寫得能夠比這個好, 在拿掉某個元素時, 順便也讓它從數組中消失, 將那個元素所在的位置的值設置爲null 便可): 我實在想不到比那個堆棧更經典的例子了,以至於我還要引用別人的例子, 下面的例子不是我想到的, 是書上看到的, 固然若是沒有在書上看到, 可能過一段時間我本身也想的到, 但是那時我說是我本身想到的也沒有人相信的。 public class Stack { private Object[] elements=new Object[10]; private int size = 0; public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if( size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if(elements.length == size) { Object[] oldElements = elements; elements = new Object[2 * elements.length+1]; System.arraycopy(oldElements,0, elements, 0, size); } } } 上面的原理應該很簡單, 假如堆棧加了10個元素, 而後所有彈出來, 雖然堆棧是空的, 沒有咱們要的東西, 可是這是個對象是沒法回收的, 這個才符合了內存泄露的兩個條件:無用, 沒法回收。 可是就是存在這樣的東西也不必定會致使什麼樣的後果, 若是這個堆棧用的比較少, 也就浪費了幾個K 內存而已, 反正咱們的內存都上G 了, 哪裏會有什麼影響, 再說這個東西很快就會被回收的, 有什麼關係。 下面看兩個例子。 例子1 public class Bad { public static Stack s=Stack(); static { s.push(new Object()); s.pop(); //這裏有一個對象發生內存泄露 s.push(new Object()); //上面的對象能夠被回收了, 等因而自愈了 } } 由於是static, 就一直存在到程序退出, 可是咱們也能夠看到它有自愈功能, 就是說若是你的Stack 最多有100個對象, 那麼最多也就只有100個對象沒法被回收其實這個應該很容易理解, Stack 內部持有100個引用, 最壞的狀況就是他們都是無用的, 由於咱們一旦放新的進取, 之前的引用天然消失! 內存泄露的另一種狀況:當一個對象被存儲進HashSet 集合中之後, 就不能修改這個對象中的那些參與計算哈希值的字段了, 不然, 對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了, 在這種狀況下, 即便在contains 方法使用該對象的當前引用做爲的參數去HashSet 集合中檢索對象, 也將返回找不到對象的結果, 這也會致使沒法從 HashSet 集合中單獨刪除當前對象, 形成內存泄露。 82、能不能本身寫個類, 也叫java.lang.String? 能夠, 但在應用的時候, 須要用本身的類加載器去加載, 不然, 系統的類加載器永遠只是去加載jre.jar 包中的那個java.lang.String。 因爲在tomcat 的web 應用程序中, 都是由webapp本身的類加載器先本身加載WEB-INF/classess 目錄中的類, 而後才委託上級的類加載器加載, 若是咱們在tomcat 的web 應用程序中寫一個java.lang.String, 這時候Servlet 程序加載的就是咱們本身寫的java.lang.String, 可是這麼幹就會出不少潛在的問題, 原來全部用了java.lang.String 類的都將出現問題。 雖然java 提供了endorsed 技術, 能夠覆蓋jdk 中的某些類, 具體作法是….。 可是, 可以被覆蓋的類是有限制範圍, 反正不包括java.lang 這樣的包中的類。 (下面的例如主要是便於你們學習理解只用, 不要做爲答案的一部分, 不然, 人家懷疑是題目泄露了) 例如, 運行下面的程序: package java.lang; public class String { /** * @paramargs */ public static void main(String[] args) { System.out.println("string"); } } 報告的錯誤以下: java.lang.NoSuchMethodError:main Exception inthread "main" 這是由於加載了jre 自帶的java.lang.String, 而該類中沒有main 方法。 83. Java 代碼查錯 1. abstract class Name { private String name; public abstract boolean isStupidName(String name) {} } 大俠們, 這有何錯誤? 答案: 錯。abstract method 必須以分號結尾, 且不帶花括號。 2. public class Something { void doSomething () { private String s = ""; int l = s.length(); } } 有錯嗎? 答案: 錯。局部變量前不能放置任何訪問修飾符 (private, public, 和protected)。final 能夠用來修飾局部變量 (final 如同abstract 和strictfp, 都是非訪問修飾符, strictfp 只能修飾class 和method 而非 variable)。 3. abstract class Something { private abstract String doSomething (); } 這好像沒什麼錯吧? 答案: 錯。abstract 的methods 不能以private 修飾。 abstract 的methods 就是讓子類implement(實現)具體細節的, 怎麼能夠用private 把abstractmethod 封鎖起來呢? (同理, abstract method 前不能加final)。 4. public class Something { public int addOne(final int x) { return ++x; } } 這個比較明顯。 答案: 錯。int x 被修飾成final, 意味着x 不能在addOne method 中被修改。 5. public class Something { public static void main(String[] args) { Other o = new Other(); new Something().addOne(o); } public void addOne(final Other o) { o.i++; } } class Other { public int i; } 和上面的很類似, 都是關於final 的問題, 這有錯嗎? 答案: 正確。在addOne method 中, 參數o 被修飾成final。若是在addOne method 裏咱們修改了o 的reference (好比: o = new Other();), 那麼如同上例這題也是錯的。但這裏修改的是o 的 member vairable(成員變量), // 而o 的reference 並無改變。 6. class Something { int i; public void doSomething() { System.out.println("i = "+ i); } } 有什麼錯呢? 看不出來啊。 答案: 正確。輸出的是"i = 0"。int i 屬於instant variable (實例變量, 或叫成員變量)。 instant variable 有default value。int 的default value 是0。 7. class Something { final int i; public void doSomething() { System.out.println("i = "+ i); } } 和上面一題只有一個地方不一樣, 就是多了一個final。這難道就錯了嗎? 答案: 錯。final int i 是個final 的instant variable (實例變量, 或叫成員變量)。final 的instant variable 沒有default value, 必須在constructor (構造器)結束以前被賦予一個明確的值。可 以修改成"final int i =0;"8. public class Something { public static void main(String[] args) { Something s = new Something(); System.out.println("s.doSomething() returns " + doSomething()); } public String doSomething() { return "Do something ..."; } } 看上去很完美。 答案: 錯。看上去在main 裏call doSomething 沒有什麼問題, 畢竟兩個methods 都在同一個class 裏。 但仔細看, main 是static 的。static method 不能直接call non-staticmethods。 可改爲"System.out.println("s.doSomething()returns " + s.doSomething());"。 同理, static method 不能訪問non-static instant variable。 9. 此處, Something 類的文件名叫OtherThing.java class Something { private static void main(String[] something_to_do) { System.out.println("Dosomething ..."); } } 這個好像很明顯。 答案: 正確。歷來沒有人說過Java 的Class 名字必須和其文件名相同。但public class 的名字必須和文件名相同。 10interface A { int x = 0; } class B { int x =1; } class C extends B implements A { public void pX() { System.out.println(x); } public static void main(String[] args) { new C().pX(); } } 答案:錯誤。在編譯時會發生錯誤(錯誤描述不一樣的JVM 有不一樣的信息, 意思就是未明確的x 調用, 兩個x 都匹配(就象在同時import java.util 和java.sql 兩個包時直接聲明Date 同樣)。 // 對於父類的變量,能夠用super.x 來明確, 而接口的屬性默認隱含爲 public static final. // 因此能夠經過A.x 來明確。 11. interface Playable { void play(); } interface Bounceable { void play(); } interface Rollable extends Playable, Bounceable { Ball ball = new Ball("PingPang"); } class Ball implements Rollable { private String name; public String getName() { return name; } public Ball(String name) { this.name =name; } public void play() { ball = new Ball("Football"); System.out.println(ball.getName()); } } 這個錯誤不容易發現。 答案: 錯。"interface Rollable extends Playable, Bounceable"沒有問題。 interface 可繼承多個interfaces, 因此這裏沒錯。 問題出在interface Rollable 裏的"Ball ball =new Ball("PingPang");"。 任何在interface 裏聲明的interface variable (接口變量, 也可稱成員變量), 默認爲public static final。也就是說"Ball ball = new Ball("PingPang");" 其實是"public Static final Ball ball = new Ball("PingPang");"。在Ball 類的Play()方法中, "ball = new Ball("Football");"改變了ball 的reference, 而這裏的ball 來自Rollable interface, Rollable interface 裏的ball 是public static final 的, final 的object 是不能被改變reference 的。 所以編譯器將在"ball = new Ball("Football");"這裏顯示有錯。
相關文章
相關標籤/搜索