• 正文
    • 計(jì)算機(jī)基礎(chǔ)
    • Java
    • JVM
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

小鵬汽車,今年我們要招 6000 人!

01/10 10:30
1756
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

圖解學(xué)習(xí)網(wǎng)站:https://xiaolincoding.com

大家好,我是小林。

現(xiàn)在新能源汽車的競(jìng)爭(zhēng)已經(jīng)開始進(jìn)入下半場(chǎng)了,各家公司都開始進(jìn)行組織架構(gòu)調(diào)整,決戰(zhàn)決賽圈,如果不能在決賽圈勝出,可能企業(yè)很難存活下來,市場(chǎng)就是這么殘酷,贏家通吃,輸家出局。

之前發(fā)過理想、蔚來、比亞迪、極越等車企公司的薪資情況和面試真題,這次來補(bǔ)上小鵬汽車的!

小鵬汽車 2025 年計(jì)劃招聘 6000 人,即使之前從小鵬離職過的,也可以重新加入小鵬。

之所以今年大力開始招聘,也是因?yàn)樾※i汽車穿過了暴風(fēng)雨,在 2024 年度銷量有突破性的增長(zhǎng),現(xiàn)在小鵬汽車月銷 4w+臺(tái)是常態(tài),而且應(yīng)該會(huì)很快上升到 5w+臺(tái),銷量上去了,自然要擴(kuò)大員工,在新的一年,將繼續(xù)推出更多新型號(hào)的汽車。

這次來看看 25 屆小鵬汽車校招的薪資,目前還沒看到 Java 崗的薪資,所以列了一些其他崗位的薪資情況,也可以做個(gè)參考:

    軟件測(cè)試:18.5k x 15 = 27.7w,辦公地點(diǎn)廣州,同學(xué)背景碩士 985嵌入式開發(fā):20k * 15 = 30w,辦公地點(diǎn)上海,同學(xué)背景碩士211產(chǎn)品經(jīng)理:18.5 x 15 = 27.7w,辦公地點(diǎn)廣州,同學(xué)背景碩士其他影像開發(fā):25k x 15 = 37.5w,辦公地點(diǎn)廣州,同學(xué)背景碩士 985

小鵬汽車的薪資對(duì)比互聯(lián)網(wǎng)公司大廠的話,是會(huì)少一些,大概是互聯(lián)網(wǎng)中廠的薪資水平,如果同學(xué)手上有互聯(lián)網(wǎng)公司選擇的話,小鵬汽車的競(jìng)爭(zhēng)力就會(huì)弱一些了。新能源汽車?yán)镩_的薪資能對(duì)標(biāo)互聯(lián)網(wǎng)大廠的,目前來看是理想汽車了。

肯定也有同學(xué)好奇,小鵬汽車的面試難度如何?

這次來看看小鵬汽車Java崗位的校招面經(jīng),主要考察計(jì)算機(jī)基、Java、JVM、網(wǎng)絡(luò)、算法這些內(nèi)容,難度的話,算中等,算法還是會(huì)考察,大部分新能源汽車都會(huì)考察,不管面互聯(lián)網(wǎng)公司,還是新能源車企,算法大家都需要準(zhǔn)備。

計(jì)算機(jī)基礎(chǔ)

ARM有了解嘛?

了解不過,大概知道是一個(gè)處理器,嵌入式領(lǐng)域用的多。

二進(jìn)制怎么轉(zhuǎn)16機(jī)制?

可以采用分組轉(zhuǎn)換法。

分組轉(zhuǎn)換法基于二進(jìn)制和十六進(jìn)制之間的位權(quán)關(guān)系。因?yàn)?^4=16,這意味著 4 位二進(jìn)制數(shù)能夠表示的狀態(tài)數(shù)剛好與十六進(jìn)制的一位所能表示的狀態(tài)數(shù)相同。所以可以將二進(jìn)制數(shù)按每 4 位一組進(jìn)行劃分,每一組都能唯一地對(duì)應(yīng)一個(gè)十六進(jìn)制數(shù)字。

下面我給出了二進(jìn)制的數(shù),我們將它轉(zhuǎn)換為十六進(jìn)制,例如:0101101,我們將這個(gè)數(shù)按4個(gè)一組來劃分,變成 0010 1101(這里本來是010 1101前面不夠4位我們就湊一個(gè)0),可以得到 0010 =2、 1101=D
所以轉(zhuǎn)換成十六進(jìn)制就是2D。

byte類型的-1怎么表示?

byte類型是有符號(hào)的 8 位整數(shù),取值范圍是-128127-1byte類型中的二進(jìn)制表示是補(bǔ)碼形式,正數(shù)的補(bǔ)碼與原碼相同,負(fù)數(shù)的補(bǔ)碼是在反碼的基礎(chǔ)上加 1,這是因?yàn)橛?jì)算機(jī)中采用補(bǔ)碼來進(jìn)行減法運(yùn)算,可以將減法轉(zhuǎn)換為加法,方便硬件實(shí)現(xiàn),計(jì)算過程如下:

先寫出1的原碼:00000001。然后得到-1的原碼:10000001。接著求-1的反碼:11111110。最后求-1的補(bǔ)碼:11111111。

