前言
疫情確診的人數(shù)每天都在增加,離去的人也在增多鄙信,這個(gè)世界上有很多事我們無(wú)能為力也無(wú)從選擇瞪醋,日升日落,白晝黑夜装诡,我們能看見(jiàn)白晝中的光芒银受,我們也能看見(jiàn)黑暗里的流氓。暮色四合鸦采,齷齪八開(kāi)宾巍。鮮花還是塑料花,香或臭渔伯,當(dāng)潮水散去顶霞,現(xiàn)在即歷史,而歷史通常是由后人說(shuō)的锣吼。
所以還是上次跟鄉(xiāng)親們說(shuō)的选浑,我們不要傳播未經(jīng)證實(shí)或者不該傳播的消息,輿論的力量是我們無(wú)法估計(jì)的玄叠,有些也是我們無(wú)法承擔(dān)的古徒,所以鄉(xiāng)親們也要重視起來(lái),點(diǎn)到即止读恃。無(wú)法控制別人隧膘,但可以做好自己,幫不了別人寺惫,但可以不禍害別人舀寓。
我不是個(gè)喜歡蹭熱度的人,上面那段話鄉(xiāng)親們看看就好肌蜻,現(xiàn)階段最重要的就是老老實(shí)實(shí)待在家里,不聚集必尼,也盡量不出門蒋搜,自己和家人都要做好安全防護(hù),老百姓經(jīng)歷了太多風(fēng)風(fēng)雨雨判莉,相信這次一定也會(huì)安然無(wú)恙的渡過(guò)此劫豆挽。那待在家里的這段時(shí)間,如果能遠(yuǎn)程辦公的券盅,就做好公司交代的事帮哈,無(wú)法辦公的鄉(xiāng)親們也不要停止學(xué)習(xí),因?yàn)橐咔檫^(guò)去之后锰镀,一定會(huì)有巨大的變動(dòng)或者機(jī)會(huì)來(lái)臨娘侍,而到那時(shí)咖刃,你準(zhǔn)備好了嗎?
這幾天一直在想憾筏,碼之初能做點(diǎn)什么嚎杨?最終決定在這個(gè)期間就推出一個(gè)面試系列,都是經(jīng)過(guò)我精心整理的氧腰,希望能給鄉(xiāng)親們一點(diǎn)幫助枫浙。下面進(jìn)入正題。
高頻面試題
1古拴、說(shuō)說(shuō)對(duì)象的四中引用箩帚?
1)強(qiáng)引用只要引用存在,垃圾回收器永遠(yuǎn)不會(huì)回收黄痪。
Object obj = new Object();
User user=new User();
可直接通過(guò)obj取得對(duì)應(yīng)的對(duì)象 如 obj.equels(new Object()); 而這樣 obj 對(duì)象對(duì)后面 new Object 的一個(gè)強(qiáng) 引用紧帕,只有當(dāng) obj 這個(gè)引用被釋放之后,對(duì)象才會(huì)被釋放掉满力,這也是我們經(jīng)常所用到的編碼形式焕参。
2)**軟引用 **非必須引用,內(nèi)存溢出之前進(jìn)行回收油额,可以通過(guò)以下代碼實(shí)現(xiàn)
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;sf.get();//有時(shí)候會(huì)返回null
這時(shí)候sf是對(duì)obj的一個(gè)軟引用叠纷,通過(guò)sf.get()方法可以取到這個(gè)對(duì)象,當(dāng)然潦嘶,當(dāng)這個(gè)對(duì)象被標(biāo)記為需要回收的對(duì)象 時(shí)涩嚣,則返回null; 軟引用主要用戶實(shí)現(xiàn)類似緩存的功能,在內(nèi)存足夠的情況下直接通過(guò)軟引用取值掂僵,無(wú)需從繁忙的 真實(shí)來(lái)源查詢數(shù)據(jù)航厚,提升速度;當(dāng)內(nèi)存不足時(shí),自動(dòng)刪除這部分緩存數(shù)據(jù)锰蓬,從真正的來(lái)源查詢這些數(shù)據(jù)幔睬。
3)**弱引用 **第二次垃圾回收時(shí)回收,可以通過(guò)如下代碼實(shí)現(xiàn)
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;wf.get();//有時(shí)候會(huì)返回null
wf.isEnQueued();//返回是否被垃圾回收器標(biāo)記為即將回收的垃圾
弱引用是在第二次垃圾回收時(shí)回收芹扭,短時(shí)間內(nèi)通過(guò)弱引用取對(duì)應(yīng)的數(shù)據(jù)麻顶,可以取到,當(dāng)執(zhí)行過(guò)第二次垃圾回收時(shí)舱卡, 將返回null辅肾。弱引用主要用于監(jiān)控對(duì)象是否已經(jīng)被垃圾回收器標(biāo)記為即將回收的垃圾,可以通過(guò)弱引用的isEnQueued 方法返回對(duì)象是否被垃圾回收器標(biāo)記轮锥。
ThreadLocal 中有使用到弱引用:
public class ThreadLocal<T> {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v; }
}
//....
}
//.....
}
4)虛引用垃圾回收時(shí)回收矫钓,無(wú)法通過(guò)引用取到對(duì)象值,可以通過(guò)如下代碼實(shí)現(xiàn):
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永遠(yuǎn)返回null
pf.isEnQueued();//返回是否從內(nèi)存中已經(jīng)刪除
虛引用是每次垃圾回收的時(shí)候都會(huì)被回收,通過(guò)虛引用的get方法永遠(yuǎn)獲取到的數(shù)據(jù)為null新娜,因此也被成為幽靈引 用赵辕。虛引用主要用于檢測(cè)對(duì)象是否已經(jīng)從內(nèi)存中刪除。
2杯活、HashSet是如何保證不重復(fù)的匆帚?
向 HashSet 中 add ()元素時(shí),判斷元素是否存在的依據(jù)旁钧,不僅要比較hash值吸重,同時(shí)還要結(jié)合 equles 方法比較。
HashSet 中的 add ()方法會(huì)使用 HashMap 的 add ()方法歪今。以下是 HashSet 部分源碼:
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
}
HashMap 的 key 是唯一的嚎幸,由上面的代碼可以看出 HashSet 添加進(jìn)去的值就是作為 HashMap 的key。所以不會(huì) 重復(fù)( HashMap 比較key是否相等是先比較 hashcode 在比較 equals )寄猩。
3嫉晶、HashMap是線程安全的嗎,為什么不是線程安全的(最好畫(huà)圖說(shuō)明多線程 環(huán)境下不安全)?
不是線程安全的;
如果有兩個(gè)線程A和B田篇,都進(jìn)行插入數(shù)據(jù)替废,剛好這兩條不同的數(shù)據(jù)經(jīng)過(guò)哈希計(jì)算后得到的哈希碼是一樣的,且該位 置還沒(méi)有其他的數(shù)據(jù)泊柬。所以這兩個(gè)線程都會(huì)進(jìn)入我在上面標(biāo)記為1的代碼中椎镣。假設(shè)一種情況,線程A通過(guò)if判斷兽赁,該 位置沒(méi)有哈希沖突状答,進(jìn)入了if語(yǔ)句,還沒(méi)有進(jìn)行數(shù)據(jù)插入刀崖,這時(shí)候 CPU 就把資源讓給了線程B惊科,線程A停在了if語(yǔ)句 里面,線程B判斷該位置沒(méi)有哈希沖突(線程A的數(shù)據(jù)還沒(méi)插入)亮钦,也進(jìn)入了if語(yǔ)句馆截,線程B執(zhí)行完后,輪到線程A執(zhí) 行蜂莉,現(xiàn)在線程A直接在該位置插入而不用再判斷孙咪。這時(shí)候,你會(huì)發(fā)現(xiàn)線程A把線程B插入的數(shù)據(jù)給覆蓋了巡语。發(fā)生了線 程不安全情況。本來(lái)在 HashMap 中淮菠,發(fā)生哈希沖突是可以用鏈表法或者紅黑樹(shù)來(lái)解決的男公,但是在多線程中,可能 就直接給覆蓋了。
上面所說(shuō)的是一個(gè)圖來(lái)解釋可能更加直觀枢赔。如下面所示澄阳,兩個(gè)線程在同一個(gè)位置添加數(shù)據(jù),后面添加的數(shù)據(jù)就覆蓋住了前面添加的踏拜。
如果上述插入是插入到鏈表上碎赢,如兩個(gè)線程都在遍歷到最后一個(gè)節(jié)點(diǎn),都要在最后添加一個(gè)數(shù)據(jù)速梗,那么后面添加數(shù)據(jù)的線程就會(huì)把前面添加的數(shù)據(jù)給覆蓋住肮塞。則
在擴(kuò)容的時(shí)候也可能會(huì)導(dǎo)致數(shù)據(jù)不一致,因?yàn)閿U(kuò)容是從一個(gè)數(shù)組拷貝到另外一個(gè)數(shù)組姻锁。
5枕赵、HashMap的擴(kuò)容過(guò)程
當(dāng)向容器添加元素的時(shí)候,會(huì)判斷當(dāng)前容器的元素個(gè)數(shù)位隶,如果大于等于閾值(知道這個(gè)閾字怎么念嗎?不念 fa 值拷窜,念 yu 值四聲)---即當(dāng)前數(shù)組的長(zhǎng)度乘以加載因子的值的時(shí)候,就要自動(dòng)擴(kuò)容啦涧黄。
擴(kuò)容( resize )就是重新計(jì)算容量篮昧,向 HashMap 對(duì)象里不停的添加元素,而 HashMap 對(duì)象內(nèi)部的數(shù)組無(wú)法裝載更 多的元素時(shí)笋妥,對(duì)象就需要擴(kuò)大數(shù)組的長(zhǎng)度懊昨,以便能裝入更多的元素。當(dāng)然 Java 里的數(shù)組是無(wú)法自動(dòng)擴(kuò)容的挽鞠,方法 是使用一個(gè)新的數(shù)組代替已有的容量小的數(shù)組疚颊,就像我們用一個(gè)小桶裝水,如果想裝更多的水信认,就得換大水桶材义。
HashMap hashMap=new HashMap(cap);
- cap =3, hashMap 的容量為4;
- cap =4嫁赏, hashMap 的容量為4;
- cap=5其掂, 的容量為8;
- cap =9, hashMap 的容量為16;
- 如果 cap 是2的n次方潦蝇,則容量為 cap 款熬,否則為大于 cap 的第一個(gè)2的n次方的數(shù)。
6攘乒、Arrays.sort 和 Collections.sort 實(shí)現(xiàn)原理和區(qū)別贤牛?
Collection和Collections區(qū)別
java.util.Collection 是一個(gè)集合接口。它提供了對(duì)集合對(duì)象進(jìn)行基本操作的通用接口方法则酝。
java.util.Collections 是針對(duì)集合類的一個(gè)幫助類殉簸,他提供一系列靜態(tài)方法實(shí)現(xiàn)對(duì)各種集合的搜索、排序、 線程安全等操作般卑。然后還有混排(Shuffling)武鲁、反轉(zhuǎn)(Reverse)、替換所有的元素(fill)蝠检、拷貝(copy)沐鼠、返 回Collections中最小元素(min)、返回Collections中最大元素(max)叹谁、返回指定源列表中最后一次出現(xiàn)指定目 標(biāo)列表的起始位置( lastIndexOfSubList )饲梭、返回指定源列表中第一次出現(xiàn)指定目標(biāo)列表的起始位置
( IndexOfSubList )、根據(jù)指定的距離循環(huán)移動(dòng)指定列表中的元素(Rotate);
事實(shí)上Collections.sort方法底層就是調(diào)用的array.sort方法本慕,
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
//void java.util.ComparableTimSort.sort()
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen)
{
assert a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi);
binarySort(a, lo, hi, lo + initRunLen);
return;
}
}
legacyMergeSort (a):歸并排序 ComparableTimSort.sort() : Timsort 排序
Timsort 排序是結(jié)合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法排拷。
Timsort的核心過(guò)程:
TimSort 算法為了減少對(duì)升序部分的回溯和對(duì)降序部分的性能倒退,將輸入按其升序和降序特點(diǎn)進(jìn)行了分 區(qū)锅尘。排序的輸入的單位不是一個(gè)個(gè)單獨(dú)的數(shù)字监氢,而是一個(gè)個(gè)的塊-分區(qū)。其中每一個(gè)分區(qū)叫一個(gè)run藤违。針對(duì)這 些 run 序列浪腐,每次拿一個(gè) run 出來(lái)按規(guī)則進(jìn)行合并。每次合并會(huì)將兩個(gè) run合并成一個(gè) run顿乒。合并的結(jié)果保 存到棧中议街。合并直到消耗掉所有的 run,這時(shí)將棧上剩余的 run合并到只剩一個(gè) run 為止璧榄。這時(shí)這個(gè)僅剩的run 便是排好序的結(jié)果特漩。
綜上述過(guò)程,Timsort算法的過(guò)程包括:
- 如何數(shù)組長(zhǎng)度小于某個(gè)值骨杂,直接用二分插入排序算法涂身。
- 找到各個(gè)run,并入棧搓蚪。
- 按規(guī)則合并run蛤售。
7、Cloneable接口實(shí)現(xiàn)原理妒潭?
Cloneable接口是Java開(kāi)發(fā)中常用的一個(gè)接口悴能, 它的作用是使一個(gè)類的實(shí)例能夠?qū)⒆陨砜截惖搅硪粋€(gè)新的實(shí)例中, 注意雳灾,這里所說(shuō)的“拷貝”拷的是對(duì)象實(shí)例漠酿,而不是類的定義,進(jìn)一步說(shuō)谎亩,拷貝的是一個(gè)類的實(shí)例中各字段的值记靡。
在開(kāi)發(fā)過(guò)程中谈竿,拷貝實(shí)例是常見(jiàn)的一種操作,如果一個(gè)類中的字段較多摸吠,而我們又采用在客戶端中逐字段復(fù)制的方 法進(jìn)行拷貝操作的話,將不可避免的造成客戶端代碼繁雜冗長(zhǎng)嚎花,而且也無(wú)法對(duì)類中的私有成員進(jìn)行復(fù)制寸痢,而如果讓需要 具備拷貝功能的類實(shí)現(xiàn)Cloneable接口,并重寫(xiě)clone()方法紊选,就可以通過(guò)調(diào)用clone()方法的方式簡(jiǎn)潔地實(shí)現(xiàn)實(shí)例 拷貝功
深拷貝(深復(fù)制)和淺拷貝(淺復(fù)制)是兩個(gè)比較通用的概念啼止,尤其在C++語(yǔ)言中,若不弄懂兵罢,則會(huì)在delete的時(shí)候出問(wèn) 題献烦,但是我們?cè)谶@幸好用的是Java。雖然Java自動(dòng)管理對(duì)象的回收卖词,但對(duì)于深拷貝(深復(fù)制)和淺拷貝(淺復(fù)制)巩那,我們 還是要給予足夠的重視,因?yàn)橛袝r(shí)這兩個(gè)概念往往會(huì)給我們帶來(lái)不小的困惑此蜈。
淺拷貝是指拷貝對(duì)象時(shí)僅僅拷貝對(duì)象本身(包括對(duì)象中的基本變量)即横,而不拷貝對(duì)象包含的引用指向的對(duì)象。深拷 貝不僅拷貝對(duì)象本身裆赵,而且拷貝對(duì)象包含的引用指向的所有對(duì)象东囚。舉例來(lái)說(shuō)更加清楚:對(duì)象 A1 中包含對(duì) B1 的引 用, B1 中包含對(duì) C1 的引用战授。淺拷貝 A1 得到 A2 页藻, A2 中依然包含對(duì) B1 的引用, B1 中依然包含對(duì) C1 的引 用植兰。深拷貝則是對(duì)淺拷貝的遞歸份帐,深拷貝 A1 得到 A2 , A2 中包含對(duì) B2 ( B1 的 copy )的引用钉跷, B2 中包含 對(duì) C2 ( C1 的 copy )的引用弥鹦。
若不對(duì)clone()方法進(jìn)行改寫(xiě),則調(diào)用此方法得到的對(duì)象即為淺拷貝 爷辙。
8彬坏、簡(jiǎn)單講一下異常分類以及處理機(jī)制?
Java標(biāo)準(zhǔn)庫(kù)內(nèi)建了一些通用的異常膝晾,這些類以Throwable為頂層父類栓始。Throwable又派生出Error類和Exception類。
錯(cuò)誤:Error類以及他的子類的實(shí)例血当,代表了JVM本身的錯(cuò)誤幻赚。錯(cuò)誤不能被程序員通過(guò)代碼處理禀忆,Error很少出現(xiàn)。因此落恼,程序員應(yīng)該關(guān)注Exception為父類的分支下的各種異常類箩退。
異常:Exception以及他的子類,代表程序運(yùn)行時(shí)發(fā)送的各種不期望發(fā)生的事件佳谦〈骼裕可以被Java異常處理機(jī)制使用, 是異常處理的核心钻蔑。
總體上我們根據(jù) Javac 對(duì)異常的處理要求啥刻,將異常類分為二類。
- 非檢查異常( unckecked exception ): Error 和 RuntimeException 以及他們的子類咪笑。javac 在編譯時(shí)可帽, 不會(huì)提示和發(fā)現(xiàn)這樣的異常,不要求在程序處理這些異常窗怒。所以如果愿意映跟,我們可以編寫(xiě)代碼處理(使用catch...finally )這樣的異常,也可以不處理兜粘。對(duì)于這些異常申窘,我們應(yīng)該修正代碼,而不是去通過(guò)異常處理器處 理 孔轴。這樣的異常發(fā)生的原因多半是代碼寫(xiě)的有問(wèn)題剃法。如除0錯(cuò)誤 ArithmeticException ,錯(cuò)誤的強(qiáng)制類型轉(zhuǎn)換錯(cuò) 誤 ClassCastException 路鹰,數(shù)組索引越界 ArrayIndexOutOfBoundsException 贷洲,使用了空對(duì)象NullPointerException 等等。
- 檢查異常( checked exception ):除了 Error 和 RuntimeException 的其它異常晋柱。javac 強(qiáng)制要求程序員 為這樣的異常做預(yù)備處理工作(使用 try...catch...finally 或者 throws )优构。在方法中要么用 try-catch 語(yǔ)句捕 獲它并處理,要么用throws子句聲明拋出它雁竞,否則編譯不會(huì)通過(guò)钦椭。這樣的異常一般是由程序的運(yùn)行環(huán)境導(dǎo)致的。因 為程序可能被運(yùn)行在各種未知的環(huán)境下碑诉,而程序員無(wú)法干預(yù)用戶如何使用他編寫(xiě)的程序彪腔,于是程序員就應(yīng)該為這樣 的異常時(shí)刻準(zhǔn)備著。如 SQLException , ClassNotFoundException 等进栽。
需要明確的是:檢查和非檢查是對(duì)于 javac 來(lái)說(shuō)的德挣,這樣就很好理解和區(qū)分了。
9快毛、wait和sleep的區(qū)別格嗅?
源碼如下:
public class Thread implements Runnable {
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++; }
sleep(millis);
}
//...
}
public class Object {
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++; }
wait(timeout);
}
//...
}
1番挺、 sleep 來(lái)自 Thread 類,和 wait 來(lái)自 Object 類屯掖。
2玄柏、最主要是sleep方法沒(méi)有釋放鎖,而wait方法釋放了 鎖贴铜,使得其他線程可以使用同步控制塊或者方法禁荸。
3、wait阀湿,notify和 notifyAll 只能在同步控制方法或者同步控制塊里面使用,而 sleep 可以在任何地方使用(使 用范圍)
4瑰妄、 sleep 必須捕獲異常陷嘴,而 wait , notify 和 notifyAll 不需要捕獲異常
- sleep 方法屬于 Thread 類中方法间坐,表示讓一個(gè)線程進(jìn)入睡眠狀態(tài)灾挨,等待一定的時(shí)間之后,自動(dòng)醒來(lái)進(jìn)入到可 運(yùn)行狀態(tài)竹宋,不會(huì)馬上進(jìn)入運(yùn)行狀態(tài)劳澄,因?yàn)榫€程調(diào)度機(jī)制恢復(fù)線程的運(yùn)行也需要時(shí)間,一個(gè)線程對(duì)象調(diào)用了 sleep方法之后蜈七,并不會(huì)釋放他所持有的所有對(duì)象鎖秒拔,所以也就不會(huì)影響其他進(jìn)程對(duì)象的運(yùn)行。但在 sleep 的過(guò)程中過(guò) 程中有可能被其他對(duì)象調(diào)用它的 interrupt() ,產(chǎn)生 InterruptedException 異常飒硅,如果你的程序不捕獲這個(gè)異 常砂缩,線程就會(huì)異常終止,進(jìn)入 TERMINATED 狀態(tài)三娩,如果你的程序捕獲了這個(gè)異常庵芭,那么程序就會(huì)繼續(xù)執(zhí)行catch語(yǔ) 句塊(可能還有 finally 語(yǔ)句塊)以及以后的代碼。注意 sleep() 方法是一個(gè)靜態(tài)方法雀监,也就是說(shuō)他只對(duì)當(dāng)前對(duì)象有效双吆,通過(guò) t.sleep() 讓t對(duì)象進(jìn)入 sleep ,這樣 的做法是錯(cuò)誤的会前,它只會(huì)是使當(dāng)前線程被 sleep 而不是 t 線程好乐。
- wait 屬于 Object 的成員方法,一旦一個(gè)對(duì)象調(diào)用了wait方法回官,必須要采用 notify() 和 notifyAll() 方法 喚醒該進(jìn)程;如果線程擁有某個(gè)或某些對(duì)象的同步鎖曹宴,那么在調(diào)用了 wait() 后,這個(gè)線程就會(huì)釋放它持有的所有 同步資源歉提,而不限于這個(gè)被調(diào)用了 wait() 方法的對(duì)象笛坦。wait() 方法也同樣會(huì)在 wait 的過(guò)程中有可能被其他對(duì) 象調(diào)用 interrupt() 方法而產(chǎn)生 区转。
10、數(shù)組在內(nèi)存中如何分配版扩?
對(duì)于 Java 數(shù)組的初始化废离,有以下兩種方式,這也是面試中經(jīng)辰嘎考到的經(jīng)典題目蜻韭。
靜態(tài)初始化:初始化時(shí)由程序員顯式指定每個(gè)數(shù)組元素的初始值,由系統(tǒng)決定數(shù)組長(zhǎng)度柿扣,如:
//只是指定初始值肖方,并沒(méi)有指定數(shù)組的長(zhǎng)度,但是系統(tǒng)為自動(dòng)決定該數(shù)組的長(zhǎng)度為4
String[] computers = {"Dell", "Lenovo", "Apple", "Acer"}; //1
//只是指定初始值未状,并沒(méi)有指定數(shù)組的長(zhǎng)度俯画,但是系統(tǒng)為自動(dòng)決定該數(shù)組的長(zhǎng)度為3
String[] names = new String[]{"多啦A夢(mèng)", "大雄", "靜香"}; //2
動(dòng)態(tài)初始化:初始化時(shí)由程序員顯示的指定數(shù)組的長(zhǎng)度,由系統(tǒng)為數(shù)據(jù)每個(gè)元素分配
初始值司草,如:
//只是指定了數(shù)組的長(zhǎng)度艰垂,并沒(méi)有顯示的為數(shù)組指定初始值,但是系統(tǒng)會(huì)默認(rèn)給數(shù)組數(shù)組元素分配初始值為null
String[] cars = new String[4]; //3
因?yàn)?Java 數(shù)組變量是引用類型的變量埋虹,所以上述幾行初始化語(yǔ)句執(zhí)行后猜憎,三個(gè)數(shù)組在內(nèi)存中的分配情況如下圖所 示:
由上圖可知,靜態(tài)初始化方式搔课,程序員雖然沒(méi)有指定數(shù)組長(zhǎng)度胰柑,但是系統(tǒng)已經(jīng)自動(dòng)幫我們給分配了,而動(dòng)態(tài)初始化 方式辣辫,程序員雖然沒(méi)有顯示的指定初始化值旦事,但是因?yàn)?Java 數(shù)組是引用類型的變量,所以系統(tǒng)也為每個(gè)元素分配 了初始化值 null 急灭,當(dāng)然不同類型的初始化值也是不一樣的姐浮,假設(shè)是基本類型int類型,那么為系統(tǒng)分配的初始化值 也是對(duì)應(yīng)的默認(rèn)值0葬馋。
11卖鲤、說(shuō)說(shuō)Java反射機(jī)制?
Java 反射機(jī)制是在運(yùn)行狀態(tài)中畴嘶,對(duì)于任意一個(gè)類蛋逾,都能夠獲得這個(gè)類的所有屬性和方法,對(duì)于任意一個(gè)對(duì)象都能夠 調(diào)用它的任意一個(gè)屬性和方法窗悯。這種在運(yùn)行時(shí)動(dòng)態(tài)的獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為 Java 的反射機(jī) 制区匣。
Class 類與 java.lang.reflect 類庫(kù)一起對(duì)反射的概念進(jìn)行了支持,該類庫(kù)包含了 Field,Method,Constructor 類 (每 個(gè)類都實(shí)現(xiàn)了 Member 接口)蒋院。這些類型的對(duì)象時(shí)由 JVM 在運(yùn)行時(shí)創(chuàng)建的亏钩,用以表示未知類里對(duì)應(yīng)的成員莲绰。
這樣你就可以使用 Constructor 創(chuàng)建新的對(duì)象,用 get() 和 set() 方法讀取和修改與 Field 對(duì)象關(guān)聯(lián)的字段姑丑,用 invoke() 方法調(diào)用與 Method 對(duì)象關(guān)聯(lián)的方法蛤签。另外,還可以調(diào)用 getFields() getMethods() 和 getConstructors() 等很便利的方法栅哀,以返回表示字段震肮,方法,以及構(gòu)造器的對(duì)象的數(shù)組留拾。這樣匿名對(duì)象的信息 就能在運(yùn)行時(shí)被完全確定下來(lái)戳晌,而在編譯時(shí)不需要知道任何事情。
import java.lang.reflect.Constructor;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class clazz = null;
clazz = Class.forName("com.jas.reflect.Fruit");
Constructor<Fruit> constructor1 = clazz.getConstructor();
Constructor<Fruit> constructor2 = clazz.getConstructor(String.class);
Fruit fruit1 = constructor1.newInstance();
Fruit fruit2 = constructor2.newInstance("Apple");
}
}
class Fruit{
public Fruit(){
System.out.println("無(wú)參構(gòu)造器 Run..........."); }
public Fruit(String type){
System.out.println("有參構(gòu)造器 Run..........." + type);
}
}
運(yùn)行結(jié)果: 無(wú)參構(gòu)造器 Run........... 有參構(gòu)造器 Run...........Apple
12痴柔、Java獲取反射的三種方法躬厌?
- 通過(guò)new對(duì)象實(shí)現(xiàn)反射機(jī)制。
- 通過(guò)路徑實(shí)現(xiàn)反射機(jī)制竞帽。
- 通過(guò)類名實(shí)現(xiàn)反射機(jī)制。
代碼示例:
public class Student {
private int id;
String name;
protected boolean sex;
public float score;
}
public class Get {
//獲取反射機(jī)制三種方式
public static void main(String[] args) throws ClassNotFoundException {
//方式一(通過(guò)建立對(duì)象)
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
//方式二(所在通過(guò)路徑-相對(duì)路徑)
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
//方式三(通過(guò)類名)
Class classobj3 = Student.class;
System.out.println(classobj3.getName());
}
}
13鸿捧、String 和 StringBuilder屹篓、StringBuffer 的區(qū)別?
Java 平臺(tái)提供了兩種類型的字符串:String 和StringBuffer/StringBuilder,它們可以儲(chǔ)存和操作字符串匙奴。其中 String 是只 讀字符串堆巧,也就意味著 String 引用的字符串內(nèi)容是不能被改變的。而StringBuffer/StringBuilder 類表示的字符串對(duì)象可以直接進(jìn)行修改泼菌。StringBuilder 是 Java 5 中引入的谍肤,它和 StringBuffer 的方法完全相同,區(qū) 別在于它是在單線程環(huán)境下使用的哗伯,因?yàn)樗乃蟹矫娑紱](méi)有被 synchronized修飾荒揣,因此它的效率也比 StringBuffer 要高。
14焊刹、描述一下 JVM 加載 class 文件的原理機(jī)制?
JVM 中類的裝載是由類加載器(ClassLoader)和它的子類來(lái)實(shí)現(xiàn)的系任,Java中的類加載器是一個(gè)重要的 Java 運(yùn)行時(shí)系統(tǒng)組件,它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類文件中的類虐块。
由于 Java 的跨平臺(tái)性俩滥,經(jīng)過(guò)編譯的 Java 源程序并不是一個(gè)可執(zhí)行程序,而是 一個(gè)或多個(gè)類文件贺奠。當(dāng) Java 程序需要使用某個(gè)類時(shí)霜旧,JVM 會(huì)確保這個(gè)類已經(jīng)被 加載、連接(驗(yàn)證儡率、準(zhǔn)備和解析)和初始化挂据。類的加載是指把類的.class 文件 中的數(shù)據(jù)讀入到內(nèi)存中以清,通常是創(chuàng)建一個(gè)字節(jié)數(shù)組讀入.class 文件,然后產(chǎn)生 與所加載類對(duì)應(yīng)的 Class 對(duì)象棱貌。加載完成后玖媚,Class 對(duì)象還不完整,所以此時(shí) 的類還不可用婚脱。當(dāng)類被加載后就進(jìn)入連接階段今魔,這一階段包括驗(yàn)證、準(zhǔn)備(為靜態(tài)變量分配內(nèi)存并設(shè)置默認(rèn)的初始值)和解析(將符號(hào)引用替換為直接引 用)三個(gè)步驟障贸。最后 JVM 對(duì)類進(jìn)行初始化错森,包括:1)如果類存在直接的父類并 且這個(gè)類還沒(méi)有被初始化,那么就先初始化父類;2)如果類中存在初始化語(yǔ) 句篮洁,就依次執(zhí)行這些初始化語(yǔ)句涩维。
類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)袁波、擴(kuò)展 加載器(Extension)瓦阐、系統(tǒng)加載器(System)和用戶自定義類加載器 (java.lang.ClassLoader 的子類)。從 Java 2(JDK 1.2)開(kāi)始篷牌,類加載過(guò)程 采取了父親委托機(jī)制(PDM)睡蟋。PDM 更好的保證了 Java 平臺(tái)的安全性,在該機(jī) 制中枷颊,JVM 自帶的 Bootstrap 是根加載器戳杀,其他的加載器都有且僅有一個(gè)父類 加載器。類的加載首先請(qǐng)求父類加載器加載夭苗,父類加載器無(wú)能為力時(shí)才由其子 類加載器自行加載信卡。JVM 不會(huì)向 Java 程序提供對(duì) Bootstrap 的引用。下面是關(guān) 于幾個(gè)類加載器的說(shuō)明:
- Bootstrap:一般用本地代碼實(shí)現(xiàn)题造,負(fù)責(zé)加載 JVM 基礎(chǔ)核心類庫(kù) (rt.jar);
- Extension:從 java.ext.dirs 系統(tǒng)屬性所指定的目錄中加載類 庫(kù)傍菇,它的父加載器是 Bootstrap;
- System:又叫應(yīng)用類加載器,其父類是 Extension界赔。它是應(yīng)用最 廣泛的類加載器桥嗤。它從環(huán)境變量 classpath 或者系統(tǒng)屬性 java.class.path 所指定的目錄中記載類,是用戶自定義加載器 的默認(rèn)父加載器仔蝌。
15泛领、闡述 ArrayList、Vector敛惊、LinkedList 的存儲(chǔ)性能和特性渊鞋。
ArrayList 和 Vector 都是使用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際 存儲(chǔ)的數(shù)據(jù)以便增加和插入元素,它們都允許直接按序號(hào)索引元素锡宋,但是插入 元素要涉及數(shù)組元素移動(dòng)等內(nèi)存操作儡湾,所以索引數(shù)據(jù)快而插入數(shù)據(jù)慢,Vector中的方法由于添加了 synchronized 修飾执俩,因此 Vector 是線程安全的容器徐钠,但 性能上較 ArrayList 差,因此已經(jīng)是 Java 中的遺留容器役首。
LinkedList 使用雙 向鏈表實(shí)現(xiàn)存儲(chǔ)(將內(nèi)存中零散的內(nèi)存單元通過(guò)附加的引用關(guān)聯(lián)起來(lái)尝丐,形成一 個(gè)可以按序號(hào)索引的線性結(jié)構(gòu),這種鏈?zhǔn)酱鎯?chǔ)方式與數(shù)組的連續(xù)存儲(chǔ)方式相 比衡奥,內(nèi)存的利用率更高)爹袁,按序號(hào)索引數(shù)據(jù)需要進(jìn)行前向或后向遍歷,但是插 入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可矮固,所以插入速度較快失息。
Vector 屬于遺留 容器(Java 早期的版本中提供的容器,除此之外档址,Hashtable盹兢、Dictionary、BitSet守伸、Stack蛤迎、Properties 都是遺留容器),已經(jīng)不推薦使用含友,但是由于ArrayList 和 LinkedListed 都是非線程安全的,如果遇到多個(gè)線程操作同一個(gè) 容器的場(chǎng)景校辩,則可以通過(guò)工具類 Collections 中的 synchronizedList 方法將其 轉(zhuǎn)換成線程安全的容器后再使用(這是對(duì)裝潢模式的應(yīng)用窘问,將已有對(duì)象傳入另 一個(gè)類的構(gòu)造器中創(chuàng)建新的對(duì)象來(lái)增強(qiáng)實(shí)現(xiàn))。
16宜咒、 內(nèi)存模型以及分區(qū)惠赫,需要詳細(xì)到每個(gè)區(qū)放什么?
JVM 分為堆區(qū)和棧區(qū)故黑,還有方法區(qū)儿咱,初始化的對(duì)象放在堆里面,引用放在棧里面场晶,class 類信息常量池(static 常量和 static 變量)等放在方法區(qū)new:
- ? 方法區(qū):主要是存儲(chǔ)類信息混埠,常量池(static 常量和 static 變量),編譯后的代碼(字 節(jié)碼)等數(shù)據(jù)诗轻。
- ? 堆:初始化的對(duì)象钳宪,成員變量 (那種非 static 的變量),所有的對(duì)象實(shí)例和數(shù)組都要 在堆上分配。
- ? 棧:棧的結(jié)構(gòu)是棧幀組成的吏颖,調(diào)用一個(gè)方法就壓入一幀搔体,幀上面存儲(chǔ)局部變量表,操 作數(shù)棧半醉,方法出口等信息疚俱,局部變量表存放的是 8 大基礎(chǔ)類型加上一個(gè)應(yīng)用類型,所 以還是一個(gè)指向地址的指針缩多。
- ? 本地方法棧:主要為 Native 方法服務(wù)呆奕。? 程序計(jì)數(shù)器:記錄當(dāng)前線程執(zhí)行的行號(hào)。
17瞧壮、java 中垃圾收集的方法有哪些?
1. 標(biāo)記-清除:這是垃圾收集算法中最基礎(chǔ)的登馒,根據(jù)名字就可以知道,它的思想就是標(biāo)記哪些要被 回收的對(duì)象咆槽,然后統(tǒng)一回收陈轿。這種方法很簡(jiǎn)單,但是會(huì)有兩個(gè)主要問(wèn)題:1.效率不 高秦忿,標(biāo)記和清除的效率都很低;2.會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片麦射,導(dǎo)致以后程序在 分配較大的對(duì)象時(shí),由于沒(méi)有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動(dòng)作灯谣。
2. 復(fù)制算法:為了解決效率問(wèn)題潜秋,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只 使用其中的一塊胎许,當(dāng)一塊內(nèi)存用完時(shí)峻呛,就將還存活的對(duì)象復(fù)制到第二塊內(nèi)存上,然 后一次性清楚完第一塊內(nèi)存辜窑,再將第二塊上的對(duì)象復(fù)制到第一塊钩述。但是這種方式,內(nèi)存的代價(jià)太高穆碎,每次基本上都要浪費(fèi)一般的內(nèi)存牙勘。于是將該算法進(jìn)行了改進(jìn),內(nèi)存區(qū)域不再是按照 1:1 去劃分所禀,而是將內(nèi)存劃分為8:1:1 三部分方面,較大那份內(nèi)存交 Eden 區(qū),其余是兩塊較小的內(nèi)存區(qū)叫 Survior 區(qū)色徘。每次都會(huì)優(yōu)先使用 Eden 區(qū)恭金,若 Eden 區(qū)滿,就將對(duì)象復(fù)制到第二塊內(nèi)存區(qū)上褂策,然 后清除 Eden 區(qū)衫冻,如果此時(shí)存活的對(duì)象太多,以至于 Survivor 不夠時(shí)秽梅,會(huì)將這些對(duì) 象通過(guò)分配擔(dān)保機(jī)制復(fù)制到老年代中危彩。(java 堆又分為新生代和老年代)
3. 標(biāo)記-整理該算法主要是為了解決標(biāo)記-清除,產(chǎn)生大量?jī)?nèi)存碎片的問(wèn)題;當(dāng)對(duì)象存活率較高 時(shí),也解決了復(fù)制算法的效率問(wèn)題。它的不同之處就是在清除對(duì)象的時(shí)候現(xiàn)將可回 收對(duì)象移動(dòng)到一端,然后清除掉端邊界以外的對(duì)象丹擎,這樣就不會(huì)產(chǎn)生內(nèi)存碎片了。
4. 分代收集現(xiàn)在的虛擬機(jī)垃圾收集大多采用這種方式歇父,它根據(jù)對(duì)象的生存周期蒂培,將堆分為新生 代和老年代。在新生代中榜苫,由于對(duì)象生存期短护戳,每次回收都會(huì)有大量對(duì)象死去,那 么這時(shí)就采用復(fù)制算法垂睬。老年代里的對(duì)象存活率較高媳荒,沒(méi)有額外的空間進(jìn)行分配擔(dān) 保,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除驹饺。
18钳枕、簡(jiǎn)述 java 類加載機(jī)制?
虛擬機(jī)把描述類的數(shù)據(jù)從 Class 文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)赏壹,解析和初始化鱼炒,最 終形成可以被虛擬機(jī)直接使用的 java 類型。
19蝌借、類加載器雙親委派模型機(jī)制?
當(dāng)一個(gè)類收到了類加載請(qǐng)求時(shí)昔瞧,不會(huì)自己先去加載這個(gè)類,而是將其委派給父類菩佑,由父類去加載自晰,如果此時(shí)父類不能加載,反饋給子類擎鸠,由子類去完成類的加載。
20缘圈、什么是類加載器劣光,類加載器有哪些?
實(shí)現(xiàn)通過(guò)類的權(quán)限定名獲取該類的二進(jìn)制字節(jié)流的代碼塊叫做類加載器。主要有一下四種類加載器:
- 啟動(dòng)類加載器(Bootstrap ClassLoader)用來(lái)加載 java 核心類庫(kù)糟把,無(wú)法被 java 程序直接 引用绢涡。
- 擴(kuò)展類加載器(extensions class loader):它用來(lái)加載 Java 的擴(kuò)展庫(kù)。Java 虛擬機(jī)的 實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄遣疯。該類加載器在此目錄里面查找并加載 Java 類雄可。
- 系統(tǒng)類加載器(system class loader):它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH) 來(lái)加載 Java 類。一般來(lái)說(shuō),Java 應(yīng)用的類都是由它來(lái)完成加載的数苫〈鲜妫可以通過(guò)ClassLoader.getSystemClassLoader()來(lái)獲取它。
- 用戶自定義類加載器虐急,通過(guò)繼承 java.lang.ClassLoader 類的方式實(shí)現(xiàn)箱残。
21、簡(jiǎn)述 java 內(nèi)存分配與回收策率以及 Minor GC 和Major GC
1. 對(duì)象優(yōu)先在堆的 Eden 區(qū)分配止吁。
2. 大對(duì)象直接進(jìn)入老年代.
3. 長(zhǎng)期存活的對(duì)象將直接進(jìn)入老年代被辑。
當(dāng) Eden 區(qū)沒(méi)有足夠的空間進(jìn)行分配時(shí),虛擬機(jī)會(huì)執(zhí)行一次 Minor GC.Minor Gc 通 常發(fā)生在新生代的 Eden 區(qū)敬惦,在這個(gè)區(qū)的對(duì)象生存期短盼理,往往發(fā)生 Gc 的頻率較高, 回收速度比較快;Full Gc/Major GC 發(fā)生在老年代俄删,一般情況下宏怔,觸發(fā)老年代 GC的時(shí)候不會(huì)觸發(fā) Minor GC,但是通過(guò)配置,可以在 Full GC 之前進(jìn)行一次 Minor GC 這樣可以加快老年代的回收速度抗蠢。
分享不易举哟,如果感興趣的話,可以關(guān)注我的公眾號(hào)“碼之初”或者“ma_zhichu”迅矛,閱讀更多精品技術(shù)文章妨猩。