java面試題(三)

40final, finally,finalize的區別。

final 用於聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。

內部類要訪問局部變量,局部變量必須定義成final類型,例如,一段代碼……

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

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

41、運行時異常與一般異常有何異同?

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

42errorexception有什麼區別?

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

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

異常是指java程序運行時(非編譯)所發生的非正常情況或錯誤,與現實生活中的事件很相似,現實生活中的事件可以包含事件發生的時間、地點、人物、情節等信息,可以用一個對象來表示,Java使用面向對象的方式來處理異常,它把程序中發生的每個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。

Java對異常進行了分類,不同類型的異常分別用不同的Java類表示,所有異常的根類爲java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception,Error表示應用程序本身無法克服和恢復的一種嚴重問題,程序只有死的份了,例如,說內存溢出和線程死鎖等系統問題。Exception表示程序還能夠克服和恢復的問題,其中又分爲系統異常和普通異常,系統異常是軟件本身缺陷所導致的問題,也就是軟件開發人員考慮不周所導致的

 

問題,軟件使用者無法克服和恢復這種問題,但在這種問題下還可以讓軟件系統繼續運行或者讓軟件死掉,例如,數組腳本越界(

ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);普通異常是運行環境的變化或異常所導致的問題,是用戶能夠克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不應該死掉。

java爲系統異常和普通異常提供了不同的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,所以普通異常也稱爲checked異常,而系統異常可以處理也可以不處理,所以,編譯器不強制用try..catch處理或用throws聲明,所以系統異常也稱爲unchecked異常。

提示答題者:就按照三個級別去思考:虛擬機必須宕機的錯誤,程序可以死掉也可以不死掉的錯誤,程序不應該死掉的錯誤;

44、請寫出你最常見到的5runtime exception

這道題主要考你的代碼量到底多大,如果你長期寫代碼的,應該經常都看到過一些系統方面的異常,你不一定真要回答出5個具體的系統異常,但你要能夠說出什麼是系統異常,以及幾個系統異常就可以了,當然,這些異常完全用其英文名稱來寫是最好的,如果實在寫不出,那就用中文吧,有總比沒有強!

所謂系統異常,就是…..,它們都是RuntimeException的子類,在jdk doc中查RuntimeException類,就可以看到其所有的子類列表,也就是看到了所有的系統異常。我比較有印象的系統異常有:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。

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

throws捕獲並向外拋出異常

throw拋出異常

try catch是內部捕獲異常並做自定義處理

finally是無論是否有異常都會被處理的語句,除非在finally前存在被執行的System.exit(int i)時除外

46java中有幾種方法可以實現一個線程?用什麼關鍵字修飾同步方法?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 voidrun(){

}

}

).start();

從java5開始,還有如下一些線程池創建多線程的方式:

ExecutorService pool = Executors.newFixedThreadPool(3)

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

{

pool.execute(newRunable(){public void run(){}});

}

Executors.newCachedThreadPool().execute(new Runable(){publicvoidrun(){}});

Executors.newSingleThreadExecutor().execute(new Runable(){publicvoidrun(){}});

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

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

用synchronized關鍵字修飾同步方法

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

47sleep()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;