所以,在 Java 的byte類型中,-1用二進(jìn)制補(bǔ)碼表示為11111111。當(dāng)進(jìn)行運(yùn)算或存儲(chǔ)時(shí),計(jì)算機(jī)使用這個(gè)補(bǔ)碼來處理-1相關(guān)的操作。例如,在進(jìn)行加法運(yùn)算時(shí),-1 + 1的計(jì)算過程如下:

- 1的補(bǔ)碼是11111111,1的補(bǔ)碼是00000001。相加得到:11111111 + 00000001 = 100000000(9 位,超出byte范圍)。由于byte類型是 8 位,會(huì)發(fā)生截?cái)?,得?code>00000000,也就是0,這符合數(shù)學(xué)運(yùn)算結(jié)果。

Java

兩個(gè)方法都被synchronized修飾,其中一個(gè)調(diào)用另一個(gè)可以成功嘛?synchronized修飾方法鎖的那一部分?

如果兩個(gè)方法都被synchronized修飾,一個(gè)方法內(nèi)部調(diào)用另一個(gè)方法是可以成功的。這是因?yàn)?code>synchronized方法默認(rèn)是對(duì)當(dāng)前對(duì)象(this)加鎖。當(dāng)一個(gè)線程進(jìn)入了一個(gè)synchronized方法,它已經(jīng)獲得了該對(duì)象的鎖,在這個(gè)方法內(nèi)部調(diào)用另一個(gè)synchronized方法時(shí),由于是同一個(gè)對(duì)象的鎖,所以線程可以繼續(xù)執(zhí)行被調(diào)用的synchronized方法,不會(huì)出現(xiàn)鎖競(jìng)爭(zhēng)導(dǎo)致無法調(diào)用的情況。

例如下面的代碼,method1調(diào)用method2時(shí),因?yàn)樗鼈兌际峭粋€(gè)對(duì)象examplesynchronized方法,所以可以正常執(zhí)行。

public?class?SynchronizedExample?{
????public?synchronized?void?method1()?{
????????System.out.println("Method?1?started");
????????method2();
????????System.out.println("Method?1?ended");
????}
????public?synchronized?void?method2()?{
????????System.out.println("Method?2?is?running");
????}
????public?static?void?main(String[]?args)?{
????????SynchronizedExample?example?=?new?SynchronizedExample();
????????example.method1();
????}
}

synchronized 修飾方法鎖的對(duì)象:

對(duì)于非靜態(tài)方法:當(dāng)synchronized修飾一個(gè)非靜態(tài)方法時(shí),鎖的是當(dāng)前對(duì)象(this)。這意味著同一時(shí)刻,對(duì)于同一個(gè)對(duì)象實(shí)例,只有一個(gè)線程能夠執(zhí)行這個(gè)對(duì)象的synchronized非靜態(tài)方法。不同的對(duì)象實(shí)例之間的synchronized非靜態(tài)方法可以被不同的線程同時(shí)執(zhí)行,因?yàn)樗鼈兊逆i對(duì)象(this)是不同的。

對(duì)于靜態(tài)方法:當(dāng)synchronized修飾一個(gè)靜態(tài)方法時(shí),鎖的是這個(gè)類的Class對(duì)象。因?yàn)殪o態(tài)方法是屬于類的,而不是屬于某個(gè)具體的對(duì)象實(shí)例。所以同一時(shí)刻,對(duì)于一個(gè)類的所有實(shí)例,只有一個(gè)線程能夠執(zhí)行這個(gè)類的synchronized靜態(tài)方法。例如,下面的例子,staticMethod1staticMethod2都是靜態(tài)的synchronized方法,它們共享同一個(gè)類的Class對(duì)象作為鎖。所以當(dāng)thread1thread2同時(shí)啟動(dòng)時(shí),其中一個(gè)方法會(huì)先獲得類的Class對(duì)象鎖,另一個(gè)方法需要等待鎖釋放后才能執(zhí)行。

public?class?SynchronizedStaticExample?{
????public?static?synchronized?void?staticMethod1()?{
????????System.out.println("Static?Method?1?started");
????}
????public?static?synchronized?void?staticMethod2()?{
????????System.out.println("Static?Method?2?started");
????}
????public?static?void?main(String[]?args)?{
????????Thread?thread1?=?new?Thread(()?->?{
????????????SynchronizedStaticExample.staticMethod1();
????????});
????????Thread?thread2?=?new?Thread(()?->?{
????????????SynchronizedStaticExample.staticMethod2();
????????});
????????thread1.start();
????????thread2.start();
????}
}

靜態(tài)內(nèi)部類和匿名內(nèi)部類有什么區(qū)別嗎?

靜態(tài)內(nèi)部類是定義在另一個(gè)類內(nèi)部的類,并且使用static關(guān)鍵字修飾。它就像是類的一個(gè)靜態(tài)成員,不依賴于外部類的實(shí)例,就像下面的例子中,StaticInnerClass可以直接訪問OuterClassouterStaticVar靜態(tài)變量。

class?OuterClass?{
????private?static?int?outerStaticVar?=?10;
????static?class?StaticInnerClass?{
????????public?void?printOuterStaticVar()?{
????????????System.out.println(outerStaticVar);
????????}
????}
}

靜態(tài)內(nèi)部類不能直接訪問外部類的非靜態(tài)成員,因?yàn)榉庆o態(tài)成員是依賴于外部類的實(shí)例存在的。如果要訪問外部類的非靜態(tài)成員,需要通過外部類的實(shí)例來訪問。

