轉(zhuǎn)載請注明出處:http://blog.csdn.net/zhoubin1992/article/details/46481759
在Android編程或者面試中經(jīng)常會遇到Java 面向?qū)ο蠛图系闹R點。自己結(jié)合實際的編程以及閱讀網(wǎng)上資料總結(jié)一下。
java面向?qū)ο?萬物皆為對象
一、==和equal()的區(qū)別
1、基本數(shù)據(jù)類型: byte,short,char,int,long,float,double,boolean 他們之間的比較,應(yīng)用雙等號(==),比較的是他們的值。
2、復(fù)合數(shù)據(jù)類型(類) :當(dāng)他們用(==)進(jìn)行比較的時候,比較的是他們在JVM中的存放地址,所以,除非是同一個new出來的對象,他們的比較后的結(jié)果為true,否則比較后結(jié)果為false。 JAVA當(dāng)中所有的類都是繼承于Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為也是比較對象的內(nèi)存地址,但在一些類庫當(dāng)中這個方法被覆蓋掉了,如String,Integer,Date在這些類當(dāng)中equals有其自身的實現(xiàn),而不再是比較類在堆內(nèi)存中的存放地址了。
3、String的equal()
(1)String類中的equals首先比較地址,如果是同一個對象的引用,可知對象相等,返回true。
(2)若果不是同一個對象,equals方法挨個比較兩個字符串對象內(nèi)的字符,只有完全相等才返回true,否則返回false。
二、String、StringBuffer、StringBuilder的區(qū)別
String 字符串常量(對象不可變,線程安全) private final char value[];
StringBuffer 字符串變量(線程安全)
StringBuilder 字符串變量(非線程安全) char[] value;
如果程序不是多線程的,那么使用StringBuilder效率高于StringBuffer。
在大部分情況下運行效率:StringBuilder > StringBuffer> String
三、final, finally, finalize的區(qū)別
final 用于聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
finally是異常處理語句結(jié)構(gòu)的一部分,表示總是執(zhí)行。
finalize是Object類的一個方法,在垃圾收集器執(zhí)行的時候會調(diào)用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關(guān)閉文件等。
四、Overload和Override的區(qū)別
方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調(diào)用子類中的定義,對它而言,父類中的定義如同被”屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數(shù)個數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。
五、java 繼承
extends關(guān)鍵字,子類擴展了父類,也具有父類的全部成員變量和方法,但是Java的子類不能獲得父類的構(gòu)造器。 Java沒有C++中的多繼承特征,每個類最多只有一個直接父類(單繼承) 當(dāng)調(diào)用子類構(gòu)造器來初始化子類對象時,父類構(gòu)造器總會在子類構(gòu)造器之前執(zhí)行。 創(chuàng)建任何Java對象,最先執(zhí)行的總是java.lang.object類的構(gòu)造器,從該類所在繼承樹最頂層類的構(gòu)造器開始執(zhí)行,然后依次向下執(zhí)行。
六、Java多態(tài)
如果Java引用變量的編譯時類型和運行時類型不一致是,就可能出現(xiàn)多態(tài)。 運行時該引用變量的方法總是表現(xiàn)出子類方法的行為特征。(出現(xiàn)重寫) Java中多態(tài)的實現(xiàn)方式:接口實現(xiàn),繼承父類進(jìn)行方法重寫(父類引用指向子類對象),同一個類中進(jìn)行方法重載。
七、抽象類和接口的區(qū)別
接口和抽象類的概念不一樣。接口是對動作的抽象(吃),抽象類是對根源的抽象(人)。 一個類只能繼承一個類(抽象類),但是可以實現(xiàn)多個接口(吃,行)。 1. 接口可以多繼承,抽象類不行 2. 抽象類中可以定義一些子類的公共方法,子類只需要增加新的功能,不需要重復(fù)寫已經(jīng)存在的方法;而接口中只是對方法的申明和常量的定義。 3. 接口中基本數(shù)據(jù)類型為public static 而抽類象不是,是普通變量類型。 4. 抽象類和接口都不能直接實例化,如果要實例化,抽象類變量必須指向?qū)崿F(xiàn)所有抽象方法的子類對象,接口變量必須指向?qū)崿F(xiàn)所有接口方法的類對象。 5. 抽象類里的抽象方法必須全部被子類所實現(xiàn),如果子類不能全部實現(xiàn)父類抽象方法,那么該子類只能是抽象類。同樣,一個實現(xiàn)接口的時候,如不能全部實現(xiàn)接口方法,那么該類也只能為抽象類。
八、內(nèi)部類作用
放在一個類的內(nèi)部的類我們就叫內(nèi)部類。
作用:
- 內(nèi)部類可以很好的實現(xiàn)隱藏,方便將存在一定邏輯關(guān)系的類組織在一起。 一般的非內(nèi)部類,是不允許有private與protected權(quán)限的,但內(nèi)部類可以。
- 內(nèi)部類擁有外圍類的所有元素的訪問權(quán)限。
- 可是實現(xiàn)多重繼承,每個內(nèi)部類都能獨立的繼承一個接口的實現(xiàn),所以無論外部類是否已經(jīng)繼承了某個(接口的)實現(xiàn),對于內(nèi)部類都沒有影響。內(nèi)部類使得多繼承的解決方案變得完整。
- 可以避免修改接口而實現(xiàn)同一個類中兩種同名方法的調(diào)用。
- 方便編寫線程代碼。
九、java對象序列化
序列化:把Java對象轉(zhuǎn)換為字節(jié)序列的過程。
反序列化:把字節(jié)序列恢復(fù)為Java對象的過程。
只有實現(xiàn)了Serializable和Externalizable接口的類的對象才能被序列化。 讀取對象的順序與寫入時的順序要一致。 對象的默認(rèn)序列化機制寫入的內(nèi)容是:對象的類,類簽名,以及非瞬態(tài)和非靜態(tài)字段的值。
用途 :
1) 把對象轉(zhuǎn)換成平臺無關(guān)的二進(jìn)制流永久地保存到硬盤上,通常存放在一個文件中;
2) 在網(wǎng)絡(luò)上傳送對象的字節(jié)序列。
十、java集合
HashSet類
hashSet用Hash算法來存儲集合中的元素,具有很好的存取和查找性能。
特點:
- 無序
- 不是同步的
- 集合元素值可為null
- 不允許包含相同的元素
存入一個元素: HashSet調(diào)用該對象的hashCode()方法得到hashCode值,根據(jù)該hashCode值確定該對象的存儲位置。
訪問一個元素: HashSet先計算該元素的hashCode值,然后直接到該hashCode值對應(yīng)的位置去取出該元素。 如果元素相同則添加失敗add()返回false。
HashSet集合判斷兩個元素相同的標(biāo)準(zhǔn)是兩個對象equals()方法比較相等,并且hashCode值也相等。
如果兩個對象的hashCode值相同,equals()返回false時,會在同一個位置用鏈?zhǔn)浇Y(jié)構(gòu)來保存。導(dǎo)致性能下降。
十一、ArrayList和Vector有何異同點
相同點:
- 兩者都是基于索引的,內(nèi)部由一個數(shù)組支持。
- 兩者維護插入的順序,我們可以根據(jù)插入順序來獲取元素。
- ArrayList和Vector的迭代器實現(xiàn)都是fail-fast的。
- ArrayList和Vector兩者允許null值,也可以使用索引值對元素進(jìn)行隨機訪問。
不同點:
- Vector是同步的,而ArrayList不是。然而,如果你尋求在迭代的時候?qū)α斜磉M(jìn)行改變,你應(yīng)該使用CopyOnWriteArrayList。
- ArrayList比Vector快,它因為有同步,不會過載。
- ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表。
十二、HashMap類和Hashtable類
??HashMap和Hashtable判斷兩個key相等的標(biāo)準(zhǔn)是兩個key的equals()方法比較返回true,并且hashCode值也相等。 對同一個Key,只會有一個對應(yīng)的value值存在。 如何算是同一個Key? 首先,兩個key對象的hash值相同,其次,key對象的equals方法返回真 所以用做key的對象必須重寫equals()和hashCode()方法,保證兩個方法的判斷標(biāo)準(zhǔn)一致——兩個key的equals方法返回真,hashCode值也相同。
HashMap和Hashtable的區(qū)別
HashMap允許key和value為null,而HashTable不允許。HashTable是同步的(線程安全),而HashMap不是(線程不安全)。所以HashMap適合單線程環(huán)境,HashTable適合多線程環(huán)境。HashMap比HashTable的性能高點。
HashMap和Hashtable中key-value對無序。但在Java1.4中引入了LinkedHashMap,HashMap的一個子類,假如你想要遍歷順序,你很容易從HashMap轉(zhuǎn)向LinkedHashMap,但是HashTable不是這樣的,它的順序是不可預(yù)知的。
HashMap提供對key的Set進(jìn)行iterator遍歷,因此它是fail-fast的,但Hashtable提供對key的Enumeration進(jìn)行遍歷,它不支持fail-fast。(fail-fast 機制是java集合(Collection)中的一種錯誤機制。當(dāng)多個線程對同一個集合的內(nèi)容進(jìn)行操作時,就可能會產(chǎn)生fail-fast事件。例如:當(dāng)某一個線程A通過iterator去遍歷某集合的過程中,若該集合的內(nèi)容被其他線程所改變了;那么線程A訪問集合時,就會拋出ConcurrentModificationException異常,產(chǎn)生fail-fast事件)Hashtable被認(rèn)為是個遺留的類,如果你尋求在迭代的時候修改Map,你應(yīng)該使用CocurrentHashMap。HashTable中hash數(shù)組默認(rèn)大小是11,增加的方式是 old*2+1。HashMap中hash數(shù)組的默認(rèn)大小是16,而且一定是2的指數(shù)。
hashCode()和equals()方法有何重要性
hashCode是根類Obeject中的方法。 默認(rèn)情況下,Object中的hashCode() 返回對象的32位jvm內(nèi)存地址。也就是說如果對象不重寫該方法,則返回相應(yīng)對象的32為JVM內(nèi)存地址。
HashMap使用Key對象的hashCode()和equals()方法去決定key-value對的索引。當(dāng)我們試著從HashMap中獲取值的時候,這些方法也會被用到。如果這些方法沒有被正確地實現(xiàn),在這種情況下,兩個不同Key也許會產(chǎn)生相同的hashCode()和equals()輸出,HashMap將會認(rèn)為它們是相同的,然后覆蓋它們,而非把它們存儲到不同的地方。同樣的,所有不允許存儲重復(fù)數(shù)據(jù)的集合類都使用hashCode()和equals()去查找重復(fù),所以正確實現(xiàn)它們非常重要。equals()和hashCode()的實現(xiàn)應(yīng)該遵循以下規(guī)則: 2.1 如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()總是為true的。 2.2 如果o1.hashCode() == o2.hashCode(),并不意味著o1.equals(o2)會為true。
*在Java中,HashMap是如何工作的?
??HashMap在Map.Entry靜態(tài)內(nèi)部類實現(xiàn)中存儲key-value對。HashMap使用哈希算法,在put和get方法中,它使用hashCode()和equals()方法。當(dāng)我們通過傳遞key-value對調(diào)用put方法的時候,HashMap使用Key hashCode()和哈希算法來找出存儲key-value對的索引。Entry存儲在LinkedList中,所以如果存在entry,它使用equals()方法來檢查傳遞的key是否已經(jīng)存在,如果存在,它會覆蓋value,如果不存在,它會創(chuàng)建一個新的entry然后保存。當(dāng)我們通過傳遞key調(diào)用get方法時,它再次使用hashCode()來找到數(shù)組中的索引,然后使用equals()方法找出正確的Entry,然后返回它的值。 其它關(guān)于HashMap比較重要的問題是容量、負(fù)荷系數(shù)和閥值調(diào)整。HashMap默認(rèn)的初始容量是32,負(fù)荷系數(shù)是0.75。閥值是為負(fù)荷系數(shù)乘以容量,無論何時我們嘗試添加一個entry,如果map的大小比閥值大的時候,HashMap會對map的內(nèi)容進(jìn)行重新哈希,且使用更大的容量。容量總是2的冪,所以如果你知道你需要存儲大量的key-value對,比如緩存從數(shù)據(jù)庫里面拉取的數(shù)據(jù),使用正確的容量和負(fù)荷系數(shù)對HashMap進(jìn)行初始化是個不錯的做法。