面试之Java知识整理
1.面向對象都有哪些特性
繼承、封裝、多態性、抽象
2.Java中實現多態的機制是什么?
繼承與接口
3.Java中異常分為哪些種類
3.1按照異常需要處理的時機分為編譯時異常(CheckedException)和運行時異常(RuntimeException)。
3.2對于編譯時異常的處理方法有兩種:
???????? (1)當前方法知道如何處理該異常,則用try…catch塊來處理該異常。
???????? (2)當前方法不知道如何處理,則在定義該方法時聲明拋出該異常。
3.3運行時異常,如果顯式聲明或者捕獲將會對程序的可讀性和運行效率影響很大,所以由系統自動檢測并將它們交給缺省的異常處理程序,當然如果有處理要求也可以顯式捕獲它們。
4.Java的數據類型
4.1Java的基本數據類型都有哪些,各占幾個字節
Byte 1?? char 2? short 2? int 4? float 4? double 8? long 8? boolean 1
4.2ing是基本數據類型嗎?可以被繼承嗎?
String是引用類型,底層用char數組實現的。因為String是final類,在java中被final修飾的類不能被繼承,因此String當然不可以被繼承。
5.Java的IO
???????? 5.1.Java中有幾種類型的流
?????????????????? 字節流和字符流。字節流繼承于InputStream和OutputStream,字符流繼承于InputStreamReader和OutputStreamWriter。
???????? 5.2字節流如何轉為字符流
?????????????????? 字節輸入流轉字符輸入流通過InputStreamReader實現,該類的構造函數可以傳入InputStream對象。
?????????????????? 字節輸出流轉字符輸出流通過OutputSreamWriter實現,該類的構造函數可以傳入OutputStream對象。
6.Java的集合
6.1ArrayList、HashSet、HashMap是線程安全的嗎?如果不是想要的線程安全的集合怎么辦?
每個方法都沒有加鎖,顯然都是線程不安全的。
在集合中Vector和HashTable是線程安全的。
Collections工具類提供了相關的API,可以讓上面3個不安全的集合變為安全,如下:
Collections.synchronizedCollection(c)
Collections.synchronizedList(list)
Collections.synchronizedMap(m)
Collections.synchronizedSet(s)
6.2并發集合和普通集合如何區別?
并發集合常見的有ConCurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。并發集合位于java.util.concurrent包下,是jdk1.5之后才有的,主要作者是Doug Lea(http://baike.baidu.com/view/3141057.http)完成的。
在java中有普通集合、同步(線程安全)的集合、并發集合。普通集合通常性能最高,但是不保證多線程的安全性和并發的可靠性。線程安全集合僅僅是給集合添加了synchronized同步鎖,嚴重犧牲了性能,而且對并發的效率就更低了,并發集合則通過復雜的策略不僅保證了多線程的安全又提高的并發時的效率。
ConcurrentHashMap是線程安全的HashMap的實現,默認構造同樣有initialCapacity和loadFactor屬性,不過還多了一個concurrencyLevel屬性,三屬性默認值分別為16、0.75及16。其內部使用鎖分段技術,維持著鎖Segment的數組,在Segment數組中又存放著Entity[]數組,內部hash算法將數據較均勻分布在不同鎖中。
Put操作:并沒有在此方法上加上synchronized,首先對key.hashcode進行hash操作,得到key的hash值。Hash操作的算法和map不同,根據此hash值計算并獲取其對應的數組中的Segment對象(繼承自ReentrantLock),接著調用此Segment對象的put方法來完成當前操作。
???????? ConcurrentHashMap基于concurrencyLevel劃分出了多個Segment來對key-value進行存儲,從而避免每次put操作都得鎖住整個數組。在默認的情況下,最佳情況下可允許16個線程并發無阻塞的操作集合對象,盡可能地減少并發時的阻塞現象。
???????? Get(key)
???????? 首先對key.hashCode進行hash操作,基于其值找到對應的Segment對象,調用其get方法完成當前操作。而Segment的get操作首先通過hash值和對象數組大小減1的值進行按位與操作來獲取數組上對應位置的HashEntry。在這個步驟中,可能會因為對象數組大小的改變,以及數組上對應位置的HashEntry產生不一致性,那么ConcurrentHashMap是如何保證的?
???????? 對象數組大小的改變只有在put操作時有可能發生,由于HashEntry對象數組對應的變量是volatile類型的,因此可以保證如HashEntry對象數組大小發生改變,讀操作可看到最新的對象數組大小。
???????? 在獲取到了HashEntry對象后,怎么能保證它及其next屬性構成的鏈表上的對象不會改變呢?這點ConcurrentHashMap采用了一個簡單的方式,即HashEntry對象中的hash、key、next屬性都是final的,這也就意味著沒辦法插入一個HashEntry對象到基于next屬性構成的鏈表中間或末尾。這樣就可以保證當獲取到HashEntry對象后,其基于next屬性構建的鏈表是不會發生變化的。
ConcurrentHashMap默認情況下采用將數據分為16個段進行存儲,并且16個段分別持有各自不同的鎖Segment,鎖僅用于put和remove等改變集合對象的操作,基于volatile及HashEntry鏈表的不變性實現了讀取的不加鎖。這些方式使得ConcurrentHashMap能夠保持極好的并發支持,尤其是對于讀取比插入和刪除頻繁的Map而言,而它采用的這些方法也可謂是對于Java內存模型、并發機制深刻掌握的體現。
7.Java的多線程
7.1多線程的兩種創建方式
Java.lang.Thread類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由于線程類本身就是調用的Runnable接口所以可以繼承java.lang.Thread類或者直接實現Runnable接口來重寫run()方法實現線程。
7.2在java中wait和sleep方法的不同?
最大的不同是在等待時wait會釋放鎖,而sleep一直持有鎖。Wait通常被用于線程間交互,sleep通常被用于暫停執行。
7.3synchronized和volatile關鍵字的作用
一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾之后,那么就具備了兩層語義:
(1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。
(2)禁止進行指令重排序。
Volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取;
Synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
(1)volatile僅能使用在變量級別;synchronized則可以使用在變量、方法和類級別的。
(2)volatile僅能實現變量的修改可見性,并不能保證原子性;synchronized則可以保證變量的修改可見性和原子性。
(3)volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
(4)volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化。
7.4什么是線程池,如何使用
線程池就是事先將多個線程對象放到一個容器中,當使用的時候就不用new線程而是直接去池中拿線程即可,節省了開辟子線程的時間,提高了代碼的執行效率。
在JDK的java.util.concurrent.Executors中提供了生成多種線程池的靜態方法:
ExecutorService newCanchedThreadPool = Excutors.newCachedThreadPool();
ExecutorService newFixedThreadPool = Excutors.newFixedThreadPool(4);
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
然后調用他們的execute方法即可。
7.5線程池的理解
說一下線程池如何用、線程池的好處、線程池的啟動策略
合理利用線程池能夠帶來三個好處:
第一:降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
第二:提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配、調優和監控。
7.6線程池的啟動策略
(1)線程池剛創建時,里面沒有一個線程。任務隊列是作為參數傳進來的。不過,就算隊列里面有任務,線程池也不會馬上執行它們。
(2)當調用execute()方法添加一個任務時,線程池會做如下判斷:
???????? a.如果正在執行的線程數量小于corePoolSize,那么馬上創建線程運行這個任務;
???????? b.如果正在執行的線程數量大于或等于corePoolSize,那么將這個任務放入隊列;
???????? c.如果這時候隊列滿了,而且正在執行的線程數量小于maximumPoolSize,那么還是要創建線程運行這個任務;
???????? d.如果隊列滿了,而且正在運行的線程數量大于或等于maximumPoolSize,那么線程池會拋出異常,告訴調用者“不能再接受任務了”。
(3)當一個線程完成任務時,它會從隊列中取下一個任務來執行。
(4)當一個線程無事可做,超過一定的時間(keyAliveTime)時,線程池會判斷,如果當前運行的線程數大于corePoolSize,那么這個線程就被停掉。所以線程池的所有任務完成后,它最終會收縮到coorPoolSize的大小。
7.7如何控制某個方法允許并發訪問線程的大小?
可以使用Semaphore控制,就是信號,初始化n個信號,在線程中,運行semaphore.acquire()申請請求,這樣最多只能有n個線程并發訪問,多余n個線程時就排隊等待。線程完成后釋放信號,這樣新的線程就可以使用了。
8.Java中的反射
8.1Java中反射的理解
Java中的反射首先是能夠獲取到Java中要反射類的字節碼,獲取字節碼有三種方法,1.Class.forName(className)2.類名.Class3.this.getClass()。然后將字節碼中的方法、變量、構造函數等映射成相應的Method、Filed、Contructor等類,這些類提供了豐富的方法可以被使用。
8.2動靜態代理的區別,什么場景使用?
靜態代理通常只代理一個類,動態代理是代理一個接口下的多個實現類。
靜態代理事先知道要代理的是什么,而動態代理不知道要代理什么東西,只有在運行時才知道。
動態代理是顯示JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是業務類必須要實現接口,通過Proxy里的newProxyInstance得到代理對象。
還有一種動態代理CGLIB,代理的是類,不需要業務類繼承接口,通過派生的子類來實現代理。通過在運行時,動態修改字節碼達到修改類的目的。
AOP編程就是基于動態代理實現的,比如著名的Spring框架、Hibernate框架等等都是動態代理的使用例子。
9.Java中的回收機制
9.1.Java垃圾回收機制和常見算法
Sun公司只定義了垃圾回收機制規則而不局限于其實現算法,因此不同廠商生產的虛擬機采用的算法也不盡相同。
GC(Garbage Collector)在回收對象前首先必須發現那些無用的對象,如何去發現定位這些無用的對象?通常的搜素算法如下:
(1)引用計數器算法(廢棄)
???????? 引用計數器算法是給每個對象設置一個計數器,當有地方引用這個對象的時候,計數器+1,當引用失效的時候,計算器-1,當計數器為0的時候,JVM就認為對象不再被使用,是“垃圾”了。
???????? 引用計數器實現簡單,效率高;但是不能解決循環引用問題(A對象引用B對象,B對象引用A對象,但是A,B對象已不再被任何其他對象引用),同時每次計數器的增加和減少都帶來了很多額外的開銷,所以在JDK1.1之后,這個算法就不再使用了。
(2)根搜索算法(使用)
根搜素算法是通過一些“GC Roots”對象作為起點,從這些節點開始往下搜索,搜索通過的路徑成為引用鏈(Reference Chain),當一個對象沒有被GC Roots的引用鏈連接的時候,說明這個對象是不可用的。
GC Roots對象包括:
a.虛擬機棧(棧幀中的本地變量表)中的引用的對象。
b.方法區域中的類靜態屬性引用的對象。
c.方法區域中常量引用的對象。
d.本地方法棧中JNI(Native方法)的引用的對象。
通過上面的算法搜索到無用對象之后,就是回收過程,回收算法如下:
(1)標記-清除算法(Mark-Sweep)(DVM使用的算法)
標記-清除算法包括連個階段:“標記”和“清除”。在標記階段,確定所有要回收的對象,并做標記。清除階段緊隨標記階段,將標記階段確定不可用的對象清除。標記-清除算法是基礎的收集算法,標記和清除階段的效率不高,而且清楚后回產生大量的不連續空間,這樣當程序需要分配大內存對象時,可能無法找到足夠的連續空間。
(2)復制算法(Copying)
復制算法是把內存分成大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的對象復制到另一塊上,然后把這塊內存整個清理掉。復制算法實現簡單,運行效率高,但是由于每次只能使用其中的一半,造成內存的利用率不高。現在的JVM用復制方法收集新生代,由于新生代中大部分對象(98%)都是朝生夕死的,所以兩塊內存的比例不是1:1(大概是8:1)。
(3)標記-整理算法(Mark-Compact)
標記-整理算法和標記-清除算法一樣,但是標記-整理算法不是把存活對象復制到另一塊內存,而是把存活對象往內存的一端移動,然后直接回收邊界以外的內存。標記-整理算法提高了內存的利用率,并且它適合在收集對象存活時間較長的老年代。
(4)分代收集(Generational Collection)
分代收集是根據對象的存活時間把內存分為新生代和老生代,根據各個代對象的存活特點,每個代采用不同的垃圾回收算法。新生代采用復制算法,老生代采用標記-整理算法。垃圾算法的實現涉及大量的程序細節,而且不同的虛擬機平臺實現的方法也各不相同。
9.2.JVM的內存結構和內存分配
(1)java內存模型
Java虛擬機將其管轄的內存大致分三個邏輯部分:方法區(Method Area)、Java棧和Java堆。
a.方法區是靜態分配的,編譯器將變量綁定在某個存儲位置上,而且這些綁定不會再運行時改變。常數池,源代碼中的命名常量、String常量和static變量保存在方法區。
b.Java Stack是一個邏輯概念,特點是后進先出。一個棧的空間可能是連續的,也可能是不連續的。最典型的Stack應用是方法的調用,Java虛擬機每調用一次方法就創建一個方法幀(frame),退出該方法則對應的方法幀被彈出(pop)。棧中存儲的數據也是運行時確定的。
c.Java堆分配(heap allocation)意味著以隨意的順序,在運行時進行存儲空間分配和回收的內存管理模型。堆中存儲的數據常常是大小、數量和生命期在編譯時無法確定的。Java對象的內存總是在heap中分配。
(2)Java內存分配
a.基礎數據類型直接在棧空間分配。
b.方法的形式參數,直接在棧空間分配,當方法調用完成后從棧空間回收。
c.引用數據類型,需要用new來創建,即在棧空間分配一個地址空間,又在堆空間分配對象的類變量。
d.方法的引用參數,在棧空間分配一個地址空間,并指向堆空間的對象區,當方法調用完后從棧空間回收。
e.局部變量new出來時,在棧空間和堆空間中分配空間,當局部變量生命周期結束后,棧空間立即被回收,堆空間區域等待GC回收。
f.方法調用時傳入的實際參數,現在棧空間分配,在方法調用完成后從棧空間釋放。
g.字符串常量在DATA區域分配,this在堆空間分配。
h.數組即在棧空間分配數組名稱,又在堆空間分配數組實際的大小。
9.3.Java中引用類型都有哪些?
Java中對象的引用分為四種級別,這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。
(1)強引用
如果一個對象被人擁有強引用,那么垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象阿里解決內存不足問題。
Java的對象是位于heap中的,heap中對象有強可及對象、軟可及對象、弱可及對象、虛可及對象和不可到達對象。應用的強弱順序是強、軟、弱和虛。對于對象是屬于哪種可及的對象,由他的最強的引用決定。
String abc = new String(“abc”);//強引用,abc為強可及
SoftReference<String> softRef = new SoftReference<String>(abc);//軟引用
WeakReference<String> weakRef = new WeakReference<String>(abc);//弱引用
abc=null;//abc軟可及
softRef.clear();//abc變成弱可及
(2)軟引用
如果一個對象只具有軟引用,那么如果內存空間足夠,垃圾回收器就不會回收它,如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。
軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
軟引用主要用于內存敏感的高速緩存。在jvm報告內存不足之前會清除所有的軟引用,這樣以來gc就有可能手機軟可及的對象,可能解決內存吃緊問題,避免內存溢出。什么時候會被收集取決于gc的算法和gc運行時可用內存的大小。當gc決定要收集軟引用時執行步驟如下:(以上面的softRef為例)
a.首先將softRef的referent(abc)設置為null,不再引用heap中的new String(“abc”)對象。
b.將heap中的new String(“abc”)對象設置為可結束的(finalizable)。
c.當heap中的new String(“abc”)對象的finalize()方法被運行而且該對象占用的內存被釋放,softRef被添加到它的ReferenceQueue(如果有的話)中。
注意:對ReferenceQueue軟引用和弱引用可有可無,但虛引用必須有。
被Soft Reference指到的對象,即使沒有任何Direct Reference,也不會被清除。一直要到JVM內存不足且沒有Direct Reference時才會清除,SoftReference是用來設計objct-cache之用的。如此一來SoftReference不但可以把對象cache起來,也不會造成內存不足的錯誤(OutOfMemoryError)。
(3)弱引用
如果一個對象只具有弱引用,那該類就是可有可無的對象,因為只要該對象被gc掃描到隨時都會把它干掉。
弱引用和軟引用的區別:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由于垃圾回收器是一個優先級很低的線程,因此不一定會很快發現只具有弱引用的對象。
弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象唄垃圾回收器回收,Java虛擬機就會把這個弱引用加到與之關聯的引用隊列中。
(4)虛引用
“虛引用”與其他集中引用不同,虛引用并不決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
虛引用與軟引用和弱引用的區別:虛引用必須和引用隊列(ReferneceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還是虛引用,就會在回收對象的內存之前,把這個虛引用假如到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否加入了虛引用。來了解被引用的對象是否要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那么就可以在引用的對象的內存被回收之前采用必要的行動。
建立虛引用之后通過get方法返回結果始終為null,通過源代碼會發現,虛引用通常會把引用的對象寫進referent,只是get方法返回結果為null。和gc交互的過程:a.不把referent設置為null,直接把heap中的new String(“abc”)對象設置為可結束的(finalizable)。b.與軟引用和弱引用不同,先把PhantomReference對象添加到它的ReferencQueue中,然后在釋放虛可及的對象。
10.Java的類加載器
10.1.Java的類加載器的種類都有哪些?
(1)根類加載器(Bootstrap)---C++寫的,看不到源碼
(2)擴展類加載器(Extension)---加載位置:jre\lib\ext中
(3)系統(應用)類加載器(System\App)---加載位置:classpath中
(4)自定義加載器(必須繼承ClassLoader)
10.2.類什么時候被初始化?
(1)創建類的實例,也就是new一個對象。
(2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值。
(3)調用類的靜態方法。
(4)反射。
(5)初始化一個類的子類。
(6)JVM啟動時標明的啟動類,即文件名和類名相同的那個類。
只有這6種情況才會導致類的初始化。
10.3.類的初始化步驟
(1)如果這個類還沒有被加載和鏈接,那先進行加載和鏈接。
(2)假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中,類智能初始化一次),那就初始化直接的父類(不適用于接口)。
(3)假如類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。
轉載于:https://www.cnblogs.com/zhangmiao14/p/6385854.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的面试之Java知识整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos 7 密码破解 rm -r
- 下一篇: (OPC Client .NET 开发类