靜態(tài)內(nèi)部類的生命周期與外部類的靜態(tài)成員相似。它在外部類加載時(shí)不會(huì)自動(dòng)加載,只有在第一次被使用(例如,通過new關(guān)鍵字創(chuàng)建實(shí)例或者訪問靜態(tài)成員)時(shí)才會(huì)加載。加載后,只要類加載器沒有卸載這個(gè)類,它就一直存在于內(nèi)存中。

實(shí)例化靜態(tài)內(nèi)部類時(shí),不需要外部類的實(shí)例??梢灾苯油ㄟ^外部類名.靜態(tài)內(nèi)部類名的方式來創(chuàng)建實(shí)例,例如OuterClass.StaticInnerClass innerObj = new OuterClass.StaticInnerClass();。

當(dāng)一個(gè)類只與另一個(gè)類有比較緊密的關(guān)聯(lián),并且主要是為了輔助外部類完成某些功能,同時(shí)又不依賴于外部類的實(shí)例時(shí),適合使用靜態(tài)內(nèi)部類。例如,一個(gè)工具類中的一些工具方法可以組織成靜態(tài)內(nèi)部類,這些方法可能會(huì)共享一些外部類的靜態(tài)資源。靜態(tài)內(nèi)部類還可以用于實(shí)現(xiàn)單例模式。通過將單例對(duì)象的實(shí)例化放在靜態(tài)內(nèi)部類中,可以保證在第一次訪問單例對(duì)象時(shí)才進(jìn)行實(shí)例化,并且保證了線程安全。

匿名內(nèi)部類是一種沒有名字的內(nèi)部類。它是在創(chuàng)建對(duì)象的同時(shí)定義類的一種方式,通常用于只需要使用一次的類,并且是作為某個(gè)接口或者抽象類的實(shí)現(xiàn)(或者某個(gè)類的子類)出現(xiàn)。例如,在下面實(shí)現(xiàn)接口的例子中,匿名內(nèi)部類是在main方法內(nèi)部定義的,它的行為可能會(huì)受到main方法中的其他變量或者外部類的狀態(tài)的影響。

interface?MyInterface?{
????void?myMethod();
}
class?Main?{
????public?static?void?main(String[]?args)?{
????????MyInterface?anonymousClass?=?new?MyInterface()?{
????????????@Override
????????????public?void?myMethod()?{
????????????????System.out.println("This?is?an?anonymous?class?implementing?MyInterface");
????????????}
????????};
????????anonymousClass.myMethod();
????}
}

匿名內(nèi)部類可以訪問外部類的成員變量和方法,包括靜態(tài)和非靜態(tài)的。如果訪問外部類的局部變量,這些局部變量必須是final(在 Java 8 之后,實(shí)際上是隱式final)的,這是為了保證在匿名內(nèi)部類的生命周期內(nèi),這些變量的值不會(huì)被改變。

匿名內(nèi)部類的生命周期取決于它的使用場(chǎng)景。如果它是在一個(gè)方法內(nèi)部定義的,那么當(dāng)方法執(zhí)行結(jié)束后,只要沒有其他引用指向這個(gè)匿名內(nèi)部類的對(duì)象,它就會(huì)被垃圾回收。如果它是作為一個(gè)類的成員變量定義的,那么它的生命周期會(huì)和這個(gè)類的對(duì)象生命周期相關(guān)。匿名內(nèi)部類在定義的同時(shí)就會(huì)被實(shí)例化,并且只能創(chuàng)建一個(gè)實(shí)例。因?yàn)樗鼪]有類名,所以不能像普通類一樣通過new關(guān)鍵字在其他地方再次創(chuàng)建實(shí)例。

當(dāng)只需要臨時(shí)實(shí)現(xiàn)一個(gè)接口或者繼承一個(gè)抽象類來提供特定的功能,并且這個(gè)實(shí)現(xiàn)類只使用一次時(shí),匿名內(nèi)部類是一個(gè)很好的選擇。它避免了為一個(gè)簡(jiǎn)單的功能定義一個(gè)完整的類,從而簡(jiǎn)化了代碼結(jié)構(gòu)。

匿名內(nèi)部?jī)?nèi)可以使用外部類的引用嗎?靜態(tài)的呢?

