001java面试笔记——【java基础篇】从团800失败面试总结的java面试题
?第一章:Java基礎(chǔ)篇
1、談?wù)勀銓ava的認(rèn)識
? ? ?這個問題很大,很抽象,要回答好確實不容易。宏觀上面來說,從C語言面向過程到C++面向?qū)ο蟮絡(luò)ava語言純面向?qū)ο筮@一發(fā)展過程都是為了提高公用性、重用性、可讀性,降低耦合性。java程序是對象的集合,是一系列帶有方法的對象組合,這些方法以其他對象為參數(shù),并發(fā)送消息給其他對象。這樣由于java中的對象是由狀態(tài)、行為和標(biāo)識組成。狀態(tài)可以認(rèn)為是對象存在的具體值;行為可認(rèn)為是對象所能做的操作;而標(biāo)識是對象在內(nèi)存中的唯一地址。通常在程序中只能看到對象的前兩個屬性。在java中所有的代碼都必須寫在class里,而每個對象都是某個類(class)的一個實例(instance),也就是說java中的所有操作都是由對象完成的。
? ?java有許多優(yōu)良的特性,使得Java應(yīng)用具有無比的健壯性和可靠性,這也減少了應(yīng)用系統(tǒng)的維護(hù)費(fèi)用。Java對對象技術(shù)的全面支持和Java平臺內(nèi)嵌的API能縮短應(yīng)用系統(tǒng)的開發(fā)時間并降低成本。Java的編譯一次,到處可運(yùn)行的特性使得它能夠提供一個隨處可用的開放結(jié)構(gòu)和在多平臺之間傳遞信息的低成本方式。java的優(yōu)良特性說明如下:
?1)簡單。Java丟棄了C++ 中很少使用的、很難理解的、令人迷惑的那些特性,如操作符重載、多繼承、自動的強(qiáng)制類型轉(zhuǎn)換。特別是,Java語言不使用指針,并提供了自動的垃圾回收機(jī)制,使得程序員不必為內(nèi)存管理而擔(dān)憂。
?2)面向?qū)ο?/span>。Java語言提供類、接口和繼承等原語,為了簡單起見,只支持類之間的單繼承,但支持接口之間的多繼承,并支持類與接口之間的實現(xiàn)機(jī)制。Java語言全面支持動態(tài)綁定,而C++ 語言只對虛函數(shù)使用動態(tài)綁定。總之,Java語言是一個純的面向?qū)ο蟪绦蛟O(shè)計語言。
?3)分布式。Java語言支持Internet應(yīng)用的開發(fā),在基本的Java應(yīng)用編程接口中有一個網(wǎng)絡(luò)應(yīng)用編程接口(java.net),它提供了用于網(wǎng)絡(luò)應(yīng)用編程的類庫,包括URL、URLConnection、Socket、 ServerSocket等。Java的RMI(遠(yuǎn)程方法激活)機(jī)制也是開發(fā)分布式應(yīng)用的重要手段。
?4)健壯。Java的強(qiáng)類型機(jī)制、異常處理、廢料的自動收集等是Java程序健壯性的重要保證,Java的安全檢查機(jī)制使得Java更具健壯性。
?5)安全。Java通常被用在網(wǎng)絡(luò)環(huán)境中,為此,Java提供了一個安全機(jī)制以防惡意代碼的攻擊。除了Java語言具有的許多安全特性以外,Java對通過網(wǎng)絡(luò)下載的類具有一個安全防范機(jī)制(類ClassLoader),如分配不同的名字空間以防替代本地的同名類、字節(jié)代碼檢查,并提供安全管理機(jī)制(類SecurityManager)讓Java應(yīng)用設(shè)置安全哨兵。
?6)體系結(jié)構(gòu)中立。Java程序在Java平臺上被編譯為體系結(jié)構(gòu)中立的字節(jié)碼格式, 然后可以在實現(xiàn)這個Java平臺的任何系統(tǒng)中運(yùn)行。這種途徑適合于異構(gòu)的網(wǎng)絡(luò)環(huán)境和軟件的分發(fā)。?
?7)可移植性。任意一個JAVA程序,不論它運(yùn)行在何種CPU、操作系統(tǒng)或JAVA編譯器上,都將產(chǎn)生同樣的結(jié)果。人們使用C、C++也可以產(chǎn)生同樣的效果。但C或C++在許多細(xì)節(jié)上它都沒有嚴(yán)格定義,如:未初始化變量的值、對已釋放的內(nèi)存的存取、浮點運(yùn)算的尾數(shù)值等等。所以除非你一開始就嚴(yán)格按照系統(tǒng)無關(guān)的概念來進(jìn)行設(shè)計,否則這種可移植性只能是一種理論上的設(shè)想而不能形成實踐。而JAVA定義了嚴(yán)密的語意結(jié)構(gòu),它的特性能夠減小在不同平臺上運(yùn)行的JAVA程序之間的差異,也使得JAVA具有即使沒有JAVA虛擬機(jī)的存在的情況下比C和C++更好的平臺無關(guān)性。
?8)解釋型。Java程序在Java平臺上被編譯為字節(jié)碼格式, 然后可以在實現(xiàn)這個Java平臺的任何系統(tǒng)中運(yùn)行。在運(yùn)行時,Java平臺中的Java解釋器對這些字節(jié)碼進(jìn)行解釋執(zhí)行,執(zhí)行過程中需要的類在聯(lián)接階段被載入到運(yùn)行環(huán)境中。
?9)高性能。與那些解釋型的高級腳本語言相比,Java的確是高性能的。事實上,Java的運(yùn)行速度隨著JIT(Just-In-Time)編譯器技術(shù)的發(fā)展越來越接近于C++。?
? 10)多態(tài)。Java語言的設(shè)計目標(biāo)之一是適應(yīng)于動態(tài)變化的環(huán)境。Java程序需要的類能夠動態(tài)地被載入到運(yùn)行環(huán)境,也可以通過網(wǎng)絡(luò)來載入所需要的類。這也有利于軟件的升級。另外,Java中的類有一個運(yùn)行時刻的表示,能進(jìn)行運(yùn)行時刻的類型檢查。
? ?注:在回答這個問題時,應(yīng)該先從宏觀方面說出java面向?qū)ο蟮奶攸c,然后從java特性展開說明。其中1)、2)、3)、4)、5)能說出來最好,其他幾點相對來說無關(guān)緊要。
2、抽象類與接口
? ?抽象類:抽象類往往用來表征我們在對問題領(lǐng)域進(jìn)行分析、設(shè)計中得出的抽象概念,是對一系列看上去不同,但是本質(zhì)上相同的具體概念的抽象,我們不能把它們實例化(拿不出一個具體的東西)所以稱之為抽象。在面向?qū)ο箢I(lǐng)域,抽象類主要用來進(jìn)行類型隱藏。我們可以構(gòu)造出一個固定的一組行為的抽象描述,但是這組行為卻能夠有任意個可能的具體實現(xiàn)方式。這個抽象描述就是抽象類,而這一組任意個可能的具體實現(xiàn)則表現(xiàn)為這個抽象類的所有派生類。
? ?接口:Java中的接口是一系列方法的聲明,是一些方法特征的集合,一個接口只有方法的特征沒有方法的實現(xiàn),因此這些方法可以在不同的地方被不同的類實現(xiàn),而這些實現(xiàn)可以具有不同的行為(功能)。
? ? ?區(qū)別:1)從設(shè)計理念層面上:abstract class在Java語言中體現(xiàn)了一種繼承關(guān)系,要想使得繼承關(guān)系合理,父類和派生類之間必須存在"is-a"關(guān)系,即父類和派生類在概念本質(zhì)上應(yīng)該是相同的。對于interface來說則不然,并不要求interface的實現(xiàn)者和interface定義在概念本質(zhì)上是一致的,僅僅是實現(xiàn)了interface定義的契約而已,接口與實現(xiàn)類 之間的關(guān)系是"like-a"的關(guān)系。(舉例:class AlarmDoor extends Door implements Alarm);2)從語法定義層面上:在abstract class方式中,抽象類可以有自己的數(shù)據(jù)成員,也可以有非 abstract的成員方法,而在interface方式的實現(xiàn)中,只能有有靜態(tài)的不能被修改的數(shù)據(jù)成員(也就是必須是static final的,不過在interface中一般不定義數(shù)據(jù)成員),所有的成員方法都是abstract的。從某種意義上說,interface是一種特殊形式的 abstract class。abstract class 在 Java 語言中表示的是一種繼承關(guān)系,一個類只能使用一次繼承關(guān)系。但是,一個類卻可以實現(xiàn)多個interface。這是Java語言的設(shè)計者在考慮Java對于多重繼承的支持方面的一種折中考慮。在abstract class的定義中,我們可以賦予方法的默認(rèn)行為。但是在interface的定義中,方法卻不能擁有默認(rèn)行為。
3、overload和override的區(qū)別?
?? ?overload是重載,是同一個類中有相同的方法名,但參數(shù)類型或個數(shù)彼此不同。1)參數(shù)類型、個數(shù)、順序至少有一個不相同。2)不能重載只有返回值不同的方法名。3)存在于父類和子類、同類中。
? ? ? override是重寫,是在子類與父類中,子類中的方法的方法名,參數(shù)個數(shù)、類型都與父類中的完全一樣,在子類中覆蓋掉了父類的改方法?。1)方法名、參數(shù)、返回值相同。2)子類方法不能縮小父類方法的訪問權(quán)限。3)子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。4)存在于父類和子類之間。5)方法被定義為final不能被重寫。
4、String與StringBuffer的區(qū)別?
? ? ?JAVA平臺提供了兩個類:String和StringBuffer,它們可以儲存和操作字符串,即包含多個字符的字符數(shù)據(jù)。這個String類提供了數(shù)值不可改變的字符串。而這個StringBuffer類提供的字符串進(jìn)行修改。當(dāng)你知道字符數(shù)據(jù)要改變的時候你就可以使用StringBuffer。典型地,你可以使用StringBuffers來動態(tài)構(gòu)造字符數(shù)據(jù)。另外,String實現(xiàn)了equals方法,new String(“abc”).equals(new String(“abc”)的結(jié)果為true,而StringBuffer沒有實現(xiàn)equals方法,所以,new StringBuffer(“abc”).equals(newStringBuffer(“abc”)的結(jié)果為false。?
接著要舉一個具體的例子來說明,我們要把1到100的所有數(shù)字拼起來,組成一個串。
String str = new String(); for(int i=0;i<100;i++) {str = str + i; } String覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法,所以,將StringBuffer對象存儲進(jìn)Java集合類中時會出現(xiàn)問題。 ?
5、java集合框架結(jié)構(gòu)(Map,List,Set......)
1)類結(jié)構(gòu)圖
簡化圖
詳細(xì)圖
2)ArrayList,Vector容量相關(guān)問題
ArrayList:如果以new ArrayList()方式創(chuàng)建時,初始容量為10個;如果以new ArrayList(Collection c)初始化時,容量為c.size()*1.1,即增加10%的容量;當(dāng)向ArrayList中添加一個元素時,先進(jìn)行容器的容量調(diào)整,如果容量不夠時,則增加至原來的1.5倍加1,再然后把元素加入到容器中,即以原始容量的0.5倍比率增加。
Vector:初始化時容量可以設(shè)定,如果以new Vector()方式創(chuàng)建時,則初始容量為10,超過容量時以2倍容量增加。如果以new Vector(Collection c)方式創(chuàng)建時,初始容量為c.size()*1.1,超過時以2倍容量增加。如果以new Vector(int initialCapacity, int capacityIncrement),則以capacityIncrement容量增加。
3)集合特點?
- List:保證以某種特定插入順序來維護(hù)元素順序,即保持插入的順序,另外元素可以重復(fù)。
- ArrayList:是用數(shù)組實現(xiàn)的,讀取速度快,插入與刪除速度慢(因為插入與刪除時要移動后面的元素),適合于隨機(jī)訪問。
- Vector:功能與ArrayList幾乎相同,也是以數(shù)組實現(xiàn),添加,刪除,讀取,設(shè)置都是基于線程同步的。
- LinkedList:雙向鏈表來實現(xiàn),刪除與插入速度快,讀取速度較慢,因為它讀取時是從頭向尾(如果節(jié)點在鏈的前半部分),或尾向頭(如果節(jié)點在鏈的后半部分)查找元素。因此適合于元素的插入與刪除操作。
- Set:維持它自己的內(nèi)部排序,隨機(jī)訪問不具有意義。另外元素不可重復(fù)。
- HashSet:是最常用的,查詢速度最快,因為?內(nèi)部以HashMap來實現(xiàn),所以插入元素不能保持插入次序。
- LinkedHashSet:繼承了HashSet,保持元素的插入次序,因為內(nèi)部使用LinkedHashMap實現(xiàn),所以能保持元素插入次序。
- TreeSet:基于TreeMap,生成一個總是處于排序狀態(tài)的set,它實現(xiàn)了SortedSet接口,內(nèi)部以?TreeMap來實現(xiàn)
- TreeMap:鍵以某種排序規(guī)則排序,內(nèi)部以red-black(紅-黑)樹數(shù)據(jù)結(jié)構(gòu)實現(xiàn),實現(xiàn)了SortedMap接口,具體可參《RED-BLACK(紅黑)樹的實現(xiàn)TreeMap源碼閱讀?》
- HashMap: 以哈希表數(shù)據(jù)結(jié)構(gòu)實現(xiàn),查找對象時通過哈希函數(shù)計算其位置,它是為快速查詢而設(shè)計的,其內(nèi)部定義了一個hash表數(shù)組(Entry[] table),元素會通過哈希轉(zhuǎn)換函數(shù)將元素的哈希地址轉(zhuǎn)換成數(shù)組中存放的索引,如果有沖突,則使用散列鏈表的形式將所有相同哈希地址的元素串起來,可能通過查看HashMap.Entry的源碼它是一個單鏈表結(jié)構(gòu)。
- Hashtable:也是以哈希表數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,解決沖突時與HashMap也一樣也是采用了散列鏈表的形式,不過性能比HashMap要低。
- LinkedHashMap:繼承HashMap,內(nèi)部實體LinkedHashMap.Entry繼承自HashMap.Entry,LinkedHashMap.Entry在HashMap.Entry的基礎(chǔ)上新增了兩個實體引用(Entry before, after),這樣實體可以相互串鏈起來形成鏈,并且在LinkedHashMap中就定義了一個頭節(jié)點(Entry header)用來指向循環(huán)雙向鏈的第一個元素(通過after指向)與最后一個元素(通過before指向)。在添加一個元素時,先會通過父類HashMap將元素加入到hash表數(shù)組里,然后再會在鏈尾(header.before指向位置)添加(當(dāng)然這一過程只是調(diào)整LinkedHashMap.Entry對象內(nèi)部的before, after而已,而不是真真創(chuàng)建一個什么新的鏈表結(jié)構(gòu)向里加那樣);刪除先從hash表數(shù)組中刪除,再將被刪除的元素徹底的從雙向鏈中斷開。其實在鏈中添加與刪除操作與LinkedList是一樣的,可以參考《Java集合框架之LinkedList及ListIterator實現(xiàn)源碼分析?》?
4)HashMap與HashTable的區(qū)別?
- Hashtable中的方法是同步的,而HashMap中的方法在缺省情況下是非同步的。在多線程應(yīng)用程序中,我們應(yīng)該使用Hashtable;而對于HashMap,則需要額外的同步機(jī)制。但HashMap的同步問題可通過Collections的一個靜態(tài)方法得到解決:Map Collections.synchronizedMap(Map m),當(dāng)然與可以自己在使用地方加鎖。
- 在HashMap中,可以允許null作為鍵,且只可以有一個,否則覆蓋,但可以有一個或多個值為null。因為當(dāng)get()方法返回null值時,即可以表示 HashMap中沒有該鍵,也可以表示該鍵所對應(yīng)的值為null,所以HashMap不能由get()方法來判斷否存在某個鍵,而應(yīng)該用containsKey()方法來判斷;而Hashtable不允許null鍵與null值。
- HashTable使用Enumeration,HashMap使用Iterator。
- Hashtable是Dictionary的子類,HashMap是Map接口的一個實現(xiàn)類;
- HashTable中hash table數(shù)組默認(rèn)大小是11,增加的方式是 int newCapacity = oldCapacity * 2 + 1;,即增加至2倍(而不是2倍加1,因為擴(kuò)容是在增加元素前進(jìn)行的,在擴(kuò)容后會將新增元素放入容器中)。HashMap中hash數(shù)組的默認(rèn)大小是16,而且一定是2的多少次方;另外兩者的默認(rèn)負(fù)載因子都是0.75。
- 求哈希地址與哈希地址轉(zhuǎn)hash數(shù)組(Entry table[])索引方法不同:
- HashTable直接使用對象的hashCode: int hash = key.hashCode();//直接使用鍵的hashCode方法求哈希值
//哈希地址轉(zhuǎn)hash數(shù)組索引,先使用最大正int數(shù)與,這樣將負(fù)轉(zhuǎn)正數(shù),再與數(shù)組長度求模得到存入的hash數(shù)組索引位置
int index = (hash & 0x7FFFFFFF) % tab.length; 而HashMap重新計算hash值,而且用位運(yùn)算&代替求模: int hash = hash(k);
int i = indexFor(hash, table.length); static int hash(Object x) {
//以鍵本身的hash碼為基礎(chǔ)求哈希地址,但看不懂是什么意思 int h = x.hashCode(); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h;
}
static int indexFor(int h, int length) { return h & (length-1);//將哈希地址轉(zhuǎn)換成哈希數(shù)組中存入的索引號
} HashMap實現(xiàn)圖:
5)?集合中鍵值是否允許null小結(jié)
- List:可以有多個null,可以有重復(fù)值
- HashSet:能插入一個null(因為內(nèi)部是以?HashMap實現(xiàn)?),忽略不插入重復(fù)元素。
- TreeSet:不能插入null?(因為內(nèi)部是以?TreeMap?實現(xiàn)?)?,元素不能重復(fù),如果待插入的元素存在,則忽略不插入,對元素進(jìn)行排序。
- HashMap:允許一個null鍵與多個null值,若重復(fù)鍵,則覆蓋以前值。
- TreeMap:不允許null鍵(實際上可以插入一個null鍵,如果這個Map里只有一個元素是不會報錯的,因為一個元素時沒有進(jìn)行排序操作,也就不會報空指針異常,但如果插入第二個時就會立即報錯),但允許多個null值,覆蓋已有鍵值。
- HashTable:不允許null鍵與null值(否則運(yùn)行進(jìn)報空指針異常)。也會覆蓋以重復(fù)值。基于線程同步。
6)?對List的選擇
- 對于隨機(jī)查詢與迭代遍歷操作,數(shù)組比所有的容器都要快。
- 從中間的位置插入和刪除元素,LinkedList要比ArrayList快,特別是刪除操作。
- Vector通常不如ArrayList快,則且應(yīng)該避免使用,它目前仍然存在于類庫中的原因是為了支持過去的代碼。
- 最佳實踐:將ArrayList作為默認(rèn)首選,只有當(dāng)程序的性能因為經(jīng)常從list中間進(jìn)行插入和刪除而變差的時候,才去選擇LinkedList。當(dāng)然了,如果只是使用固定數(shù)量的元素,就應(yīng)該選擇數(shù)組了。
7)?對Set的選擇
- HashSet的性能總比TreeSet好(特別是最常用的添加和查找元素操作)。
- TreeSet存在的唯一原因是,它可以維持元素的排序狀態(tài),所以只有當(dāng)你需要一個排好序的Set時,才應(yīng)該使用TreeSet。
- 對于插入操作,LinkedHashSet比HashSet略微慢一點:這是由于維護(hù)鏈表所帶來額外開銷造成的。不過,因為有了鏈表,遍歷LinkedHashSet會比HashSet更快。
8)?對Map的選擇
- Hashtable和HashMap的效率大致相同(通常HashMap更快一點,所以HashMap有意取代Hashtable)。
- TreeMap通常比HashMap慢,因為要維護(hù)排序。
- HashMap正是為快速查詢而設(shè)計的。
- LinkedHashMap比HashMap慢一點,因為它維護(hù)散列數(shù)據(jù)結(jié)構(gòu)的同時還要維護(hù)鏈表。
9)?Stack,Vector:Stack基于線程安全,Stack類是用Vector來實現(xiàn)的(public class Stack extends Vector),但最好不要用集合API里的這個實現(xiàn)棧,因為它繼承于Vector,本就是一個錯誤的設(shè)計,應(yīng)該是一個組合的設(shè)計關(guān)系。
10)?Iterator對ArrayList(LinkedList)的操作限制:
- 剛實例化的迭代器如果還沒有進(jìn)行后移(next)操作是不能馬上進(jìn)行刪除與修改操作的。。
- 可以用ListIterator對集合連續(xù)添加與修改,但不能連續(xù)刪除。。
- 進(jìn)行添加操作后是不能立即進(jìn)行刪除與修改操作的。。
- 進(jìn)行刪除操作后可以進(jìn)行添加,但不能進(jìn)行修改操作。
- 進(jìn)行修改后是可以立即進(jìn)行刪除與添加操作的。
11)?hashCode(),equals()方法:當(dāng)以自己的對象做為HashMap、HashTable、LinkedHashMap、HashSet 、LinkedHashSet 的鍵時,一定要重寫hashCode ()與equals ()方法,因為Object的hashCode()是返回內(nèi)存地址,且equals()方法也是比較內(nèi)存地址,所以當(dāng)要在這些hash集合中查找時,如果是另外new出的新對象是查不到的,除非重寫這兩個方法。因為AbstractMap類的containsKey(Object key)方法實現(xiàn)如下:
if (e.hash == hash && eq(k, e.key))//先比對hashcode,再使用equals return true; static boolean eq(Object x, Object y) { return x == y || x.equals(y); } String對象是可以準(zhǔn)確做為鍵的,因為已重寫了這兩個方法。因此,Java中的集合框架中的哈希是以一個對象查找另外一個對象,所以重寫hasCode與equals方法很重要。?重寫hashCode()與equals()這兩個方法是針對哈希類,至于其它集合,如果要用public boolean contains(Object o)或containsValue(Object value)查找時,只需要實現(xiàn)equals()方法即可,他們都只使用對象的 equals方法進(jìn)行比對,沒有使用 hashCode方法。12)?TreeMap,TreeSet:放入其中的元素一定要具有自然比較能力(即要實現(xiàn)java.lang.Comparable接口)或者在構(gòu)造TreeMap/TreeSet時傳入一個比較器(實現(xiàn)java.util.Comparator接口),如果在創(chuàng)建時沒有傳入比較器,而放入的元素也沒有自然比較能力時,會出現(xiàn)類型轉(zhuǎn)換錯誤(因為在沒有較器時,會試著轉(zhuǎn)成Comparable型)。
14)?通過迭代器修改集合結(jié)構(gòu):通過迭代器修改集合結(jié)構(gòu)在使用迭代器遍歷集合時,我們不能通過集合本身來修改集合的結(jié)構(gòu)(添加、刪除),只能通過迭代器來操作,下面是拿對HashMap刪除操作的測試,其它集合也是這樣:
public static void main(String[] args) { Map map = new HashMap(); map.put(1, 1); map.put(2, 3); Set entrySet = map.entrySet(); Iterator it = entrySet.iterator(); while (it.hasNext()) { Entry entry = (Entry) it.next(); /* * 可以通過迭代器來修改集合結(jié)構(gòu),但前提是要在已執(zhí)行過 next 或 * 前移操作,否則會拋異常:IllegalStateException */ // it.remove(); /* * 拋異常:ConcurrentModificationException 因為通過 迭代 器操 * 作時,不能使用集合本身來修 * 改集合的結(jié)構(gòu) */ // map.remove(entry.getKey()); } System.out.println(map); }總結(jié)
以上是生活随笔為你收集整理的001java面试笔记——【java基础篇】从团800失败面试总结的java面试题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OSChina 周二乱弹 ——普通高等男
- 下一篇: LaTeX公式编辑器+mathtype6