java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題
java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題
如果兩個方法的參數(shù)列表完全一樣,是否可以讓它們的返回值不同來實(shí)現(xiàn)重載Overload。Java就無法確定編程者倒底是想調(diào)用哪個方法了,因為它無法通過返回結(jié)果類型來判斷。下面就由學(xué)習(xí)啦小編為大家介紹一下java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題的文章,歡迎閱讀。
java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題篇1
1、請說出作用域public,private,protected,以及不寫時的區(qū)別
這四個作用域的可見范圍如下表所示。
說明:如果在修飾的元素上面沒有寫任何訪問修飾符,則表示friendly。
作用域 當(dāng)前類 同一package 子孫類 其他package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
備注:只要記住了有4種訪問權(quán)限,4個訪問范圍,然后將全選和范圍在水平和垂直方向上分別按排從小到大或從大到小的順序排列,就很容易畫出上面的圖了。
2、Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?
重載Overload表示同一個類中可以有多個名稱相同的方法,但這些方法的參數(shù)列表各不相同(即參數(shù)個數(shù)或類型不同)。
重寫Override表示子類中的方法可以與父類中的某個方法的名稱和參數(shù)完全相同,通過子類創(chuàng)建的實(shí)例對象調(diào)用這個方法時,將調(diào)用子類中的定義方法,這相當(dāng)于把父類中定義的那個完全相同的方法給覆蓋了,這也是面向?qū)ο缶幊痰亩鄳B(tài)性的一種表現(xiàn)。
如果兩個方法的參數(shù)列表完全一樣,是否可以讓它們的返回值不同來實(shí)現(xiàn)重載Overload。Java就無法確定編程者倒底是想調(diào)用哪個方法了,因為它無法通過返回結(jié)果類型來判斷。
3.HashMap和Hashtable的區(qū)別
(1 )HashMap不是線程安全的 .
hashmap是一個接口 是map接口的子接口,是將鍵映射到值的對象,其中鍵和值都是對象,并且不能包含重復(fù)鍵,但可以包含重復(fù)值。HashMap允許null key和null value,而hashtable不允許。
(2) HashTable是線程安全的一個Collection。
HashMap是Hashtable的輕量級實(shí)現(xiàn)(非線程安全的實(shí)現(xiàn)),他們都完成了Map接口,主要區(qū)別在于HashMap允許空(null)鍵值(key),由于非線程安全,效率上可能高于Hashtable。
HashMap允許將null作為一個entry的key或者value,而Hashtable不允許。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進(jìn)的Map interface的一個實(shí)現(xiàn)。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己為它的方法實(shí)現(xiàn)同步,而HashMap 就必須為之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
4. ArrayList和LinkedList
List 接口對Collection進(jìn)行了簡單的擴(kuò)充,它的具體實(shí)現(xiàn)類常用的有ArrayList和LinkedList。你可以將任何東西放到一個List容器中,并在需要時從中取出。ArrayList從其命名中可以看出它是一種類似數(shù)組的形式進(jìn)行存儲,因此它的隨機(jī)訪問速度極快,而LinkedList的內(nèi)部實(shí)現(xiàn)是鏈表,它適合于在鏈表中間需要頻繁進(jìn)行插入和刪除操作。在具體應(yīng)用時可以根據(jù)需要自由選擇。前面說的Iterator只能對容器進(jìn)行向前遍歷,而 ListIterator則繼承了Iterator的思想,并提供了對List進(jìn)行雙向遍歷的方法。
5..hashCode方法的作用?
(網(wǎng)友提供的一段,待改進(jìn):hashcode這個方法是用來鑒定2個對象是否相等的。
那你會說,不是還有equals這個方法嗎?
不錯,這2個方法都是用來判斷2個對象是否相等的。但是他們是有區(qū)別的。
一般來講,equals這個方法是給用戶調(diào)用的,如果你想判斷2個對象是否相等,你可以重寫equals方法,然后在代碼中調(diào)用,就可以判斷他們是否相等了。簡單來講,equals方法主要是用來判斷從表面上看或者從內(nèi)容上看,2個對象是不是相等。舉個例子,有個學(xué)生類,屬性只有姓名和性別,那么我們可以認(rèn)為只要姓名和性別相等,那么就說這2個對象是相等的。
hashcode方法一般用戶不會去調(diào)用,比如在hashmap中,由于key是不可以重復(fù)的,他在判斷key是不是重復(fù)的時候就判斷了hashcode這個方法,而且也用到了equals方法。這里不可以重復(fù)是說equals和hashcode只要有一個不等就可以了!所以簡單來講,hashcode相當(dāng)于是一個對象的編碼,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比較起來不直觀。我們一般在覆蓋equals的同時也要覆蓋hashcode,讓他們的邏輯一致。舉個例子,還是剛剛的例子,如果姓名和性別相等就算2個對象相等的話,那么hashcode的方法也要返回姓名的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。
要從物理上判斷2個對象是否相等,用==就可以了。
)
6、靜態(tài)變量和實(shí)例變量的區(qū)別?
在語法定義上的區(qū)別:靜態(tài)變量前要加static關(guān)鍵字,而實(shí)例變量前則不加。
在程序運(yùn)行時的區(qū)別:實(shí)例變量屬于某個對象的屬性,必須創(chuàng)建了實(shí)例對象,其中的實(shí)例變量才會被分配空間,才能使用這個實(shí)例變量。靜態(tài)變量不屬于某個實(shí)例對象,而是屬于類,所以也稱為類變量,只要程序加載了類的字節(jié)碼,不用創(chuàng)建任何實(shí)例對象,靜態(tài)變量就會被分配空間,靜態(tài)變量就可以被使用了??傊瑢?shí)例變量必須創(chuàng)建對象后才可以通過這個對象來使用,靜態(tài)變量則可以直接使用類名來引用
7、Integer與int的區(qū)別
int是java提供的8種原始數(shù)據(jù)類型之一。Java為每個原始類型提供了封裝類,Integer是java為int提供的封裝類。int的默認(rèn)值為0,而Integer的默認(rèn)值為null,即Integer可以區(qū)分出未賦值和值為0的區(qū)別,int則無法表達(dá)出未賦值的情況,
8、Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math類中提供了三個與取整有關(guān)的方法:ceil、floor、round,這些方法的作用與它們的英文名稱的含義相對應(yīng),例如,ceil的英文意義是天花板,該方法就表示向上取整,所以,Math.ceil(11.3)的結(jié)果為12,Math.ceil(-11.3)的結(jié)果是-11;floor的英文意義是地板,該方法就表示向下取整,所以,Math.floor(11.6)的結(jié)果為11,Math.floor(-11.6)的結(jié)果是-12;最難掌握的是round方法,它表示“四舍五入”,算法為Math.floor(x+0.5),即將原來的數(shù)字加上0.5后再向下取整,所以,Math.round(11.5)的結(jié)果為12,Math.round(-11.5)的結(jié)果為-11。
9.AOP
(1).概念介紹:所謂AOP,即Aspect orientied program,就是面向方面(切面)的編程,
(2).解釋什么是方面:貫穿到系統(tǒng)的各個模塊中的系統(tǒng)一個功能就是一個方面,
比如,記錄日志,統(tǒng)一異常處理,事務(wù)處理,權(quán)限檢查,這些功能都是軟件系統(tǒng)
的一個面,而不是一點(diǎn),在各個模塊中都要出現(xiàn)。
(3).什么是面向方面編程:把系統(tǒng)的一個方面的功能封裝成對象的形式來處理
(4).怎么進(jìn)行面向方面編程:把功能模塊對應(yīng)的對象作為切面嵌入到原來的各個系統(tǒng)模塊中,采用代理技術(shù),代理會調(diào)用目標(biāo),同時把切面功能的代碼(對象)加入進(jìn)來,所以,
用spring配置代理對象時只要要配兩個屬性,分別表示目標(biāo)和切面對象(Advisor)。
10、構(gòu)造器Constructor是否可被override?
構(gòu)造器Constructor不能被繼承,因此不能重寫Override,但可以被重載Overload。
java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題篇2
1、寫clone()方法時,通常都有一行代碼,是什么?
clone 有缺省行為,super.clone();因為首先要把父類中的成員復(fù)制到位,然后才是復(fù)制自己的成員。
2、java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
靠的是父類或接口定義的引用變量可以指向子類或具體實(shí)現(xiàn)類的實(shí)例對象,而程序調(diào)用的方法在運(yùn)行期才動態(tài)綁定,就是引用變量所指向的具體實(shí)例對象的方法,也就是內(nèi)存里正在運(yùn)行的那個對象的方法,而不是引用變量的類型中定義的方法。
3、abstract class和interface有什么區(qū)別?
含有abstract修飾符的class即為抽象類,abstract 類不能創(chuàng)建的實(shí)例對象。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實(shí)現(xiàn),所以,不能有抽象構(gòu)造方法或抽象靜態(tài)方法。如果的子類沒有實(shí)現(xiàn)抽象父類中的所有抽象方法,那么子類也必須定義為abstract類型。
接口(interface)可以說成是抽象類的一種特例,接口中的所有方法都必須是抽象的。接口中的方法定義默認(rèn)為public abstract類型,接口中的成員變量類型默認(rèn)為public static final。
下面比較一下兩者的語法區(qū)別:
1.抽象類可以有構(gòu)造方法,接口中不能有構(gòu)造方法。
2.抽象類中可以有普通成員變量,接口中沒有普通成員變量
3.抽象類中可以包含非抽象的普通方法,接口中的所有方法必須都是抽象的,不能有非抽象的普通方法。
4. 抽象類中的抽象方法的訪問類型可以是public,protected和(默認(rèn)類型,雖然
eclipse下不報錯,但應(yīng)該也不行),但接口中的抽象方法只能是public類型的,并且默認(rèn)即為public abstract類型。
5. 抽象類中可以包含靜態(tài)方法,接口中不能包含靜態(tài)方法
6. 抽象類和接口中都可以包含靜態(tài)成員變量,抽象類中的靜態(tài)成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,并且默認(rèn)即為public static final類型。
7. 一個類可以實(shí)現(xiàn)多個接口,但只能繼承一個抽象類。
下面接著再說說兩者在應(yīng)用上的區(qū)別:
接口更多的是在系統(tǒng)架構(gòu)設(shè)計方法發(fā)揮作用,主要用于定義模塊之間的通信契約。而抽象類在代碼實(shí)現(xiàn)方面發(fā)揮作用,可以實(shí)現(xiàn)代碼的重用,例如,模板方法設(shè)計模式是抽象類的一個典型應(yīng)用,假設(shè)某個項目的所有Servlet類都要用相同的方式進(jìn)行權(quán)限判斷、記錄訪問日志和處理異常,那么就可以定義一個抽象的基類,讓所有的Servlet都繼承這個抽象基類,在抽象基類的service方法中完成權(quán)限判斷、記錄訪問日志和處理異常的代碼,在各個子類中只是完成各自的業(yè)務(wù)邏輯代碼,偽代碼如下:
public abstract class BaseServlet extends HttpServlet
{
public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{
記錄訪問日志
進(jìn)行權(quán)限判斷
if(具有權(quán)限)
{
try
{
doService(request,response);
}
catch(Excetpion e)
{
記錄異常信息
}
}
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException;
//注意訪問權(quán)限定義成protected,顯得既專業(yè),又嚴(yán)謹(jǐn),因為它是專門給子類用的
}
public class MyServlet1 extends BaseServlet
{
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{
本Servlet只處理的具體業(yè)務(wù)邏輯代碼
}
}
父類方法中間的某段代碼不確定,留給子類干,就用模板方法設(shè)計模式。
備注:這道題的思路是先從總體解釋抽象類和接口的基本概念,然后再比較兩者的語法細(xì)節(jié),最后再說兩者的應(yīng)用區(qū)別。比較兩者語法細(xì)節(jié)區(qū)別的條理是:先從一個類中的構(gòu)造方法、普通成員變量和方法(包括抽象方法),靜態(tài)變量和方法,繼承性等6個方面逐一去比較回答,接著從第三者繼承的角度的回答,特別是最后用了一個典型的例子來展現(xiàn)自己深厚的技術(shù)功底。
4. jdk中哪些類是不能繼承的?
不能繼承的是類是那些用final關(guān)鍵字修飾的類。一般比較基本的類型或防止擴(kuò)展類無意間破壞原來方法的實(shí)現(xiàn)的類型都應(yīng)該是final的,在jdk中System,String,StringBuffer等都是基本類型
5、String s = "Hello";s = s + " world!";這兩行代碼執(zhí)行后,原始的String對象中的內(nèi)容到底變了沒有?
沒有。因為String被設(shè)計成不可變(immutable)類,所以它的所有對象都是不可變對象。在這段代碼中,s原先指向一個String對象,內(nèi)容是 "Hello",然后我們對s進(jìn)行了+操作,那么s所指向的那個對象是否發(fā)生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另一個 String對象,內(nèi)容為"Hello world!",原來那個對象還存在于內(nèi)存之中,只是s這個引用變量不再指向它了。
通過上面的說明,我們很容易導(dǎo)出另一個結(jié)論,如果經(jīng)常對字符串進(jìn)行各種各樣的修改,或者說,不可預(yù)見的修改,那么使用String來代表字符串的話會引起很大的內(nèi)存開銷。因為 String對象建立之后不能再改變,所以對于每一個不同的字符串,都需要一個String對象來表示。這時,應(yīng)該考慮使用StringBuffer類,它允許修改,而不是每個不同的字符串都要生成一個新的對象。并且,這兩種類的對象轉(zhuǎn)換十分容易。
同時,我們還可以知道,如果要使用內(nèi)容相同的字符串,不必每次都new一個String。例如我們要在構(gòu)造器中對一個名叫s的String引用變量進(jìn)行初始化,把它設(shè)置為初始值,應(yīng)當(dāng)這樣做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都會調(diào)用構(gòu)造器,生成新對象,性能低下且內(nèi)存開銷大,并且沒有意義,因為String對象不可改變,所以對于內(nèi)容相同的字符串,只要一個String對象來表示就可以了。也就說,多次調(diào)用上面的構(gòu)造器創(chuàng)建多個對象,他們的String類型屬性s都指向同一個對象。
上面的結(jié)論還基于這樣一個事實(shí):對于字符串常量,如果內(nèi)容相同,Java認(rèn)為它們代表同一個String對象。而用關(guān)鍵字new調(diào)用構(gòu)造器,總是會創(chuàng)建一個新的對象,無論內(nèi)容是否相同。
至于為什么要把String類設(shè)計成不可變類,是它的用途決定的。其實(shí)不只String,很多Java標(biāo)準(zhǔn)類庫中的類都是不可變的。在開發(fā)一個系統(tǒng)的時候,我們有時候也需要設(shè)計不可變類,來傳遞一組相關(guān)的值,這也是面向?qū)ο笏枷氲捏w現(xiàn)。不可變類有一些優(yōu)點(diǎn),比如因為它的對象是只讀的,所以多線程并發(fā)訪問也不會有任何問題。當(dāng)然也有一些缺點(diǎn),比如每個不同的狀態(tài)都要一個對象來代表,可能會造成性能上的問題。所以Java標(biāo)準(zhǔn)類庫還提供了一個可變版本,即 StringBuffer。
6、java中會存在內(nèi)存泄漏嗎,請簡單描述。
所謂內(nèi)存泄露就是指一個不再被程序使用的對象或變量一直被占據(jù)在內(nèi)存中。java中有垃圾回收機(jī)制,它可以保證一對象不再被引用的時候,即對象編程了孤兒的時候,對象將自動被垃圾回收器從內(nèi)存中清除掉。由于Java 使用有向圖的方式進(jìn)行垃圾回收管理,可以消除引用循環(huán)的問題,例如有兩個對象,相互引用,只要它們和根進(jìn)程不可達(dá)的,那么GC也是可以回收它們的,例如下面的代碼可以看到這種情況的內(nèi)存回收:
package com.huawei.interview;
import java.io.IOException;
public class GarbageTest {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
gcTest();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("has exited gcTest!");
System.in.read();
System.in.read();
System.out.println("out begin gc!");
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中的內(nèi)存泄露的情況:長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內(nèi)存泄露,盡管短生命周期對象已經(jīng)不再需要,但是因為長生命周期對象持有它的引用而導(dǎo)致不能被回收,這就是java中內(nèi)存泄露的發(fā)生場景,通俗地說,就是程序員可能創(chuàng)建了一個對象,以后一直不再使用這個對象,這個對象卻一直被引用,即這個對象無用但是卻無法被垃圾回收器回收的,這就是java中可能出現(xiàn)內(nèi)存泄露的情況,例如,緩存系統(tǒng),我們加載了一個對象放在緩存中(例如放在一個全局map對象中),然后一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。
檢查java中的內(nèi)存泄露,一定要讓程序?qū)⒏鞣N分支情況都完整執(zhí)行到程序結(jié)束,然后看某個對象是否被使用過,如果沒有,則才能判定這個對象屬于內(nèi)存泄露。
如果一個外部類的實(shí)例對象的方法返回了一個內(nèi)部類的實(shí)例對象,這個內(nèi)部類對象被長期引用了,即使那個外部類實(shí)例對象不再被使用,但由于內(nèi)部類持久外部類的實(shí)例對象,這個外部類對象將不會被垃圾回收,這也會造成內(nèi)存泄露。
下面內(nèi)容來自于網(wǎng)上(主要特點(diǎn)就是清空堆棧中的某個元素,并不是徹底把它從數(shù)組中拿掉,而是把存儲的總數(shù)減少,本人寫得可以比這個好,在拿掉某個元素時,順便也讓它從數(shù)組中消失,將那個元素所在的位置的值設(shè)置為null即可):
我實(shí)在想不到比那個堆棧更經(jīng)典的例子了,以致于我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,當(dāng)然如果沒有在書上看到,可能過一段時間我自己也想的到,可是那時我說是我自己想到的也沒有人相信的。
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);
}
}
}
上面的原理應(yīng)該很簡單,假如堆棧加了10個元素,然后全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內(nèi)存泄露的兩個條件:無用,無法回收。
但是就是存在這樣的東西也不一定會導(dǎo)致什么樣的后果,如果這個堆棧用的比較少,也就浪費(fèi)了幾個K內(nèi)存而已,反正我們的內(nèi)存都上G了,哪里會有什么影響,再說這個東西很快就會被回收的,有什么關(guān)系。下面看兩個例子。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //這里有一個對象發(fā)生內(nèi)存泄露
s.push(new Object()); //上面的對象可以被回收了,等于是自愈了
}
}
因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那么最多也就只有100個對象無法被回收其實(shí)這個應(yīng)該很容易理解,Stack內(nèi)部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進(jìn)取,以前的引用自然消失!
內(nèi)存泄露的另外一種情況:當(dāng)一個對象被存儲進(jìn)HashSet集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段了,否則,對象修改后的哈希值與最初存儲進(jìn)HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當(dāng)前引用作為的參數(shù)去HashSet集合中檢索對象,也將返回找不到對象的結(jié)果,這也會導(dǎo)致無法從HashSet集合中單獨(dú)刪除當(dāng)前對象,造成內(nèi)存泄露。
7、多線程有幾種實(shí)現(xiàn)方法?同步有幾種實(shí)現(xiàn)方法?
多線程有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口
同步的實(shí)現(xiàn)方面有兩種,一種同步方法,一種同步代碼!分別是synchronized,wait與notify
wait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
sleep():使一個正在運(yùn)行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。
notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭
8、sleep() 和 wait() 有什么區(qū)別?
(網(wǎng)上的答案:sleep是線程類(Thread)的方法,導(dǎo)致此線程暫停執(zhí)行指定時間,給執(zhí)行機(jī)會給其他線程,但是監(jiān)控狀態(tài)依然保持,到時后會自動恢復(fù)。調(diào)用sleep不會釋放對象鎖。 wait是Object類的方法,對此對象調(diào)用wait方法導(dǎo)致本線程放棄對象鎖,進(jìn)入等待此對象的等待鎖定池,只有針對此對象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對象鎖定池準(zhǔn)備獲得對象鎖進(jìn)入運(yùn)行狀態(tài)。)
sleep就是正在執(zhí)行的線程主動讓出cpu,cpu去執(zhí)行其他線程,在sleep指定的時間過后,cpu才會回到這個線程上繼續(xù)往下執(zhí)行,如果當(dāng)前線程進(jìn)入了同步鎖,sleep方法并不會釋放鎖,即使當(dāng)前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執(zhí)行。wait是指在一個已經(jīng)進(jìn)入了同步鎖的線程內(nèi),讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運(yùn)行,只有其他線程調(diào)用了notify方法(notify并不釋放鎖,只是告訴調(diào)用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因為鎖還在別人手里,別人還沒釋放。如果notify方法后面的代碼還有很多,需要這些代碼執(zhí)行完后才會釋放鎖,可以在notfiy方法后增加一個等待和一些代碼,看看效果),調(diào)用wait方法的線程就會解除wait狀態(tài)和程序可以再次得到鎖后繼續(xù)向下運(yùn)行。對于wait的講解一定要配合例子代碼來說明,才顯得自己真明白。
package com.huawei.interview;
public class MultiThread {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
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() {
// TODO Auto-generated method stub
//由于這里的Thread1和下面的Thread2內(nèi)部run方法要用同一對象作為監(jiān)視器,我們這里不能用this,因為在Thread2里面的this和這個Thread1的this不是同一個對象。我們用MultiThread.class這個字節(jié)碼對象,當(dāng)前虛擬機(jī)里引用這個變量時,指向的都是同一個對象。
synchronized (MultiThread.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting");
try {
//釋放鎖有兩種方式,第一種方式是程序自然離開監(jiān)視器的范圍,也就是離開了synchronized關(guān)鍵字管轄的代碼范圍,另一種方式就是在synchronized關(guān)鍵字管轄的代碼內(nèi)部調(diào)用監(jiān)視器對象的wait方法。這里,使用wait方法釋放鎖。
MultiThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
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() {
// TODO Auto-generated method stub
synchronized (MultiThread.class) {
System.out.println("enter thread2...");
System.out.println("thread2 notify other thread can release wait status..");
//由于notify方法并不釋放鎖, 即使thread2調(diào)用下面的sleep方法休息了10毫秒,但thread1仍然不會執(zhí)行,因為thread2沒有釋放鎖,所以Thread1無法得不到鎖。
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("thread2 is going on...");
System.out.println("thread2 is being over!");
}
}
}
}
9、java中有幾種方法可以實(shí)現(xiàn)一個線程?用什么關(guān)鍵字修飾同步方法? stop()和suspend()方法為何不推薦使用?
java5以前,有如下兩種:
第一種:
new Thread(){}.start();這表示調(diào)用Thread子類對象的run方法,new Thread(){}表示一個Thread的匿名子類的實(shí)例對象,子類加上run方法后的代碼如下:
new Thread(){
public void run(){
}
}.start();
第二種:
new Thread(new Runnable(){}).start();這表示調(diào)用Thread對象接受的Runnable對象的run方法,new Runnable(){}表示一個Runnable的匿名子類的實(shí)例對象,runnable的子類加上run方法后的代碼如下:
new Thread(new Runnable(){
public void run(){
}
}
).start();
從java5開始,還有如下一些線程池創(chuàng)建多線程的方式:
ExecutorService pool = Executors.newFixedThreadPool(3)
for(int i=0;i<10;i++)
{
pool.execute(new Runable(){public void run(){}});
}
Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});
Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});
有兩種實(shí)現(xiàn)方法,分別使用new Thread()和new Thread(runnable)形式,第一種直接調(diào)用thread的run方法,所以,我們往往使用Thread子類,即new SubThread()。第二種調(diào)用runnable的run方法。
有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口
用synchronized關(guān)鍵字修飾同步方法
反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處于一種不連貫狀態(tài),那么其他線程能在那種狀態(tài)下檢查和修改它們。結(jié)果很難檢查出真正的問題所在。suspend()方法容易發(fā)生死鎖。調(diào)用suspend()的時候,目標(biāo)線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復(fù)運(yùn)行。對任何線程來說,如果它們想恢復(fù)目標(biāo)線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應(yīng)該使用suspend(),而應(yīng)在自己的Thread類中置入一個標(biāo)志,指出線程應(yīng)該活動還是掛起。若標(biāo)志指出線程應(yīng)該掛起,便用wait()命其進(jìn)入等待狀態(tài)。若標(biāo)志指出線程應(yīng)當(dāng)恢復(fù),則用一個notify()重新啟動線程。
10、Java中的異常處理機(jī)制的簡單原理和應(yīng)用。
異常是指java程序運(yùn)行時(非編譯)所發(fā)生的非正常情況或錯誤,與現(xiàn)實(shí)生活中的事件很相似,現(xiàn)實(shí)生活中的事件可以包含事件發(fā)生的時間、地點(diǎn)、人物、情節(jié)等信息,可以用一個對象來表示,Java使用面向?qū)ο蟮姆绞絹硖幚懋惓#殉绦蛑邪l(fā)生的每個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。
Java對異常進(jìn)行了分類,不同類型的異常分別用不同的Java類表示,所有異常的根類為java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception,Error表示應(yīng)用程序本身無法克服和恢復(fù)的一種嚴(yán)重問題,程序只有死的份了,例如,說內(nèi)存溢出和線程死鎖等系統(tǒng)問題。Exception表示程序還能夠克服和恢復(fù)的問題,其中又分為系統(tǒng)異常和普通異常,系統(tǒng)異常是軟件本身缺陷所導(dǎo)致的問題,也就是軟件開發(fā)人員考慮不周所導(dǎo)致的問題,軟件使用者無法克服和恢復(fù)這種問題,但在這種問題下還可以讓軟件系統(tǒng)繼續(xù)運(yùn)行或者讓軟件死掉,例如,數(shù)組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉(zhuǎn)換異常(ClassCastException);普通異常是運(yùn)行環(huán)境的變化或異常所導(dǎo)致的問題,是用戶能夠克服的問題,例如,網(wǎng)絡(luò)斷線,硬盤空間不夠,發(fā)生這樣的異常后,程序不應(yīng)該死掉。
java為系統(tǒng)異常和普通異常提供了不同的解決方案,編譯器強(qiáng)制普通異常必須try..catch處理或用throws聲明繼續(xù)拋給上層調(diào)用方法處理,所以普通異常也稱為checked異常,而系統(tǒng)異常可以處理也可以不處理,所以,編譯器不強(qiáng)制用try..catch處理或用throws聲明,所以系統(tǒng)異常也稱為unchecked異常。
11、寫clone()方法時,通常都有一行代碼,是什么?
clone 有缺省行為,super.clone();因為首先要把父類中的成員復(fù)制到位,然后才是復(fù)制自己的成員。
12、java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
靠的是父類或接口定義的引用變量可以指向子類或具體實(shí)現(xiàn)類的實(shí)例對象,而程序調(diào)用的方法在運(yùn)行期才動態(tài)綁定,就是引用變量所指向的具體實(shí)例對象的方法,也就是內(nèi)存里正在運(yùn)行的那個對象的方法,而不是引用變量的類型中定義的方法。
13、abstract class和interface有什么區(qū)別?
含有abstract修飾符的class即為抽象類,abstract 類不能創(chuàng)建的實(shí)例對象。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實(shí)現(xiàn),所以,不能有抽象構(gòu)造方法或抽象靜態(tài)方法。如果的子類沒有實(shí)現(xiàn)抽象父類中的所有抽象方法,那么子類也必須定義為abstract類型。
接口(interface)可以說成是抽象類的一種特例,接口中的所有方法都必須是抽象的。接口中的方法定義默認(rèn)為public abstract類型,接口中的成員變量類型默認(rèn)為public static final。
下面比較一下兩者的語法區(qū)別:
1.抽象類可以有構(gòu)造方法,接口中不能有構(gòu)造方法。
2.抽象類中可以有普通成員變量,接口中沒有普通成員變量
3.抽象類中可以包含非抽象的普通方法,接口中的所有方法必須都是抽象的,不能有非抽象的普通方法。
4. 抽象類中的抽象方法的訪問類型可以是public,protected和(默認(rèn)類型,雖然
eclipse下不報錯,但應(yīng)該也不行),但接口中的抽象方法只能是public類型的,并且默認(rèn)即為public abstract類型。
5. 抽象類中可以包含靜態(tài)方法,接口中不能包含靜態(tài)方法
6. 抽象類和接口中都可以包含靜態(tài)成員變量,抽象類中的靜態(tài)成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,并且默認(rèn)即為public static final類型。
7. 一個類可以實(shí)現(xiàn)多個接口,但只能繼承一個抽象類。
下面接著再說說兩者在應(yīng)用上的區(qū)別:
接口更多的是在系統(tǒng)架構(gòu)設(shè)計方法發(fā)揮作用,主要用于定義模塊之間的通信契約。而抽象類在代碼實(shí)現(xiàn)方面發(fā)揮作用,可以實(shí)現(xiàn)代碼的重用,例如,模板方法設(shè)計模式是抽象類的一個典型應(yīng)用,假設(shè)某個項目的所有Servlet類都要用相同的方式進(jìn)行權(quán)限判斷、記錄訪問日志和處理異常,那么就可以定義一個抽象的基類,讓所有的Servlet都繼承這個抽象基類,在抽象基類的service方法中完成權(quán)限判斷、記錄訪問日志和處理異常的代碼,在各個子類中只是完成各自的業(yè)務(wù)邏輯代碼,偽代碼如下:
public abstract class BaseServlet extends HttpServlet
{
public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{
記錄訪問日志
進(jìn)行權(quán)限判斷
if(具有權(quán)限)
{
try
{
doService(request,response);
}
catch(Excetpion e)
{
記錄異常信息
}
}
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException;
//注意訪問權(quán)限定義成protected,顯得既專業(yè),又嚴(yán)謹(jǐn),因為它是專門給子類用的
}
public class MyServlet1 extends BaseServlet
{
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException
{
本Servlet只處理的具體業(yè)務(wù)邏輯代碼
}
}
父類方法中間的某段代碼不確定,留給子類干,就用模板方法設(shè)計模式。
備注:這道題的思路是先從總體解釋抽象類和接口的基本概念,然后再比較兩者的語法細(xì)節(jié),最后再說兩者的應(yīng)用區(qū)別。比較兩者語法細(xì)節(jié)區(qū)別的條理是:先從一個類中的構(gòu)造方法、普通成員變量和方法(包括抽象方法),靜態(tài)變量和方法,繼承性等6個方面逐一去比較回答,接著從第三者繼承的角度的回答,特別是最后用了一個典型的例子來展現(xiàn)自己深厚的技術(shù)功底。
14. jdk中哪些類是不能繼承的?
不能繼承的是類是那些用final關(guān)鍵字修飾的類。一般比較基本的類型或防止擴(kuò)展類無意間破壞原來方法的實(shí)現(xiàn)的類型都應(yīng)該是final的,在jdk中System,String,StringBuffer等都是基本類型
15、String s = "Hello";s = s + " world!";這兩行代碼執(zhí)行后,原始的String對象中的內(nèi)容到底變了沒有?
沒有。因為String被設(shè)計成不可變(immutable)類,所以它的所有對象都是不可變對象。在這段代碼中,s原先指向一個String對象,內(nèi)容是 "Hello",然后我們對s進(jìn)行了+操作,那么s所指向的那個對象是否發(fā)生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另一個 String對象,內(nèi)容為"Hello world!",原來那個對象還存在于內(nèi)存之中,只是s這個引用變量不再指向它了。
通過上面的說明,我們很容易導(dǎo)出另一個結(jié)論,如果經(jīng)常對字符串進(jìn)行各種各樣的修改,或者說,不可預(yù)見的修改,那么使用String來代表字符串的話會引起很大的內(nèi)存開銷。因為 String對象建立之后不能再改變,所以對于每一個不同的字符串,都需要一個String對象來表示。這時,應(yīng)該考慮使用StringBuffer類,它允許修改,而不是每個不同的字符串都要生成一個新的對象。并且,這兩種類的對象轉(zhuǎn)換十分容易。
同時,我們還可以知道,如果要使用內(nèi)容相同的字符串,不必每次都new一個String。例如我們要在構(gòu)造器中對一個名叫s的String引用變量進(jìn)行初始化,把它設(shè)置為初始值,應(yīng)當(dāng)這樣做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都會調(diào)用構(gòu)造器,生成新對象,性能低下且內(nèi)存開銷大,并且沒有意義,因為String對象不可改變,所以對于內(nèi)容相同的字符串,只要一個String對象來表示就可以了。也就說,多次調(diào)用上面的構(gòu)造器創(chuàng)建多個對象,他們的String類型屬性s都指向同一個對象。
上面的結(jié)論還基于這樣一個事實(shí):對于字符串常量,如果內(nèi)容相同,Java認(rèn)為它們代表同一個String對象。而用關(guān)鍵字new調(diào)用構(gòu)造器,總是會創(chuàng)建一個新的對象,無論內(nèi)容是否相同。
至于為什么要把String類設(shè)計成不可變類,是它的用途決定的。其實(shí)不只String,很多Java標(biāo)準(zhǔn)類庫中的類都是不可變的。在開發(fā)一個系統(tǒng)的時候,我們有時候也需要設(shè)計不可變類,來傳遞一組相關(guān)的值,這也是面向?qū)ο笏枷氲捏w現(xiàn)。不可變類有一些優(yōu)點(diǎn),比如因為它的對象是只讀的,所以多線程并發(fā)訪問也不會有任何問題。當(dāng)然也有一些缺點(diǎn),比如每個不同的狀態(tài)都要一個對象來代表,可能會造成性能上的問題。所以Java標(biāo)準(zhǔn)類庫還提供了一個可變版本,即 StringBuffer。
16、java中會存在內(nèi)存泄漏嗎,請簡單描述。
所謂內(nèi)存泄露就是指一個不再被程序使用的對象或變量一直被占據(jù)在內(nèi)存中。java中有垃圾回收機(jī)制,它可以保證一對象不再被引用的時候,即對象編程了孤兒的時候,對象將自動被垃圾回收器從內(nèi)存中清除掉。由于Java 使用有向圖的方式進(jìn)行垃圾回收管理,可以消除引用循環(huán)的問題,例如有兩個對象,相互引用,只要它們和根進(jìn)程不可達(dá)的,那么GC也是可以回收它們的,例如下面的代碼可以看到這種情況的內(nèi)存回收:
package com.huawei.interview;
import java.io.IOException;
public class GarbageTest {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
gcTest();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("has exited gcTest!");
System.in.read();
System.in.read();
System.out.println("out begin gc!");
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中的內(nèi)存泄露的情況:長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內(nèi)存泄露,盡管短生命周期對象已經(jīng)不再需要,但是因為長生命周期對象持有它的引用而導(dǎo)致不能被回收,這就是java中內(nèi)存泄露的發(fā)生場景,通俗地說,就是程序員可能創(chuàng)建了一個對象,以后一直不再使用這個對象,這個對象卻一直被引用,即這個對象無用但是卻無法被垃圾回收器回收的,這就是java中可能出現(xiàn)內(nèi)存泄露的情況,例如,緩存系統(tǒng),我們加載了一個對象放在緩存中(例如放在一個全局map對象中),然后一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。
檢查java中的內(nèi)存泄露,一定要讓程序?qū)⒏鞣N分支情況都完整執(zhí)行到程序結(jié)束,然后看某個對象是否被使用過,如果沒有,則才能判定這個對象屬于內(nèi)存泄露。
如果一個外部類的實(shí)例對象的方法返回了一個內(nèi)部類的實(shí)例對象,這個內(nèi)部類對象被長期引用了,即使那個外部類實(shí)例對象不再被使用,但由于內(nèi)部類持久外部類的實(shí)例對象,這個外部類對象將不會被垃圾回收,這也會造成內(nèi)存泄露。
下面內(nèi)容來自于網(wǎng)上(主要特點(diǎn)就是清空堆棧中的某個元素,并不是徹底把它從數(shù)組中拿掉,而是把存儲的總數(shù)減少,本人寫得可以比這個好,在拿掉某個元素時,順便也讓它從數(shù)組中消失,將那個元素所在的位置的值設(shè)置為null即可):
我實(shí)在想不到比那個堆棧更經(jīng)典的例子了,以致于我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,當(dāng)然如果沒有在書上看到,可能過一段時間我自己也想的到,可是那時我說是我自己想到的也沒有人相信的。
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);
}
}
}
上面的原理應(yīng)該很簡單,假如堆棧加了10個元素,然后全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內(nèi)存泄露的兩個條件:無用,無法回收。
但是就是存在這樣的東西也不一定會導(dǎo)致什么樣的后果,如果這個堆棧用的比較少,也就浪費(fèi)了幾個K內(nèi)存而已,反正我們的內(nèi)存都上G了,哪里會有什么影響,再說這個東西很快就會被回收的,有什么關(guān)系。下面看兩個例子。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //這里有一個對象發(fā)生內(nèi)存泄露
s.push(new Object()); //上面的對象可以被回收了,等于是自愈了
}
}
因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那么最多也就只有100個對象無法被回收其實(shí)這個應(yīng)該很容易理解,Stack內(nèi)部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進(jìn)取,以前的引用自然消失!
內(nèi)存泄露的另外一種情況:當(dāng)一個對象被存儲進(jìn)HashSet集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段了,否則,對象修改后的哈希值與最初存儲進(jìn)HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當(dāng)前引用作為的參數(shù)去HashSet集合中檢索對象,也將返回找不到對象的結(jié)果,這也會導(dǎo)致無法從HashSet集合中單獨(dú)刪除當(dāng)前對象,造成內(nèi)存泄露。
17、多線程有幾種實(shí)現(xiàn)方法?同步有幾種實(shí)現(xiàn)方法?
多線程有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口
同步的實(shí)現(xiàn)方面有兩種,一種同步方法,一種同步代碼!分別是synchronized,wait與notify
wait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
sleep():使一個正在運(yùn)行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。
notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭
18、sleep() 和 wait() 有什么區(qū)別?
(網(wǎng)上的答案:sleep是線程類(Thread)的方法,導(dǎo)致此線程暫停執(zhí)行指定時間,給執(zhí)行機(jī)會給其他線程,但是監(jiān)控狀態(tài)依然保持,到時后會自動恢復(fù)。調(diào)用sleep不會釋放對象鎖。 wait是Object類的方法,對此對象調(diào)用wait方法導(dǎo)致本線程放棄對象鎖,進(jìn)入等待此對象的等待鎖定池,只有針對此對象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對象鎖定池準(zhǔn)備獲得對象鎖進(jìn)入運(yùn)行狀態(tài)。)
sleep就是正在執(zhí)行的線程主動讓出cpu,cpu去執(zhí)行其他線程,在sleep指定的時間過后,cpu才會回到這個線程上繼續(xù)往下執(zhí)行,如果當(dāng)前線程進(jìn)入了同步鎖,sleep方法并不會釋放鎖,即使當(dāng)前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執(zhí)行。wait是指在一個已經(jīng)進(jìn)入了同步鎖的線程內(nèi),讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運(yùn)行,只有其他線程調(diào)用了notify方法(notify并不釋放鎖,只是告訴調(diào)用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因為鎖還在別人手里,別人還沒釋放。如果notify方法后面的代碼還有很多,需要這些代碼執(zhí)行完后才會釋放鎖,可以在notfiy方法后增加一個等待和一些代碼,看看效果),調(diào)用wait方法的線程就會解除wait狀態(tài)和程序可以再次得到鎖后繼續(xù)向下運(yùn)行。對于wait的講解一定要配合例子代碼來說明,才顯得自己真明白。
package com.huawei.interview;
public class MultiThread {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
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() {
// TODO Auto-generated method stub
//由于這里的Thread1和下面的Thread2內(nèi)部run方法要用同一對象作為監(jiān)視器,我們這里不能用this,因為在Thread2里面的this和這個Thread1的this不是同一個對象。我們用MultiThread.class這個字節(jié)碼對象,當(dāng)前虛擬機(jī)里引用這個變量時,指向的都是同一個對象。
synchronized (MultiThread.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting");
try {
//釋放鎖有兩種方式,第一種方式是程序自然離開監(jiān)視器的范圍,也就是離開了synchronized關(guān)鍵字管轄的代碼范圍,另一種方式就是在synchronized關(guān)鍵字管轄的代碼內(nèi)部調(diào)用監(jiān)視器對象的wait方法。這里,使用wait方法釋放鎖。
MultiThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
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() {
// TODO Auto-generated method stub
synchronized (MultiThread.class) {
System.out.println("enter thread2...");
System.out.println("thread2 notify other thread can release wait status..");
//由于notify方法并不釋放鎖, 即使thread2調(diào)用下面的sleep方法休息了10毫秒,但thread1仍然不會執(zhí)行,因為thread2沒有釋放鎖,所以Thread1無法得不到鎖。
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("thread2 is going on...");
System.out.println("thread2 is being over!");
}
}
}
}
19、java中有幾種方法可以實(shí)現(xiàn)一個線程?用什么關(guān)鍵字修飾同步方法? stop()和suspend()方法為何不推薦使用?
java5以前,有如下兩種:
第一種:
new Thread(){}.start();這表示調(diào)用Thread子類對象的run方法,new Thread(){}表示一個Thread的匿名子類的實(shí)例對象,子類加上run方法后的代碼如下:
new Thread(){
public void run(){
}
}.start();
第二種:
new Thread(new Runnable(){}).start();這表示調(diào)用Thread對象接受的Runnable對象的run方法,new Runnable(){}表示一個Runnable的匿名子類的實(shí)例對象,runnable的子類加上run方法后的代碼如下:
new Thread(new Runnable(){
public void run(){
}
}
).start();
從java5開始,還有如下一些線程池創(chuàng)建多線程的方式:
ExecutorService pool = Executors.newFixedThreadPool(3)
for(int i=0;i<10;i++)
{
pool.execute(new Runable(){public void run(){}});
}
Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});
Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});
有兩種實(shí)現(xiàn)方法,分別使用new Thread()和new Thread(runnable)形式,第一種直接調(diào)用thread的run方法,所以,我們往往使用Thread子類,即new SubThread()。第二種調(diào)用runnable的run方法。
有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口
用synchronized關(guān)鍵字修飾同步方法
反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處于一種不連貫狀態(tài),那么其他線程能在那種狀態(tài)下檢查和修改它們。結(jié)果很難檢查出真正的問題所在。suspend()方法容易發(fā)生死鎖。調(diào)用suspend()的時候,目標(biāo)線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復(fù)運(yùn)行。對任何線程來說,如果它們想恢復(fù)目標(biāo)線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應(yīng)該使用suspend(),而應(yīng)在自己的Thread類中置入一個標(biāo)志,指出線程應(yīng)該活動還是掛起。若標(biāo)志指出線程應(yīng)該掛起,便用wait()命其進(jìn)入等待狀態(tài)。若標(biāo)志指出線程應(yīng)當(dāng)恢復(fù),則用一個notify()重新啟動線程。
20、Java中的異常處理機(jī)制的簡單原理和應(yīng)用。
異常是指java程序運(yùn)行時(非編譯)所發(fā)生的非正常情況或錯誤,與現(xiàn)實(shí)生活中的事件很相似,現(xiàn)實(shí)生活中的事件可以包含事件發(fā)生的時間、地點(diǎn)、人物、情節(jié)等信息,可以用一個對象來表示,Java使用面向?qū)ο蟮姆绞絹硖幚懋惓?,它把程序中發(fā)生的每個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。
Java對異常進(jìn)行了分類,不同類型的異常分別用不同的Java類表示,所有異常的根類為java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception,Error表示應(yīng)用程序本身無法克服和恢復(fù)的一種嚴(yán)重問題,程序只有死的份了,例如,說內(nèi)存溢出和線程死鎖等系統(tǒng)問題。Exception表示程序還能夠克服和恢復(fù)的問題,其中又分為系統(tǒng)異常和普通異常,系統(tǒng)異常是軟件本身缺陷所導(dǎo)致的問題,也就是軟件開發(fā)人員考慮不周所導(dǎo)致的問題,軟件使用者無法克服和恢復(fù)這種問題,但在這種問題下還可以讓軟件系統(tǒng)繼續(xù)運(yùn)行或者讓軟件死掉,例如,數(shù)組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉(zhuǎn)換異常(ClassCastException);普通異常是運(yùn)行環(huán)境的變化或異常所導(dǎo)致的問題,是用戶能夠克服的問題,例如,網(wǎng)絡(luò)斷線,硬盤空間不夠,發(fā)生這樣的異常后,程序不應(yīng)該死掉。
java為系統(tǒng)異常和普通異常提供了不同的解決方案,編譯器強(qiáng)制普通異常必須try..catch處理或用throws聲明繼續(xù)拋給上層調(diào)用方法處理,所以普通異常也稱為checked異常,而系統(tǒng)異??梢蕴幚硪部梢圆惶幚恚?,編譯器不強(qiáng)制用try..catch處理或用throws聲明,所以系統(tǒng)異常也稱為unchecked異常。
java軟件開發(fā)工程師實(shí)習(xí)求職常見面試題篇3
1、final, finally, finalize的區(qū)別。
final 用于聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
內(nèi)部類要訪問局部變量,局部變量必須定義成final類型,例如,一段代碼……
finally是異常處理語句結(jié)構(gòu)的一部分,表示總是執(zhí)行。
finalize是Object類的一個方法,在垃圾收集器執(zhí)行的時候會調(diào)用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關(guān)閉文件等。JVM不保證此方法總被調(diào)用
2、數(shù)組有沒有l(wèi)ength()這個方法? String有沒有l(wèi)ength()這個方法?
數(shù)組沒有l(wèi)ength()這個方法,有l(wèi)ength的屬性。String有l(wèi)ength()這個方法。
3.StringBuffer與StringBuilder的區(qū)別
StringBuffer和StringBuilder類都表示內(nèi)容可以被修改的字符串,StringBuilder是線程不安全的,運(yùn)行效率高,如果一個字符串變量是在方法里面定義,這種情況只可能有一個線程訪問它,不存在不安全的因素了,則用StringBuilder。如果要在類里面定義成員變量,并且這個類的實(shí)例對象會在多線程環(huán)境下使用,那么最好用StringBuffer。
4、String 和StringBuffer的區(qū)別
String類表示內(nèi)容不可改變的字符串。而StringBuffer類表示內(nèi)容可以被修改的字符串
5、Anonymous Inner Class (匿名內(nèi)部類) 是否可以extends(繼承)其它類,是否可以implements(實(shí)現(xiàn))interface(接口)?
可以繼承其他類或?qū)崿F(xiàn)其他接口。不僅是可以,而是必須!
6.Java單例模式
Java中 單例模式是一種常見的設(shè)計模式,
單例模式分三種:懶漢式單例、餓漢式單例、登記式單例
單例模式有一下特點(diǎn):
1、單例類只能有一個實(shí)例。
2、單例類必須自己自己創(chuàng)建自己的唯一實(shí)例。
3、單例類必須給所有其他對象提供這一實(shí)例。
7、說出一些數(shù)據(jù)庫優(yōu)化方面的經(jīng)驗?
用PreparedStatement 一般來說比Statement性能高:一個sql 發(fā)給服務(wù)器去執(zhí)行,涉及步驟:語法檢查、語義分析, 編譯,緩存
“inert into user values(1,1,1)”-à二進(jìn)制
“inert into user values(2,2,2)”-à二進(jìn)制
“inert into user values(?,?,?)”-à二進(jìn)制
有外鍵約束會影響插入和刪除性能,如果程序能夠保證數(shù)據(jù)的完整性,那在設(shè)計數(shù)據(jù)庫時就去掉外鍵。(比喻:就好比免檢產(chǎn)品,就是為了提高效率,充分相信產(chǎn)品的制造商)
(對于hibernate來說,就應(yīng)該有一個變化:empleyee->Deptment對象,現(xiàn)在設(shè)計時就成了employeeàdeptid)
看MySQL幫助文檔子查詢章節(jié)的最后部分,例如,根據(jù)掃描的原理,下面的子查詢語句要比第二條關(guān)聯(lián)查詢的效率高:
1. select e.name,e.salary where e.managerid=(select id from employee where name='zxx');
2. select e.name,e.salary,m.name,m.salary from employees e,employees m where
e.managerid = m.id and m.name='zxx';
表中允許適當(dāng)冗余,譬如,主題帖的回復(fù)數(shù)量和最后回復(fù)時間等
將姓名和密碼單獨(dú)從用戶表中獨(dú)立出來。這可以是非常好的一對一的案例喲!
sql語句全部大寫,特別是列名和表名都大寫。特別是sql命令的緩存功能,更加需要統(tǒng)一大小寫,sql語句à發(fā)給Oracle服務(wù)器à語法檢查和編譯成為內(nèi)部指令à緩存和執(zhí)行指令。根據(jù)緩存的特點(diǎn),不要拼湊條件,而是用?和PreparedStatment
還有索引對查詢性能的改進(jìn)也是值得關(guān)注的。
8、XML文檔定義有幾種形式?它們之間有何本質(zhì)區(qū)別?解析XML文檔有哪幾種方式?
a: 兩種形式 dtd schema,b: 本質(zhì)區(qū)別:schema本身是xml的,可以被XML解析器解析(這也是從DTD上發(fā)展schema的根本目的),c:有DOM,SAX,STAX等
DOM:處理大型文件時其性能下降的非常厲害。這個問題是由DOM的樹結(jié)構(gòu)所造成的,這種結(jié)構(gòu)占用的內(nèi)存較多,而且DOM必須在解析文件之前把整個文檔裝入內(nèi)存,適合對XML的隨機(jī)訪問
SAX:不現(xiàn)于DOM,SAX是事件驅(qū)動型的XML解析方式。它順序讀取XML文件,不需要一次全部裝載整個文件。當(dāng)遇到像文件開頭,文檔結(jié)束,或者標(biāo)簽開頭與標(biāo)簽結(jié)束時,它會觸發(fā)一個事件,用戶通過在其回調(diào)事件中寫入處理代碼來處理XML文件,適合對XML的順序訪問
STAX:Streaming API for XML (StAX)
9、hibernate中的update()和saveOrUpdate()的區(qū)別,session的load()和get()的區(qū)別。
update針對的是已存在的實(shí)體對象
saveOrUpdate()對象存在與否都不會有任何影響
session的load()和get()的區(qū)別
load是只在緩存中加載數(shù)據(jù)
get是先緩存中查找或者緩存中沒有到數(shù)據(jù)庫中查找
10. How to set many-to-many relationship in Hibernate?
Hibernate中怎樣實(shí)現(xiàn)類之間的關(guān)系?(如:一對多、多對多的關(guān)系)
類與類之間的關(guān)系主要體現(xiàn)在表與表之間的關(guān)系進(jìn)行操作,它們都市對對象進(jìn)行操作,我們程序中把所有的表與類都映射在一起,它們通過配置文件中的many-to-one、one-to-many、many-many
11如何優(yōu)化Hibernate?
* 使用雙向一對多關(guān)聯(lián),不使用單向一對多
* 靈活使用單向一對多關(guān)聯(lián)
* 不用一對一,用多對一取代
* 配置對象緩存,不使用集合緩存
* 一對多集合使用Bag,多對多集合使用Set
* 繼承類使用顯式多態(tài)
* 表字段要少,表關(guān)聯(lián)不要怕多,有二級緩存撐腰