HashMap和HashTable區(qū)別?

    HashMap線程不安全,效率高一點(diǎn),可以存儲(chǔ)null的key和value,null的key只能有一個(gè),null的value可以有多個(gè)。默認(rèn)初始容量為16,每次擴(kuò)充變?yōu)樵瓉?倍。創(chuàng)建時(shí)如果給定了初始容量,則擴(kuò)充為2的冪次方大小。底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組+鏈表,插入元素后如果鏈表長(zhǎng)度大于閾值(默認(rèn)為8),先判斷數(shù)組長(zhǎng)度是否小于64,如果小于,則擴(kuò)充數(shù)組,反之將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時(shí)間。HashTable線程安全,效率低一點(diǎn),其內(nèi)部方法基本都經(jīng)過synchronized修飾,不可以有null的key和value。默認(rèn)初始容量為11,每次擴(kuò)容變?yōu)樵瓉淼?n+1。創(chuàng)建時(shí)給定了初始容量,會(huì)直接用給定的大小。底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組+鏈表。它基本被淘汰了,要保證線程安全可以用ConcurrentHashMap。ConcurrentHashMap是Java中的一個(gè)線程安全的哈希表實(shí)現(xiàn),它可以在多線程環(huán)境下并發(fā)地進(jìn)行讀寫操作,而不需要像傳統(tǒng)的HashTable那樣在讀寫時(shí)加鎖。ConcurrentHashMap的實(shí)現(xiàn)原理主要基于分段鎖和CAS操作。它將整個(gè)哈希表分成了多Segment(段),每個(gè)Segment都類似于一個(gè)小的HashMap,它擁有自己的數(shù)組和一個(gè)獨(dú)立的鎖。在ConcurrentHashMap中,讀操作不需要鎖,可以直接對(duì)Segment進(jìn)行讀取,而寫操作則只需要鎖定對(duì)應(yīng)的Segment,而不是整個(gè)哈希表,這樣可以大大提高并發(fā)性能。

講一下ConcurrentHashMap?

JDK 1.7 ConcurrentHashMap

在 JDK 1.7 中它使用的是數(shù)組加鏈表的形式實(shí)現(xiàn)的,而數(shù)組又分為:大數(shù)組 Segment 和小數(shù)組 HashEntry。Segment 是一種可重入鎖(ReentrantLock),在 ConcurrentHashMap 里扮演鎖的角色;HashEntry 則用于存儲(chǔ)鍵值對(duì)數(shù)據(jù)。一個(gè) ConcurrentHashMap 里包含一個(gè) Segment 數(shù)組,一個(gè) Segment 里包含一個(gè) HashEntry 數(shù)組,每個(gè) HashEntry 是一個(gè)鏈表結(jié)構(gòu)的元素。

JDK 1.7 ConcurrentHashMap 分段鎖技術(shù)將數(shù)據(jù)分成一段一段的存儲(chǔ),然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問其中一個(gè)段數(shù)據(jù)的時(shí)候,其他段的數(shù)據(jù)也能被其他線程訪問,能夠?qū)崿F(xiàn)真正的并發(fā)訪問。

JDK 1.8 ConcurrentHashMap

在 JDK 1.7 中,ConcurrentHashMap 雖然是線程安全的,但因?yàn)樗牡讓訉?shí)現(xiàn)是數(shù)組 + 鏈表的形式,所以在數(shù)據(jù)比較多的情況下訪問是很慢的,因?yàn)橐闅v整個(gè)鏈表,而 JDK 1.8 則使用了數(shù)組 + 鏈表/紅黑樹的方式優(yōu)化了 ConcurrentHashMap 的實(shí)現(xiàn),具體實(shí)現(xiàn)結(jié)構(gòu)如下:

JDK 1.8 ConcurrentHashMap JDK 1.8 ConcurrentHashMap 主要通過 volatile + CAS 或者 synchronized 來實(shí)現(xiàn)的線程安全的。添加元素時(shí)首先會(huì)判斷容器是否為空:

如果為空則使用 ?volatile ?加 ?CAS ?來初始化

如果容器不為空,則根據(jù)存儲(chǔ)的元素計(jì)算該位置是否為空。

如果根據(jù)存儲(chǔ)的元素計(jì)算結(jié)果為空,則利用 ?CAS ?設(shè)置該節(jié)點(diǎn);

如果根據(jù)存儲(chǔ)的元素計(jì)算結(jié)果不為空,則使用 synchronized ?,然后,遍歷桶中的數(shù)據(jù),并替換或新增節(jié)點(diǎn)到桶中,最后再判斷是否需要轉(zhuǎn)為紅黑樹,這樣就能保證并發(fā)訪問時(shí)的線程安全了。

如果把上面的執(zhí)行用一句話歸納的話,就相當(dāng)于是ConcurrentHashMap通過對(duì)頭結(jié)點(diǎn)加鎖來保證線程安全的,鎖的粒度相比 Segment 來說更小了,發(fā)生沖突和加鎖的頻率降低了,并發(fā)操作的性能就提高了。

而且 JDK 1.8 使用的是紅黑樹優(yōu)化了之前的固定鏈表,那么當(dāng)數(shù)據(jù)量比較大的時(shí)候,查詢性能也得到了很大的提升,從之前的 O(n) 優(yōu)化到了 O(logn) 的時(shí)間復(fù)雜度。

JVM

類加載過程?

類從被加載到虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存為止,它的整個(gè)生命周期包括以下 7 個(gè)階段:

加載:通過類的全限定名(包名 + 類名),獲取到該類的.class文件的二進(jìn)制字節(jié)流,將二進(jìn)制字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu),轉(zhuǎn)化為方法區(qū)運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu),在內(nèi)存中生成一個(gè)代表該類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口

連接:驗(yàn)證、準(zhǔn)備、解析 3 個(gè)階段統(tǒng)稱為連接。

驗(yàn)證:確保class文件中的字節(jié)流包含的信息,符合當(dāng)前虛擬機(jī)的要求,保證這個(gè)被加載的class類的正確性,不會(huì)危害到虛擬機(jī)的安全。驗(yàn)證階段大致會(huì)完成以下四個(gè)階段的檢驗(yàn)動(dòng)作:文件格式校驗(yàn)、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證、符號(hào)引用驗(yàn)證

