天天看點

java寶典(3)

26、内部類可以引用他包含類的成員嗎?有沒有什麼限制?

完全可以。如果不是靜态内部類,那沒有什麼限制!

如果你把靜态嵌套類當作内部類的一種特例,那在這種情況下不可以通路外部類的普通成員變量,而隻能通路外部類中的靜态成員,例如,下面的代碼:

class Outer

{

static int x;

static class Inner

void test()

syso(x);

}

如果問靜态内部類能否通路外部類的成員這個問題,該如何回答:

答題時,也要能察言觀色,揣摩提問者的心思,顯然人家希望你說的是靜态内部類不能通路外部類的成員,但你一上來就頂牛,這不好,要先順着人家,讓人家滿意,然後再說特殊情況,讓人家吃驚。

27、Static Nested Class 和 Inner Class的不同。

參見前面的什麼是内部類的那道題

28、Anonymous Inner Class (匿名内部類) 是否可以extends(繼承)其它類,是否可以implements(實作)interface(接口)?

可以繼承其他類或實作其他接口。

29、String是最基本的資料類型嗎?

基本資料類型包括byte、int、char、long、float、double、boolean和short。

java.lang.String類是final類型的,是以不可以繼承這個類、不能修改這個類。為了提高效率節省空間,我們應該用StringBuffer類

30、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。

31、是否可以繼承String類?

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

32、String s = new String("xyz");建立了幾個String Object? 二者之間有什麼差別?

兩個,一個放在常量區,不管寫多少遍,都是同一個。New String每寫一遍,就建立一個新。

33、String 和StringBuffer的差別

JAVA平台提供了兩個類:String和StringBuffer,它們可以儲存和操作字元串,即包含多個字元的字元資料。這個String類提供了數值不可改變的字元串。而這個StringBuffer類提供的字元串進行修改。當你知道字元資料要改變的時候你就可以使用StringBuffer。典型地,你可以使用StringBuffers來動态構造字元資料。另外,String實作了equals方法,new String(“abc”).equals(new String(“abc”)的結果為true,而StringBuffer沒有實作equals方法,是以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的結果為false。

接着要舉一個具體的例子來說明,我們要把1到100的所有數字拼起來,組成一個串。

StringBuffer sbf = new StringBuffer();  

for(int i=0;i<100;i++)

sbf.append(i);

上面的代碼效率很高,因為隻建立了一個StringBuffer對象,而下面的代碼效率很低,因為建立了101個對象。

String str = new String();  

str = str + i;

34、如何把一段逗号分割的字元串轉換成一個數組?

如果不查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();}

35、數組有沒有length()這個方法? String有沒有length()這個方法?

數組沒有length()這個方法,有length的屬性。String有有length()這個方法。

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

會執行,在return前執行。

我的答案是在return中間執行,參看下一題的講解。

