JAVA基礎(chǔ)
- JAVA中的幾種基本數(shù)據(jù)類(lèi)型是什么芽隆,各自占用多少字節(jié)。
1B=8bit
1Byte=8bit
1KB=1024Byte(字節(jié))=8*1024bit
1MB=1024KB
1GB=1024MB
1TB=1024GB
boolean 1bit
byte 8bit
short 16bit
char 16bit
float 32bit
int 32bit
long 64bit
double 64bit
/**
* 輸出各種基礎(chǔ)類(lèi)型的bit大小统屈,也就是所占二進(jìn)制的位數(shù),1Byte=8bit
*/
private static void getBit() {
//The number of bits used to represent a {@code byte} value in two's complement binary form.
//用來(lái)表示Byte類(lèi)型的值的位數(shù)胚吁,說(shuō)到底,就是bit的個(gè)數(shù)愁憔,也就是二進(jìn)制的位數(shù)腕扶。
System.out.println("Byte: " + Byte.SIZE);
System.out.println("Short: " + Short.SIZE);
System.out.println("Character: " + Character.SIZE);
System.out.println("Integer: " + Integer.SIZE);
System.out.println("Float: " + Float.SIZE);
System.out.println("Long: " + Long.SIZE);
System.out.println("Double: " + Double.SIZE);
System.out.println("Boolean: " + Boolean.toString(false));
}
- String類(lèi)能被繼承嗎,為什么吨掌。
不可以半抱,因?yàn)镾tring類(lèi)有final修飾符脓恕,而final修飾的類(lèi)是不能被繼承的,實(shí)現(xiàn)細(xì)節(jié)不允許改變窿侈。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
- String进肯,Stringbuffer,StringBuilder的區(qū)別棉磨。
String 字符串常量
StringBuffer 字符串變量(線(xiàn)程安全)
StringBuilder 字符串變量(非線(xiàn)程安全)
- ArrayList和LinkedList有什么區(qū)別江掩。
ArrayList 本質(zhì)上是一個(gè)可改變大小的數(shù)組.當(dāng)元素加入時(shí),其大小將會(huì)動(dòng)態(tài)地增長(zhǎng).內(nèi)部的元素可以直接通過(guò)get與set方法進(jìn)行訪(fǎng)問(wèn).元素順序存儲(chǔ) ,隨機(jī)訪(fǎng)問(wèn)很快,刪除非頭尾元素慢乘瓤,新增元素慢而且費(fèi)資源 ,較適用于無(wú)頻繁增刪的情況 ,比數(shù)組效率低环形,如果不是需要可變數(shù)組,可考慮使用數(shù)組 ,非線(xiàn)程安全.
LinkedList 是一個(gè)雙鏈表,在添加和刪除元素時(shí)具有比ArrayList更好的性能.但在get與set方面弱于A(yíng)rrayList. 適用于 :沒(méi)有大規(guī)模的隨機(jī)讀取衙傀,有大量的增加/刪除操作.隨機(jī)訪(fǎng)問(wèn)很慢抬吟,增刪操作很快,不耗費(fèi)多余資源 ,允許null元素,非線(xiàn)程安全.
Vector (類(lèi)似于A(yíng)rrayList)但其是同步的统抬,開(kāi)銷(xiāo)就比ArrayList要大火本。如果你的程序本身是線(xiàn)程安全的,那么使用ArrayList是更好的選擇聪建。 Vector和ArrayList在更多元素添加進(jìn)來(lái)時(shí)會(huì)請(qǐng)求更大的空間钙畔。Vector每次請(qǐng)求其大小的雙倍空間,而ArrayList每次對(duì)size增長(zhǎng)50%.
- 講講類(lèi)的實(shí)例化順序金麸,比如父類(lèi)靜態(tài)數(shù)據(jù)擎析,構(gòu)造函數(shù),字段挥下,子類(lèi)靜態(tài)數(shù)據(jù)揍魂,構(gòu)造函數(shù),字段棚瘟,當(dāng)new的時(shí)候现斋,他們的執(zhí)行順序。
public class InitialOrderTest {
/* 靜態(tài)變量 */
public static String staticField = "靜態(tài)變量";
/* 變量 */
public String field = "變量";
/* 靜態(tài)初始化塊 */
static {
System.out.println( staticField );
System.out.println( "靜態(tài)初始化塊" );
}
/* 初始化塊 */
{
System.out.println( field );
System.out.println( "初始化塊" );
}
/* 構(gòu)造器 */
public InitialOrderTest()
{
System.out.println( "構(gòu)造器" );
}
public static void main( String[] args )
{
new InitialOrderTest();
}
}
運(yùn)行以上代碼偎蘸,我們會(huì)得到如下的輸出結(jié)果:
1靜態(tài)變量
2靜態(tài)初始化塊
3變量
4初始化塊
5構(gòu)造器
- 用過(guò)哪些Map類(lèi)庄蹋,都有什么區(qū)別,HashMap是線(xiàn)程安全的嗎,并發(fā)下使用的Map是什么禀苦,他們內(nèi)部原理分別是什么蔓肯,比如存儲(chǔ)方式,hashcode振乏,擴(kuò)容蔗包,默認(rèn)容量等。
java為數(shù)據(jù)結(jié)構(gòu)中的映射定義了一個(gè)接口java.util.Map;它有四個(gè)實(shí)現(xiàn)類(lèi),分別是HashMap Hashtable LinkedHashMap 和TreeMap.
Map主要用于存儲(chǔ)健值對(duì)慧邮,根據(jù)鍵得到值调限,因此不允許鍵重復(fù)(重復(fù)了覆蓋了),但允許值重復(fù)舟陆。
Hashmap 是一個(gè)最常用的Map,它根據(jù)鍵的HashCode值存儲(chǔ)數(shù)據(jù),根據(jù)鍵可以直接獲取它的值,具有很快的訪(fǎng)問(wèn)速度耻矮,遍歷時(shí)秦躯,取得數(shù)據(jù)的順序是完全隨機(jī)的。 HashMap最多只允許一條記錄的鍵為Null;允許多條記錄的值為 Null;HashMap不支持線(xiàn)程的同步裆装,即任一時(shí)刻可以有多個(gè)線(xiàn)程同時(shí)寫(xiě)HashMap;可能會(huì)導(dǎo)致數(shù)據(jù)的不一致踱承。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力哨免,或者使用ConcurrentHashMap茎活。
Hashtable與 HashMap類(lèi)似,它繼承自Dictionary類(lèi),不同的是:它不允許記錄的鍵或者值為空;它支持線(xiàn)程的同步琢唾,即任一時(shí)刻只有一個(gè)線(xiàn)程能寫(xiě)Hashtable,因此也導(dǎo)致了 Hashtable在寫(xiě)入時(shí)會(huì)比較慢载荔。
LinkedHashMap 是HashMap的一個(gè)子類(lèi),保存了記錄的插入順序采桃,在用Iterator遍歷LinkedHashMap時(shí)懒熙,先得到的記錄肯定是先插入的.也可以在構(gòu)造時(shí)用帶參數(shù),按照應(yīng)用次數(shù)排序普办。在遍歷的時(shí)候會(huì)比HashMap慢工扎,不過(guò)有種情況例外,當(dāng)HashMap容量很大泌豆,實(shí)際數(shù)據(jù)較少時(shí)定庵,遍歷起來(lái)可能會(huì)比 LinkedHashMap慢,因?yàn)長(zhǎng)inkedHashMap的遍歷速度只和實(shí)際數(shù)據(jù)有關(guān)踪危,和容量無(wú)關(guān),而HashMap的遍歷速度和他的容量有關(guān)猪落。
TreeMap實(shí)現(xiàn)SortMap接口贞远,能夠把它保存的記錄根據(jù)鍵排序,默認(rèn)是按鍵值的升序排序,也可以指定排序的比較器笨忌,當(dāng)用Iterator 遍歷TreeMap時(shí)蓝仲,得到的記錄是排過(guò)序的。
一般情況下官疲,我們用的最多的是HashMap,在Map 中插入袱结、刪除和定位元素,HashMap 是最好的選擇途凫。但如果您要按自然順序或自定義順序遍歷鍵垢夹,那么TreeMap會(huì)更好。如果需要輸出的順序和輸入的相同,那么用LinkedHashMap 可以實(shí)現(xiàn),它還可以按讀取順序來(lái)排列.
HashMap是一個(gè)最常用的Map维费,它根據(jù)鍵的hashCode值存儲(chǔ)數(shù)據(jù)果元,根據(jù)鍵可以直接獲取它的值促王,具有很快的訪(fǎng)問(wèn)速度。HashMap最多只允許一條記錄的鍵為NULL而晒,允許多條記錄的值為NULL蝇狼。
HashMap不支持線(xiàn)程同步,即任一時(shí)刻可以有多個(gè)線(xiàn)程同時(shí)寫(xiě)HashMap倡怎,可能會(huì)導(dǎo)致數(shù)據(jù)的不一致性迅耘。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力监署。
Hashtable與HashMap類(lèi)似豹障,不同的是:它不允許記錄的鍵或者值為空;它支持線(xiàn)程的同步焦匈,即任一時(shí)刻只有一個(gè)線(xiàn)程能寫(xiě)Hashtable血公,因此也導(dǎo)致了Hashtable在寫(xiě)入時(shí)會(huì)比較慢。
LinkedHashMap保存了記錄的插入順序缓熟,在用Iterator遍歷LinkedHashMap時(shí)累魔,先得到的記錄肯定是先插入的。
在遍歷的時(shí)候會(huì)比HashMap慢TreeMap能夠把它保存的記錄根據(jù)鍵排序够滑,默認(rèn)是按升序排序垦写,也可以指定排序的比較器。當(dāng)用Iterator遍歷TreeMap時(shí)彰触,得到的記錄是排過(guò)序的梯投。
- JAVA8的ConcurrentHashMap為什么放棄了分段鎖,有什么問(wèn)題嗎况毅,如果你來(lái)設(shè)計(jì)分蓖,你如何設(shè)計(jì)。
jdk 1.8 取消了基于 Segment 的分段鎖思想尔许,改用 CAS + synchronized 控制并發(fā)操作么鹤,在某些方面提升了性能。并且追隨 1.8 版本的 HashMap 底層實(shí)現(xiàn)味廊,使用數(shù)組+鏈表+紅黑樹(shù)進(jìn)行數(shù)據(jù)存儲(chǔ)蒸甜。
- 有沒(méi)有有順序的Map實(shí)現(xiàn)類(lèi),如果有余佛,他們是怎么保證有序的柠新。
TreeMap實(shí)現(xiàn)SortMap接口,能夠把它保存的記錄根據(jù)鍵排序,默認(rèn)是按鍵值的升序排序辉巡,也可以指定排序的比較器恨憎,當(dāng)用Iterator 遍歷TreeMap時(shí),得到的記錄是排過(guò)序的红氯。
抽象類(lèi)和接口的區(qū)別框咙,類(lèi)可以繼承多個(gè)類(lèi)么咕痛,接口可以繼承多個(gè)接口么,類(lèi)可以實(shí)現(xiàn)多個(gè)接口么。
繼承和聚合的區(qū)別在哪喇嘱。
繼承指的是一個(gè)類(lèi)繼承另外的一個(gè)類(lèi)的功能茉贡,并可以增加它自己的新功能的能力,繼承是類(lèi)與類(lèi)或者接口與接口之間最常見(jiàn)的關(guān)系者铜;在Java中此類(lèi)關(guān)系通過(guò)關(guān)鍵字extends明確標(biāo)識(shí)腔丧。
聚合指的是聚合體現(xiàn)的是整體與部分、擁有的關(guān)系愉粤,此時(shí)整體與部分之間是可分離的,他們可以具有各自的生命周期衣厘;比如計(jì)算機(jī)與CPU、公司與員工的關(guān)系等压恒;
- IO模型有哪些影暴,講講你理解的nio ,他和bio探赫,aio的區(qū)別是啥型宙,談?wù)剅eactor模型。
1.什么是BIO,NIO,AIO
JAVA BIO:同步并阻塞伦吠,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線(xiàn)程妆兑,即客戶(hù)端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線(xiàn)程并處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的開(kāi)銷(xiāo)毛仪,當(dāng)然可以通過(guò)線(xiàn)程池機(jī)制改善
JAVA NIO:同步非阻塞搁嗓,服務(wù)器實(shí)現(xiàn)模式為一個(gè)請(qǐng)求一個(gè)線(xiàn)程,即客戶(hù)端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上潭千,多路復(fù)用器輪詢(xún)到連接有IO請(qǐng)求時(shí)才啟動(dòng)一個(gè)線(xiàn)程進(jìn)行處理
JAVA AIO(NIO2):異步非阻塞谱姓,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線(xiàn)程,客戶(hù)端的I/O請(qǐng)求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線(xiàn)程進(jìn)行處理
2.使用場(chǎng)景
BIO方式適用于連接數(shù)目比較小且固定的架構(gòu)刨晴,這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中路翻,JDK1.4以前的唯一選擇狈癞,但程序直觀(guān)簡(jiǎn)單易理解。
NIO方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu)茂契,比如聊天服務(wù)器蝶桶,并發(fā)局限于應(yīng)用中,編程比較復(fù)雜掉冶,JDK1.4開(kāi)始支持真竖。
AIO方式使用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu)脐雪,比如相冊(cè)服務(wù)器,充分調(diào)用OS參與并發(fā)操作恢共,編程比較復(fù)雜战秋,JDK7開(kāi)始支持。
3.BIO 同步并阻塞
tomcat采用的傳統(tǒng)的BIO(同步阻塞IO模型)+線(xiàn)程池模式讨韭,對(duì)于十萬(wàn)甚至百萬(wàn)連接的時(shí)候脂信,傳統(tǒng)BIO模型是無(wú)能為力的:
①線(xiàn)程的創(chuàng)建和銷(xiāo)毀成本很高,在linux中透硝,線(xiàn)程本質(zhì)就是一個(gè)進(jìn)程狰闪,創(chuàng)建銷(xiāo)毀都是重量級(jí)的系統(tǒng)函數(shù)
②線(xiàn)程本身占用較大的內(nèi)存,像java的線(xiàn)程棧一般至少分配512K-1M的空間濒生,如果系統(tǒng)線(xiàn)程過(guò)高埋泵,內(nèi)存占用是個(gè)問(wèn)題
③線(xiàn)程的切換成本高,操作系統(tǒng)發(fā)生線(xiàn)程切換的時(shí)候罪治,需要保留線(xiàn)程的上下文丽声,然后執(zhí)行系統(tǒng)調(diào)用,如果線(xiàn)程數(shù)過(guò)高可能執(zhí)行線(xiàn)程切換的時(shí)間甚至大于線(xiàn)程執(zhí)行的時(shí)間规阀,這時(shí)候帶來(lái)的表現(xiàn)是系統(tǒng)load偏高恒序,CPUsy使用率很高
④容易造成鋸齒狀的系統(tǒng)負(fù)載。系統(tǒng)負(fù)載是用活動(dòng)線(xiàn)程數(shù)或CPU核心數(shù)谁撼,一旦線(xiàn)程數(shù)量高但外部網(wǎng)絡(luò)環(huán)境不是很穩(wěn)定歧胁,就很容易造成大量請(qǐng)求的結(jié)果同時(shí)返回,激活大量阻塞線(xiàn)程從而使系統(tǒng)負(fù)載壓力過(guò)大厉碟。
4NIO同步非阻塞
NIO基于Reactor喊巍,當(dāng)socket有流可讀或可寫(xiě)入socket,操作系統(tǒng)會(huì)相應(yīng)的通知引用程序進(jìn)行處理箍鼓,應(yīng)用再將流讀取到緩沖區(qū)或?qū)懭氩僮飨到y(tǒng)崭参。也就是,不是一個(gè)鏈接就要對(duì)應(yīng)一個(gè)處理線(xiàn)程款咖,而是一個(gè)有效請(qǐng)求對(duì)應(yīng)一個(gè)線(xiàn)程何暮,當(dāng)連接沒(méi)有數(shù)據(jù)時(shí),是沒(méi)有工作線(xiàn)程來(lái)處理的
Reactor模型
nio只有acceptor的服務(wù)線(xiàn)程是堵塞進(jìn)行的铐殃,其他讀寫(xiě)線(xiàn)程是通過(guò)注冊(cè)事件的方式海洼,有讀寫(xiě)事件激活時(shí)才調(diào)用線(xiàn)程資源區(qū)執(zhí)行,不會(huì)一直堵塞等著讀寫(xiě)操作富腊,Reactor的瓶頸主要在于acceptor的執(zhí)行坏逢,讀寫(xiě)事件也是在這一塊分發(fā)
5AIO異步非堵塞IO
AIO需要一個(gè)鏈接注冊(cè)讀寫(xiě)事件和回調(diào)方法,當(dāng)進(jìn)行讀寫(xiě)操作時(shí),只須直接調(diào)用API的read或write方法即可是整,這兩種方法均為異步肖揣,對(duì)于讀操作而言,當(dāng)有流可讀取時(shí)浮入,操作系統(tǒng)會(huì)將可讀的流傳入read方法的緩沖區(qū)龙优,并通知應(yīng)用程序;對(duì)于寫(xiě)操作而言舵盈,當(dāng)操作系統(tǒng)將write方法傳遞的流寫(xiě)入完畢時(shí)陋率,操作系統(tǒng)主動(dòng)通知應(yīng)用程序
即,read/write方法都是異步的秽晚,完成后會(huì)主動(dòng)調(diào)用回調(diào)函數(shù)
- 反射的原理瓦糟,反射創(chuàng)建類(lèi)實(shí)例的三種方式是什么。
反射的好處(為什么需要反射機(jī)制):
通過(guò)反射機(jī)制可以獲取到一個(gè)類(lèi)的完整信息赴蝇,例如:所有(包含private修飾)屬性和方法菩浙,包信息等。
換句話(huà)說(shuō)句伶,Class本身表示一個(gè)類(lèi)的本身劲蜻,通過(guò)Class可以完整獲取一個(gè)類(lèi)中的完整結(jié)構(gòu),包含此類(lèi)中的方法定義考余,屬性定義等先嬉。
反射的核心概念:
一切的操作都是講使用Object完成,類(lèi)或者數(shù)組的引用是可以用Object進(jìn)行接收楚堤。
這里疫蔓,個(gè)人的理解是,對(duì)象的多態(tài):Object object= 任何引用類(lèi)型的實(shí)例對(duì)象
Class類(lèi)的API特性和三種創(chuàng)建方式
在Java中,可以通過(guò)Class類(lèi)獲取到任何一個(gè)類(lèi)中完整的信息身冬。
一個(gè)很重要的概念:
在Java中衅胀,Object是所有類(lèi)的超類(lèi)墅冷,所有類(lèi)的對(duì)象實(shí)際上也是java.lang,Class類(lèi)的實(shí)例艘款。 因此茫因,所有的對(duì)象都可以轉(zhuǎn)變成Class類(lèi)型表示屁商。
Class類(lèi)常用的API
forName() : 傳入完整的”包.類(lèi)”名稱(chēng)實(shí)例化Class對(duì)象
getConstructors() : 得到一個(gè)類(lèi)中的全部構(gòu)造方法
getConstructor(Class<?>...parameterTypes):獲取到指定參數(shù)類(lèi)型的(public修飾的)構(gòu)造方法
getDeclaredConstructor(Class<?>... parameterTypes):獲取到指定參數(shù)類(lèi)型的構(gòu)造方法,包含private修飾和public修飾
getDeclaredFields(): 獲得某個(gè)類(lèi)的單獨(dú)定義全部的字段(即包括public绊困、private和proteced等修飾符的全部屬性)船殉,但是不包括父類(lèi)的申明字段或者實(shí)現(xiàn)接口中的字段薪韩。
getFields(): 獲得某個(gè)類(lèi)的所有的公共(public)的字段宙帝,包括父類(lèi)中的公共字段或者實(shí)現(xiàn)接口的公共屬性和類(lèi)本身定義的公共屬性阅束。
getMethods() : 獲取到本類(lèi)中全部public修飾的方法,包含父類(lèi)中公共方法和覆蓋重寫(xiě)的方法和自己本身定義的公共方法茄唐。
getMethod(String name,Class...parameterType): 獲取到指定名字,指定方法參數(shù)類(lèi)型的公共方法。
getSuperclass(): 獲取到父類(lèi)的Class
getInterfaces() : 獲然Ρ唷(實(shí)現(xiàn)的全部接口對(duì)應(yīng)的)Class數(shù)組呼盆。
newInstance(): 實(shí)例化Class中定義類(lèi)型的實(shí)例化對(duì)象。
getComponentType(): 獲取數(shù)組類(lèi)型的Class.
isArray(): 判斷此Class是否是一個(gè)數(shù)組蚁廓。
getName() : 獲取到一個(gè)類(lèi)完整”包.類(lèi)”名稱(chēng)
創(chuàng)建Class類(lèi)對(duì)象的方式有三種:
對(duì)象.getClass()方式
類(lèi)名.Class方式
Class.forName( 類(lèi)的包名 ) 方式
案例實(shí)戰(zhàn)
創(chuàng)建一個(gè)測(cè)試類(lèi):
package com.xingen.classdemo;
public class ClassTest1 {
public static ClassTest1 newInstance() {
return new ClassTest1();
}
public static void createClassInstance1() {
//對(duì)象.getClass() 方式獲取Class對(duì)象
Class<?> mClass = ClassTest1.newInstance().getClass();
//輸出類(lèi)所在的包路徑
System.out.println(" 通過(guò)對(duì)象.getClass()方式访圃, 反射出類(lèi)所在的包路徑: " + mClass.getName());
}
public static void createClassInstance2() {
//類(lèi)名.class 方式獲取Class對(duì)象
Class<?> mClass = ClassTest1.class;
//輸出類(lèi)所在的包路徑
System.out.println(" 通過(guò)類(lèi)名.Class 方式,反射出 類(lèi)所在的包路徑: " + mClass.getName());
}
public static void createClassInstance3() {
//Class.forName 方式獲取Class對(duì)象
Class<?> mClass = null;
try {
mClass = Class.forName("com.xingen.classdemo.ClassTest1");
} catch (Exception e) {
e.printStackTrace();
}
//輸出類(lèi)所在的包路徑
System.out.println(" 通過(guò)Class.forName 方式相嵌,反射出 類(lèi)所在的包路徑: " + mClass.getName());
}}
public class Client {
public static void main(String[] args) {
createClassInstance();
}
/**
* 創(chuàng)建Class類(lèi)實(shí)例對(duì)象的三種方式
*/
private static void createClassInstance() {
ClassTest1.createClassInstance1();
ClassTest1.createClassInstance2();
ClassTest1.createClassInstance3();
}
}
- 反射中腿时,Class.forName和ClassLoader區(qū)別 。
java中class.forName()和classLoader都可用來(lái)對(duì)類(lèi)進(jìn)行加載饭宾。
class.forName()前者除了將類(lèi)的.class文件加載到j(luò)vm中之外批糟,還會(huì)對(duì)類(lèi)進(jìn)行解釋?zhuān)瑘?zhí)行類(lèi)中的static塊。
而classLoader只干一件事情看铆,就是將.class文件加載到j(luò)vm中徽鼎,不會(huì)執(zhí)行static中的內(nèi)容,只有在newInstance才會(huì)去執(zhí)行static塊。
Class.forName(name, initialize, loader)帶參函數(shù)也可控制是否加載static塊弹惦。并且只有調(diào)用了newInstance()方法采用調(diào)用構(gòu)造函數(shù)否淤,創(chuàng)建類(lèi)的對(duì)象
- 描述動(dòng)態(tài)代理的幾種實(shí)現(xiàn)方式,分別說(shuō)出相應(yīng)的優(yōu)缺點(diǎn)棠隐。
看這里 - 動(dòng)態(tài)代理與cglib實(shí)現(xiàn)的區(qū)別石抡。
一 JDK和CGLIB動(dòng)態(tài)代理原理
1、JDK動(dòng)態(tài)代理
利用攔截器(攔截器必須實(shí)現(xiàn)InvocationHanlder)加上反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類(lèi)助泽,
在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理啰扛。
2、CGLIB動(dòng)態(tài)代理
利用ASM開(kāi)源包报咳,對(duì)代理對(duì)象類(lèi)的class文件加載進(jìn)來(lái)侠讯,通過(guò)修改其字節(jié)碼生成子類(lèi)來(lái)處理。
3暑刃、何時(shí)使用JDK還是CGLIB厢漩?
1)如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP岩臣。
2)如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口溜嗜,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP。
3)如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)了接口架谎,必須采用CGLIB庫(kù)炸宵,Spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換。
4谷扣、如何強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP土全?
1)添加CGLIB庫(kù)(aspectjrt-xxx.jar捎琐、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
5裹匙、JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別瑞凑?
1)JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理,而不能針對(duì)類(lèi)概页。
2)CGLIB是針對(duì)類(lèi)實(shí)現(xiàn)代理籽御,主要是對(duì)指定的類(lèi)生成一個(gè)子類(lèi),覆蓋其中的方法惰匙,
并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)技掏,但是因?yàn)椴捎玫氖抢^承,所以該類(lèi)或方法最好不要聲明成final项鬼,
對(duì)于final類(lèi)或方法哑梳,是無(wú)法繼承的。
6秃臣、CGlib比JDK快涧衙?
1)使用CGLib實(shí)現(xiàn)動(dòng)態(tài)代理,CGLib底層采用ASM字節(jié)碼生成框架奥此,使用字節(jié)碼技術(shù)生成代理類(lèi)弧哎,
在jdk6之前比使用Java反射效率要高。唯一需要注意的是稚虎,CGLib不能對(duì)聲明為final的方法進(jìn)行代理撤嫩,
因?yàn)镃GLib原理是動(dòng)態(tài)生成被代理類(lèi)的子類(lèi)。
2)在jdk6蠢终、jdk7序攘、jdk8逐步對(duì)JDK動(dòng)態(tài)代理優(yōu)化之后,在調(diào)用次數(shù)較少的情況下寻拂,JDK代理效率高于CGLIB代理效率程奠,
只有當(dāng)進(jìn)行大量調(diào)用的時(shí)候,jdk6和jdk7比CGLIB代理效率低一點(diǎn)祭钉,但是到j(luò)dk8的時(shí)候瞄沙,jdk代理效率高于CGLIB代理,
總之慌核,每一次jdk版本升級(jí)距境,jdk代理效率都得到提升,而CGLIB代理消息確有點(diǎn)跟不上步伐垮卓。
7垫桂、Spring如何選擇用JDK還是CGLIB?
1)當(dāng)Bean實(shí)現(xiàn)接口時(shí)粟按,Spring就會(huì)用JDK的動(dòng)態(tài)代理诬滩。
2)當(dāng)Bean沒(méi)有實(shí)現(xiàn)接口時(shí)霹粥,Spring使用CGlib是實(shí)現(xiàn)。
3)可以強(qiáng)制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)碱呼。
- 為什么CGlib方式可以對(duì)接口實(shí)現(xiàn)代理蒙挑。
look - final的用途。
final是Java中的一個(gè)關(guān)鍵字愚臀,這個(gè)關(guān)鍵字有著很多種不同的用法,而且在不同的環(huán)境下矾利,語(yǔ)義也不盡相同姑裂。所以,要想理解好final男旗, 我們就需要將final在Java中的藏身之地一網(wǎng)打盡舶斧。
final是一個(gè)關(guān)鍵字,在Java中表示為一個(gè)修飾符(Modifier)察皇,有時(shí)候?qū)ξ易约簛?lái)說(shuō)茴厉,我也很好奇,這些修飾符是怎么起到作用的什荣,例如我們舉一個(gè)例子來(lái)說(shuō)矾缓,一個(gè)被final修飾的類(lèi)是無(wú)法有子類(lèi)的,那么它為什么不能有子類(lèi)稻爬,修飾符只是在Java語(yǔ)言層面上限制了這個(gè)關(guān)系嗜闻,那么程序運(yùn)行的時(shí)候系統(tǒng)是怎么知道的呢?后面也會(huì)簡(jiǎn)單的介紹這個(gè)內(nèi)容桅锄。
那么琉雳,final既然是一個(gè)修飾符,在Java中友瘤,final 能修飾哪些東西呢翠肘?基本上可以概括的說(shuō),在Java中基本可以修飾面向?qū)ο蟮慕^大部分元素辫秧。我們可以通過(guò)如下的代碼段來(lái)看final修飾的部分束倍。
1.修飾類(lèi)(Class)
public final class SystemUtils {...}
public class OuterClass { final class InnerClass{...} }
2.修飾方法(Method)
public final void foo() {...}
3.修飾域(Field)
public final int fee = 25;
private static final float POINT_X = 2.6f;
public void foo() { final int type = 3; }
4.修飾方法參數(shù)(Method Argument)
public void foo(final int x, final int y) {...}
由上面的代碼片段我們可以看出來(lái)final關(guān)
- 寫(xiě)出三種單例模式實(shí)現(xiàn) 。
java中單例模式是一種常見(jiàn)的設(shè)計(jì)模式茶没,單例模式分三種:懶漢式單例肌幽、餓漢式單例、登記式單例三種抓半。
單例模式有以下特點(diǎn):
單例類(lèi)只能有一個(gè)實(shí)例喂急。
單例類(lèi)必須自己創(chuàng)建自己的唯一實(shí)例。
單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例笛求。
單例模式確保某個(gè)類(lèi)只有一個(gè)實(shí)例廊移,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例糕簿。在計(jì)算機(jī)系統(tǒng)中,線(xiàn)程池狡孔、緩存懂诗、日志對(duì)象、對(duì)話(huà)框苗膝、打印機(jī)殃恒、顯卡的驅(qū)動(dòng)程序?qū)ο蟪1辉O(shè)計(jì)成單例。選擇單例模式就是為了避免不一致?tīng)顟B(tài)辱揭,避免政出多頭离唐。
在程序開(kāi)發(fā)中,Service和Dao都可以設(shè)置為單例模式(保證線(xiàn)程安全)问窃。
1. 餓漢單例模式
餓漢式在類(lèi)創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用亥鬓,以后不再改變,所以是線(xiàn)程安全的域庇。
public class StudentServiceImpl {
private static StudentServiceImpl studentService;
static{ //靜態(tài)塊
System.out.println("-----static 在類(lèi)加載到JVM中會(huì)觸發(fā)嵌戈,且觸發(fā)一次-----");
studentService = new StudentServiceImpl();
}
//為了控制創(chuàng)建對(duì)象的數(shù)量,將構(gòu)造方法設(shè)為私有
private StudentServiceImpl(){}
//單例模式中听皿,創(chuàng)建對(duì)象的方法一定是靜態(tài)的
public static StudentServiceImpl getInstane(){
return studentService;
}
//主函數(shù)測(cè)試
public static void main(String[] args) {
System.out.println(StudentServiceImpl.getInstane());
System.out.println(StudentServiceImpl.getInstane());
}
}
測(cè)試結(jié)果:
-----懶漢模式測(cè)試-----
實(shí)例被創(chuàng)建
StudentServiceImpl@4c4975
StudentServiceImpl@4c4975
2. 懶漢單例模式
在第一次調(diào)用的時(shí)候?qū)嵗约骸?
public class StudentServiceImpl {
private static StudentServiceImpl studentService;
//為了控制創(chuàng)建對(duì)象的數(shù)量熟呛,將構(gòu)造方法設(shè)為私有
private StudentServiceImpl(){
System.out.println("實(shí)例被創(chuàng)建");
}
//懶漢模式,在使用時(shí)才創(chuàng)建写穴,
public static StudentServiceImpl getInstane(){
if(studentService == null){
studentService = new StudentServiceImpl();
}
return studentService;
}
//主函數(shù)測(cè)試
public static void main(String[] args) {
System.out.println("-----懶漢模式測(cè)試-----");
System.out.println(StudentServiceImpl.getInstane());
System.out.println(StudentServiceImpl.getInstane());
}
}
注意:此時(shí)的程序是線(xiàn)程不安全的惰拱。
public class StudentServiceImpl {
private static StudentServiceImpl studentService;
//為了控制創(chuàng)建對(duì)象的數(shù)量,將構(gòu)造方法設(shè)為私有
private StudentServiceImpl(){
System.out.println("實(shí)例被創(chuàng)建");
}
//懶漢模式啊送,在使用時(shí)才創(chuàng)建偿短,synchronized: 同一事件只能有一個(gè)線(xiàn)程進(jìn)入臨界區(qū)
public static StudentServiceImpl getInstane(){
//if:為了提升性能
if(studentService == null){
synchronized (StudentServiceImpl.class) { //定制synchronized所屬
//if:保證創(chuàng)建對(duì)象數(shù)量唯一
if(studentService == null){
studentService = new StudentServiceImpl();
}
}
}
return studentService;
}
//主函數(shù)測(cè)試
public static void main(String[] args) {
System.out.println("-----懶漢模式測(cè)試-----");
System.out.println(StudentServiceImpl.getInstane());
System.out.println(StudentServiceImpl.getInstane());
}
}
為了保證線(xiàn)程安全,可以采用以下代碼:
3. 登記式單例
類(lèi)似Spring里面的方法馋没,將類(lèi)名注冊(cè)昔逗,下次從里面直接獲取。
public class Dengjishi {
private static Map<String,Dengjishi> map = new HashMap<String,Dengjishi>();
static{
Dengjishi single = new Dengjishi();
map.put(single.getClass().getName(), single);
}
//保護(hù)的默認(rèn)構(gòu)造子
protected Dengjishi(){
System.out.println("-----實(shí)例對(duì)象被創(chuàng)建-----");
}
//靜態(tài)工廠(chǎng)方法,返還此類(lèi)惟一的實(shí)例
public static Dengjishi getInstance(String name) {
if(name == null) {
name = Dengjishi.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Dengjishi) Class.forName(name).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
return map.get(name);
}
public static void main(String[] args) {
System.out.println(Dengjishi.getInstance("Dengjishi"));
System.out.println(Dengjishi.getInstance("Dengjishi"));
}
如何在父類(lèi)中為子類(lèi)自動(dòng)完成所有的hashcode和equals實(shí)現(xiàn)篷朵?這么做有何優(yōu)劣勾怒。
請(qǐng)結(jié)合OO設(shè)計(jì)理念,談?wù)勗L(fǎng)問(wèn)修飾符public声旺、private笔链、protected、default在應(yīng)用設(shè)計(jì)中的作用腮猖。
public: Java語(yǔ)言中訪(fǎng)問(wèn)限制最寬的修飾符鉴扫,一般稱(chēng)之為“公共的”。被其修飾的類(lèi)澈缺、屬性以及方法不僅可以跨類(lèi)訪(fǎng)問(wèn)坪创,而且允許跨包(package)訪(fǎng)問(wèn)炕婶。
private: Java語(yǔ)言中對(duì)訪(fǎng)問(wèn)權(quán)限限制的最窄的修飾符,一般稱(chēng)之為“私有的”莱预。被其修飾的類(lèi)柠掂、屬性以及方法只能被該類(lèi)的對(duì)象訪(fǎng)問(wèn),其子類(lèi)不能訪(fǎng)問(wèn)依沮,更不能允許跨包訪(fǎng)問(wèn)涯贞。
protect: 介于public 和 private 之間的一種訪(fǎng)問(wèn)修飾符,一般稱(chēng)之為“保護(hù)形”悉抵。被其修飾的類(lèi)肩狂、屬性以及方法只能被類(lèi)本身的方法及子類(lèi)訪(fǎng)問(wèn),即使子類(lèi)在不同的包中也可以訪(fǎng)問(wèn)姥饰。
default:即不加任何訪(fǎng)問(wèn)修飾符,通常稱(chēng)為“默認(rèn)訪(fǎng)問(wèn)模式“孝治。該模式下列粪,只允許在同一個(gè)包中進(jìn)行訪(fǎng)問(wèn)。
類(lèi)的只有兩種public,default(不同包不可以訪(fǎng)問(wèn))
public–都可訪(fǎng)問(wèn)(公有)
private–類(lèi)內(nèi)可訪(fǎng)問(wèn)(私有)
protected–包內(nèi)和子類(lèi)可訪(fǎng)問(wèn)(保護(hù))
不寫(xiě)(default)–包內(nèi)可訪(fǎng)問(wèn) (默認(rèn))
public>protected>default>private
Java 方法默認(rèn)訪(fǎng)問(wèn)級(jí)別 : 包訪(fǎng)問(wèn)
Java 類(lèi)默認(rèn)訪(fǎng)問(wèn)級(jí)別 : 包訪(fǎng)問(wèn)對(duì)于一個(gè)Class的成員變量或成員函數(shù)谈飒,如果不用public, protected, private中的任何一個(gè)修飾岂座,那么該成員獲得“默認(rèn)訪(fǎng)問(wèn)控制”級(jí)別,即package access (包訪(fǎng)問(wèn))杭措。
屬于package access的成員可以被同一個(gè)包中的其他類(lèi)訪(fǎng)問(wèn)费什,但不能被其他包的類(lèi)訪(fǎng)問(wèn)。
包訪(fǎng)問(wèn)的控制力弱于private手素,但強(qiáng)于protected鸳址。因?yàn)橐环矫妫灰亲宇?lèi)泉懦,不管子類(lèi)與父類(lèi)是否位于同一個(gè)包中稿黍,那么子類(lèi)都可以訪(fǎng)問(wèn)父 類(lèi)中的protected方法。但是一旦位于原類(lèi)的包外崩哩,不管是否是其子類(lèi)巡球,都無(wú)法訪(fǎng)問(wèn)其屬于package access級(jí)別的成員。而另一方面邓嘹,一個(gè)類(lèi)可以訪(fǎng)問(wèn)同一個(gè)包中另一個(gè)類(lèi)的package access成員酣栈,同時(shí)也能訪(fǎng)問(wèn)其protected成員。
(注:package是Java中的關(guān)鍵字汹押,雖然包訪(fǎng)問(wèn)也是一種訪(fǎng)問(wèn)控制級(jí)別矿筝,但關(guān)鍵字”package”只能用來(lái)表示類(lèi)屬于哪個(gè)包,而不能像”private”,”public”那樣放到成員變量或函數(shù)前面鲸阻,作為訪(fǎng)問(wèn)控制修飾符跋涣。)
訪(fǎng)問(wèn)級(jí)別保護(hù)的強(qiáng)度:public<protected<默認(rèn)<private
- 深拷貝和淺拷貝區(qū)別缨睡。
1、淺拷貝:對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行值傳遞陈辱,對(duì)引用數(shù)據(jù)類(lèi)型進(jìn)行引用傳遞般的拷貝奖年,此為淺拷貝。
2沛贪、深拷貝:對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行值傳遞陋守,對(duì)引用數(shù)據(jù)類(lèi)型,創(chuàng)建一個(gè)新的對(duì)象利赋,并復(fù)制其內(nèi)容水评,此為深拷貝。
- 數(shù)組和鏈表數(shù)據(jù)結(jié)構(gòu)描述媚送,各自的時(shí)間復(fù)雜度中燥。
兩種數(shù)據(jù)結(jié)構(gòu)都是線(xiàn)性表,在排序和查找等算法中都有廣泛的應(yīng)用
各自的特點(diǎn):
數(shù)組:
數(shù)組是將元素在內(nèi)存中連續(xù)存放塘偎,由于每個(gè)元素占用內(nèi)存相同疗涉,可以通過(guò)下標(biāo)迅速訪(fǎng)問(wèn)數(shù)組中任何元素。但是如果要在數(shù)組中增加一個(gè)元素吟秩,需要移動(dòng)大量元素咱扣,在內(nèi)存中空出一個(gè)元素的空間,然后將要增加的元素放在其中涵防。同樣的道理闹伪,如果想刪除一個(gè)元素,同樣需要移動(dòng)大量元素去填掉被移動(dòng)的元素壮池。如果應(yīng)用需要快速訪(fǎng)問(wèn)數(shù)據(jù)偏瓤,很少或不插入和刪除元素,就應(yīng)該用數(shù)組火窒。
鏈表:
鏈表恰好相反硼补,鏈表中的元素在內(nèi)存中不是順序存儲(chǔ)的,而是通過(guò)存在元素中的指針聯(lián)系到一起熏矿。比如:上一個(gè)元素有個(gè)指針指到下一個(gè)元素已骇,以此類(lèi)推,直到最后一個(gè)元素票编。如果要訪(fǎng)問(wèn)鏈表中一個(gè)元素褪储,需要從第一個(gè)元素開(kāi)始,一直找到需要的元素位置慧域。但是增加和刪除一個(gè)元素對(duì)于鏈表數(shù)據(jù)結(jié)構(gòu)就非常簡(jiǎn)單了鲤竹,只要修改元素中的指針就可以了。如果應(yīng)用需要經(jīng)常插入和刪除元素你就需要用鏈表數(shù)據(jù)結(jié)構(gòu)了。
數(shù)組和鏈表的區(qū)別:
1.從邏輯結(jié)構(gòu)角度來(lái)看:
a, 數(shù)組必須事先定義固定的長(zhǎng)度(元素個(gè)數(shù))辛藻,不能適應(yīng)數(shù)據(jù)動(dòng)態(tài)地增減的情況碘橘。當(dāng)數(shù)據(jù)增加時(shí),可能超出原先定義的元素個(gè)數(shù)吱肌;當(dāng)數(shù)據(jù)減少時(shí)痘拆,造成內(nèi)存浪費(fèi)。
b,鏈表動(dòng)態(tài)地進(jìn)行存儲(chǔ)分配氮墨,可以適應(yīng)數(shù)據(jù)動(dòng)態(tài)地增減的情況纺蛆,且可以方便地插入、刪除數(shù)據(jù)項(xiàng)规揪。(數(shù)組中插入桥氏、刪除數(shù)據(jù)項(xiàng)時(shí),需要移動(dòng)其它數(shù)據(jù)項(xiàng))
2.數(shù)組元素在棧區(qū)猛铅,鏈表元素在堆區(qū)字支;
3.從內(nèi)存存儲(chǔ)角度來(lái)看:
a,(靜態(tài))數(shù)組從棧中分配空間, 對(duì)于程序員方便快速,但自由度小。
b, 鏈表從堆中分配空間, 自由度大但申請(qǐng)管理比較麻煩.
數(shù)組利用下標(biāo)定位奸忽,時(shí)間復(fù)雜度為O(1)祥款,鏈表定位元素時(shí)間復(fù)雜度O(n);
數(shù)組插入或刪除元素的時(shí)間復(fù)雜度O(n)月杉,鏈表的時(shí)間復(fù)雜度O(1)。
- error和exception的區(qū)別抠艾,CheckedException苛萎,RuntimeException的區(qū)別。
1.Throwable 類(lèi)是 Java 語(yǔ)言中所有錯(cuò)誤或異常的超類(lèi)检号。它的兩個(gè)子類(lèi)是Error和Exception腌歉;
2.Error 是 Throwable 的子類(lèi),用于指示合理的應(yīng)用程序不應(yīng)該試圖捕獲的嚴(yán)重問(wèn)題齐苛。大多數(shù)這樣的錯(cuò)誤都是異常條件翘盖。雖然 ThreadDeath 錯(cuò)誤是一個(gè)“正規(guī)”的條件,但它也是 Error 的子類(lèi)凹蜂,因?yàn)榇蠖鄶?shù)應(yīng)用程序都不應(yīng)該試圖捕獲它馍驯。在執(zhí)行該方法期間,無(wú)需在其 throws 子句中聲明可能拋出但是未能捕獲的 Error 的任何子類(lèi)玛痊,因?yàn)檫@些錯(cuò)誤可能是再也不會(huì)發(fā)生的異常條件汰瘫。
3.Exception 類(lèi)及其子類(lèi)是 Throwable 的一種形式,它指出了合理的應(yīng)用程序想要捕獲的條件擂煞。
4.RuntimeException 是那些可能在 Java 虛擬機(jī)正常運(yùn)行期間拋出的異常的超類(lèi)混弥。可能在執(zhí)行方法期間拋出但未被捕獲的RuntimeException 的任何子類(lèi)都無(wú)需在 throws 子句中進(jìn)行聲明对省。它是Exception的子類(lèi)蝗拿。
5.方法重寫(xiě)時(shí):在子類(lèi)中一個(gè)重寫(xiě)的方法可能只拋出父類(lèi)中聲明過(guò)的異沉滥螅或者異常的子類(lèi)
二者的不同之處:
Exception:
1.可以是可被控制(checked) 或不可控制的(unchecked)
2.表示一個(gè)由程序員導(dǎo)致的錯(cuò)誤
3.應(yīng)該在應(yīng)用程序級(jí)被處理
Error:
1.總是不可控制的(unchecked)
2.經(jīng)常用來(lái)用于表示系統(tǒng)錯(cuò)誤或低層資源的錯(cuò)誤
3.如何可能的話(huà),應(yīng)該在系統(tǒng)級(jí)被捕捉
- 請(qǐng)列出5個(gè)運(yùn)行時(shí)異常哀托。
ClassCastException(類(lèi)轉(zhuǎn)換異常)
IndexOutOfBoundsException(數(shù)組越界)
NullPointerException(空指針)
ArrayStoreException(數(shù)據(jù)存儲(chǔ)異常惦辛,操作數(shù)組時(shí)類(lèi)型不一致)
還有IO操作的BufferOverflowException異常
在自己的代碼中,如果創(chuàng)建一個(gè)java.lang.String類(lèi)萤捆,這個(gè)類(lèi)是否可以被類(lèi)加載器加載裙品?為什么。
說(shuō)一說(shuō)你對(duì)java.lang.Object對(duì)象中hashCode和equals方法的理解俗或。在什么場(chǎng)景下需要重新實(shí)現(xiàn)這兩個(gè)方法市怎。
這里有一個(gè)約定:hashCode相等,對(duì)象不一定相等辛慰,對(duì)象相等区匠,hashCode一定相等。
為什么需要hashCode?
1帅腌、 在map等集合中驰弄,通過(guò)hashCode可以很快的找到元素的位置
2、比較兩個(gè)對(duì)象是否相等時(shí)速客,先通過(guò)hashCode比較戚篙,這樣速度比較快,如果不相等直接返回false
為什么要重載equal方法溺职?
Object對(duì)象默認(rèn)比較的是兩個(gè)對(duì)象的內(nèi)存地址是否一樣岔擂,正常大家應(yīng)該比較的是對(duì)象里面的值是否一樣。
為什么重載hashCode方法浪耘?
如果我們只重寫(xiě)equals乱灵,而不重寫(xiě)hashCode方法,就會(huì)出現(xiàn)兩個(gè)對(duì)象一樣七冲,但是hashCode不相等情況痛倚,在map等集合中應(yīng)用時(shí),就會(huì)出現(xiàn)問(wèn)題澜躺,因?yàn)閔ashCode不一樣蝉稳,兩個(gè)一樣的對(duì)象會(huì)放到集合中。
- 在jdk1.5中苗踪,引入了泛型颠区,泛型的存在是用來(lái)解決什么問(wèn)題。
類(lèi)型的參數(shù)化通铲,就是可以把類(lèi)型像方法的參數(shù)那樣傳遞毕莱。這一點(diǎn)意義非凡。
泛型使編譯器可以在編譯期間對(duì)類(lèi)型進(jìn)行檢查以提高類(lèi)型安全,減少運(yùn)行時(shí)由于對(duì)象類(lèi)型不匹配引發(fā)的異常朋截。
- 這樣的a.hashcode() 有什么用蛹稍,與a.equals(b)有什么關(guān)系。
hashcode()方法提供了對(duì)象的hashCode值部服,是一個(gè)native方法唆姐,返回的默認(rèn)值與System.identityHashCode(obj)一致。
通常這個(gè)值是對(duì)象頭部的一部分二進(jìn)制位組成的數(shù)字廓八,具有一定的標(biāo)識(shí)對(duì)象的意義存在奉芦,但絕不定于地址。
作用是:用一個(gè)數(shù)字來(lái)標(biāo)識(shí)對(duì)象剧蹂。比如在HashMap声功、HashSet等類(lèi)似的集合類(lèi)中,如果用某個(gè)對(duì)象本身作為Key宠叼,即要基于這個(gè)對(duì)象實(shí)現(xiàn)Hash的寫(xiě)入和查找先巴,那么對(duì)象本身如何實(shí)現(xiàn)這個(gè)呢?就是基于hashcode這樣一個(gè)數(shù)字來(lái)完成的冒冬,只有數(shù)字才能完成計(jì)算和對(duì)比操作伸蚯。
hashcode是否唯一
hashcode只能說(shuō)是標(biāo)識(shí)對(duì)象,在hash算法中可以將對(duì)象相對(duì)離散開(kāi)简烤,這樣就可以在查找數(shù)據(jù)的時(shí)候根據(jù)這個(gè)key快速縮小數(shù)據(jù)的范圍剂邮,但hashcode不一定是唯一的,所以hash算法中定位到具體的鏈表后横侦,需要循環(huán)鏈表抗斤,然后通過(guò)equals方法來(lái)對(duì)比Key是否是一樣的。
equals與hashcode的關(guān)系
equals相等兩個(gè)對(duì)象丈咐,則hashcode一定要相等。但是hashcode相等的兩個(gè)對(duì)象不一定equals相等
有沒(méi)有可能2個(gè)不相等的對(duì)象有相同的hashcode龙宏。
Java中的HashSet內(nèi)部是如何工作的棵逊。
什么是序列化,怎么序列化银酗,為什么序列化辆影,反序列化會(huì)遇到什么問(wèn)題,如何解決黍特。
## 用來(lái)處理對(duì)象流
簡(jiǎn)單來(lái)說(shuō)序列化就是一種用來(lái)處理對(duì)象流的機(jī)制蛙讥,所謂對(duì)象流也就是將對(duì)象的內(nèi)容進(jìn)行流化,流的概念這里不用多說(shuō)(就是I/O)灭衷,我們可以對(duì)流化后的對(duì)象進(jìn)行讀寫(xiě)操作次慢,也可將流化后的對(duì)象傳輸于網(wǎng)絡(luò)之間**(注:要想將對(duì)象傳輸于網(wǎng)絡(luò)必須進(jìn)行流化)**!在對(duì)對(duì)象流進(jìn)行讀寫(xiě)操作時(shí)會(huì)引發(fā)一些問(wèn)題,而序列化機(jī)制正是用來(lái)解決這些問(wèn)題的迫像!
## 問(wèn)題的引出:
如上所述劈愚,讀寫(xiě)對(duì)象會(huì)有什么問(wèn)題呢?比如:我要將對(duì)象寫(xiě)入一個(gè)磁盤(pán)文件而后再將其讀出來(lái)會(huì)有什么問(wèn)題嗎闻妓?別急菌羽,其中一個(gè)最大的問(wèn)題就是**對(duì)象引用**!
舉個(gè)例子來(lái)說(shuō):假如我有兩個(gè)類(lèi)由缆,分別是A和B注祖,**B類(lèi)中含有一個(gè)指向A類(lèi)對(duì)象的引用**,現(xiàn)在我們對(duì)兩個(gè)類(lèi)進(jìn)行實(shí)例化{ A a = new A(); B b = new B(); }均唉,這時(shí)在內(nèi)存中實(shí)際上分配了兩個(gè)空間是晨,一個(gè)存儲(chǔ)對(duì)象a,一個(gè)存儲(chǔ)對(duì)象b浸卦,**接下來(lái)我們想將它們寫(xiě)入到磁盤(pán)的一個(gè)文件中去**
就在寫(xiě)入文件時(shí)出現(xiàn)了問(wèn)題署鸡!**因?yàn)閷?duì)象b包含對(duì)對(duì)象a的引用**,所以系統(tǒng)**會(huì)自動(dòng)的將a的數(shù)據(jù)復(fù)制一份到b中**限嫌,這樣的話(huà)當(dāng)我們從文件中恢復(fù)對(duì)象時(shí)(也就是重新加載到內(nèi)存中)時(shí)靴庆,**內(nèi)存分配了三個(gè)空間**,而對(duì)象a同時(shí)在內(nèi)存中存在兩份怒医,想一想后果吧炉抒,如果我想修改對(duì)象a的數(shù)據(jù)的話(huà),**那不是還要搜索它的每一份拷貝來(lái)達(dá)到對(duì)象數(shù)據(jù)的一致性稚叹,這不是我們所希望的**焰薄!
### 以下序列化機(jī)制的解決方案:
1.保存到磁盤(pán)的所有對(duì)象都獲得一個(gè)序列號(hào)(1, 2, 3等等)
2.當(dāng)要保存一個(gè)對(duì)象時(shí),先檢查該對(duì)象是否被保存了扒袖。
3.如果以前保存過(guò)塞茅,只需寫(xiě)入"與已經(jīng)保存的具有序列號(hào)x的對(duì)象相同"的標(biāo)記,否則季率,保存該對(duì)象
通過(guò)以上的步驟序列化機(jī)制解決了對(duì)象引用的問(wèn)題野瘦!
## 序列化的實(shí)現(xiàn)
將需要被序列化的類(lèi)實(shí)現(xiàn)Serializable接口,該接口沒(méi)有需要實(shí)現(xiàn)的方法飒泻,implements Serializable只是為了標(biāo)注該對(duì)象是可被序列化的鞭光,然后使用一個(gè)輸出流(如:FileOutputStream)來(lái)構(gòu)造一個(gè) ObjectOutputStream(對(duì)象流)對(duì)象,接著泞遗,使用ObjectOutputStream對(duì)象的writeObject(Object obj)方法就可以將參數(shù)為obj的對(duì)象寫(xiě)出(即保存其狀態(tài))惰许,要恢復(fù)的話(huà)則用輸入流。
- java8的新特性史辙。
look