準(zhǔn)備:為類中的靜態(tài)字段分配內(nèi)存,并設(shè)置默認(rèn)的初始值,比如int類型初始值是0。被final修飾的static字段不會(huì)設(shè)置,因?yàn)閒inal在編譯的時(shí)候就分配了

解析:解析階段是虛擬機(jī)將常量池的「符號(hào)引用」直接替換為「直接引用」的過程。符號(hào)引用是以一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用的時(shí)候可以無歧義地定位到目標(biāo)即可。直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄,直接引用是和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān)的。如果有了直接引用, 那引用的目標(biāo)必定已經(jīng)存在在內(nèi)存中了。

初始化:初始化是整個(gè)類加載過程的最后一個(gè)階段,初始化階段簡(jiǎn)單來說就是執(zhí)行類的構(gòu)造器方法,要注意的是這里的構(gòu)造器方法()并不是開發(fā)者寫的,而是編譯器自動(dòng)生成的。

使用:使用類或者創(chuàng)建對(duì)象

卸載:如果有下面的情況,類就會(huì)被卸載:1. 該類所有的實(shí)例都已經(jīng)被回收,也就是java堆中不存在該類的任何實(shí)例。2. 加載該類的ClassLoader已經(jīng)被回收。3. 類對(duì)應(yīng)的java.lang.Class對(duì)象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。

雙親委派機(jī)制為什么叫雙親?有什么好處?

雙親委派模型,簡(jiǎn)單說就是當(dāng)類加載器(Class-Loader)試圖加載某個(gè)類型的時(shí)候,除非父加載器找不到相應(yīng)類型,否則盡量將這個(gè)任務(wù)代理給當(dāng)前加載器的父加載器去做。使用委派模型的目的是避免重復(fù)加載 Java 類型。

“雙親” 并不是指有兩個(gè)父母一樣的角色。實(shí)際上,這里的 “雙親” 是一種形象的比喻,它是指除了最頂層的啟動(dòng)類加載器(Bootstrap ClassLoader)外,每個(gè)類加載器都有一個(gè)父類加載器。當(dāng)一個(gè)類加載器需要加載類時(shí),它會(huì)先委托給它的父類加載器去嘗試加載,這個(gè)過程就好像孩子(子加載器)先請(qǐng)求父母(父加載器)幫忙做事一樣,所以稱為 “雙親委派”。

雙親委派機(jī)制好處主要是:

防止核心 API 被篡改:Java 的核心類庫(如java.lang包中的類)是由啟動(dòng)類加載器(Bootstrap ClassLoader)加載的。因?yàn)殡p親委派機(jī)制的存在,自定義的類加載器在加載類時(shí),首先會(huì)將加載請(qǐng)求委托給父加載器。這就保證了像java.lang.Object這樣的核心類不會(huì)被自定義的同名類隨意替換。例如,如果沒有雙親委派機(jī)制,惡意代碼可能會(huì)定義一個(gè)自己的java.lang.Object類,并且通過自定義的類加載器加載,從而破壞 Java 程序的基本運(yùn)行規(guī)則。

避免類的重復(fù)加載:由于類加載請(qǐng)求是由下向上委托,然后再從上向下嘗試加載。如果父加載器已經(jīng)成功加載了某個(gè)類,子加載器就不會(huì)再重復(fù)加載該類,從而避免了因多次加載同一類而可能導(dǎo)致的類型不一致等問題。例如,系統(tǒng)中有多個(gè)不同的類加載器都可能需要加載java.util.ArrayList類,通過雙親委派機(jī)制,只有啟動(dòng)類加載器會(huì)加載這個(gè)類,其他類加載器會(huì)直接使用已經(jīng)加載好的類。

保證類的一致性:

    在 Java 的運(yùn)行環(huán)境中,對(duì)于同樣全限定名的類,應(yīng)該只有一份字節(jié)碼被加載并使用。雙親委派機(jī)制確保了在整個(gè)類加載體系中,類的加載是有層次和順序的。例如,在一個(gè)復(fù)雜的 Java 應(yīng)用系統(tǒng)中,可能存在多個(gè)模塊都依賴于同一個(gè)第三方庫中的類。通過雙親委派機(jī)制,這些模塊所使用的該類是由同一個(gè)類加載器加載的,保證了在整個(gè)系統(tǒng)中該類的一致性,使得不同模塊之間可以正確地交互和共享對(duì)象。

class文件和字節(jié)碼文件的區(qū)別?

概念上的區(qū)別:

Class 文件:在 Java 中,.class文件是 Java 編譯器(javac)將.java源文件編譯后生成的文件格式。它是一種二進(jìn)制文件,存儲(chǔ)了 Java 程序的字節(jié)碼指令、常量池、訪問標(biāo)志、類名、方法名、字段名等各種信息??梢园?code>.class文件看作是字節(jié)碼的一種物理存儲(chǔ)形式,是字節(jié)碼的載體。

字節(jié)碼(Byte - code):字節(jié)碼是一種中間形式的機(jī)器語言,它是 Java 程序經(jīng)過編譯后產(chǎn)生的指令集。字節(jié)碼是一種高度抽象的、與具體機(jī)器硬件無關(guān)的指令代碼,它可以在任何安裝了 Java 虛擬機(jī)(JVM)的平臺(tái)上執(zhí)行。字節(jié)碼指令是 JVM 能夠理解和執(zhí)行的基本單位,這些指令類似于匯編語言指令,但更加抽象和高級(jí)。