publicclass MultiThread {

 

/**

* @paramargs

*/

public static voidmain(String[] args) {

// TODO Auto-generated method stub

new Thread(newThread1()).start();

try {

Thread.sleep(10);

} catch (InterruptedException e) {

// TODO Auto-generated catchblock

e.printStackTrace();

}

new Thread(newThread2()).start();

}

private static classThread1implements Runnable

{

@Override

public void run() {

// TODO Auto-generated methodstub

//由於這裏的Thread1和下面的Thread2內部run方法要用同一對象作爲監視器,我們這裏不能用this,因爲在Thread2裏面的this和這個Thread1的this不是同一個對象。我們用MultiThread.class這個字節碼對象,當前虛擬機裏引用這個變量時,指向的都是同一個對象。

synchronized (MultiThread.class){

System.out.println("enterthread1...");

System.out.println("thread1is waiting"); 

try {

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

MultiThread.class.wait();

} catch(InterruptedException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

System.out.println("thread1is going on...");

System.out.println("thread1is being over!");

}

}

}

private static classThread2implements Runnable

{

@Override

public void run() {

// TODO Auto-generated methodstub

synchronized (MultiThread.class){

System.out.println("enterthread2...");

System.out.println("thread2notify other thread can releasewait status..");

//由於notify方法並不釋放鎖,即使thread2調用下面的sleep方法休息了10毫秒,但thread1仍然不會執行,因爲thread2沒有釋放鎖,所以Thread1無法得不到鎖。

 

MultiThread.class.notify();

System.out.println("thread2is sleeping tenmillisecond...");

try {

Thread.sleep(10);

} catch (InterruptedExceptione) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

System.out.println("thread2is going on...");

System.out.println("thread2is being over!");

}

}

}

}

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

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

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

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

class Test 

{

synchronizedstatic void say Hello3()

{

}

synchronizedvoid getX(){}

}

50、多線程有幾種實現方法?同步有幾種實現方法?

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

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

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

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

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

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

51、啓動一個線程是用run()還是start()? .

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

52、當一個線程進入一個對象的一個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、簡述synchronizedjava.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 { 

/**

* @paramargs

*/

private int j;

private Lock lock =newReentrantLock();

public static voidmain(String[] args) {

// TODO Auto-generated method stub

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 SubtractorimplementsRunnable

{

@Override

public void run() {

// TODO Auto-generated methodstub

while(true)

{

/*synchronized (ThreadTest.this) {

System.out.println("j--="+ j--);

//這裏拋異常了,鎖能釋放嗎?

}*/

lock.lock();

try 

{

System.out.println("j--="+ j--);

}finally

{

lock.unlock();

}

}

}

}

private class AdderimplementsRunnable

{

@Override

public void run() {

// TODO Auto-generated methodstub

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=newThreadTest1();

Inc inc=tt.new Inc();

Dec dec=tt.new Dec();

for(inti=0;i<2;i++){

Thread t=newThread(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(

newRunnable(){ public void run(){while(true){j.accumulate()}}}

).start(); 

new Thread(newRunnable(){ public voidrun(){while(true){j.sub()}}}).start();

}

}

}

class JManager

{

private j = 0;

public synchronized voidsubtract()

{

j--

}

public synchronized voidaccumulate()

{

j++;

}

}

56、子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次,請寫出程序。

最終的程序代碼如下:

public class ThreadTest {

/**

* @paramargs

*/

public static voidmain(String[] args) {

// TODO Auto-generated method stub

new ThreadTest().init();

}

public void init()

{

final Business business =newBusiness();

new Thread(

new Runnable()

{

public voidrun() {

for(inti=0;i<50;i++)

{

business.SubThread(i);

}

}

}

).start();

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

{

business.MainThread(i);

}

}

private class Business

{

booleanbShouldSub =true;//這裏相當於定義了控制該誰執行的一個信號燈

public synchronized voidMainThread(int i)

{

if(bShouldSub)

try {

this.wait();

} catch(InterruptedException e) {

// TODO Auto-generatedcatch block

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 voidSubThread(int i)

{

if(!bShouldSub)

try {

this.wait();

} catch (InterruptedExceptione) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

for(intj=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) {

// TODO Auto-generated method stub

/*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 Locklock = new ReentrantLock();

private staticCondition subThreadCondition = lock.newCondition();

private staticboolean bBhouldSubThread = false;

public static voidmain(String [] args)

{

ExecutorServicethreadPool = Executors.newFixedThreadPool(3);

threadPool.execute(newRunnable(){

publicvoid run()

{

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= 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框架的結構

答:隨意發揮題,天南海北誰便談,只要讓別覺得你知識淵博,理解透徹即可。

58Collection框架中實現比較要實現什麼接口

comparable/comparator

59ArrayListVector的區別

答:

這兩個類都實現了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倍。

60HashMapHashtable的區別

(條理上還需要整理,也是先說相同點,再說不同點)

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

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