




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、Java面試寶典相關概念面向對象的三個特征封裝,繼承,多態(tài).這個應該是人人皆知.有時候也會加上抽象.多態(tài)的好處允許不同類對象對同一消息做出響應,即同一消息可以根據發(fā)送對象的不同而采用多種不同的行為方式(發(fā)送消息就是函數(shù)調用).主要有以下優(yōu)點:1. 可替換性:多態(tài)對已存在代碼具有可替換性.2. 可擴充性:增加新的子類不影響已經存在的類結構.3. 接口性:多態(tài)是超累通過方法簽名,想子類提供一個公共接口,由子類來完善或者重寫它來實現(xiàn)的.4. 靈活性:5. 簡化性:代碼中如何實現(xiàn)多態(tài)實現(xiàn)多態(tài)主要有以下三種方式: 1. 接口實現(xiàn) 2. 繼承父類重寫方法 3. 同一類中進行方法重載虛擬機是如何實現(xiàn)多態(tài)的動
2、態(tài)綁定技術(dynamic binding),執(zhí)行期間判斷所引用對象的實際類型,根據實際類型調用對應的方法.接口的意義接口的意義用三個詞就可以概括:規(guī)范,擴展,回調.抽象類的意義抽象類的意義可以用三句話來概括:1. 為其他子類提供一個公共的類型2. 封裝子類中重復定義的內容3. 定義抽象方法,子類雖然有不同的實現(xiàn),但是定義時一致的 # 接口和抽象類的區(qū)別比較抽象類接口默認方法抽象類可以有默認的方法實現(xiàn),java 8之前,接口中不存在方法的實現(xiàn).實現(xiàn)方式子類使用extends關鍵字來繼承抽象類.如果子類不是抽象類,子類需要提供抽象類中所聲明方法的實現(xiàn).子類使用implements來實現(xiàn)接口,需要
3、提供接口中所有聲明的實現(xiàn).構造器抽象類中可以有構造器,接口中不能和正常類區(qū)別抽象類不能被實例化接口則是完全不同的類型訪問修飾符抽象方法可以有public,protected和default等修飾接口默認是public,不能使用其他修飾符多繼承一個子類只能存在一個父類一個子類可以存在多個接口添加新方法想抽象類中添加新方法,可以提供默認的實現(xiàn),因此可以不修改子類現(xiàn)有的代碼如果往接口中添加新方法,則子類中需要實現(xiàn)該方法.父類的靜態(tài)方法能否被子類重寫不能.子類繼承父類后,有相同的靜態(tài)方法和非靜態(tài),這是非靜態(tài)方法覆蓋父類中的方法(即方法重寫),父類的該靜態(tài)方法被隱藏(如果對象是父類則調用該隱藏的方法),
4、另外子類可集成父類的靜態(tài)與非靜態(tài)方法,至于方法重載我覺得它其中一要素就是在同一類中,不能說父類中的什么方法與子類里的什么方法是方法重載的體現(xiàn).什么是不可變對象不可變對象指對象一旦被創(chuàng)建,狀態(tài)就不能再改變。任何修改都會創(chuàng)建一個新的對象,如 String、Integer及其它包裝類。能否創(chuàng)建一個包含可變對象的不可變對象?當然可以創(chuàng)建一個包含可變對象的不可變對象的,你只需要謹慎一點,不要共享可變對象的引用就可以了,如果需要變化時,就返回原對象的一個拷貝。最常見的例子就是對象中包含一個日期對象的引用.java 創(chuàng)建對象的幾種方式1. 采用new2. 通過反射3. 采用clone4. 通過序列化機制前2
5、者都需要顯式地調用構造方法. 造成耦合性最高的恰好是第一種,因此你發(fā)現(xiàn)無論什么框架,只要涉及到解耦必先減少new的使用.switch中能否使用string做參數(shù)在idk 1.7之前,switch只能支持byte,short,char,int或者其對應的封裝類以及Enum類型。從idk 1.7之后switch開始支持String.Object中有哪些公共方法?1. equals()2. clone()3. getClass()4. notify(),notifyAll(),wait()java當中的四種引用強引用,軟引用,弱引用,虛引用.不同的引用類型主要體現(xiàn)在GC上:1. 強引用:如果一個對象
6、具有強引用,它就不會被垃圾回收器回收。即使當前內存空間不足,JVM也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。如果想中斷強引用和某個對象之間的關聯(lián),可以顯式地將引用賦值為null,這樣一來的話,JVM在合適的時間就會回收該對象2. 軟引用:在使用軟引用時,如果內存的空間足夠,軟引用就能繼續(xù)被使用,而不會被垃圾回收器回收,只有在內存不足時,軟引用才會被垃圾回收器回收。3. 弱引用:具有弱引用的對象擁有的生命周期更短暫。因為當 JVM 進行垃圾回收,一旦發(fā)現(xiàn)弱引用對象,無論當前內存空間是否充足,都會將弱引用回收。不過由于垃圾回收器是一個優(yōu)先級較低的線程,所以并不
7、一定能迅速發(fā)現(xiàn)弱引用對象4. 虛引用:顧名思義,就是形同虛設,如果一個對象僅持有虛引用,那么它相當于沒有引用,在任何時候都可能被垃圾回收器回收。更多了解參見深入對象引用WeakReference與SoftReference的區(qū)別?這點在四種引用類型中已經做了解釋,這里簡單說明一下即可: 雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 內存的效率,但是 WeakReference ,一旦失去最后一個強引用,就會被 GC 回收,而軟引用雖然不能阻止被回收,但是可以延遲到 JVM 內存不足的時候。為什么要有不同的引用類型不像C語言,我們可以控制內存的申請和釋
8、放,在Java中有時候我們需要適當?shù)目刂茖ο蟊换厥盏臅r機,因此就誕生了不同的引用類型,可以說不同的引用類型實則是對GC回收時機不可控的妥協(xié).有以下幾個使用場景可以充分的說明:1. 利用軟引用和弱引用解決OOM問題:用一個HashMap來保存圖片的路徑和相應圖片對象關聯(lián)的軟引用之間的映射關系,在內存不足時,JVM會自動回收這些緩存圖片對象所占用的空間,從而有效地避免了OOM的問題.2. 通過軟引用實現(xiàn)Java對象的高速緩存:比如我們創(chuàng)建了一Person的類,如果每次需要查詢一個人的信息,哪怕是幾秒中之前剛剛查詢過的,都要重新構建一個實例,這將引起大量Person對象的消耗,并且由于這些對象的生命
9、周期相對較短,會引起多次GC影響性能。此時,通過軟引用和 HashMap 的結合可以構建高速緩存,提供性能.java中=和eqauls()的區(qū)別,equals()和hashcode的區(qū)別=是運算符,用于比較兩個變量是否相等,而equals是Object類的方法,用于比較兩個對象是否相等.默認Object類的equals方法是比較兩個對象的地址,此時和=的結果一樣.換句話說:基本類型比較用=,比較的是他們的值.默認下,對象用=比較時,比較的是內存地址,如果需要比較對象內容,需要重寫equal方法equals()和hashcode()的聯(lián)系hashCode()是Object類的一個方法,返回一個哈
10、希值.如果兩個對象根據equal()方法比較相等,那么調用這兩個對象中任意一個對象的hashCode()方法必須產生相同的哈希值. 如果兩個對象根據eqaul()方法比較不相等,那么產生的哈希值不一定相等(碰撞的情況下還是會相等的.)a.hashCode()有什么用?與a.equals(b)有什么關系hashCode() 方法是相應對象整型的 hash 值。它常用于基于 hash 的集合類,如 Hashtable、HashMap、LinkedHashMap等等。它與 equals() 方法關系特別緊密。根據 Java 規(guī)范,兩個使用 equal() 方法來判斷相等的對象,必須具有相同的 has
11、hcode。將對象放入到集合中時,首先判斷要放入對象的hashcode是否已經在集合中存在,不存在則直接放入集合.如果hashcode相等,然后通過equal()方法判斷要放入對象與集合中的任意對象是否相等:如果equal()判斷不相等,直接將該元素放入集合中,否則不放入.有沒有可能兩個不相等的對象有相同的hashcode有可能,兩個不相等的對象可能會有相同的 hashcode 值,這就是為什么在 hashmap 中會有沖突。相等 hashcode 值的規(guī)定只是說如果兩個對象相等,必須有相同的hashcode 值,但是沒有關于不相等對象的任何規(guī)定。可以在hashcode中使用隨機數(shù)字嗎?不行,
12、因為同一對象的 hashcode 值必須是相同的“a=b”與a.equals(b)有什么區(qū)別如果a 和b 都是對象,則 a=b 是比較兩個對象的引用,只有當 a 和 b 指向的是堆中的同一個對象才會返回 true,而 a.equals(b) 是進行邏輯比較,所以通常需要重寫該方法來提供邏輯一致性的比較。例如,String 類重寫 equals() 方法,所以可以用于兩個不同對象,但是包含的字母相同的比較。3*0.1=0.3返回值是什么false,因為有些浮點數(shù)不能完全精確的表示出來。a=a+b與a+=b有什么區(qū)別嗎?隱式的將加操作的結果類型強制轉換為持有結果的類型。如果兩這個整型相加,如 by
13、te、short 或者 int,首先會將它們提升到 int 類型,然后在執(zhí)行加法操作。如果加法操作的結果比 a 的最大值要大,則 a+b 會出現(xiàn)編譯錯誤,但是 a += b 沒問題,如下: byte a = 127; byte b = 127; b = a + b; / error : cannot convert from int to byte b += a; / ok (譯者注:這個地方應該表述的有誤,其實無論 a+b 的值為多少,編譯器都會報錯,因為 a+b 操作會將 a、b 提升為 int 類型,所以將 int 類型賦值給 byte 就會編譯出錯)內部類的作用內部類可以用多個實例,每
14、個實例都有自己的狀態(tài)信息,并且與其他外圍對象的信息相互獨立.在單個外圍類當中,可以讓多個內部類以不同的方式實現(xiàn)同一接口,或者繼承同一個類.創(chuàng)建內部類對象的時刻病不依賴于外部類對象的創(chuàng)建.內部類并沒有令人疑惑的”is-a”關系,它就像是一個獨立的實體.內部類提供了更好的封裝,除了該外圍類,其他類都不能訪問final,finalize和finally的不同之處final 是一個修飾符,可以修飾變量、方法和類。如果 final 修飾變量,意味著該變量的值在初始化后不能被改變。finalize 方法是在對象被回收之前調用的方法,給對象自己最后一個復活的機會,但是什么時候調用 finalize 沒有保證
15、。finally 是一個關鍵字,與 try 和 catch 一起用于異常的處理。finally 塊一定會被執(zhí)行,無論在 try 塊中是否有發(fā)生異常。clone()是哪個類型的方法?java.lang.Cloneable 是一個標示性接口,不包含任何方法,clone 方法在 object 類中定義。并且需要知道 clone() 方法是一個本地方法,這意味著它是由 c 或 c+ 或 其他本地語言實現(xiàn)的。深拷貝和淺拷貝的區(qū)別是什么?淺拷貝:被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺拷貝僅僅復制所考慮的對象,而不復制它所引用的對象。深拷貝:被
16、復制對象的所有變量都含有與原來的對象相同的值,而那些引用其他對象的變量將指向被復制過的新對象,而不再是原有的那些被引用的對象。換言之,深拷貝把要復制的對象所引用的對象都復制了一遍。static都有哪些用法?幾乎所有的人都知道static關鍵字這兩個基本的用法:靜態(tài)變量和靜態(tài)方法.也就是被static所修飾的變量/方法都屬于類的靜態(tài)資源,類實例所共享.除了靜態(tài)變量和靜態(tài)方法之外,static也用于靜態(tài)塊,多用于初始化操作:public calss PreCache static /執(zhí)行相關操作 此外static也多用于修飾內部類,此時稱之為靜態(tài)內部類.最后一種用法就是靜態(tài)導包,即import s
17、tatic.import static是在JDK 1.5之后引入的新特性,可以用來指定導入某個類中的靜態(tài)資源,并且不需要使用類名.資源名,可以直接使用資源名,比如:import static java.lang.Math.*;public class Test public static void main(String args) /System.out.println(Math.sin(20);傳統(tǒng)做法 System.out.println(sin(20); final有哪些用法final也是很多面試喜歡問的地方,能回答下以下三點就不錯了: 1.被final修飾的類不可以被繼承 2.被fi
18、nal修飾的方法不可以被重寫 3.被final修飾的變量不可以被改變.如果修飾的引用,那么表示引用不可變,引用指向的內容可變. 4.被final修飾的方法,JVM會嘗試將其內聯(lián),以提高運行效率 5.被final修飾的常量,在編譯階段會存入常量池中.當前回答出編譯器對final域要遵守的兩個重排序規(guī)則更好: 1.在構造函數(shù)內對一個final域的寫入,與隨后把這個被構造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序. 2.初次讀一個包含final域的對象的引用,與隨后初次讀這個final域,這兩個操作之間不能重排序.數(shù)據類型相關java中int char,long各占多少字節(jié)?|類型|位數(shù)
19、|字節(jié)數(shù)| |-|-|-| |short|2|16| |int|4|32| |long|8|64| |float|4|32 |double|8|64| |char|2|16|64位的JVM當中,int的長度是多少?Java 中,int 類型變量的長度是一個固定值,與平臺無關,都是 32 位。意思就是說,在 32 位 和 64 位 的Java 虛擬機中,int 類型的長度是相同的。java int和Integer的區(qū)別Integer是int的包裝類型,在拆箱和裝箱中,而知自動轉換.int是基本類型,直接存數(shù)值,而integer是對象,用一個引用指向這個對象.int 和Integer誰占用的內存更
20、多?Integer 對象會占用更多的內存。Integer是一個對象,需要存儲對象的元數(shù)據。但是 int 是一個原始類型的數(shù)據,所以占用的空間更少。String,StringBuffer和StringBuilder區(qū)別String是字符串常量,final修飾;StringBuffer字符串變量(線程安全); StringBuilder 字符串變量(線程不安全).String和StringBufferString和StringBuffer主要區(qū)別是性能:String是不可變對象,每次對String類型進行操作都等同于產生了一個新的String對象,然后指向新的String對象.所以盡量不在對Str
21、ing進行大量的拼接操作,否則會產生很多臨時對象,導致GC開始工作,影響系統(tǒng)性能.StringBuffer是對對象本身操作,而不是產生新的對象,因此在通常在有大量拼接的情況下我們建議使用StringBuffer.但是需要注意現(xiàn)在JVM會對String拼接做一定的優(yōu)化: String s=“This is only ”+”simple”+”test”會被虛擬機直接優(yōu)化成String s=“This is only simple test”,此時就不存在拼接過程.StringBuffer和StringBuilderStringBuffer是線程安全的可變字符串,其內部實現(xiàn)是可變數(shù)組.St
22、ringBuilder是java 5.0新增的,其功能和StringBuffer類似,但是非線程安全.因此,在沒有多線程問題的前提下,使用StringBuilder會取得更好的性能.什么是編譯器常量?使用它有什么風險?公共靜態(tài)不可變(public static final )變量也就是我們所說的編譯期常量,這里的 public 可選的。實際上這些變量在編譯時會被替換掉,因為編譯器知道這些變量的值,并且知道這些變量在運行時不能改變。這種方式存在的一個問題是你使用了一個內部的或第三方庫中的公有編譯時常量,但是這個值后面被其他人改變了,但是你的客戶端仍然在使用老的值,甚至你已經部署了一個新的jar。
23、為了避免這種情況,當你在更新依賴 JAR 文件時,確保重新編譯你的程序。java當中使用什么類型表示價格比較好?如果不是特別關心內存和性能的話,使用BigDecimal,否則使用預定義精度的 double 類型。如何將byte轉為String可以使用 String 接收 byte 參數(shù)的構造器來進行轉換,需要注意的點是要使用的正確的編碼,否則會使用平臺默認編碼,這個編碼可能跟原來的編碼相同,也可能不同??梢詫nt強轉為byte類型么?會產生什么問題?我們可以做強制轉換,但是Java中int是32位的而byte是8 位的,所以,如果強制轉化int類型的高24位將會被丟棄,byte 類型的范圍是
24、從-128.到128關于垃圾回收你知道哪些垃圾回收算法?垃圾回收從理論上非常容易理解,具體的方法有以下幾種:1. 標記-清除2. 標記-復制3. 標記-整理4. 分代回收 更詳細的內容參見深入理解垃圾回收算法如何判斷一個對象是否應該被回收這就是所謂的對象存活性判斷,常用的方法有兩種:1.引用計數(shù)法;2:對象可達性分析.由于引用計數(shù)法存在互相引用導致無法進行GC的問題,所以目前JVM虛擬機多使用對象可達性分析算法.簡單的解釋一下垃圾回收Java 垃圾回收機制最基本的做法是分代回收。內存中的區(qū)域被劃分成不同的世代,對象根據其存活的時間被保存在對應世代的區(qū)域中。一般的實現(xiàn)是劃分成3個世代:年輕、年老
25、和永久。內存的分配是發(fā)生在年輕世代中的。當一個對象存活時間足夠長的時候,它就會被復制到年老世代中。對于不同的世代可以使用不同的垃圾回收算法。進行世代劃分的出發(fā)點是對應用中對象存活時間進行研究之后得出的統(tǒng)計規(guī)律。一般來說,一個應用中的大部分對象的存活時間都很短。比如局部變量的存活時間就只在方法的執(zhí)行過程中?;谶@一點,對于年輕世代的垃圾回收算法就可以很有針對性.調用System.gc()會發(fā)生什么?通知GC開始工作,但是GC真正開始的時間不確定.進程,線程相關說說進程,線程,協(xié)程之間的區(qū)別簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程.進程在執(zhí)行過程
26、中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數(shù),從而效率更高.線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位.同一進程中的多個線程之間可以并發(fā)執(zhí)行.你了解守護線程嗎?它和非守護線程有什么區(qū)別程序運行完畢,jvm會等待非守護線程完成后關閉,但是jvm不會等待守護線程.守護線程最典型的例子就是GC線程什么是多線程上下文切換多線程的上下文切換是指CPU控制權由一個已經正在運行的線程切換到另外一個就緒并等待獲取CPU執(zhí)行權的線程的過程。創(chuàng)建兩種線程的方式?他們有什么區(qū)別?通過實現(xiàn)java.lang.Runnable或者通過擴展java.lang.Th
27、read類.相比擴展Thread,實現(xiàn)Runnable接口可能更優(yōu).原因有二:1. Java不支持多繼承.因此擴展Thread類就代表這個子類不能擴展其他類.而實現(xiàn)Runnable接口的類還可能擴展另一個類.2. 類可能只要求可執(zhí)行即可,因此集成整個Thread類的開銷過大.Runnable和Callable的區(qū)別Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執(zhí)行的結果。 這其實是很有用的一個特性,因為多線程
28、相比單線程更難、更復雜的一個重要原因就是因為多線程充滿著未知性,某條線程是否執(zhí)行了?某條線程執(zhí)行了多久?某條線程執(zhí)行的時候我們期望的數(shù)據是否已經賦值完畢?無法得知,我們能做的只是等待這條多線程的任務執(zhí)行完畢而已。而Callable+Future/FutureTask卻可以獲取多線程運行的結果,可以在等待時間太長沒獲取到需要的數(shù)據的情況下取消該線程的任務,真的是非常有用。什么導致線程阻塞阻塞指的是暫停一個線程的執(zhí)行以等待某個條件發(fā)生(如某資源就緒),學過操作系統(tǒng)的同學對它一定已經很熟悉了。Java 提供了大量方法來支持阻塞,下面讓我們逐一分析。方法說明sleep()sleep() 允許 指定以毫
29、秒為單位的一段時間作為參數(shù),它使得線程在指定的時間內進入阻塞狀態(tài),不能得到CPU 時間,指定的時間一過,線程重新進入可執(zhí)行狀態(tài)。 典型地,sleep() 被用在等待某個資源就緒的情形:測試發(fā)現(xiàn)條件不滿足后,讓線程阻塞一段時間后重新測試,直到條件滿足為止suspend() 和 resume()兩個方法配套使用,suspend()使得線程進入阻塞狀態(tài),并且不會自動恢復,必須其對應的resume() 被調用,才能使得線程重新進入可執(zhí)行狀態(tài)。典型地,suspend() 和 resume() 被用在等待另一個線程產生的結果的情形:測試發(fā)現(xiàn)結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用 res
30、ume() 使其恢復。yield()yield() 使得線程放棄當前分得的 CPU 時間,但是不使線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時可能再次分得 CPU 時間。調用 yield() 的效果等價于調度程序認為該線程已執(zhí)行了足夠的時間從而轉到另一個線程wait() 和 notify()兩個方法配套使用,wait() 使得線程進入阻塞狀態(tài),它有兩種形式,一種允許 指定以毫秒為單位的一段時間作為參數(shù),另一種沒有參數(shù),前者當對應的 notify() 被調用或者超出指定時間時線程重新進入可執(zhí)行狀態(tài),后者則必須對應的 notify() 被調用.wait(),notify()和suspend(),resu
31、me()之間的區(qū)別初看起來它們與 suspend() 和 resume() 方法對沒有什么分別,但是事實上它們是截然不同的。區(qū)別的核心在于,前面敘述的所有方法,阻塞時都不會釋放占用的鎖(如果占用了的話),而這一對方法則相反。上述的核心區(qū)別導致了一系列的細節(jié)上的區(qū)別。首先,前面敘述的所有方法都隸屬于 Thread 類,但是這一對卻直接隸屬于 Object 類,也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的,調用任意對象的 wait() 方法導致線程阻塞,并且該對象上的鎖被釋放。而調用 任意對象的not
32、ify()方法則導致因調用該對象的 wait() 方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。其次,前面敘述的所有方法都可在任何位置調用,但是這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在synchronized 方法或塊中當前線程才占有鎖,才有鎖可以釋放。同樣的道理,調用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運行時會出現(xiàn)IllegalMon
33、itorStateException 異常。wait() 和 notify() 方法的上述特性決定了它們經常和synchronized 方法或塊一起使用,將它們和操作系統(tǒng)的進程間通信機制作一個比較就會發(fā)現(xiàn)它們的相似性:synchronized方法或塊提供了類似于操作系統(tǒng)原語的功能,它們的執(zhí)行不會受到多線程機制的干擾,而這一對方法則相當于 block 和wakeup 原語(這一對方法均聲明為 synchronized)。它們的結合使得我們可以實現(xiàn)操作系統(tǒng)上一系列精妙的進程間通信的算法(如信號量算法),并用于解決各種復雜的線程間通信問題。關于 wait() 和 notify() 方法最后再說明兩點
34、: 第一:調用 notify() 方法導致解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用,唯一的區(qū)別在于,調用 notifyAll() 方法將把因調用該對象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然,只有獲得鎖的那一個線程才能進入可執(zhí)行狀態(tài)。談到阻塞,就不能不談一談死鎖,略一分析就能發(fā)現(xiàn),suspend() 方法和不指定超時期限的 wait() 方法的調用都可能產生死鎖。遺憾
35、的是,Java 并不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖。以上我們對 Java 中實現(xiàn)線程阻塞的各種方法作了一番分析,我們重點分析了 wait() 和 notify() 方法,因為它們的功能最強大,使用也最靈活,但是這也導致了它們的效率較低,較容易出錯。實際使用中我們應該靈活使用各種方法,以便更好地達到我們的目的。為什么wait()方法和notify()/notifyAll()方法要在同步塊中被調用這是JDK強制的,wait()方法和notify()/notifyAll()方法在調用前都必須先獲得對象的鎖wait()方法和notify()/notifyAll()方法在放棄
36、對象監(jiān)視器時有什么區(qū)別wait()方法和notify()/notifyAll()方法在放棄對象監(jiān)視器的時候的區(qū)別在于:wait()方法立即釋放對象監(jiān)視器,notify()/notifyAll()方法則會等待線程剩余代碼執(zhí)行完畢才會放棄對象監(jiān)視器。wait()與sleep()的區(qū)別關于這兩者已經在上面進行詳細的說明,這里就做個概括好了:· sleep()來自Thread類,和wait()來自Object類.調用sleep()方法的過程中,線程不會釋放對象鎖。而 調用 wait 方法線程會釋放對象鎖· sleep()睡眠后不出讓系統(tǒng)資源,wait讓其他線程可以占用CPU
37、3; sleep(milliseconds)需要指定一個睡眠時間,時間一到會自動喚醒.而wait()需要配合notify()或者notifyAll()使用synchronized和ReentrantLock的區(qū)別synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區(qū)別。既然ReentrantLock是類,那么它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock比synchronized的擴展性體現(xiàn)在幾點上: (1)ReentrantLock可以對獲取
38、鎖的等待時間進行設置,這樣就避免了死鎖 (2)ReentrantLock可以獲取各種鎖的信息 (3)ReentrantLock可以靈活地實現(xiàn)多路通知 另外,二者的鎖機制其實也是不一樣的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word.FutureTask是什么這個其實前面有提到過,F(xiàn)utureTask表示一個異步運算的任務。FutureTask里面可以傳入一個Callable的具體實現(xiàn)類,可以對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操作。當然,由于FutureTask也是Runna
39、ble接口的實現(xiàn)類,所以FutureTask也可以放入線程池中。一個線程如果出現(xiàn)了運行時異常怎么辦?如果這個異常沒有被捕獲的話,這個線程就停止執(zhí)行了。另外重要的一點是:如果這個線程持有某個某個對象的監(jiān)視器,那么這個對象監(jiān)視器會被立即釋放如何在兩個線程間共享數(shù)據通過在線程之間共享對象就可以了,然后通過wait/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是為線程之間共享數(shù)據而設計的如何正確的使用wait()?使用if還是while?wait() 方法應該在循環(huán)調用,因為當線程獲取到 CPU 開始執(zhí)行的時候
40、,其他條件可能還沒有滿足,所以在處理前,循環(huán)檢測條件是否滿足會更好。下面是一段標準的使用 wait 和 notify 方法的代碼: synchronized (obj) while (condition does not hold) obj.wait(); / (Releases lock, and reacquires on wakeup) . / Perform action appropriate to condition 什么是線程局部變量線程局部變量是局限于線程內部的變量,屬于線程自身所有,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現(xiàn)線程安全
41、的方式。但是在管理環(huán)境下(如 web 服務器)使用線程局部變量的時候要特別小心,在這種情況下,工作線程的生命周期比任何應用變量的生命周期都要長。任何線程局部變量一旦在工作完成后沒有釋放,Java 應用就存在內存泄露的風險。ThreadLoal的作用是什么?簡單說ThreadLocal就是一種以空間換時間的做法在每個Thread里面維護了一個ThreadLocal.ThreadLocalMap把數(shù)據進行隔離,數(shù)據不共享,自然就沒有線程安全方面的問題了.生產者消費者模型的作用是什么?(1)通過平衡生產者的生產能力和消費者的消費能力來提升整個系統(tǒng)的運行效率,這是生產者消費者模型最重要的作用 (2)解
42、耦,這是生產者消費者模型附帶的作用,解耦意味著生產者和消費者之間的聯(lián)系少,聯(lián)系越少越可以獨自發(fā)展而不需要收到相互的制約寫一個生產者-消費者隊列可以通過阻塞隊列實現(xiàn),也可以通過wait-notify來實現(xiàn).使用阻塞隊列來實現(xiàn)/消費者public class Producer implements Runnable private final BlockingQueue<Integer> queue; public Producer(BlockingQueue q) this.queue=q; Override public void run() try while (true) Th
43、read.sleep(1000);/模擬耗時 queue.put(produce(); catch (InterruptedException e) private int produce() int n=new Random().nextInt(10000); System.out.println("Thread:" + Thread.currentThread().getId() + " produce:" + n); return n; /消費者public class Consumer implements Runnable private fi
44、nal BlockingQueue<Integer> queue; public Consumer(BlockingQueue q) this.queue=q; Override public void run() while (true) try Thread.sleep(2000);/模擬耗時 consume(queue.take(); catch (InterruptedException e) private void consume(Integer n) System.out.println("Thread:" + Thread.currentThre
45、ad().getId() + " consume:" + n); /測試public class Main public static void main(String args) BlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(100); Producer p=new Producer(queue); Consumer c1=new Consumer(queue); Consumer c2=new Consumer(queue); new Thread(p).start();
46、 new Thread(c1).start(); new Thread(c2).start(); 使用wait-notify來實現(xiàn)該種方式應該最經典,這里就不做說明了ConcurrentHashMap的并發(fā)度是什么?ConcurrentHashMap的并發(fā)度就是segment的大小,默認為16,這意味著最多同時可以有16條線程操作ConcurrentHashMap,這也是ConcurrentHashMap對Hashtable的最大優(yōu)勢,任何情況下,Hashtable能同時有兩條線程獲取Hashtable中的數(shù)據嗎?CyclicBarrier和CountDownLatch區(qū)別這兩個類非常類似,都
47、在java.util.concurrent下,都可以用來表示代碼運行到某個點上,二者的區(qū)別在于:· CyclicBarrier的某個線程運行到某個點上之后,該線程即停止運行,直到所有的線程都到達了這個點,所有線程才重新運行;CountDownLatch則不是,某線程運行到某個點上之后,只是給某個數(shù)值-1而已,該線程繼續(xù)運行· CyclicBarrier只能喚起一個任務,CountDownLatch可以喚起多個任務· CyclicBarrier可重用,CountDownLatch不可重用,計數(shù)值為0該CountDownLatch就不可再用了java中的+操作符線程安
48、全么?不是線程安全的操作。它涉及到多個指令,如讀取變量值,增加,然后存儲回內存,這個過程可能會出現(xiàn)多個線程交差你有哪些多線程開發(fā)良好的實踐?1. 給線程命名2. 最小化同步范圍3. 優(yōu)先使用volatile4. 盡可能使用更高層次的并發(fā)工具而非wait和notify()來實現(xiàn)線程通信,如BlockingQueue,Semeaphore5. 優(yōu)先使用并發(fā)容器而非同步容器.6. 考慮使用線程池關于volatile關鍵字可以創(chuàng)建Volatile數(shù)組嗎?Java 中可以創(chuàng)建 volatile類型數(shù)組,不過只是一個指向數(shù)組的引用,而不是整個數(shù)組。如果改變引用指向的數(shù)組,將會受到volatile 的保護,
49、但是如果多個線程同時改變數(shù)組的元素,volatile標示符就不能起到之前的保護作用了volatile能使得一個非原子操作變成原子操作嗎?一個典型的例子是在類中有一個 long 類型的成員變量。如果你知道該成員變量會被多個線程訪問,如計數(shù)器、價格等,你最好是將其設置為 volatile。為什么?因為 Java 中讀取 long 類型變量不是原子的,需要分成兩步,如果一個線程正在修改該 long 變量的值,另一個線程可能只能看到該值的一半(前 32 位)。但是對一個 volatile 型的 long 或 double 變量的讀寫是原子。一種實踐是用 volatile 修飾 long 和 doubl
50、e 變量,使其能按原子類型來讀寫。double 和 long 都是64位寬,因此對這兩種類型的讀是分為兩部分的,第一次讀取第一個 32 位,然后再讀剩下的 32 位,這個過程不是原子的,但 Java 中 volatile 型的 long 或 double 變量的讀寫是原子的。volatile 修復符的另一個作用是提供內存屏障(memory barrier),例如在分布式框架中的應用。簡單的說,就是當你寫一個 volatile 變量之前,Java 內存模型會插入一個寫屏障(write barrier),讀一個 volatile 變量之前,會插入一個讀屏障(read barrier)。意思就是說,
51、在你寫一個 volatile 域時,能保證任何線程都能看到你寫的值,同時,在寫之前,也能保證任何數(shù)值的更新對所有線程是可見的,因為內存屏障會將其他所有寫的值更新到緩存。volatile類型變量提供什么保證?volatile 主要有兩方面的作用:1.避免指令重排2.可見性保證.例如,JVM 或者 JIT為了獲得更好的性能會對語句重排序,但是 volatile 類型變量即使在沒有同步塊的情況下賦值也不會與其他語句重排序。 volatile 提供 happens-before 的保證,確保一個線程的修改能對其他線程是可見的。某些情況下,volatile 還能提供原子性,如讀 64 位數(shù)據類型,像 l
52、ong 和 double 都不是原子的(低32位和高32位),但 volatile 類型的 double 和 long 就是原子的.關于集合Java中的集合及其繼承關系關于集合的體系是每個人都應該爛熟于心的,尤其是對我們經常使用的List,Map的原理更該如此.這里我們看這張圖即可: 更多內容可見集合類總結poll()方法和remove()方法區(qū)別?poll() 和 remove() 都是從隊列中取出一個元素,但是 poll() 在獲取元素失敗的時候會返回空,但是 remove() 失敗的時候會拋出異常。LinkedHashMap和PriorityQueue的區(qū)別PriorityQu
53、eue 是一個優(yōu)先級隊列,保證最高或者最低優(yōu)先級的的元素總是在隊列頭部,但是 LinkedHashMap 維持的順序是元素插入的順序。當遍歷一個 PriorityQueue 時,沒有任何順序保證,但是 LinkedHashMap 課保證遍歷順序是元素插入的順序。WeakHashMap與HashMap的區(qū)別是什么?WeakHashMap 的工作與正常的 HashMap 類似,但是使用弱引用作為 key,意思就是當 key 對象沒有任何引用時,key/value 將會被回收。ArrayList和LinkedList的區(qū)別?最明顯的區(qū)別是 ArrrayList底層的數(shù)據結構是數(shù)組,支持隨機訪問,而
54、LinkedList 的底層數(shù)據結構是雙向循環(huán)鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間復雜度是 O(1),而 LinkedList 是 O(n)。ArrayList和HashMap默認大小?在 Java 7 中,ArrayList 的默認大小是 10 個元素,HashMap 的默認大小是16個元素(必須是2的冪)。這就是 Java 7 中 ArrayList 和 HashMap 類的代碼片段private static final int DEFAULT_CAPACITY = 10; /from HashMap.java JDK 7 static final in
55、t DEFAULT_INITIAL_CAPACITY = 1 << 4; / aka 16Comparator和Comparable的區(qū)別?Comparable 接口用于定義對象的自然順序,而 comparator 通常用于定義用戶定制的順序。Comparable 總是只有一個,但是可以有多個 comparator 來定義對象的順序。如何實現(xiàn)集合排序?你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有順序的的集合,如 list,然后通過 Collections.sort() 來排序。如何打印數(shù)組內容你可以使用 Arrays.toString() 和 Arra
56、ys.deepToString() 方法來打印數(shù)組。由于數(shù)組沒有實現(xiàn) toString() 方法,所以如果將數(shù)組傳遞給 System.out.println() 方法,將無法打印出數(shù)組的內容,但是 Arrays.toString() 可以打印每個元素。LinkedList的是單向鏈表還是雙向?雙向循環(huán)列表,具體實現(xiàn)自行查閱源碼.TreeMap是實現(xiàn)原理采用紅黑樹實現(xiàn),具體實現(xiàn)自行查閱源碼.遍歷ArrayList時如何正確移除一個元素該問題的關鍵在于面試者使用的是 ArrayList 的 remove() 還是 Iterator 的 remove()方法。這有一段示例代碼,是使用正確的方式來實現(xiàn)在遍歷的過程中移除元素,而不會出現(xiàn) ConcurrentModificationException 異常的示例代碼。什么是ArrayMap?它和HashMap有什么區(qū)別?ArrayMap是Android SDK中提供的,非Android開發(fā)者可以略過. ArrayMap是用兩個數(shù)組來模擬map,更少的內存占用空間,更高的效率. 具體參考這篇文章:ArrayMap
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 機械外協(xié)采購管理制度
- 服裝外協(xié)加工管理制度
- 社會團組織管理制度
- 茶葉公司人員管理制度
- 教室幼兒行為管理制度
- 發(fā)現(xiàn)系統(tǒng)集成中的潛在風險試題及答案
- 取消無償贈與合同協(xié)議書
- 衛(wèi)健局人才管理制度
- 企業(yè)控制管理制度
- 收發(fā)登記記錄管理制度
- 日本表參道項目案例分析
- GB/T 17772-2018土方機械保護結構的實驗室鑒定撓曲極限量的規(guī)定
- 腦卒中風險評估(改良的弗明漢卒中量表)老年健康與醫(yī)養(yǎng)結合服務管理
- 渦街流量計技術協(xié)議書
- 09S304 衛(wèi)生設備安裝圖集
- 《弟子規(guī)》謹篇(課件)
- 膝關節(jié)骨性關節(jié)炎的防治課件
- 防蛇蟲咬傷防中暑課件
- 車輛購置稅和車船稅課件
- 國開電大《人員招聘與培訓實務》形考任務4國家開放大學試題答案
- 2023年徐州市泉山區(qū)工會系統(tǒng)招聘考試筆試題庫及答案解析
評論
0/150
提交評論