Class 文件用途

存儲(chǔ)和分發(fā).class文件是 Java 程序的一種可存儲(chǔ)和可分發(fā)的形式。當(dāng)開發(fā)一個(gè) Java 項(xiàng)目時(shí),編譯器會(huì)生成一系列的.class文件,這些文件可以被打包成.jar文件或者部署到服務(wù)器等環(huán)境中,供其他程序使用或者在運(yùn)行時(shí)被加載。

跨平臺(tái)基礎(chǔ).class文件的存在是 Java 實(shí)現(xiàn) “一次編寫,到處運(yùn)行” 特性的基礎(chǔ)之一。因?yàn)椴煌牟僮飨到y(tǒng)有不同的機(jī)器指令集,Java 編譯器將.java源文件編譯成與平臺(tái)無關(guān)的.class文件,然后由各個(gè)平臺(tái)上的 JVM 對(duì).class文件進(jìn)行解釋執(zhí)行或者編譯成機(jī)器碼執(zhí)行。

字節(jié)碼用途

JVM 執(zhí)行的指令集:字節(jié)碼是 JVM 執(zhí)行 Java 程序的實(shí)際指令。當(dāng) JVM 加載.class文件時(shí),它會(huì)解析.class文件中的字節(jié)碼指令,并按照字節(jié)碼指令的順序執(zhí)行操作。例如,當(dāng)調(diào)用一個(gè) Java 方法時(shí),JVM 會(huì)讀取方法表中的字節(jié)碼指令,逐條執(zhí)行這些指令來完成方法的功能。

動(dòng)態(tài)加載和執(zhí)行:字節(jié)碼的動(dòng)態(tài)特性使得 Java 可以實(shí)現(xiàn)一些高級(jí)的功能,如動(dòng)態(tài)代理、字節(jié)碼增強(qiáng)等。通過在運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼或者修改已有的字節(jié)碼,可以實(shí)現(xiàn)諸如 AOP等編程技術(shù),為 Java 程序提供了更大的靈活性。

弱引用和軟引用的區(qū)別?

    軟引用是一種相對(duì)較強(qiáng)的引用類型。它所引用的對(duì)象在內(nèi)存足夠的情況下,不會(huì)被垃圾回收器回收;只有在內(nèi)存不足時(shí),才會(huì)被回收。這使得軟引用適合用來緩存一些可能會(huì)被頻繁使用,但又不是必須一直存在的數(shù)據(jù),例如緩存圖片等資源。弱引用是一種比較弱的引用類型。被弱引用關(guān)聯(lián)的對(duì)象,只要垃圾回收器運(yùn)行,無論當(dāng)前內(nèi)存是否充足,都會(huì)被回收。它主要用于解決一些對(duì)象的生命周期管理問題,例如在哈希表中,如果鍵是弱引用,當(dāng)對(duì)象沒有其他強(qiáng)引用時(shí),就可以自動(dòng)被回收,避免內(nèi)存泄漏。

網(wǎng)絡(luò)

計(jì)網(wǎng)分層結(jié)構(gòu)說一下?

OSI七層模型

為了使得多種設(shè)備能通過網(wǎng)絡(luò)相互通信,和為了解決各種不同設(shè)備在網(wǎng)絡(luò)互聯(lián)中的兼容性問題,國(guó)際標(biāo)準(zhǔn)化組織制定了開放式系統(tǒng)互聯(lián)通信參考模型(Open System Interconnection Reference Model),也就是 OSI 網(wǎng)絡(luò)模型,該模型主要有 7 層,分別是應(yīng)用層、表示層、會(huì)話層、傳輸層網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層以及物理層。

每一層負(fù)責(zé)的職能都不同,如下:

    應(yīng)用層,負(fù)責(zé)給應(yīng)用程序提供統(tǒng)一的接口;表示層,負(fù)責(zé)把數(shù)據(jù)轉(zhuǎn)換成兼容另一個(gè)系統(tǒng)能識(shí)別的格式;會(huì)話層,負(fù)責(zé)建立、管理和終止表示層實(shí)體之間的通信會(huì)話;傳輸層,負(fù)責(zé)端到端的數(shù)據(jù)傳輸;網(wǎng)絡(luò)層,負(fù)責(zé)數(shù)據(jù)的路由、轉(zhuǎn)發(fā)、分片;數(shù)據(jù)鏈路層,負(fù)責(zé)數(shù)據(jù)的封幀和差錯(cuò)檢測(cè),以及 MAC 尋址;物理層,負(fù)責(zé)在物理網(wǎng)絡(luò)中傳輸數(shù)據(jù)幀;

由于 OSI 模型實(shí)在太復(fù)雜,提出的也只是概念理論上的分層,并沒有提供具體的實(shí)現(xiàn)方案。

事實(shí)上,我們比較常見,也比較實(shí)用的是四層模型,即 TCP/IP 網(wǎng)絡(luò)模型,Linux 系統(tǒng)正是按照這套網(wǎng)絡(luò)模型來實(shí)現(xiàn)網(wǎng)絡(luò)協(xié)議棧的。