public  class Test {

/**

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

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

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

static int test()

int x = 1;

try

return x;

finally

++x;

---------執行結果 ---------

1

37、下面的程式代碼輸出的結果是多少?

public class  smallT

public static void  main(String args[])

smallT t  = new  smallT();

int  b  =  t.get();

System.out.println(b);

public int  get()

return 1 ;

return 2 ;

傳回的結果是2。

我可以通過下面一個例子程式來幫助我解釋這個答案,從下面例子的運作結果中可以發現,try中的return語句調用的函數先于finally中調用的函數執行,也就是說return語句先執行,finally語句後執行,是以,傳回的結果是2。Return并不是讓函數馬上傳回,而是return語句執行後,将把傳回結果放置進函數棧中,此時函數并不是馬上傳回,它要執行finally語句後才真正開始傳回。

在講解答案時可以用下面的程式來幫助分析:

int test()

return func1();

return func2();

int func1()

System.out.println("func1");

return 1;

int func2()

System.out.println("func2");

return 2;

-----------執行結果-----------------

func1

func2

2

結論:finally中的代碼比return 和break語句後執行

38、final, finally, finalize的差別。

 final 用于聲明屬性,方法和類,分别表示屬性不可變,方法不可覆寫,類不可繼承。

内部類要通路局部變量,局部變量必須定義成final類型,例如,一段代碼……

finally是異常處理語句結構的一部分,表示總是執行。

finalize是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,可以覆寫此方法提供垃圾收集時的其他資源回收,例如關閉檔案等。JVM不保證此方法總被調用

39、運作時異常與一般異常有何異同?

異常表示程式運作過程中可能出現的非正常狀态,運作時異常表示虛拟機的通常操作中可能遇到的異常,是一種常見運作錯誤。java編譯器要求方法必須聲明抛出可能發生的非運作時異常,但是并不要求必須聲明抛出未被捕獲的運作時異常。

40、error和exception有什麼差別?

error 表示恢複不是不可能但很困難的情況下的一種嚴重問題。比如說記憶體溢出。不可能指望程式能處理這樣的情況。 exception 表示一種設計或實作問題。也就是說,它表示如果程式運作正常,從不會發生的情況。

41、Java中的異常處理機制的簡單原理和應用。

當JAVA程式違反了JAVA的語義規則時,JAVA虛拟機就會将發生的錯誤表示為一個異常。違反語義規則包括2種情況。一種是JAVA類庫内置的語義檢查。例如數組下标越界,會引發IndexOutOfBoundsException;通路null的對象時會引發NullPointerException。另一種情況就是JAVA允許程式員擴充這種語義檢查,程式員可以建立自己的異常,并自由選擇在何時用throw關鍵字引發異常。所有的異常都是java.lang.Thowable的子類。

42、給我一個你最常見到的runtime exception。

ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException

43、JAVA語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分别代表什麼意義?在try塊中可以抛出異常嗎?

Java通過面向對象的方法進行異常處理,把各種不同的異常進行分類,并提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其它子類的執行個體。當一個方法出現異常後便抛出一個異常對象,該對象中包含有異常資訊,調用這個對象的方法可以捕獲到這個異常并進行處理。Java的異常處理是通過5個關鍵詞來實作的:try、catch、throw、throws和finally。一般情況下是用try來執行一段程式,如果出現異常,系統會抛出(throws)一個異常,這時候你可以通過它的類型來捕捉(catch)它,或最後(finally)由預設處理器來處理。

用try來指定一塊預防所有"異常"的程式。緊跟在try程式後面,應包含一個catch子句來指定你想要捕捉的"異常"的類型。

throw語句用來明确地抛出一個"異常"。

throws用來标明一個成員函數可能抛出的各種"異常"。

Finally為確定一段代碼不管發生什麼"異常"都被執行一段代碼。

可以在一個成員函數調用的外面寫一個try語句,在這個成員函數内部寫另一個try語句保護其他代碼。每當遇到一個try語句,"異常"的架構就放到堆棧上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種"異常"進行處理,堆棧就會展開,直到遇到有處理這種"異常"的try語句。

44、java中有幾種方法可以實作一個線程?用什麼關鍵字修飾同步方法? stop()和suspend()方法為何不推薦使用?

有兩種實作方法,分别使用new Thread()和new Thread(runnable)形式,第一種直接調用thread的run方法,是以,我們往往使用Thread子類,即new SubThread()。第二種調用runnable的run方法。

有兩種實作方法,分别是繼承Thread類與實作Runnable接口

用synchronized關鍵字修飾同步方法

反對使用stop(),是因為它不安全。它會解除由線程擷取的所有鎖定,而且如果對象處于一種不連貫狀态,那麼其他線程能在那種狀态下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目标線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能通路鎖定的資源,除非被"挂起"的線程恢複運作。對任何線程來說,如果它們想恢複目标線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。是以不應該使用suspend(),而應在自己的Thread類中置入一個标志,指出線程應該活動還是挂起。若标志指出線程應該挂起,便用wait()命其進入等待狀态。若标志指出線程應當恢複,則用一個notify()重新啟動線程。

45、sleep() 和 wait() 有什麼差別?

    (網上的答案:sleep是線程類(Thread)的方法,導緻此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀态依然保持,到時後會自動恢複。調用sleep不會釋放對象鎖。 wait是Object類的方法,對此對象調用wait方法導緻本線程放棄對象鎖,進入等待此對象的等待鎖定池,隻有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池準備獲得對象鎖進入運作狀态。)

sleep就是正在執行的線程主動讓出cpu,cpu去執行其他線程,在sleep指定的時間過後,cpu才會回到這個線程上繼續往下執行,如果目前線程進入了同步鎖,sleep方法并不會釋放鎖,即使目前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執行。wait是指在一個已經進入了同步鎖的線程内,讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運作,隻有其他線程調用了notify方法(notify并不釋放鎖,隻是告訴調用過wait方法的線程可以去參與獲得鎖的競争了,但不是馬上得到鎖,因為鎖還在别人手裡,别人還沒釋放。如果notify方法後面的代碼還有很多,需要這些代碼執行完後才會釋放鎖,可以在notfiy方法後增加一個等待和一些代碼,看看效果),調用wait方法的線程就會解除wait狀态和程式可以再次得到鎖後繼續向下運作。對于wait的講解一定要配合例子代碼來說明,才顯得自己真明白。

package com.huawei.interview;

public class MultiThread {

* @param args

new Thread(new Thread1()).start();

try {

Thread.sleep(10);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

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");

//釋放鎖有兩種方式,第一種方式是程式自然離開螢幕的範圍,也就是離開了synchronized關鍵字管轄的代碼範圍,另一種方式就是在synchronized關鍵字管轄的代碼内部調用螢幕對象的wait方法。這裡,使用wait方法釋放鎖。

MultiThread.class.wait();

System.out.println("thread1 is going on...");

System.out.println("thread1 is being over!");

private static class Thread2 implements Runnable

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...");

System.out.println("thread2 is going on...");

System.out.println("thread2 is being over!");

46、同步和異步有何異同,在什麼情況下分别使用他們?舉例說明。

如果資料将線上程間共享。例如正在寫的資料以後可能被另一個線程讀到,或者正在讀的資料可能已經被另一個線程寫過了,那麼這些資料就是共享資料,必須進行同步存取。

當應用程式在對象上調用了一個需要花費很長時間來執行的方法,并且不希望讓程式等待方法的傳回時,就應該使用異步程式設計,在很多情況下采用異步途徑往往更有效率。

47. 下面兩個方法同步嗎?(自己發明)

class Test

synchronized static void sayHello3()

synchronized void getX(){}

48、多線程有幾種實作方法?同步有幾種實作方法?

多線程有兩種實作方法,分别是繼承Thread類與實作Runnable接口

同步的實作方面有兩種,分别是synchronized,wait與notify

wait():使一個線程處于等待狀态,并且釋放所持有的對象的lock。

sleep():使一個正在運作的線程處于睡眠狀态,是一個靜态方法,調用此方法要捕捉InterruptedException異常。

notify():喚醒一個處于等待狀态的線程,注意的是在調用此方法的時候,并不能确切的喚醒某一個等待狀态的線程,而是由JVM确定喚醒哪個線程,而且不是按優先級。

Allnotity():喚醒所有處入等待狀态的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競争。

49、啟動一個線程是用run()還是start()? .

啟動一個線程是調用start()方法,使線程就緒狀态,以後可以被排程為運作狀态,一個線程必須關聯一些具體的執行代碼,run()方法是該線程所關聯的執行代碼。

47、當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?

分幾種情況:

    1.其他方法前是否加了synchronized關鍵字,如果沒加,則能。

     2.如果這個方法内部調用了wait,則可以進入其他synchronized方法。

   3.如果其他個方法都加了synchronized關鍵字,并且内部沒有調用wait,則不能。

50、線程的基本概念、線程的基本狀态以及狀态之間的關系

一個程式中可以有多條執行線索同時執行,一個線程就是程式中的一條執行線索,每個線程上都關聯有要執行的代碼,即可以有多段程式代碼同時運作,每個程式至少都有一個線程,即main方法執行的那個線程。如果隻是一個cpu,它怎麼能夠同時執行多段程式呢?這是從宏觀上來看的,cpu一會執行a線索,一會執行b線索,切換時間很快,給人的感覺是a,b在同時執行,好比大家在同一個辦公室上網,隻有一條連結到外部網線,其實,這條網線一會為a傳資料,一會為b傳資料,由于切換時間很短暫,是以,大家感覺都在同時上網。

 狀态:就緒,運作,synchronize阻塞,wait和sleep挂起,結束。wait必須在synchronized内部調用。

 調用線程的start方法後線程進入就緒狀态,線程排程系統将就緒狀态的線程轉為運作狀态,遇到synchronized語句時,由運作狀态轉為阻塞,當synchronized獲得鎖後,由阻塞轉為運作,在這種情況可以調用wait方法轉為挂起狀态,當線程關聯的代碼執行完後,線程變為結束狀态。