JVM
乾胶、JDK
和JRE
的區(qū)別
JVM
(java
虛擬機(jī))是運(yùn)行java
字節(jié)碼的虛擬機(jī),不同的系統(tǒng)有特定的 JVM
實現(xiàn)武通,比如常見的 HotSpot VM
就是 JVM
的一種實現(xiàn),除此之外還有 J9 VM
(常用于 IBM
硬件平臺)等。
JRE
是 java
運(yùn)行時環(huán)境懂盐,包括 JVM
、java
庫糕档、java
命令和其他一些基礎(chǔ)構(gòu)件莉恼,但是 JRE
不能用于創(chuàng)建新程序。
JDK
是功能齊全的 java SDK
速那,包括 JRE
俐银、編譯器(javac
)和一些工具(如 javadoc
和jdb
,能夠創(chuàng)建和編譯程序端仰。
靜態(tài)方法為什么不能調(diào)用非靜態(tài)成員捶惜?
靜態(tài)方法在類加載的時候就會分配內(nèi)存,可以通過類名去訪問荔烧,而非靜態(tài)成員只有在對象實例化之后才存在吱七,且需要通過類的實例對象去訪問。因此在非靜態(tài)成員不存在的時候鹤竭,靜態(tài)方法就已經(jīng)存在了踊餐,此時靜態(tài)方法調(diào)用還不存在的非靜態(tài)成員是非法的。
重載與重寫的區(qū)別
重載:相同的函數(shù)名臀稚,參數(shù)列表必須不同(返回值類型可以不同)(發(fā)生在編譯器)
重寫:方法名吝岭、參數(shù)列表必須相同(子類方法的返回值類型應(yīng)比父類方法返回值類型更小或者相等,如果父類方法返回值類型是 void
或者基本數(shù)據(jù)類型則子類方法返回值類型不可更改;拋出的異常范圍小于等于父類窜管;訪問修飾符范圍大于等于父類)(private
/ final
/ static
修飾的方法不能被重寫散劫,構(gòu)造方法無法被重寫可以重載)(發(fā)生在運(yùn)行期)
==
和equals()
的區(qū)別
==
對于基本數(shù)據(jù)類型來說,比較的是變量的值幕帆;對于引用數(shù)據(jù)類型來說比較的是對象的內(nèi)存地址获搏。
equals()
不能用來判斷基本數(shù)據(jù)類型變量,只能用于判斷兩個對象是否相等蜓肆。如果沒有重寫equals()
則 equals()
相當(dāng)于==
颜凯,比較的是對象的內(nèi)存地址,如果重寫了 equals()
則可以比較對象的屬性仗扬,即屬性相等則認(rèn)為兩個對象相等症概。
為什么重寫 equals()
必須重寫 hashCode()
兩個對象的 hashCode
相同,并且equals()
返回 True
早芭,我們才認(rèn)為這兩個對象是相等彼城。equals()
返回 True
而 hashCode
并不一定相等,因此需要重寫 hashCode()
退个。
而且在 HashMap
中募壕,判斷是否有重復(fù)元素時,是先通過 hashCode
計算元素位置语盈,再通過 equals()
來判斷元素是否相等的舱馅。
java
中的基本數(shù)據(jù)類型
byte
、 short
刀荒、 int
代嗤、 long
、 float
缠借、 double
干毅、char
、 boolean
基本數(shù)據(jù)類型的線程安全
8種基本數(shù)據(jù)類型中 long
類型和 double
類型是64位的泼返,因此在32位的系統(tǒng)下讀寫操作是分兩次的硝逢,因此不是線程安全的。如果硬件绅喉,操作系統(tǒng)渠鸽,JVM
都是64位的,則單次可以操作64位的數(shù)據(jù)柴罐,讀寫應(yīng)該是原子性的拱绑。
包裝類型的常量池技術(shù)
Byte
, Short
, Integer
, Long
這 4 種包裝類默認(rèn)創(chuàng)建了數(shù)值 [-128,127] 的相應(yīng)類型的緩存數(shù)據(jù)丽蝎,Character
創(chuàng)建了數(shù)值在 [0,127] 范圍的緩存數(shù)據(jù),Boolean
直接返回 True
or False
。兩種浮點類型的包裝類 Float
和 Double
沒有實現(xiàn)常量池技術(shù)屠阻。
自動裝箱與拆箱
裝箱:將基本類型用對應(yīng)的引用類型包裝起來
Integer i=10;//等價于Integer i=Integer.valueOf(10);
拆箱:將包裝類型轉(zhuǎn)換為基本數(shù)據(jù)類型
int n=i;//等價于int n=i.intValue();
在進(jìn)行比較前會自動拆箱红省。
class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 將3自動裝箱成Integer類型
int c = 3;
System.out.println(a == b); // false 兩個引用沒有引用同一對象
System.out.println(a == c); // true a自動拆箱成int類型再和c比較
}
}
面向?qū)ο笕筇卣?/h5>
封裝:封裝是指把一個對象的狀態(tài)信息隱藏在對象內(nèi)部,不允許外界直接訪問国觉,只提供一些可以被外界訪問的方法來操作對象屬性吧恃。
繼承:用已經(jīng)存在的類作為基礎(chǔ)來創(chuàng)建新類,新類可以增加新的屬性和方法麻诀,也可以用父類的功能痕寓,但不能選擇性地繼承父類。繼承提高了代碼的可重用性和可維護(hù)性蝇闭,節(jié)省了創(chuàng)建新類的時間呻率,提高了開發(fā)效率。
多態(tài):多態(tài)具體表現(xiàn)為父類引用指向子類實例呻引。
對象類型和引用類型之間具有繼承(類)/ 實現(xiàn)(接口)的關(guān)系礼仗;
引用類型變量發(fā)出的方法調(diào)用的到底是哪個類中的方法,必須在程序運(yùn)行期才能確定逻悠;
多態(tài)不能調(diào)用只在子類存在但在父類不存在的方法元践;
如果子類重寫了父類的方法,真正執(zhí)行的是子類的方法童谒,否則執(zhí)行的是父類的方法单旁。
java
中的權(quán)限修飾符
修飾符修飾方法或?qū)傩裕?/p>
public:允許跨類和跨包訪問
default:只允許在同一個包中跨類
protected:只允許在類或者子類中訪問,子類可以在不同包中
private:只允許在本類中訪問
注意:protected和private不能用來修飾一般的類饥伊,否則會報編譯器不允許的錯誤象浑,內(nèi)部類除外。
補(bǔ)充:想要這個類的屬性和方法可以被任何子類繼承撵渡,我就用protected融柬。想要這個類的屬性和方法不能被任何子類繼承,我就用private趋距。同理粒氧,想要這個類被繼承,我就用abstract节腐。我不想這個類被繼承外盯,就用final。
https://www.cnblogs.com/AleiCui/p/12792565.html
接口和抽象類的區(qū)別
1翼雀、接口的意義在于可以對行為進(jìn)行抽象饱苟,而抽象類是對事物進(jìn)行抽象;一個類實現(xiàn)了接口狼渊,則說明這個類擁有了這個接口中的所有行為箱熬,如果接口中改變了行為类垦,則這個類也要改變。如果一個類繼承了抽象類城须,則這個類就可以使用抽象類中的方法蚤认,并可以重新去實現(xiàn)抽象類中的抽象方法,如果抽象類中改變了非抽象方法糕伐,這個類不用做其他改變砰琢。
2、抽象類中可以有靜態(tài)變量良瞧,實例變量陪汽,而接口中必須是final類型修飾的靜態(tài)變量
3、抽象類中可以抽象方法也可以有非抽象方法褥蚯,接口中只能有抽象方法
4挚冤、抽象類中有構(gòu)造函數(shù),而接口中沒有構(gòu)造函數(shù)
5遵岩、抽象類中可以有代碼塊你辣,接口中不能有代碼塊
6、抽象類只能被單繼承尘执,接口可以被多實現(xiàn)舍哄。
注意:雖然抽象類中有構(gòu)造函數(shù),但是抽象類是不能直接實例化的誊锭。只有在子類繼承了這個抽象類之后表悬,子類實例化的時候,才會調(diào)用(父類)抽象類中的構(gòu)造函數(shù)丧靡。
深拷貝和淺拷貝
淺拷貝:復(fù)制一個對象時蟆沫,創(chuàng)建了一個新的對象,對于原對象里的基本數(shù)據(jù)類型温治,將其值拷貝進(jìn)來饭庞,對于原對象里的引用數(shù)據(jù)類型,將其引用拷貝進(jìn)來熬荆。這樣如果原對象更改了引用對象舟山,會對拷貝對象產(chǎn)生影響。
深拷貝:對于原對象里的引用對象卤恳,不是拷貝引用累盗,而是創(chuàng)建一個新的對象,這樣原對象更改了引用對象突琳,也不會對拷貝對象產(chǎn)生影響若债。
引用拷貝:兩個不同的引用指向同一個對象
通過實現(xiàn) cloneable
接口,重寫 clone()
方法來實現(xiàn)淺拷貝和深拷貝拆融。
實現(xiàn)深拷貝的兩種方式
1)實現(xiàn) Cloneable
接口蠢琳,重寫 Object
類中的 clone()
啊终,通過層層克隆來實現(xiàn)深拷貝。
2)通過序列化(都要實現(xiàn) Serializable
接口)的方法挪凑,將對象寫入到流中孕索,再從流中讀取出來。這種方式雖然效率低下躏碳,但是可以實現(xiàn)真正意義上的深度克隆。
https://juejin.cn/post/6982209049416859678 "通過序列化和反序列化實現(xiàn)深拷貝"
object
類常見的方法有哪些
public final native Class<?> getClass()//native方法散怖,用于返回當(dāng)前運(yùn)行時對象的Class對象菇绵,使用了final關(guān)鍵字修飾,故不允許子類重寫镇眷。
public native int hashCode() //native方法咬最,用于返回對象的哈希碼,主要使用在哈希表中欠动,比如JDK中的HashMap永乌。
public boolean equals(Object obj)//用于比較2個對象的內(nèi)存地址是否相等,String類對該方法進(jìn)行了重寫用戶比較字符串的值是否相等具伍。
protected native Object clone() throws CloneNotSupportedException//naitive方法翅雏,用于創(chuàng)建并返回當(dāng)前對象的一份拷貝。一般情況下人芽,對于任何對象 x望几,表達(dá)式 x.clone() != x 為true,x.clone().getClass() == x.getClass() 為true萤厅。Object本身沒有實現(xiàn)Cloneable接口橄抹,所以不重寫clone方法并且進(jìn)行調(diào)用的話會發(fā)生CloneNotSupportedException異常。
public String toString()//返回類的名字@實例的哈希碼的16進(jìn)制的字符串惕味。建議Object所有的子類都重寫這個方法楼誓。
public final native void notify()//native方法,并且不能重寫名挥。喚醒一個在此對象監(jiān)視器上等待的線程(監(jiān)視器相當(dāng)于就是鎖的概念)疟羹。如果有多個線程在等待只會任意喚醒一個。
public final native void notifyAll()//native方法躺同,并且不能重寫阁猜。跟notify一樣,唯一的區(qū)別就是會喚醒在此對象監(jiān)視器上等待的所有線程蹋艺,而不是一個線程剃袍。
public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重寫捎谨。暫停線程的執(zhí)行民效。注意:sleep方法沒有釋放鎖憔维,而wait方法釋放了鎖 。timeout是等待時間畏邢。
public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos參數(shù)业扒,這個參數(shù)表示額外時間(以毫微秒為單位,范圍是 0-999999)舒萎。 所以超時的時間還需要加上nanos毫秒程储。
public final void wait() throws InterruptedException//跟之前的2個wait方法一樣,只不過該方法一直等待臂寝,沒有超時時間這個概念
protected void finalize() throws Throwable { }//實例被垃圾回收器回收的時候觸發(fā)的操作
String
章鲤、 StringBuilder
、 StringBuffer
的區(qū)別
可變性:String
是不可變的咆贬。String
類中保存字符串的數(shù)組被 final
修飾且為私有败徊,并且并沒有向外部提供修改這個字符數(shù)組的方法;且 String
類被final
修飾掏缎,使其不能被繼承皱蹦,進(jìn)而避免了子類破壞 String
。因此眷蜈,當(dāng)String
對象發(fā)生改變時沪哺,都會重新生成一個新的 String
對象,然后將引用指向新的 String
對象端蛆。(對于 final
修飾的引用類型凤粗,其指向的內(nèi)存地址不可變,但是對象內(nèi)容是可以發(fā)生變化的)
StringBuilder
和 StringBuffer
也是使用字符數(shù)組來保存字符串今豆,但是沒有使用 final
和 private
關(guān)鍵字修飾嫌拣,且還提供了修改字符串的方法,是可變的呆躲。
線程安全性:String
對象是不可變的异逐,可視為常量,因此是線程安全的插掂。StringBuffer
對方法加了同步鎖灰瞻,也是線程安全的。而 StringBuilder
沒有對方法加鎖辅甥,是非線程安全的酝润。
為什么要將String設(shè)置成不可變
1、不可變的對象可以當(dāng)做散列表的key
2璃弄、不可變的對象本身就是線程安全的要销,不需要額外進(jìn)行同步
反射會改變對象的不可變性。
String
的 hashCode
是怎么生成的
https://www.huaweicloud.com/articles/e6ae1a928b6bea00bc83bd481cafa336.html
字符串常量池
字符串常量池是 JVM
為了提升性能和減少內(nèi)存消耗針對 String
類專門開辟的一塊區(qū)域夏块,主要目的是為了避免字符串的重復(fù)創(chuàng)建疏咐。
tring aa = "ab"; // 放在常量池中
String bb = "ab"; // 從常量池中查找
System.out.println(aa==bb);// true
JDK1.7
之前運(yùn)行時常量池邏輯上包含字符串常量池纤掸,放在方法區(qū)中。JDK1.7
時字符串常量池被放到了堆中浑塞。
泛型
泛型是 JDK5
中引入的一個新特性借跪,泛型提供了編譯時類型安全檢查機(jī)制,該機(jī)制允許編譯時檢測到非法類型酌壕,其本質(zhì)是參數(shù)化類型掏愁,即將所操作的數(shù)據(jù)類型指定為一個參數(shù)。
但是仅孩,在需要轉(zhuǎn)型的時候托猩,編譯器會根據(jù)所操作數(shù)據(jù)的類型自動實行安全地強(qiáng)制轉(zhuǎn)型,所以在運(yùn)行期辽慕,所有的泛型信息都會被擦除,即常說的泛型擦除赦肃。
泛形要求能包容的是引用類型溅蛉,而基本類型在 java
里不屬于對象。所以泛型不能是基本數(shù)據(jù)類型他宛。(所有對象都繼承了 Object
類型)
(有泛型類船侧、泛型接口、泛型方法)
反射
反射可以讓我們在運(yùn)行期分析類以及執(zhí)行類中的方法厅各,通過反射可以獲取任意一個類的所有屬性和方法,并可以調(diào)用。這也使得反射引入了安全問題蜕劝,比如可以無視泛型參數(shù)的安全檢查觉吭。
反射的應(yīng)用場景
Spring
、Spring Boot
憔古、MyBatis
等框架都大量使用了反射機(jī)載遮怜。
動態(tài)代理和注解也是基于反射實現(xiàn)的。
注解
注解可以用于修飾類鸿市、方法或者屬性锯梁,其本質(zhì)是一個繼承了 Annotation
的特殊接口。
注解只有被解析后才會生效焰情,常見的解析方式有兩種:
編譯期直接掃描:編譯器在編譯代碼時掃描對應(yīng)的注解并處理陌凳,比如某個方法使用 @Override
注解,編譯器在編譯的時候就會檢測當(dāng)前的方法是否重寫了父類對應(yīng)的方法内舟。
運(yùn)行期通過反射處理:框架中自帶的注解都是通過反射處理的合敦。
異常
java
異常類的層次結(jié)構(gòu):
Exception
:程序本身可以處理的異常,可以通過 catch
來進(jìn)行捕獲谒获。Exception
又可以分為 Checked Exception
(受檢查異常蛤肌,必須處理否則無法通過編譯)和 Unchecked Exception
(非受檢查異常壁却,可以不處理,RuntimeException
及其子類都是非受檢異常)裸准。
Error
:程序無法處理的錯誤展东,沒辦法通過 catch
來進(jìn)行捕獲,發(fā)生錯誤時炒俱,JVM
一般會選擇線程終止盐肃。
try-catch-finally
的使用及注意事項
try
塊: 用于捕獲異常。其后可接零個或多個 catch
塊权悟,如果沒有 catch
塊砸王,則必須跟一個 finally
塊。
catch
塊: 用于處理 try 捕獲到的異常峦阁。
finally
塊: 無論是否捕獲或處理異常谦铃,finally
塊里的語句都會被執(zhí)行。當(dāng)在 try
塊或 catch
塊中遇到 return
語句時榔昔,finally
語句塊將在方法返回之前被執(zhí)行驹闰,且返回值在 finally
塊執(zhí)行之前就已經(jīng)確定了,不會因為 finally
塊的執(zhí)行而發(fā)生改變撒会。
注意:不要在 finally 語句塊中使用 return! 當(dāng) try
語句和 finally
語句中都有 return
語句時嘹朗,try
語句塊中的 return
語句不會被執(zhí)行。
finally
中的代碼一定會執(zhí)行嗎
在 finally
之前虛擬機(jī)被終止運(yùn)行的話(或者線程死亡诵肛、CPU關(guān)閉)屹培,finally
中的代碼就不會被執(zhí)行。
聽說過 try-with-resources
嗎
在面對必須要關(guān)閉的資源時怔檩,應(yīng)該優(yōu)先考慮使用 try-with-resources
褪秀,可以讓我們更容易編寫必須要關(guān)閉的資源的代碼。
序列化與反序列化(串行化與并行化)
序列化:將對象(實例化后的 Class
類)轉(zhuǎn)換為字節(jié)序列(二進(jìn)制字節(jié)流)珠洗,序列化后可以持久化到磁盤上溜歪,也可以在網(wǎng)絡(luò)上進(jìn)行傳輸。
反序列化:將字節(jié)序列恢復(fù)成對象许蓖。
https://juejin.cn/post/6844904176263102472
實現(xiàn)了Serializable
接口的類都是可以被序列化的蝴猪!
- 凡是被
static
修飾的字段是不會被序列化的 - 凡是被
transient
修飾符修飾的字段也是不會被序列化的(transient
只能用于修飾字段)
如果在序列化某個對象時,不希望某個字段被序列化(比如這個字段存放的是隱私值膊爪,如:密碼
等)自阱,那這時就可以用transient
修飾符來修飾該字段。這樣在反序列化時米酬,這個字段就會為被置成默認(rèn)值而非真實值沛豌。
java
中的 IO
流
按照流向可以分為輸入流和輸出流;
按照操作單元可以分為字符流和字節(jié)流;
信息的最小存儲單元是字節(jié)加派,為什么需要有字符流操作
字符是由字節(jié)轉(zhuǎn)換得到的叫确,轉(zhuǎn)換過程是比較耗時的,且如果不知道編碼類型就很容易出現(xiàn)亂碼問題芍锦,所以 I/O
流就提供了一個直接操作字符的接口竹勉,方便對字符進(jìn)行流操作。
如果音頻文件娄琉、圖片等媒體文件用字節(jié)流比較好次乓,如果涉及到字符的話使用字符流比較好。
內(nèi)部類
內(nèi)部類是一個編譯期的概念孽水,一旦編譯成功票腰,就會成為完全不同的兩類。
http://www.reibang.com/p/a9467e690eb0
1女气、由于內(nèi)部類可以直接訪問外部類中的所有變量杏慰,因此我們可以通過內(nèi)部類來訪問外部類中的私有屬性和方法。(閉包)
2炼鞠、外部類對內(nèi)部類的封裝主要表現(xiàn)為逃默,在其它地方訪問內(nèi)部類,受到了外部類的限制簇搅。
3、使用內(nèi)部類可以間接地進(jìn)行多重繼承软吐。
內(nèi)部類變量的可見性
1)靜態(tài)內(nèi)部類:創(chuàng)建在類中瘩将,和成員變量同級,帶有static關(guān)鍵字
外部類和靜態(tài)內(nèi)部類沒有訪問限制凹耙。
2)成員內(nèi)部類:創(chuàng)建在類中姿现,和成員變量同級
外部類不能直接使用內(nèi)部類中的成員變量,需要創(chuàng)建出內(nèi)部類對象后再訪問肖抱,而創(chuàng)建內(nèi)部類對象需要用外部類對象來創(chuàng)建备典。
內(nèi)部類可以直接訪問外部類中的成員變量和靜態(tài)變量,內(nèi)部類中不能有靜態(tài)變量意述。
3)局部內(nèi)部類:創(chuàng)建在方法內(nèi)部
外部類不能訪問內(nèi)部類中的變量
內(nèi)部類可以直接訪問外部類中成員變量和靜態(tài)變量提佣,不能含有靜態(tài)變量
4)匿名內(nèi)部類:建立在方法內(nèi)部,且沒有名稱
場景:只用到匿名內(nèi)部類的一個實例荤崇,且在類定義后馬上就使用到
不能有構(gòu)造方法和靜態(tài)成員拌屏、靜態(tài)方法,局部內(nèi)部類中的規(guī)則也作用與匿名內(nèi)部類术荤。