TCP/IP模型

TCP/IP協(xié)議被組織成四個(gè)概念層,其中有三層對(duì)應(yīng)于ISO參考模型中的相應(yīng)層。ICP/IP協(xié)議族并不包含物理層和數(shù)據(jù)鏈路層,因此它不能獨(dú)立完成整個(gè)計(jì)算機(jī)網(wǎng)絡(luò)系統(tǒng)的功能,必須與許多其他的協(xié)議協(xié)同工作。TCP/IP 網(wǎng)絡(luò)通常是由上到下分成 4 層,分別是應(yīng)用層,傳輸層,網(wǎng)絡(luò)層和網(wǎng)絡(luò)接口。

    應(yīng)用層 支持 HTTP、SMTP 等最終用戶進(jìn)程傳輸層 處理主機(jī)到主機(jī)的通信(TCP、UDP)網(wǎng)絡(luò)層 尋址和路由數(shù)據(jù)包(IP 協(xié)議)鏈路層 通過網(wǎng)絡(luò)的物理電線、電纜或無線信道移動(dòng)比特

TCP為什么要三次握手?

三次握手的原因:

    三次握手才可以阻止重復(fù)歷史連接的初始化(主要原因)三次握手才可以同步雙方的初始序列號(hào)三次握手才可以避免資源浪費(fèi)

原因一:避免歷史連接

我們來看看 RFC 793 指出的 TCP 連接使用三次握手的首要原因

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

簡(jiǎn)單來說,三次握手的首要原因是為了防止舊的重復(fù)連接初始化造成混亂。

我們考慮一個(gè)場(chǎng)景,客戶端先發(fā)送了 SYN(seq = 90)報(bào)文,然后客戶端宕機(jī)了,而且這個(gè) SYN 報(bào)文還被網(wǎng)絡(luò)阻塞了,服務(wù)并沒有收到,接著客戶端重啟后,又重新向服務(wù)端建立連接,發(fā)送了 SYN(seq = 100)報(bào)文(注意!不是重傳 SYN,重傳的 SYN 的序列號(hào)是一樣的)。

看看三次握手是如何阻止歷史連接的:

客戶端連續(xù)發(fā)送多次 SYN(都是同一個(gè)四元組)建立連接的報(bào)文,在網(wǎng)絡(luò)擁堵情況下:

    一個(gè)「舊 SYN 報(bào)文」比「最新的 SYN」 報(bào)文早到達(dá)了服務(wù)端,那么此時(shí)服務(wù)端就會(huì)回一個(gè) SYN + ACK 報(bào)文給客戶端,此報(bào)文中的確認(rèn)號(hào)是 91(90+1)??蛻舳耸盏胶?,發(fā)現(xiàn)自己期望收到的確認(rèn)號(hào)應(yīng)該是 100 + 1,而不是 90 + 1,于是就會(huì)回 RST 報(bào)文。服務(wù)端收到 RST 報(bào)文后,就會(huì)釋放連接。后續(xù)最新的 SYN 抵達(dá)了服務(wù)端后,客戶端與服務(wù)端就可以正常的完成三次握手了。

上述中的「舊 SYN 報(bào)文」稱為歷史連接,TCP 使用三次握手建立連接的最主要原因就是防止「歷史連接」初始化了連接。

如果是兩次握手連接,就無法阻止歷史連接,那為什么 TCP 兩次握手為什么無法阻止歷史連接呢?

我先直接說結(jié)論,主要是因?yàn)?strong>在兩次握手的情況下,服務(wù)端沒有中間狀態(tài)給客戶端來阻止歷史連接,導(dǎo)致服務(wù)端可能建立一個(gè)歷史連接,造成資源浪費(fèi)。

你想想,在兩次握手的情況下,服務(wù)端在收到 SYN 報(bào)文后,就進(jìn)入 ESTABLISHED 狀態(tài),意味著這時(shí)可以給對(duì)方發(fā)送數(shù)據(jù),但是客戶端此時(shí)還沒有進(jìn)入 ESTABLISHED 狀態(tài),假設(shè)這次是歷史連接,客戶端判斷到此次連接為歷史連接,那么就會(huì)回 RST 報(bào)文來斷開連接,而服務(wù)端在第一次握手的時(shí)候就進(jìn)入 ESTABLISHED 狀態(tài),所以它可以發(fā)送數(shù)據(jù)的,但是它并不知道這個(gè)是歷史連接,它只有在收到 RST 報(bào)文后,才會(huì)斷開連接。

可以看到,如果采用兩次握手建立 TCP 連接的場(chǎng)景下,服務(wù)端在向客戶端發(fā)送數(shù)據(jù)前,并沒有阻止掉歷史連接,導(dǎo)致服務(wù)端建立了一個(gè)歷史連接,又白白發(fā)送了數(shù)據(jù),妥妥地浪費(fèi)了服務(wù)端的資源。

因此,要解決這種現(xiàn)象,最好就是在服務(wù)端發(fā)送數(shù)據(jù)前,也就是建立連接之前,要阻止掉歷史連接,這樣就不會(huì)造成資源浪費(fèi),而要實(shí)現(xiàn)這個(gè)功能,就需要三次握手。

所以,TCP 使用三次握手建立連接的最主要原因是防止「歷史連接」初始化了連接。

