摘要:本文收錄于技術(shù)專(zhuān)家修煉文中配套資料合集路線導(dǎo)圖高清源文件點(diǎn)擊跳轉(zhuǎn)到文末點(diǎn)擊底部卡片回復(fù)資料領(lǐng)取哈嘍,大家好,我是一條最近粉絲問(wèn)我有沒(méi)有自學(xué)路線,有了方向才能按圖索驥,事半功倍。
本文收錄于《技術(shù)專(zhuān)家修煉》
文中配套資料合集
路線導(dǎo)圖高清源文件
點(diǎn)擊跳轉(zhuǎn)到文末點(diǎn)擊底部卡片回復(fù)「資料」領(lǐng)取
哈嘍,大家好,我是一條~
最近粉絲問(wèn)我有沒(méi)有Java自學(xué)路線,有了方向才能按圖索驥,事半功倍。
我一想確實(shí)如此,自己去年總結(jié)了一份,但有些地方需要修改,索性利用國(guó)慶假期,重新整理一份。
沒(méi)錯(cuò),就是這篇文章,本文分為學(xué)習(xí)路線和配套資料兩部分。
Java學(xué)習(xí),如逆水行舟,不進(jìn)則退。而自學(xué),逆水還得加個(gè)水逆,難上加難。
所以我們要做好打持久戰(zhàn)的準(zhǔn)備。
凡事預(yù)則立,不預(yù)則廢。一個(gè)好的計(jì)劃是成功的一半,而這一半,一條已經(jīng)幫你整理好了,你只需要收藏即可。
該路線圖左側(cè)為主路線,需循序漸進(jìn),步步為營(yíng);右側(cè)為輔助路線,需貫穿始終,熟練掌握。
建議做好時(shí)間規(guī)劃,不斷的提高自己的學(xué)習(xí)效率,學(xué)習(xí)過(guò)程中盡量把手機(jī)調(diào)至靜音給自己一個(gè)安靜的學(xué)習(xí)環(huán)境和氛圍。
獨(dú)腳難行,孤掌難鳴,一個(gè)人的力量終究是有限的,一個(gè)人的旅途也注定是孤獨(dú)的。當(dāng)你定好計(jì)劃,懷著滿(mǎn)腔熱血準(zhǔn)備出發(fā)的時(shí)候,一定要找個(gè)伙伴,和唐僧西天取經(jīng)一樣,師徒四人團(tuán)結(jié)一心才能通過(guò)九九八十一難。
在學(xué)習(xí)過(guò)程中看下自己身邊有沒(méi)有Java這方面的大神,盡量多問(wèn),多交流,如果沒(méi)有的話,來(lái)找我,我一定知無(wú)不言言無(wú)不盡,還可以給你找一群志同道合的人。水漲船高,柴多火旺,就是這個(gè)道理,閉門(mén)造車(chē)注定會(huì)半途而廢。
駑馬十駕,功在不舍。自學(xué)Java非一日之功,你知道的越多,不知道的也越多。所以,為自己找一個(gè)動(dòng)力,為了改變命運(yùn),或是為了心愛(ài)的人,或是為了讓別人高看一眼。男兒何不帶吳鉤,收取關(guān)山十五州。歲月無(wú)情,余生有涯,請(qǐng)將生活扛在肩上,只顧風(fēng)雨兼程。
學(xué)習(xí)任何語(yǔ)言,都是先從他的基本語(yǔ)法開(kāi)始,如果你有C語(yǔ)言的基礎(chǔ),會(huì)容易許多,沒(méi)有也不用現(xiàn)學(xué)。
Java 語(yǔ)言提供了 8 種基本類(lèi)型,大致分為 4 類(lèi)(8位=1字節(jié))
byte
- 1字節(jié)short
- 2字節(jié)int
- 4字節(jié)long
- 8字節(jié),賦值時(shí)一般在數(shù)字后加上 l
或 L
float
- 4字節(jié),直接賦值時(shí)必須在數(shù)字后加上 f
或 F
double
- 8字節(jié),賦值時(shí)一般在數(shù)字后加 d
或 D
char
- 2字節(jié),存儲(chǔ) Unicode 碼,用單引號(hào)賦值boolean
- 1字節(jié),只有 true 和 false 兩個(gè)取值,一個(gè)字節(jié)就夠了簡(jiǎn)單來(lái)說(shuō),所有的非基本數(shù)據(jù)類(lèi)型都是引用數(shù)據(jù)類(lèi)型,除了基本數(shù)據(jù)類(lèi)型對(duì)應(yīng)的引用類(lèi)型外,類(lèi)、 接口類(lèi)型、 數(shù)組類(lèi)型、 枚舉類(lèi)型、 注解類(lèi)型、 字符串型都屬于引用類(lèi)型。
主要有以下區(qū)別:
1、存儲(chǔ)位置
2、傳遞方式
訪問(wèn)修飾符就是限制變量的訪問(wèn)權(quán)限的。
比如你有個(gè)“賺錢(qián)”的方法,誰(shuí)都不想給用,那就把方法設(shè)成private
(私有);
后來(lái)你有了老婆孩子,你想讓他們也會(huì)賺錢(qián),就得設(shè)置成default
(同一個(gè)包);
后來(lái)你又有了第二個(gè)孩子,但你發(fā)現(xiàn)他不會(huì)賺錢(qián)的方法,為啥呢?因?yàn)槟惚?font color="green">綠了(default不支持不同包的子類(lèi));
可為了大局,你還是選擇接受這個(gè)孩子,悄悄把方法設(shè)置成了proteced
(保護(hù)子類(lèi),即使不同包);
后來(lái)你老了,明白了開(kāi)源才是共贏,就設(shè)置成了public
(公有的);
不知道你聽(tīng)懂了嗎,估計(jì)看到被那啥了就不想看了吧,沒(méi)關(guān)系,看圖(也是綠的)
主要意義:
我日常調(diào)用方法都是對(duì)象.方法,static
的主要意義就是可以創(chuàng)建獨(dú)立于具體對(duì)象的域變量或者方法。也就是實(shí)現(xiàn)即使沒(méi)有創(chuàng)建對(duì)象,也能使用屬性和調(diào)用方法!
另一個(gè)比較關(guān)鍵的作用就是 用來(lái)形成靜態(tài)代碼塊以?xún)?yōu)化程序性能。static
塊可以置于類(lèi)中的任何地方,可以有多個(gè)。在類(lèi)初次被加載的時(shí)候,會(huì)按照static
塊的順序來(lái)執(zhí)行每個(gè)static
塊,并且只會(huì)執(zhí)行一次,可以用來(lái)優(yōu)化程序性能
通俗理解:
static
是一個(gè)可以讓你升級(jí)的關(guān)鍵字,被static
修飾,你就不再是你了。
final
翻譯成中文是“不可更改的,最終的”,顧名思義,他的功能就是不能再修改,不能再繼承。我們常見(jiàn)的String類(lèi)
就是被final
修飾的。將類(lèi)、方法、變量聲明為final能夠提高性能,這樣JVM就有機(jī)會(huì)進(jìn)行估計(jì),然后優(yōu)化。
按照J(rèn)ava代碼慣例,final變量就是常量,而且通常常量名要大寫(xiě):
封裝
1.什么是封裝
封裝又叫隱藏實(shí)現(xiàn)。就是只公開(kāi)代碼單元的對(duì)外接口,而隱藏其具體實(shí)現(xiàn)。
其實(shí)生活中處處都是封裝,手機(jī),電腦,電視這些都是封裝。你只需要知道如何去操作他們,并不需要知道他們里面是怎么構(gòu)造的,怎么實(shí)現(xiàn)這個(gè)功能的。
2.如何實(shí)現(xiàn)封裝
在程序設(shè)計(jì)里,封裝往往是通過(guò)訪問(wèn)控制實(shí)現(xiàn)的。也就是剛才提到的訪問(wèn)修飾符。
3.封裝的意義
封裝提高了代碼的安全性,使代碼的修改變的更加容易,代碼以一個(gè)個(gè)獨(dú)立的單元存在,高內(nèi)聚,低耦合。
好比只要你手機(jī)的充電接口不變,無(wú)論以后手機(jī)怎么更新,你依然可以用同樣的數(shù)據(jù)線充電或者與其他設(shè)備連接。
封裝的設(shè)計(jì)使使整個(gè)軟件開(kāi)發(fā)復(fù)雜度大大降低。我只需要使用別人的類(lèi),而不必關(guān)心其內(nèi)部邏輯是如何實(shí)現(xiàn)的。我能很容易學(xué)會(huì)使用別人寫(xiě)好的代碼,這就讓軟件協(xié)同開(kāi)發(fā)的難度大大降低。
封裝還避免了命名沖突的問(wèn)題。
好比你家里有各種各樣的遙控器,但比還是直到哪個(gè)是電視的,哪個(gè)是空調(diào)的。因?yàn)橐粋€(gè)屬于電視類(lèi)一個(gè)屬于空調(diào)類(lèi)。不同的類(lèi)中可以有相同名稱(chēng)的方法和屬性,但不會(huì)混淆。
繼承
繼承的主要思想就是將子類(lèi)的對(duì)象作為父類(lèi)的對(duì)象來(lái)使用。比如王者榮耀的英雄作為父類(lèi),后裔作為子類(lèi)。后裔有所有英雄共有的屬性,同時(shí)也有自己獨(dú)特的技能。
多態(tài)
多態(tài)的定義:
指允許不同類(lèi)的對(duì)象對(duì)同一消息做出響應(yīng)。即同一消息可以根據(jù)發(fā)送對(duì)象的不同而采用多種不同的行為方式。(發(fā)送消息就是函數(shù)調(diào)用)
簡(jiǎn)單來(lái)說(shuō),同樣調(diào)用攻擊這個(gè)方法,后裔的普攻和亞瑟的普攻是不一樣的。
多態(tài)的條件:
多態(tài)的好處:
多態(tài)對(duì)已存在代碼具有可替換性。
多態(tài)對(duì)代碼具有可擴(kuò)充性。
它在應(yīng)用中體現(xiàn)了靈活多樣的操作,提高了使用效率。
多態(tài)簡(jiǎn)化對(duì)應(yīng)用軟件的代碼編寫(xiě)和修改過(guò)程,尤其在處理大量對(duì)象的運(yùn)算和操作時(shí),這個(gè)特點(diǎn)尤為突出和重要。
Java中多態(tài)的實(shí)現(xiàn)方式:
JavaWeb是用Java技術(shù)來(lái)解決相關(guān)web互聯(lián)網(wǎng)領(lǐng)域的技術(shù)棧。Web就是網(wǎng)頁(yè),分為靜態(tài)和動(dòng)態(tài)。涉及 的知識(shí)點(diǎn)主要包括jsp,servlet,tomcat,http,MVC等知識(shí)。
本章難度不高,但也不可忽視。其中前端基礎(chǔ)不需花過(guò)多時(shí)間,重點(diǎn)放在Tomcat上,會(huì)陪伴你整個(gè)Java生涯。
GET
:最常用的方式,用來(lái)向服務(wù)器請(qǐng)求數(shù)據(jù),沒(méi)有請(qǐng)求體,請(qǐng)求參數(shù)放在URL后面。POST
:用于向表單提交數(shù)據(jù),傳送的數(shù)據(jù)放在請(qǐng)求體中。PUT
:用來(lái)向服務(wù)器上傳文件,一般對(duì)應(yīng)修改操作,POST
用于向服務(wù)器發(fā)送數(shù)據(jù),PUT用于向服務(wù)器儲(chǔ)存數(shù)據(jù)。沒(méi)有驗(yàn)證機(jī)制,任何人都可以操作,存在安全問(wèn)題。具有冪等性。DELETE
:用于刪除服務(wù)器上的文件,具有冪等性。同樣存在安全問(wèn)題。HEAD
:用HEAD進(jìn)行請(qǐng)求服務(wù)器時(shí),服務(wù)器只返回響應(yīng)頭,不返回響應(yīng)體。與GET
一樣沒(méi)有請(qǐng)求體,常用于檢查請(qǐng)求的URL是否有效。PATCH
:對(duì)資源進(jìn)行部分修改。與PUT區(qū)別在于,PUT是修改所有資源,替代它,而PATCH只是修改部分資源。TRACE
:用來(lái)查看一個(gè)請(qǐng)求,經(jīng)過(guò)網(wǎng)關(guān),代理到達(dá)服務(wù)器,最后請(qǐng)求的變換。因安全問(wèn)題被禁用。OPTIONS
:當(dāng)客戶(hù)端不清楚對(duì)資源操作的方法,可以使用這個(gè),具有冪等性。是否具有冪等性也是一個(gè)http請(qǐng)求的重要關(guān)注點(diǎn)。
冪等性:指的是同樣的請(qǐng)求不管執(zhí)行多少次,效果都是一樣,服務(wù)器狀態(tài)也是一樣的。具有冪等性的請(qǐng)求方法沒(méi)有副作用。(統(tǒng)計(jì)用途除外)
假設(shè)這樣一個(gè)場(chǎng)景:有時(shí)我們?cè)谔顚?xiě)某些
form表單
時(shí),保存按鈕不小心快速點(diǎn)了兩次,表中竟然產(chǎn)生了兩條重復(fù)的數(shù)據(jù),只是id不一樣。這是一個(gè)比較常見(jiàn)的冪等性問(wèn)題,在高并發(fā)場(chǎng)景下會(huì)變得更加復(fù)雜,那怎么保證接口的冪等性呢?
1.insert前select
插入數(shù)據(jù)前先根據(jù)某一字段查詢(xún)一下數(shù)據(jù)庫(kù),如果已經(jīng)存在就修改,不存在再插入。
2.加鎖
加鎖可解決一切問(wèn)題,但也要考慮并發(fā)性。
主要包括悲觀鎖,樂(lè)觀鎖,分布式鎖。
悲觀鎖的并發(fā)性較低,更適合使用在防止數(shù)據(jù)重復(fù)的場(chǎng)景,注意冪等性不光是防止重復(fù)還需要結(jié)果相同。
樂(lè)觀鎖可以很高的提升性能,也就是常說(shuō)的版本號(hào)。
分布式鎖應(yīng)用在高并發(fā)場(chǎng)景,主要用redis來(lái)實(shí)現(xiàn)。
3.唯一索引
通過(guò)數(shù)據(jù)庫(kù)的唯一索引來(lái)保證結(jié)果的一致性和數(shù)據(jù)的不重復(fù)。
4.Token
兩次請(qǐng)求,第一請(qǐng)求拿到token,第二次帶著token去完成業(yè)務(wù)請(qǐng)求。
網(wǎng)絡(luò)狀態(tài)碼共三位數(shù)字組成,根據(jù)第一個(gè)數(shù)字可分為以下幾個(gè)系列:
1xx(信息性狀態(tài)碼)
代表請(qǐng)求已被接受,需要繼續(xù)處理。
包括:100、101、102
這一系列的在實(shí)際開(kāi)發(fā)中基本不會(huì)遇到,可以略過(guò)。
2xx(成功狀態(tài)碼)
表示成功處理了請(qǐng)求的狀態(tài)代碼。
200
:請(qǐng)求成功,表明服務(wù)器成功了處理請(qǐng)求。
202
:服務(wù)器已接受請(qǐng)求,但尚未處理。
204
:服務(wù)器成功處理了請(qǐng)求,但沒(méi)有返回任何內(nèi)容。
206
:服務(wù)器成功處理了部分 GET 請(qǐng)求。
3xx(重定向狀態(tài)碼)
300
:針對(duì)請(qǐng)求,服務(wù)器可執(zhí)行多種操作。
301
:永久重定向
302
:臨時(shí)性重定向
303
:303與302狀態(tài)碼有著相同的功能,但303狀態(tài)碼明確表示客戶(hù)端應(yīng)當(dāng)采用GET方法獲取資源。
301和302的區(qū)別?
301
比較常用的場(chǎng)景是使用域名跳轉(zhuǎn)。比如,我們?cè)L問(wèn) http://www.baidu.com
會(huì)跳轉(zhuǎn)到https://www.baidu.com
,發(fā)送請(qǐng)求之后,就會(huì)返回301狀態(tài)碼,然后返回一個(gè)location,提示新的地址,瀏覽器就會(huì)拿著這個(gè)新的地址去訪問(wèn)。
302
用來(lái)做臨時(shí)跳轉(zhuǎn)比如未登陸的用戶(hù)訪問(wèn)用戶(hù)中心重定向到登錄頁(yè)面。
4xx(客戶(hù)端錯(cuò)誤狀態(tài)碼)
400
:該狀態(tài)碼表示請(qǐng)求報(bào)文中存在語(yǔ)法錯(cuò)誤。但瀏覽器會(huì)像200 OK一樣對(duì)待該狀態(tài)碼。
401
:表示發(fā)送的請(qǐng)求需要有通過(guò)HTTP認(rèn)證的認(rèn)證信息。比如token
失效就會(huì)出現(xiàn)這個(gè)問(wèn)題。
403
:被拒絕,表明對(duì)請(qǐng)求資源的訪問(wèn)被服務(wù)器拒絕了。
404
:找不到,表明服務(wù)器上無(wú)法找到請(qǐng)求的資源,也可能是拒絕請(qǐng)求但不想說(shuō)明理由。
5xx(服務(wù)器錯(cuò)誤狀態(tài)碼)
500
:服務(wù)器本身發(fā)生錯(cuò)誤,可能是Web應(yīng)用存在的bug或某些臨時(shí)的故障。
502
:該狀態(tài)碼表明服務(wù)器暫時(shí)處于超負(fù)載或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。
??有時(shí)候返回的狀態(tài)碼響應(yīng)是錯(cuò)誤的,比如Web應(yīng)用程序內(nèi)部發(fā)生錯(cuò)誤,狀態(tài)碼依然返回200
上面提到了重定向,那你知道什么是轉(zhuǎn)發(fā)嗎?
1.轉(zhuǎn)發(fā)
A找B借錢(qián),B沒(méi)有錢(qián),B去問(wèn)C,C有錢(qián),C把錢(qián)借給A的過(guò)程。
客戶(hù)瀏覽器發(fā)送http請(qǐng)求,web服務(wù)器接受此請(qǐng)求,調(diào)用內(nèi)部的一個(gè)方法在容器內(nèi)部完成請(qǐng)求處理和轉(zhuǎn)發(fā)動(dòng)作,將目標(biāo)資源發(fā)送給客戶(hù)。
整個(gè)轉(zhuǎn)發(fā)一個(gè)請(qǐng)求,一個(gè)響應(yīng),地址欄不會(huì)發(fā)生變化,不能跨域訪問(wèn)。
2.重定向
A找B借錢(qián),B沒(méi)有錢(qián),B讓A去找C,A又和C借錢(qián),C有錢(qián),C把錢(qián)借給A的過(guò)程。
客戶(hù)瀏覽器發(fā)送http請(qǐng)求,web服務(wù)器接受后發(fā)送302狀態(tài)碼響應(yīng)及對(duì)應(yīng)新的location給客戶(hù)瀏覽器,客戶(hù)瀏覽器發(fā)現(xiàn)是302響應(yīng),則自動(dòng)再發(fā)送一個(gè)新的http請(qǐng)求,請(qǐng)求url是新的location地址,服務(wù)器根據(jù)此請(qǐng)求尋找資源并發(fā)送給客戶(hù)。
兩個(gè)請(qǐng)求,兩個(gè)響應(yīng),可以跨域。
servlet是一個(gè)比較抽獎(jiǎng)的概念,也是web部分的核心組件,大家回答這個(gè)問(wèn)題一定要加入自己的理解,不要背定義。
servlet
其實(shí)就是一個(gè)java程序,他主要是用來(lái)解決動(dòng)態(tài)頁(yè)面的問(wèn)題。
之前都是瀏覽器像服務(wù)器請(qǐng)求資源,服務(wù)器(tomcat)返回頁(yè)面,但用戶(hù)多了之后,每個(gè)用戶(hù)希望帶到不用的資源。這時(shí)就該servlet
上場(chǎng)表演了。
servlet
存在于tomcat
之中,用來(lái)網(wǎng)絡(luò)請(qǐng)求與響應(yīng),但他的重心更在于業(yè)務(wù)處理,我們?cè)L問(wèn)京東和淘寶的返回的商品是不一樣的,就需要程序員去編寫(xiě),目前MVC三層架構(gòu),我們都是在service
層處理業(yè)務(wù),但這其實(shí)是從servlet
中抽取出來(lái)的。
看一下servlet
處理請(qǐng)求的過(guò)程:
Servlet生命周期分為三個(gè)階段:
首先我們要明白HTTP是一種無(wú)狀態(tài)協(xié)議,怎么理解呢?很簡(jiǎn)單
夏洛:大爺,樓上322住的是馬冬梅家吧?大爺:馬冬什么? 夏洛:馬冬梅。 大爺:什么冬梅啊? 夏洛:馬冬梅啊。 大爺:馬什么梅?夏洛:行,大爺你先涼快著吧。
這段對(duì)話都熟悉吧,HTTP就是那個(gè)大爺,那如果我們就直接把“大爺”放給用戶(hù),用戶(hù)不用干別的了,就不停的登錄就行了。
既然“大爺不靠譜”,我們找“大娘”去吧。
哈哈哈,開(kāi)個(gè)玩笑,言歸正傳。
為了解決用戶(hù)頻繁登錄的問(wèn)題,在服務(wù)端和客戶(hù)端共同維護(hù)一個(gè)狀態(tài)——會(huì)話,就是所謂session
,我們根據(jù)會(huì)話id判斷是否是同一用戶(hù),這樣用戶(hù)就開(kāi)心了。
但是服務(wù)器可不開(kāi)心了,因?yàn)橛脩?hù)越來(lái)越多,都要把session
存在服務(wù)器,這對(duì)服務(wù)器來(lái)說(shuō)是一個(gè)巨大的開(kāi)銷(xiāo),這是服務(wù)器就找來(lái)了自己的兄弟幫他分擔(dān)(集群部署,負(fù)載均衡)。
但是問(wèn)題依然存在,如果兄弟掛了怎么辦,兄弟們之間的數(shù)據(jù)怎么同步,用戶(hù)1把session
存放在機(jī)器A上,下次訪問(wèn)時(shí)負(fù)載均衡到了機(jī)器B,完了,找不到,用戶(hù)又要罵娘。
這時(shí)有人思考,為什么一定要服務(wù)端保存呢,讓客戶(hù)端自己保存不就好了,所以就誕生了cookie
,下一次請(qǐng)求時(shí)客戶(hù)段把cookie
發(fā)送給服務(wù)器,說(shuō)我已經(jīng)登錄了。
但是空口無(wú)憑,服務(wù)器怎么知道哪個(gè)cookie
是我發(fā)過(guò)去的呢?如何驗(yàn)證成了新的問(wèn)題。
有人想到了一個(gè)辦法,用加密令牌,也就是token
,服務(wù)器發(fā)給客戶(hù)端一個(gè)令牌,令牌保存加密后id和密鑰,下一次請(qǐng)求時(shí)通過(guò)headers
傳給服務(wù)端,由于密鑰別人不知道,只有服務(wù)端知道,就實(shí)現(xiàn)了驗(yàn)證,且別人無(wú)法偽造。
三層架構(gòu)與MVC的目標(biāo)一致:都是為了解耦和、提高代碼復(fù)用。MVC是一種設(shè)計(jì)模式,而三層架構(gòu)是一種軟件架構(gòu)。
MVC
Model 模型
模型負(fù)責(zé)各個(gè)功能的實(shí)現(xiàn)(如登錄、增加、刪除功能),用JavaBean
實(shí)現(xiàn)。
View 視圖
用戶(hù)看到的頁(yè)面和與用戶(hù)的交互。包含各種表單。 實(shí)現(xiàn)視圖用到的技術(shù)有html/css/jsp/js等前端技術(shù)。
常用的web 容器和開(kāi)發(fā)工具
Controller 控制器
控制器負(fù)責(zé)將視圖與模型一一對(duì)應(yīng)起來(lái)。相當(dāng)于一個(gè)模型分發(fā)器。接收請(qǐng)求,并將該請(qǐng)求跳轉(zhuǎn)(轉(zhuǎn)發(fā),重定向)到模型進(jìn)行處理。模型處理完畢后,再通過(guò)控制器,返回給視圖中的請(qǐng)求處。
三層架構(gòu)
表現(xiàn)層(UI)(web層)、業(yè)務(wù)邏輯層(BLL)(service層)、數(shù)據(jù)訪問(wèn)層(DAL)(dao層) ,再加上實(shí)體類(lèi)庫(kù)(Model)
工欲善其事必先利其器,集合就是我們的器。
底層實(shí)現(xiàn)
由什么組成,我說(shuō)了不算,看源碼。怎么看呢?
List<Object> list = new ArrayList<>();
新建一個(gè)
ArrayList
,按住ctrl
或command
用鼠標(biāo)點(diǎn)擊。
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. * 翻譯 * 數(shù)組緩沖區(qū),ArrayList的元素被存儲(chǔ)在其中。ArrayList的容量是這個(gè)數(shù)組緩沖區(qū)的長(zhǎng)度。 * 任何空的ArrayList,如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA, * 當(dāng)?shù)谝粋€(gè)元素被添加時(shí),將被擴(kuò)展到DEFAULT_CAPACITY。 */ transient Object[] elementData;
毋庸置疑,底層由數(shù)組組成,那數(shù)組的特點(diǎn)就是ArrayList
的特點(diǎn)。
O(1)
。好比你去住酒店,每個(gè)房間都挨著,房門(mén)都寫(xiě)著房間號(hào)。你想找哪一間房是不是很容易。O(n)
,想像你在做excel
表格的時(shí)候,想增加一列,后面的列是不是都要跟著移動(dòng)。擴(kuò)容
我們知道數(shù)組是容量不可變的數(shù)據(jù)結(jié)構(gòu),隨著元素不斷增加,必然要擴(kuò)容。
所以擴(kuò)容機(jī)制也是集合中非常容易愛(ài)問(wèn)的問(wèn)題,在源碼中都可以一探究竟。
1.初始化容量為10,也可以指定容量創(chuàng)建。
/** * Default initial capacity. * 定義初始化容量 */ private static final int DEFAULT_CAPACITY = 10;
2.數(shù)組進(jìn)行擴(kuò)容時(shí),是將舊數(shù)據(jù)拷貝到新的數(shù)組中,新數(shù)組容量是原容量的1.5倍。(這里用位運(yùn)算是為了提高運(yùn)算速度)
private void grow(int minCapacity) { int newCapacity = oldCapacity + (oldCapacity >> 1);}
3.擴(kuò)容代價(jià)是很高得,因此再實(shí)際使用時(shí),我們因該避免數(shù)組容量得擴(kuò)張。盡可能避免數(shù)據(jù)容量得擴(kuò)張。盡可能,就至指定容量,避免數(shù)組擴(kuò)容的發(fā)生。
為什么擴(kuò)容是1.5倍?
所以,1.5
是均衡了空間占用和擴(kuò)容次數(shù)考慮的。
線程安全問(wèn)題
怎么看線程安全?說(shuō)實(shí)話我以前都不知道,看網(wǎng)上說(shuō)安全就安全,說(shuō)不安全就不安全。
其實(shí)都在源碼里。找到增加元素的方法,看看有沒(méi)有加鎖就知道了。
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
沒(méi)有加鎖,所以線程不安全
在多線程的情況下,插入數(shù)據(jù)的時(shí)可能會(huì)造成數(shù)據(jù)丟失,一個(gè)線程在遍歷,另一個(gè)線程修改,會(huì)報(bào)ConcurrentModificationException(并發(fā)修改異常)
錯(cuò)誤.
多線程下使用怎么保證線程安全?
保證線程安全的思路很簡(jiǎn)單就是加鎖,但是你可沒(méi)辦法修改源碼去加個(gè)鎖,但是你想想編寫(xiě)
java
的大佬會(huì)想不到線程安全問(wèn)題?早就給你準(zhǔn)備了線程安全的類(lèi)。
1.Vector
Vector
是一個(gè)線程安全的List
類(lèi),通過(guò)對(duì)所有操作都加上synchronized
關(guān)鍵字實(shí)現(xiàn)。
找到add
方法,可以看到被synchronized
關(guān)鍵字修飾,也就是加鎖,但synchronized
是重度鎖,并發(fā)性太低,所以實(shí)際一般不使用,隨著java
版本的更新,慢慢廢棄。
public void add(E e) { int i = cursor; synchronized (Vector.this) { checkForComodification(); Vector.this.add(i, e); expectedModCount = modCount; } cursor = i + 1; lastRet = -1; }
2.Collections
注意是Collections
而不是Collection
。
Collections
位于java.util
包下,是集合類(lèi)的工具類(lèi),提供了很多操作集合類(lèi)的方法。其中Collections.synchronizedList(list)
可以提供一個(gè)線程安全的List
。
對(duì)于Map、Set也有對(duì)應(yīng)的方法
3.CopyOnWrite(寫(xiě)時(shí)復(fù)制)
寫(xiě)時(shí)復(fù)制,簡(jiǎn)稱(chēng)COW,是計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域中的一種通用優(yōu)化策略。
當(dāng)有多人同時(shí)訪問(wèn)同一資源時(shí),他們會(huì)共同獲取指向相同的資源的指針,供訪問(wèn)者進(jìn)行讀操作。
當(dāng)某個(gè)調(diào)用者修改資源內(nèi)容時(shí),系統(tǒng)會(huì)真正復(fù)制一份副本給該調(diào)用者,而其他調(diào)用者所見(jiàn)到的最初的資源仍然保持不變。修改完成后,再把新的數(shù)據(jù)寫(xiě)回去。
通俗易懂的講,假設(shè)現(xiàn)在有一份班級(jí)名單,但有幾個(gè)同學(xué)還沒(méi)有填好,這時(shí)老師把文件通過(guò)微信發(fā)送過(guò)去讓同學(xué)們填寫(xiě)(復(fù)制一份),但不需要修改的同學(xué)此時(shí)查看的還是舊的名單,直到有同學(xué)修改好發(fā)給老師,老師用新的名單替換舊的名單,全班同學(xué)才能查看新的名單。
共享讀,分開(kāi)寫(xiě)。讀寫(xiě)分離,寫(xiě)時(shí)復(fù)制。
在java中,通過(guò)CopyOnWriteArrayList
、CopyOnWriteArraySet
容器實(shí)現(xiàn)了 COW 思想。
平時(shí)查詢(xún)的時(shí)候,都不需要加鎖,隨便訪問(wèn),只有在更新的時(shí)候,才會(huì)從原來(lái)的數(shù)據(jù)復(fù)制一個(gè)副本出來(lái),然后修改這個(gè)副本,最后把原數(shù)據(jù)替換成當(dāng)前的副本。修改操作的同時(shí),讀操作不會(huì)被阻塞,而是繼續(xù)讀取舊的數(shù)據(jù)。
/** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;
源碼里用到了ReentrantLock
鎖和volatile
關(guān)鍵字,會(huì)在《資深程序員修煉》專(zhuān)欄中做全面深度講解。
LinkedList
和ArrayList
同屬于List
集合。其共同特點(diǎn)可歸納為:存儲(chǔ)單列數(shù)據(jù)的集合,存儲(chǔ)的數(shù)據(jù)是有序并且是可以重復(fù)的。
但兩者也有不同,往下看吧
底層實(shí)現(xiàn)
LinkedList
類(lèi)的底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是一個(gè)雙向鏈表。同時(shí)還實(shí)現(xiàn)了Deque
接口,所以會(huì)有些隊(duì)列的特性,會(huì)在下面講。
class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
先簡(jiǎn)單說(shuō)一下鏈表這種數(shù)據(jù)結(jié)構(gòu),與數(shù)組相反,鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),一個(gè)最簡(jiǎn)單的鏈表(單鏈表)有節(jié)點(diǎn)Node
和數(shù)值value
組成。通俗的講,就像串在一起的小魚(yú)干,中間用線連著。
transient Node<E> first;transient Node<E> last;
鏈表中保存著對(duì)最后一個(gè)節(jié)點(diǎn)的引用,這就是雙端鏈表
在單鏈表的結(jié)點(diǎn)中增加一個(gè)指向其前驅(qū)的pre指針就是雙向鏈表,一種犧牲空間換時(shí)間的做法。
雙端鏈表不同于雙向鏈表,切記!
關(guān)于鏈表更詳細(xì)代碼級(jí)講解會(huì)放《糊涂算法》專(zhuān)欄更新。敬請(qǐng)期待!
簡(jiǎn)單了解過(guò)后分析一下鏈表的特點(diǎn):
如何解決查詢(xún)慢的問(wèn)題?
如果我查找的元素在尾部,則需要遍歷整個(gè)鏈表,所以有了雙端鏈表。
即使不在尾部,我如果只能一個(gè)方向遍歷,也很麻煩,所以有了雙向隊(duì)列,犧牲空間換時(shí)間。
那么空間可不可以再犧牲一點(diǎn)?
可以,就是跳躍鏈表,簡(jiǎn)稱(chēng)「跳表」。
通過(guò)建立多級(jí)索引來(lái)加快查詢(xún)速度。
線程安全問(wèn)題
老辦法,看看
add()
方法。分為「頭插法」和「尾插法」。
/** * Inserts the specified element at the beginning of this list. * * @param e the element to add */ public void addFirst(E e) { linkFirst(e); } /** * Appends the specified element to the end of this list. * * This method is equivalent to {@link #add}. * * @param e the element to add */
public void addLast(E e) { linkLast(e); }
都沒(méi)加鎖,百分之一百的不安全。
如何解決線程不安全問(wèn)題
1.ConcurrentLinkedQueue
一個(gè)新的類(lèi),位于java.util.concurrent
(juc)包下。實(shí)現(xiàn)了Queue
接口。
class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable{}
使用violate
關(guān)鍵字實(shí)現(xiàn)加鎖。
private transient volatile Node<E> head; private transient volatile Node<E> tail;
1.Collections
和ArrayList
一樣,使用Collections.synchronizedList()
。
Map:存儲(chǔ)雙列數(shù)據(jù)的集合,通過(guò)鍵值對(duì)存儲(chǔ)數(shù)據(jù),存儲(chǔ) 的數(shù)據(jù)是無(wú)序的,Key值不能重復(fù),value值可以重復(fù)
共同點(diǎn):有序,可重復(fù)。線程不安全。
不同點(diǎn):底層架構(gòu),查詢(xún)和刪改的速度
重點(diǎn)來(lái)了,Java程序員一定要深入研究的內(nèi)容
JVM體系結(jié)構(gòu)如下圖所示,將按照從上到下的順序講解
負(fù)責(zé)將class文件的字節(jié)碼內(nèi)容加載到內(nèi)存中并將這些內(nèi)容轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),class文件在文件開(kāi)頭有特定標(biāo)識(shí)(cafe babe:Java圖標(biāo)——咖啡和橡樹(shù))。
通俗來(lái)講:classloader相當(dāng)于快遞員的作用,只負(fù)責(zé)加載,至于是否能運(yùn)行,由Execution Engine決定
雙親委派機(jī)制
當(dāng)一個(gè)類(lèi)收到類(lèi)加載請(qǐng)求,他會(huì)先把這個(gè)請(qǐng)求交給他的父類(lèi),只有父類(lèi)無(wú)法完成這個(gè)請(qǐng)求時(shí),子加載器才會(huì)嘗試自己去加載。
雙親委派的好處是保護(hù)Java核心類(lèi),比如加載位于rt.jar中的java.lang.Object,不管是哪個(gè)加載器加載的,最終都會(huì)交給啟動(dòng)類(lèi)加載器,這樣就保證了不同的類(lèi)加載器得到的都是同一個(gè)Object對(duì)象。
代碼舉例:查看類(lèi)是被那個(gè)加載器加載的
/** * @Author: 一條IT * @Date: 2020/12/3 21:28 */public class Test { public static void main(String[] args) { System.out.println(Object.class.getClassLoader()); System.out.println(Test.class.getClassLoader().getParent().getParent()); System.out.println(Test.class.getClassLoader().getParent()); System.out.println(Test.class.getClassLoader()); }}
輸出
nullnullsun.misc.Launcher$ExtClassLoader@1b6d3586sun.misc.Launcher$AppClassLoader@14dad5dc
因?yàn)镺bject是jdk自帶的,所以在加載的時(shí)候是走Bootstrap啟動(dòng)類(lèi)加載器,而B(niǎo)ootstrap加載器是C++語(yǔ)言寫(xiě)的,所以在查的時(shí)候是null,報(bào)了NullPointException();Test類(lèi)自己寫(xiě)的,走AppClassLoder,他的父類(lèi)是擴(kuò)展加載器,再父類(lèi)是啟動(dòng)類(lèi)加載器,也輸出Null
沙箱安全機(jī)制
主要是防止惡意代碼污染java源代碼,比如定義了一個(gè)類(lèi)名為String所在包為java.lang,因?yàn)檫@個(gè)類(lèi)本來(lái)是屬于jdk的,如果沒(méi)有沙箱安全機(jī)制的話,這個(gè)類(lèi)將會(huì)污染到我所有的String,但是由于沙箱安全機(jī)制,所以就委托頂層的bootstrap加載器查找這個(gè)類(lèi),如果沒(méi)有的話就委托extsion,extsion沒(méi)有就到appclassloader,但是由于String就是jdk的源代碼,所以在bootstrap那里就加載到了,先找到先使用,所以就使用bootstrap里面的String,后面的一概不能使用,這就保證了不被惡意代碼污染。
垃圾回收是重點(diǎn)難,先理解了垃圾回收,才能理解調(diào)優(yōu)的思路。
判斷垃圾
判斷是否是垃圾共有兩種方法。引用計(jì)數(shù)法和可達(dá)性分析
1.引用計(jì)數(shù)法
非常好理解,引用一次標(biāo)記一次,沒(méi)有被標(biāo)記的就是垃圾。
在堆中存儲(chǔ)對(duì)象時(shí),在對(duì)象頭處維護(hù)一個(gè)counter
計(jì)數(shù)器,如果一個(gè)對(duì)象增加了一個(gè)引用與之相連,則將counter++
。
如果一個(gè)引用關(guān)系失效則counter--
。如果一個(gè)對(duì)象的counter變?yōu)?,則說(shuō)明該對(duì)象已經(jīng)被廢棄,不處于存活狀態(tài),此時(shí)可以被回收。
2.引用計(jì)數(shù)的缺點(diǎn)
3.可達(dá)性分析
類(lèi)似樹(shù)的樹(shù)結(jié)構(gòu),從根結(jié)點(diǎn)出發(fā),即GC root,把有關(guān)系的對(duì)象用一顆樹(shù)鏈接起來(lái)
那么我們遍歷這棵樹(shù),沒(méi)遍歷到的對(duì)象,就是垃圾
4.有哪些可以做GC Roots的對(duì)象?
回收算法
回收算法是垃圾回收的思想,回收器是垃圾回收的實(shí)現(xiàn)
1.標(biāo)記-清除
兩次遍歷:
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/122555.html
摘要:的安裝下載好之后雙擊打開(kāi)可執(zhí)行安裝文件選擇安裝目錄,需要的內(nèi)存較多,建議將其安裝在盤(pán)或者盤(pán),不建議放在系統(tǒng)盤(pán)盤(pán)。 yolov5無(wú)從下手?一篇就夠的保姆級(jí)教程,202...
摘要:哪吒社區(qū)技能樹(shù)打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無(wú)意間聽(tīng)到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹(shù)打卡?【打卡貼 day2...
摘要:前言由于寫(xiě)的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 前言 由于寫(xiě)的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時(shí)間才會(huì)更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號(hào):Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡(jiǎn)單 注解就這么簡(jiǎn)單 Druid數(shù)據(jù)庫(kù)連接池...
閱讀 3055·2023-04-26 03:01
閱讀 3546·2023-04-25 19:54
閱讀 1597·2021-11-24 09:39
閱讀 1381·2021-11-19 09:40
閱讀 4260·2021-10-14 09:43
閱讀 2076·2019-08-30 15:56
閱讀 1500·2019-08-30 13:52
閱讀 1668·2019-08-29 13:05