本文收集了一些Java常見面試題,希望能幫助大家搞定面試。
Java中 == , equals()和 hashCode() 的區(qū)別
== 在比較基本數據類型時比較的是值雌澄,而在比較引用類型的數據時比較的是引用的地址培慌,即比較兩個引用是否指向同一個對象腊尚。
equal() 是java.lang.Object的方法略荡,默認與 == 比較方式相同庵佣,可以被程序員重寫該方法實現自定義比較方式。
hashCode() 方法給對象返回一個hashcode值汛兜。當兩個對象相等時,hashcode一定相同通今,但是反過來不一定成立粥谬。
String、StringBuffer辫塌、StringBuilder區(qū)別
String對象一旦創(chuàng)建不可更改漏策,對String的任何操作都不會影響原對象。
StringBuffer與StringBuilder存儲的內容是可以更改的臼氨。
StringBuilder是線程不安全的掺喻,StringBuffer帶有synchronized關鍵字是線程安全的。
什么是內部類储矩?內部類的作用
定義在一個類的內部的類我們就叫內部類感耙。內部類可以很好的實現隱藏。一般的非內部類持隧,是不允許有 private 與protected權限的即硼,但內部類可以。內部類擁有外圍類的所有元素的訪問權限屡拨,可以實現多重繼承只酥,可以避免修改接口而實現同一個類中兩種同名方法的調用褥实。
抽象類和接口區(qū)別
1)抽象類是單一繼承,接口是多重實現裂允。 Java中子類只能有一個父類损离,而子類可以實現多個接口。
2)繼承抽象類表示“從屬”關系绝编,實現接口表示“組合”關系
3)接口中全是抽象方法草冈,抽象類中可以有抽象方法,也可有方法體的方法瓮增。
4)接口中無構造方法怎棱,不可繼承,可實現绷跑;抽象類可有構造方法拳恋,不可被實例化。
5)抽象類的抽象方法不能使用private砸捏,final谬运,static修飾,方法不能用private垦藏,final修飾梆暖。接口的屬性默認是用public,static掂骏,final修飾轰驳,接口中方法是默認用public,abstract修飾弟灼。
抽象類的意義
在面向對象方法中级解,抽象類主要用來進行類型隱藏。構造出一個固定的一組行為的抽象描述田绑,但是這組行為卻能夠有任意個可能的具體實現方式勤哗。
抽象類與接口的應用場景
抽象類的應用場合
1)定義了一組接口,但又不想強迫每個實現類都必須實現所有的接口掩驱∶⒒可以用abstract class定義一組方法體,甚至可以是空方法體欧穴,然后由子類選擇自己所感興趣的方法來覆蓋民逼。
2) 某些場合下,只靠純粹的接口不能滿足類與類之間的協(xié)調苔可,還必需類中表示狀態(tài)的變量來區(qū)別不同的關系缴挖。abstract的中介作用可以很好地滿足這一點。
3) 規(guī)范了一組相互協(xié)調的方法焚辅,其中一些方法是共同的映屋,與狀態(tài)無關的苟鸯,可以共享的,無需子類分別實現棚点;而另一些方法卻需要各個子類根據自己特定的狀態(tài)來實現特定的功能早处。
接口的應用場合
1)類與類之前需要特定的接口進行協(xié)調,而不在乎其如何實現瘫析。
2)作為能夠實現特定功能的標識存在砌梆,也可以是什么接口方法都沒有的純粹標識。
3) 需要將一組類視為單一的類贬循,而調用者只通過接口來與這組類發(fā)生聯系咸包。
4) 需要實現特定的多項功能,而這些功能之間可能完全沒有任何聯系杖虾。
抽象類是否可以沒有方法和屬性烂瘫?
抽象類即可以沒有方法也可以沒有屬性。
泛型中extends和super的區(qū)別
<? extends T>限定參數類型的上界:參數類型必須是T或T的子類型
<? super T> 限定參數類型的下界:參數類型必須是T或T的超類型
父類的靜態(tài)方法能否被子類重寫
父類的靜態(tài)方法可以被子類繼承奇适,但是不能重寫坟比。
final,finally嚷往,finalize的區(qū)別
final 修飾對象不可改變葛账,修飾類時不可繼承。
finally通常和try catch搭配使用皮仁,保證不管有沒有發(fā)生異常籍琳,資源都能夠被釋放(釋放連接、關閉IO流)魂贬。
finalize是Object類中的一個方法巩割,子類可以重寫finalize()方法實現對資源的回收。
靜態(tài)屬性和靜態(tài)方法是否可以被繼承付燥?是否可以被重寫?
Java中靜態(tài)屬性和方法可以被繼承愈犹,但是不能被重寫键科。
哪些情況下的對象會被垃圾回收機制處理掉?
1)該對象的最后一個引用指向了另一個對象或null漩怎;
2)該對象的最后一個引用的作用域結束勋颖。
靜態(tài)代理和動態(tài)代理的區(qū)別
靜態(tài)代理:由程序員創(chuàng)建或由特定工具自動生成源代碼,再對其編譯勋锤。在程序運行前饭玲,代理類的.class文件就已經存在了。
動態(tài)代理:在程序運行時叁执,運用反射機制動態(tài)創(chuàng)建而成茄厘。
Java中實現多態(tài)如何實現矮冬?
Java提供了編譯時多態(tài)和運行時多態(tài)兩種多態(tài)機制。前者是通過方法重載實現的次哈,后者是通過方法的覆蓋實現的胎署。
說說你對Java反射的理解
Java反射機制是在運行狀態(tài)中,對于任意一個類窑滞,都能夠知道這個類的所有屬性和方法琼牧。對于任意一個對象,都能夠調用它的任意一個方法和屬性。這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為Java語言的反射機制。
說說你對依賴注入的理解
依賴注入也稱控制反轉党窜。當某個角色(可能是一個Java實例剔猿,調用者)需要另一個角色(另一個Java實例,被調用者)的協(xié)助時焕刮,在傳統(tǒng)的程序設計過程中,通常由調用者來創(chuàng)建被調用者的實例。但在Spring里鼓寺,創(chuàng)建被調用者的工作不再由調用者來完成,因此稱為控制反轉勋磕。創(chuàng)建被調用者實例的工作通常由Spring容器來完成妈候,然后注入調用者,因此也稱為依賴注入挂滓。
String為什么要設計成不可變的苦银?
1)字符串池(String pool)的需求: 在Java中,當初始化一個字符串變量時赶站,如果字符串已經存在幔虏,就不會創(chuàng)建一個新的字符串變量,而是返回存在字符串的引用贝椿。
2)緩存字符串hashcode碼的需要: 字符串的hashcode是經常被使用的想括,字符串的不變性確保了hashcode的值一直是一樣的,在需要hashcode時烙博,就不需要每次都計算瑟蜈,這樣會很高效。
3)出于安全性考慮: 字符串經常作為網絡連接渣窜、數據庫連接等參數铺根,不可變就可以保證連接的安全性。
Java中Set與List有什么不同?
Set不允許重復元素存在乔宿,List允許位迂。
Set沒有索引,List有索引。
Set僅僅允許一個null值掂林,List允許多個null值
為什么Map接口不繼承Collection 接口臣缀?
Set是無序集合,并且不允許重復的元素党饮。
List是有序的集合肝陪,并且允許重復的元素。
而Map是鍵值對刑顺,它被視為是鍵的set和值的set的組合氯窍。
Map被設計為鍵值對的集合,所以不需要繼承Collection 接口蹲堂。
Comparable和Comparator的不同之處狼讨?
Comparable和Comparator接口被用來對對象集合或者數組進行排序。
Comparable接口被用來提供對象的自然排序柒竞,我們可以使用它來提供基于單個邏輯的排序政供。
Comparator接口被用來提供不同的排序算法,我們可以選擇需要使用的Comparator來對給定的對象集合進行排序朽基。
為什么Collection不能繼承Cloneable和Serializable布隔?
Collection表示一個集合,包含了一組對象稼虎。如何存儲和維護這些對象是由具體實現來決定的衅檀。因為集合的具體形式多種多樣,例如list允許重復霎俩,set則不允許哀军。而克隆(clone)和序列化(serializable)只對于具體的實體打却,對象有意義杉适,不能去把一個接口,抽象類克隆柳击,序列化甚至反序列化猿推。所以具體的collection實現類是否可以克隆,是否可以序列化應該由其自身決定捌肴,而不能由其超類強行賦予彤守。
如果collection繼承了clone和serializable,那么所有的集合實現都會實現這兩個接口哭靖,而如果某個實現它不需要被克隆,甚至不允許它序列化(序列化有風險)侈离,那么就與collection矛盾了试幽。
List和Map的實現方式以及存儲方式
List使用可變長數組實現方式;
Map使用數組加鏈表的數據結構實現。
能否使用任何類作為Map的key铺坞?
可以起宽,然而在使用它們之前,需要考慮以下幾點:
1)如果類重寫了equals()方法济榨,它也應該重寫hashCode()方法坯沪。
2)類的所有實例需要遵循與equals()和hashCode()相關的規(guī)則。請參考之前提到的這些規(guī)則擒滑。
3)如果一個類沒有使用equals()腐晾,你不應該在hashCode()中使用它。
4)用戶自定義key類的最佳實踐是使之為不可變的丐一,這樣藻糖,hashCode()值可以被緩存起來,擁有更好的性能库车。不可變的類也可以確保hashCode()和equals()在未來不會改變巨柒,這樣就會解決與可變相關的問題了。
HashMap的問題
關于HashMap的面試問題比較集中柠衍。推薦一篇博客https://www.cnblogs.com/chengxiao/p/6059914.html對于HashMap做了較為詳細的介紹洋满。不過想要更深入理解HashMap最好還是親自查看源碼。
是否可以往TreeSet或者HashSet中添加null元素珍坊?
可以往HashSet中添加一個null元素牺勾。
TreeSet也允許一個null值
ArrayMap和HashMap的對比
1)存儲方式不同
HashMap內部有一個HashMapEntry<K, V>[]對象,每一個鍵值對都存儲在這個對象里垫蛆,當使用put方法添加鍵值對時禽最,就會new一個HashMapEntry對象。
2)添加數據時擴容時的處理不一樣袱饭。HashMap重新創(chuàng)建對象川无,開銷很大。ArrayMap用的是copy數據虑乖,所以效率相對要高懦趋。
3)ArrayMap提供了數組收縮的功能,在clear或remove后疹味,會重新收縮數組仅叫,釋放空間。
HashMap和HashTable的區(qū)別
1)HashMap允許key和value為null糙捺,而HashTable不允許诫咱。
2)HashTable是同步的,而HashMap不是洪灯。所以HashMap適合單線程環(huán)境坎缭,HashTable適合多線程環(huán)境。
3)在Java1.4中引入了LinkedHashMap,HashMap的一個子類掏呼,假如你想要遍歷順序坏快,你很容易從HashMap轉向LinkedHashMap,但是HashTable不是這樣的憎夷,它的順序是不可預知的莽鸿。
4)HashMap提供對key的Set進行遍歷,因此它是fail-fast的拾给,但HashTable提供對key的Enumeration進行遍歷祥得,它不支持fail-fast。
HashMap與HashSet的區(qū)別
1)HashMap實現了Map接口鸣戴,HashSet實現了Set接口啃沪。
2)HashMap儲存鍵值對,HashSet僅僅存儲對象使用put()方法將元素放入map中窄锅,使用add()方法將元素放入set中创千。
3)HashMap中使用鍵對象來計算hashcode值。HashSet使用成員對象來計算hashcode值入偷。
4)HashMap比較快追驴,因為是使用唯一的鍵來獲取對象HashSet較HashMap來說比較慢。
BlockingQueue是什么疏之?
BlockingQueue是一個隊列殿雪,在進行檢索或移除一個元素的時候,它會等待隊列變?yōu)榉强辗孀Γ划斣谔砑右粋€元素時丙曙,它會等待隊列中的可用空間。BlockingQueue接口是Java集合框架的一部分其骄,主要用于實現生產者-消費者模式亏镰。我們不需要擔心等待生產者有可用的空間,或消費者有可用的對象拯爽,因為它都在BlockingQueue的實現類中被處理了索抓。
什么是深拷貝和淺拷貝
淺拷貝:
淺拷貝又叫淺復制,將對象中的所有字段復制到新的對象(副本)中毯炮。其中逼肯,值類型字段(java中8中原始類型)的值被復制到副本中后,在副本中的修改不會影響到源對象對應的值桃煎。而引用類型的字段被復制到副本中的還是引用類型的引用篮幢,而不是引用的對象,在副本中對引用類型的字段值做修改會影響到源對象本身为迈。
深拷貝:
深拷貝將對象中的所有字段復制到新的對象中洲拇。不過奈揍,無論是對象的值類型字段,還是引用類型字段赋续,都會被重新創(chuàng)建并賦值,對于副本的修改另患,不會影響到源對象本身纽乱。
開啟線程的三種方式?
1.繼承Thread類
2.實現Runnable接口
3.直接在函數體使用(匿名內部類)昆箕。
線程和進程
進程:
進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動鸦列,進程是系統(tǒng)進行資源分配和調度的一個獨立單位。
線程:
線程是進程的一個實體鹏倘,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位薯嗤。
run()和start()方法區(qū)別
start():
start()方法的作用是啟動一個新線程,新線程會執(zhí)行相應的run()方法纤泵。start()不能被重復調用骆姐。
run():
run()就和普通的成員方法一樣,可以被重復調用捏题。單獨調用run()的話玻褪,會在當前線程中執(zhí)行run(),而并不會啟動新線程公荧。
如何控制某個方法允許并發(fā)訪問線程的個數
使用Semaphore信號量控制數目带射。
Runnable接口和Callable接口的區(qū)別
Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已循狰。
Callable接口中的call()方法是有返回值的窟社,是一個泛型,和Future绪钥、FutureTask配合可以用來獲取異步執(zhí)行的結果灿里。
在Java中wait()和sleep()方法的不同
1)調用sleep()方法正在執(zhí)行的線程主動讓出CPU,并不會釋放同步資源鎖昧识;wait()方法則是指當前線程讓自己暫時退讓出同步資源鎖钠四,以便其他正在等待該資源的線程得到該資源進而運行,只有調用了notify()方法跪楞,之前調用wait()的線程才會解除wait狀態(tài)缀去,可以去參與競爭同步資源鎖,進而得到執(zhí)行甸祭;
2) sleep()方法可以在任何地方使用缕碎;wait()方法則只能在同步方法或同步塊中使用;
什么導致線程阻塞池户?
1)線程執(zhí)行了Thread.sleep(int millsecond);方法咏雌,當前線程放棄CPU凡怎,睡眠一段時間,然后再恢復執(zhí)行赊抖。
2)線程執(zhí)行一段同步代碼统倒,但是尚且無法獲得相關的同步鎖,只能進入阻塞狀態(tài)氛雪,等到獲取了同步鎖房匆,才能回復執(zhí)行。
3)線程執(zhí)行了一個對象的wait()方法报亩,直接進入阻塞狀態(tài)浴鸿,等待其他線程執(zhí)行notify()或者notifyAll()方法。
4)線程執(zhí)行某些IO操作弦追,因為等待相關的資源而進入了阻塞狀態(tài)岳链。比如說監(jiān)聽system.in,但是尚且沒有收到鍵盤的輸入劲件,則進入阻塞狀態(tài)掸哑。
線程如何關閉?
1)使用退出標志寇仓,使線程正常退出举户,也就是當run方法完成后線程終止。
2)使用stop方法強行終止線程(這個方法不推薦使用遍烦,因為stop和suspend俭嘁、resume一樣,也可能發(fā)生不可預料的結果)服猪。
3)使用interrupt方法中斷線程供填。
同步方法有哪些
1)同步方法:即有synchronized關鍵字修飾的方法。由于java的每個對象都有一個內置鎖罢猪,當用此關鍵字修飾方法時近她,內置鎖會保護整個方法。在調用該方法前膳帕,需要獲得內置鎖粘捎,否則就處于阻塞狀態(tài)。
2)同步代碼塊:即有synchronized關鍵字修飾的語句塊危彩。被該關鍵字修飾的語句塊會自動被加上內置鎖攒磨,從而實現同步。
3)wait與notify方法保證線程同步汤徽。
4)使用volatile:volatile關鍵字為域變量的訪問提供了一種免鎖機制娩缰,使用volatile修飾域相當于告訴虛擬機該域可能會被其他線程更新。
5)使用重入鎖實現線程同步:ReentrantLock類是可重入谒府、互斥拼坎、實現了Lock接口的鎖浮毯, 它與使用synchronized方法和快具有相同的基本行為和語義,并且擴展了其能力泰鸡。
6)使用局部變量實現線程同步:使用ThreadLocal管理變量债蓝,則每一個使用該變量的線程都獲得該變量的副本,副本之間相互獨立鸟顺,這樣每一個線程都可以隨意修改自己的變量副本惦蚊,而不會對其他線程產生影響。
7)使用阻塞隊列實現線程同步:JavaSE5.0版本中新增的java.util.concurrent包將有助于簡化開發(fā)讯嫂。
synchronized和volatile關鍵字的區(qū)別
1)volatile本質是在告訴jvm當前變量在寄存器中的值是不確定的,需要從主存中讀取兆沙,synchronized則是鎖定當前變量欧芽,只有當前線程可以訪問該變量,其他線程被阻塞住。
2)volatile僅能使用在變量級別葛圃,synchronized則可以使用在變量千扔,方法。
3)volatile僅能實現變量的修改可見性库正,而synchronized則可以保證變量的修改可見性和原子性曲楚。
4)volatile不會造成線程的阻塞,而synchronized可能會造成線程的阻塞褥符。
synchronized與Lock的區(qū)別
1)同步代碼塊其實自身是具有自動上鎖龙誊、自動解鎖功能的。Lock鎖機制則是手動解鎖喷楣,手動上鎖的
2)用synchronized修飾的同步代碼塊還有同步方法是有同步鎖對象的趟大。Lock鎖機制是沒有同步鎖對象的。
3)因為synchronized修飾的同步代碼塊還有同步方法是具有鎖對象的铣焊,因此逊朽,可以調用notify()、wait()曲伊、notifyAll()的方法叽讳。但是因為Lock鎖機制是不具有鎖對象的,因此是不可以去調用notify()坟募、wait()岛蚤、notifyAll()方法的,否則會發(fā)生報錯婿屹。
產生死鎖的原因
1)因為系統(tǒng)資源不足灭美。
2)進程運行推進的順序不合適。
3)資源分配不當等昂利。
產生死鎖的四個必要條件:
1)互斥條件:一個資源每次只能被一個進程使用届腐。
2)請求與保持條件:一個進程因請求資源而阻塞時铁坎,對已獲得的資源保持不放。
3)不剝奪條件:進程已獲得的資源犁苏,在末使用完之前硬萍,不能強行剝奪。
4)循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關系围详。
怎么避免死鎖朴乖?
1)死鎖檢測和恢復
2)死鎖預防
3)死鎖避免
多線程與線程池
如何保證多線程讀寫文件的安全?
寫線程中需要獲取獨占鎖助赞。
讀線程中不需要做任何特殊的處理买羞。