原因二:同步雙方初始序列號(hào)

TCP 協(xié)議的通信雙方, 都必須維護(hù)一個(gè)「序列號(hào)」, 序列號(hào)是可靠傳輸?shù)囊粋€(gè)關(guān)鍵因素,它的作用:

    接收方可以去除重復(fù)的數(shù)據(jù);接收方可以根據(jù)數(shù)據(jù)包的序列號(hào)按序接收;可以標(biāo)識(shí)發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對(duì)方收到的(通過 ACK 報(bào)文中的序列號(hào)知道);

可見,序列號(hào)在 TCP 連接中占據(jù)著非常重要的作用,所以當(dāng)客戶端發(fā)送攜帶「初始序列號(hào)」的 SYN 報(bào)文的時(shí)候,需要服務(wù)端回一個(gè) ACK 應(yīng)答報(bào)文,表示客戶端的 SYN 報(bào)文已被服務(wù)端成功接收,那當(dāng)服務(wù)端發(fā)送「初始序列號(hào)」給客戶端的時(shí)候,依然也要得到客戶端的應(yīng)答回應(yīng),這樣一來一回,才能確保雙方的初始序列號(hào)能被可靠的同步。

四次握手其實(shí)也能夠可靠的同步雙方的初始化序號(hào),但由于第二步和第三步可以優(yōu)化成一步,所以就成了「三次握手」。

而兩次握手只保證了一方的初始序列號(hào)能被對(duì)方成功接收,沒辦法保證雙方的初始序列號(hào)都能被確認(rèn)接收。

原因三:避免資源浪費(fèi)

如果只有「兩次握手」,當(dāng)客戶端發(fā)生的 SYN 報(bào)文在網(wǎng)絡(luò)中阻塞,客戶端沒有接收到 ACK 報(bào)文,就會(huì)重新發(fā)送 SYN ,由于沒有第三次握手,服務(wù)端不清楚客戶端是否收到了自己回復(fù)的 ACK 報(bào)文,所以服務(wù)端每收到一個(gè) SYN 就只能先主動(dòng)建立一個(gè)連接,這會(huì)造成什么情況呢?

如果客戶端發(fā)送的 SYN 報(bào)文在網(wǎng)絡(luò)中阻塞了,重復(fù)發(fā)送多次 SYN 報(bào)文,那么服務(wù)端在收到請(qǐng)求后就會(huì)建立多個(gè)冗余的無效鏈接,造成不必要的資源浪費(fèi)。

即兩次握手會(huì)造成消息滯留情況下,服務(wù)端重復(fù)接受無用的連接請(qǐng)求 SYN 報(bào)文,而造成重復(fù)分配資源

算法

反轉(zhuǎn)鏈表

通過迭代遍歷鏈表,在遍歷過程中改變鏈表節(jié)點(diǎn)指針的指向,將當(dāng)前節(jié)點(diǎn)的next指針指向前一個(gè)節(jié)點(diǎn),從而實(shí)現(xiàn)鏈表的反轉(zhuǎn)。需要使用三個(gè)指針來輔助操作,分別指向當(dāng)前節(jié)點(diǎn)、前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)。

class?ListNode?{
????int?val;
????ListNode?next;
????ListNode(int?val)?{
????????this.val?=?val;
????}
}

public?class?ReverseLinkedList?{
????public?static?ListNode?reverseList(ListNode?head)?{
????????ListNode?prev?=?null;
????????ListNode?curr?=?head;
????????while?(curr!=?null)?{
????????????ListNode?nextTemp?=?curr.next;
????????????curr.next?=?prev;
????????????prev?=?curr;
????????????curr?=?nextTemp;
????????}
????????return?prev;
????}
}

在上述代碼中:

prev初始化為null,代表反轉(zhuǎn)后鏈表的末尾(也就是原鏈表的頭節(jié)點(diǎn)反轉(zhuǎn)后的前一個(gè)節(jié)點(diǎn))。

curr初始化為原鏈表的頭節(jié)點(diǎn)head,然后在循環(huán)中,先保存當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)到nextTemp,接著將當(dāng)前節(jié)點(diǎn)的next指針指向前一個(gè)節(jié)點(diǎn)prev,再更新prevcurr的值,繼續(xù)下一輪循環(huán),直到遍歷完整個(gè)鏈表,最后返回prev,它就是反轉(zhuǎn)后鏈表的頭節(jié)點(diǎn)。

小鵬汽車

小鵬汽車

小鵬汽車成立于2014年,是一家專注未來出行的科技公司。我們一直堅(jiān)持飽和式研發(fā)投入,構(gòu)建全棧自研的核心能力,今天小鵬汽車已經(jīng)成為中國(guó)領(lǐng)先的智能電動(dòng)汽車公司之一。小鵬汽車的使命是,用科技為人類創(chuàng)造更便捷愉悅的出行生活。

小鵬汽車成立于2014年,是一家專注未來出行的科技公司。我們一直堅(jiān)持飽和式研發(fā)投入,構(gòu)建全棧自研的核心能力,今天小鵬汽車已經(jīng)成為中國(guó)領(lǐng)先的智能電動(dòng)汽車公司之一。小鵬汽車的使命是,用科技為人類創(chuàng)造更便捷愉悅的出行生活。收起

查看更多

相關(guān)推薦