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