前言
JAVA掃盲筆記,旨在對一些模糊的概念做出解釋。
正文
基本數(shù)據(jù)類型和引用類型的區(qū)別
基本數(shù)據(jù)類型的訪問是按值訪問的,數(shù)據(jù)只存放在棧內(nèi),而引用類型的數(shù)據(jù)是棧指向堆。
八種基本數(shù)據(jù)類型:
整數(shù)型:byte short int long 8-16-32-64位 最大值為2的x-1次方,帶正負號
浮點類型:float double 32-64位
float----符號位占用1位,指數(shù)位占用8位,小數(shù)位占用23位。
double:符號位占用1位,指數(shù)位占用11位,小數(shù)位占用52位。
字符類型char 16位char c='a';char c=97;
布爾型-true or false
基本數(shù)據(jù)類型組成的數(shù)組,使用new為數(shù)組分配內(nèi)存:
一維數(shù)組int arr[]=new int[5];
二維數(shù)組int arr[][]=new int[2][3]
其他類型數(shù)組
List、ArrayList、HashMap
堆、棧、堆棧
棧包括:ring3用戶棧,ring0內(nèi)核棧(受操作系統(tǒng)級別保護)
堆向棧移動,棧向堆移動共同使用一塊內(nèi)存區(qū)域,直至用完。
一般只討論用戶棧stack、堆heap
棧:存放局部變量、函數(shù)調(diào)用時的寄存器信息 ESP棧頂指針、EBP棧底指針(先進后出)
堆:面向?qū)ο蟮膎ew和C的malloc都是在堆上申請內(nèi)存。(先進先出),堆空間遠大于棧。
常見的“堆?!倍嘀笚?。
final、finally、finalize的區(qū)別:
final:java關(guān)鍵字
修飾類(禁止繼承)、方法(禁止重寫)、變量(禁止賦值)
finally:java關(guān)鍵字
作為異常處理的一部分,關(guān)閉資源、釋放鎖
Finalize():Object類中的資源釋放方法,可以重寫。
定義在java.lang.Object中的方法,垃圾回收前調(diào)用,在對象回收前釋放資源,每個對象的Finalize()只會被GC調(diào)用一次。
GC垃圾回收機制、System.gc()認定的垃圾有哪些:
1.對象引用超過其作用范圍
2.對象置空Null
instanceof
java關(guān)鍵字,判斷兩個類的關(guān)系。
如果a繼承于父類A,那么a instanceof A = true ,A instanceof A = true,A instanceof a = false
但是如果B instanceof A則會出現(xiàn)編譯錯誤:不兼容的條件操作數(shù)類型(incompatible conditional operand types)
多態(tài)
官方解釋是,利用多態(tài)可以使程序具有良好的擴展性,并可以對所有類對象進行通用的處理。
簡單理解,多態(tài)是一種具體表現(xiàn)為利用面向?qū)ο笏枷胍约爸剌d、重寫、繼承、接口、向上轉(zhuǎn)型等實現(xiàn)的一種概念。他的實現(xiàn)方法有很多種,但其實主要的是學(xué)習(xí)思想。
反射、注解
反射:通過Java反射機制,可以在程序中訪問已經(jīng)裝載到JVM中的JAVA對象的描述,實現(xiàn)訪問、檢測和修改描述Java對象本身信息的功能。
注解:Annotation類型,用于類、構(gòu)造方法、成員變量、方法、參數(shù)等的聲明。該功能并不影響程序的運行,但是會對編譯器警告等輔助工具產(chǎn)生影響。
定義Annotation類型的關(guān)鍵字為@interface,繼承了Annotation接口。
@Target設(shè)置Annotation類型使用的程序元素種類。
@Retention設(shè)置Annotation的有效范圍。
利用反射可以獲得Annotation信息。
反射和注解常用于框架的開發(fā),比如Spring、Mybatis 和JPA都有用到。
Unsafe類
Unsafe類,全限定名是sun.misc.Unsafe,他可以獲得對象的內(nèi)存地址,獲得對象變量偏移量,從而對內(nèi)存空間直接做操作,無視權(quán)限控制,甚至還可以直接在一個地址上讀寫。
獲取unsafe對象:
Unsafe unsafe=Unsafe.getUnsafe();
volatile
java關(guān)鍵字
1.保證了變量的可見性(visibility)。被volatile關(guān)鍵字修飾的變量,如果值發(fā)生了變更volatile會保證修改的值立馬被更新到主存,其他線程需要讀取時會去內(nèi)存中讀取新值,避免出現(xiàn)臟讀的現(xiàn)象。
2.防止指令重排序。在執(zhí)行程序時,編譯器和處理器會對指令做重排序。
分三個等級(
1.編譯器優(yōu)化重排序
2.指令級并行重排序
3.內(nèi)存系統(tǒng)的重排序)
有如下一個問題:
a=b=x=y=0;
new Thread(()->{
x=1;
a=y;
}).start();
new Thread(()->{
y=1;
b=x;
}).start();
高并發(fā)的時候會出現(xiàn)a=0,b=0的情況。對a,b,x,y添加volatile保證讀寫操作的原子性,相當(dāng)于加了一個讀寫鎖。(但是不保證其他操作的原子性)
JMM
JAVA內(nèi)存模型,定義了JVM只在計算機內(nèi)存中的工作方式。
JAVA線程--->工作緩存(本地緩存)--->加載和保存操作--->主內(nèi)存
在MESI協(xié)議中,每個Cache line一共有4種狀態(tài),可用2個bit表示:
M: 被修改(Modified)
該緩存行只被緩存在該CPU的緩存中,并且是被修改過的(dirty),即與主存中的數(shù)據(jù)不一致,該緩存行中的內(nèi)存需要在未來的某個時間點(允許其它CPU讀取請主存中相應(yīng)內(nèi)存之前)寫回(write back)主存。
當(dāng)被寫回主存之后,該緩存行的狀態(tài)會變成獨享(exclusive)狀態(tài)。
E: 獨享的(Exclusive)
該緩存行只被緩存在該CPU的緩存中,它是未被修改過的(clean),與主存中數(shù)據(jù)一致。該狀態(tài)可以在任何時刻當(dāng)有其它CPU讀取該內(nèi)存時變成共享狀態(tài)(shared)。
同樣地,當(dāng)CPU修改該緩存行中內(nèi)容時,該狀態(tài)可以變成Modified狀態(tài)。
S: 共享的(Shared)
該狀態(tài)意味著該緩存行可能被多個CPU緩存,并且各個緩存中的數(shù)據(jù)與主存數(shù)據(jù)一致(clean),當(dāng)有一個CPU修改該緩存行中,其它CPU中該緩存行可以被作廢(變成無效狀態(tài)(Invalid))。
I: 無效的(Invalid)
該緩存是無效的(可能有其它CPU修改了該緩存行)。
當(dāng)volatile write被觸發(fā)后,其他的本地緩存狀態(tài)被改為無效的。
線程安全
在多線程由于線程對資源的互相搶占,會導(dǎo)致資源訪問沖突,我們稱這種現(xiàn)象叫線程不安全的。i++在底層也是分步走的,不遵從原子性,所以也是線程不安全的。
synchronized同步鎖
在多線程線程不安全時可以加鎖,常見的同步機制有悲觀鎖(synchronized)、樂觀鎖(CAS)、手動加鎖(LOCK指令)。
悲觀鎖、樂觀鎖:
synchronized關(guān)鍵字可以修飾同步塊(臨界區(qū))或者定義同步方法從而解決線程安全問題。事實上synchronized就是一個悲觀鎖,采用獨占的方式來訪問這些變量,獨占鎖其實就是一種悲觀鎖。
相對悲觀鎖而言,樂觀鎖主要就是兩個步驟:沖突檢測和數(shù)據(jù)更新。假設(shè)認為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
常用的樂觀鎖解決方案Compare and Swap(比較和交換)CAS技術(shù),非常直白一看就是一個樂觀鎖。
它有三個操作數(shù)分別為:V表示要更新的變量,E表示預(yù)估值,N表示新值。當(dāng)且僅當(dāng)V值等于E值時,才將V的值設(shè)置為N;如果V值和E值不同, 返回當(dāng)前V的值。
悲觀鎖代碼:
public class Safetest implements Runnable {
@Override
public void run() {
boolean isrun=true;
while(isrun) {
synchronized ("") {
if(ticketpool.num>0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ticketpool.num--;
System.out.println("購票成功還剩下"+ticketpool.num+"張票" );
}else {
isrun=false;
}
}
}
}
}
public class ticketpool {
public static volatile int num=20;
}
偽--樂觀鎖代碼,執(zhí)行效率比純悲觀鎖好一點:
public class Safetest implements Runnable {
@Override
public void run() {
boolean isrun=true;
while(isrun) {
int version= ticketpool.version;
int ttnum= ticketpool.num;
if(ttnum>0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ttnum--;
if(CAS(version+1,ttnum)) {
System.out.println("購票成功還剩下"+ttnum+"張票" );
}else {
System.out.println("購票失敗,請重試");
}
}else {
isrun=false;
}
}
}
/**
*
* @param E 期望值
* @param N 新值
* @param V 要更新的變量
* @return boolean
*/
public synchronized boolean CAS(int N,int V) {
if((ticketpool.version+1)==N) {
ticketpool.version=N;
ticketpool.num=V;
return true;
}else {
return false;
}
}
}
真正的CAS樂觀鎖實現(xiàn),使用Unsafe類的CAS或者使用AtomicIntger原子操作類。
public class Safetest implements Runnable {
@Override
public void run() {
boolean isrun=true;
while(isrun) {
AtomicInteger ttnum= ticketpool.NUM;
int expect=ttnum.get();
if(expect>0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int update=expect-1;
if( ttnum.compareAndSet(expect, update)) {
System.out.println("購票成功還剩下"+update+"張票" );
}else {
System.out.println("購票失敗,請重試");
}
}else {
isrun=false;
}
}
}
ABA問題:A,B兩個線程,A的期望值和新值確定后沒有立即執(zhí)行,B搶占資源進行一系列修改后把數(shù)據(jù)恢復(fù)成A進入等待之前的狀態(tài),結(jié)果A還可以順利執(zhí)行。
使用帶印記(版本號)的原子操作類,AtomicStampedReference可以解決ABA問題:
public class Safetest implements Runnable {
@Override
public void run() {
boolean isrun=true;
while(isrun) {
AtomicStampedReference ato = ticketpool.atomicStampedRef;
int[] stampHolder = new int[1];
int expect=(int) ato.get(stampHolder);
int version=ato.getStamp();
if(expect>0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int update=expect-1;
if( ato.compareAndSet(expect, update, version, version+1)) {
System.out.println("購票成功還剩下"+update+"張票" );
}else {
System.out.println("購票失敗,請重試");
}
}else {
isrun=false;
}
}
}
自旋問題:最好避免重操作和需要頻繁修改的數(shù)據(jù)段使用樂觀鎖,以此規(guī)避自旋產(chǎn)生的問題。
Lock手動加鎖代碼:
private Lock lock = new ReentrantLock();
try{
lock.lock;
i--;
}finnally{
lock.unlcok;
}
Serializable
JDK自帶的IO接口,一個類的對象要想序列化成功,必須滿足兩個條件:
該類必須實現(xiàn) java.io.Serializable 接口。
該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須注明是短暫(transient)的?;蛉绻胁幌胄蛄谢膶傩?,也聲明為transient,當(dāng)對該類序列化時,會自動忽略被 transient 修飾的屬性。
https://blog.csdn.net/wayne566/article/details/80292689
可以使對象數(shù)據(jù)序列化以二進制文本的方式被持久化到了磁盤文件中或在網(wǎng)絡(luò)間傳播,并通過反序列化得到對象。
雙親委派機制
ClassLoader負責(zé)將class文件加載到JVM中去執(zhí)行,JVM中提供了三層的ClassLoader:
1.Bootstrap ClassLoader :最頂層的加載類,主要加載核心類庫,也就是我們環(huán)境變量下面%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通過啟動jvm時指定-Xbootclasspath和路徑來改變Bootstrap ClassLoader的加載目錄。比如java -Xbootclasspath/a:path被指定的文件追加到默認的bootstrap路徑中。我們可以打開我的電腦,在上面的目錄下查看,看看這些jar包是不是存在于這個目錄。
2.Extention ClassLoader :擴展的類加載器,加載目錄%JRE_HOME%\lib\ext目錄下的jar包和class文件。還可以加載-D java.ext.dirs選項指定的目錄。
3.Appclass Loader:也稱為SystemAppClass。 加載當(dāng)前應(yīng)用的classpath的所有類。
當(dāng)一個類加載器收到類加載任務(wù),會先交給其父類加載器去完成,因此最終加載任務(wù)都會傳遞到頂層的啟動類加載器,只有當(dāng)父類加載器無法完成加載任務(wù)時,才會嘗試執(zhí)行加載任務(wù)。
好處:
1.可以避免重復(fù)加載,父類已經(jīng)加載了,子類就不需要再次加載
2.更加安全,很好的解決了各個類加載器的基礎(chǔ)類的統(tǒng)一問題,如果不使用該種方式,那么用戶可以隨意定義類加載器來加載核心api,會帶來相關(guān)隱患。
IOC
Spring框架的基本知識,中文名為控制反轉(zhuǎn),既獲得依賴對象的過程被反轉(zhuǎn)了。涉及的設(shè)計模式:
1.工廠設(shè)計模式 : Spring使用工廠模式通過 BeanFactory、ApplicationContext 創(chuàng)建 bean 對象。
2.單例設(shè)計模式 : Spring 中的 Bean 默認都是單例的。
3.模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結(jié)尾的對數(shù)據(jù)庫操作的類,它們就使用到了模板模式。
4.包裝器設(shè)計模式 : 我們的項目需要連接多個數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會去訪問不同的數(shù)據(jù)庫。這種模式讓我們可以根據(jù)客戶的需求能夠動態(tài)切換不同的數(shù)據(jù)源。
依賴注入DI和控制反轉(zhuǎn)IOC就是從不同的角度描述同一件事情,是指通過引入IOC容器,利用依賴關(guān)系注入的方式實現(xiàn)對象之間的解耦。
其底層核心就是利用反射的方法動態(tài)生成JAVABEAN(Bean:在計算機英語中,有可重用組件的含義)對象,并把他們放到IOC容器里供程序使用。由于JAVA垃圾回收機制的存在,我們就需要一個容器來保護、管理實體類對象所以IOC容器的主要工作就是負責(zé)創(chuàng)建對象,管理對象,裝配對象,配置對象,并且管理這些對象的整個生命周期。
bean對象的生命周期:
單例對象
出生:當(dāng)容器創(chuàng)建時對象出生
活著:只要容器還在,對象一直活著
死亡:容器銷毀,對象消亡
總結(jié):單例對象的生命周期和容器相同
多例對象
出生:當(dāng)我們使用對象時spring框架為我們創(chuàng)建
活著:對象只要是在使用過程中就一直活著。
死亡:當(dāng)對象長時間不用,且沒有別的對象引用時,由Java的垃圾回收器回收
我們可以通過XML文件或者使用注解的方式來配置進行依賴關(guān)系的維護,依賴注入。
注解配置和 xml 配置要實現(xiàn)的功能都是一樣的,都是要降低程序間的耦合。只是配置的形式不一樣。
AOP
Spring框架的基本知識,中文名為面向切面編程,通過配置的方法實現(xiàn)基于接口和基于子類的動態(tài)代理,具體實現(xiàn)也是分為XML文件配置和注解配置。
其目的就是為了使得減少重復(fù)代碼、提高開發(fā)效率、維護方便。
一共有五種切面通知:
1.前置通知(before)
2.后置通知(after-returning)---try
3.異常通知(after-thorwing)---catch
4.最終通知(after)---finally
5.環(huán)繞通知(around)
同時Spring也內(nèi)嵌了事物通知(transaction)
SpringMVC與五大核心組件
SpringMVC就是建立在Spring應(yīng)用平臺之上的MVC模型--[模型(model)-視圖(view)-控制器(controller)]??梢哉fSpringMVC的核心組成就是這五大組件:
1.DispatcherServlet 前置控制器
2.HandlerMapping 處理器映射
3.Controller 控制器
4.ModelAndView 封裝數(shù)據(jù)信息和視圖信息的模型
5.ViewResolver 視圖解析器
他們工作的流程圖大概如下圖:
我還是比較看好這個解釋的:
五大組件
關(guān)于SpringMVC的說法也很多有人就提出了九大組件也可以當(dāng)當(dāng)擴展看看:
九大組件
因為它沒有前端控制器所以不好理解暫且就不學(xué)習(xí)了,以后深入的時候應(yīng)該會接觸到。
Servlet
Servlet是sun公司提供的一門用于開發(fā)動態(tài)web資源的技術(shù)。
Sun公司在其API中提供了一個servlet接口,用戶若想用發(fā)一個動態(tài)web資源(即開發(fā)一個Java程序向瀏覽器輸出數(shù)據(jù)),需要完成以下2個步驟:
1、編寫一個Java類,實現(xiàn)servlet接口。
2、把開發(fā)好的Java類部署到web服務(wù)器中。
按照一種約定俗成的稱呼習(xí)慣,通常我們也把實現(xiàn)了servlet接口的java程序,稱之為Servlet
其中Tomcat服務(wù)器不同版本就是對JAVA EE Servlet不同版本規(guī)范的實現(xiàn),Servlet是一個接口,而Tomcat就是對其進行底層實現(xiàn)的一套解決方案。
比如說:Apache Tomcat 9.X 就是遵循Servlet4.0,JSP2.3,EL3.0規(guī)范的一Servlet服務(wù)器,支持jdk8.0及以上的版本的開發(fā)。
Servlet程序、Filter過濾器、Listener監(jiān)聽器并成為JavaWeb三大組件。
Servlet的生命周期從第一次訪問時開始創(chuàng)建、初始化,并且他是一個單例對象,接下來請求此Servlet都是使用同一對象,直到Web工程停止時對對象進行銷毀。
過濾器在請求進入服務(wù)前進行過濾,可以對其進行身份校驗選擇是否進行重定向。
監(jiān)聽器可以對三大域(request、session、ServletContext)進行監(jiān)聽,其提供了八種監(jiān)聽器接口。這八種監(jiān)聽器可以分為四大種類:
1.監(jiān)聽對象的創(chuàng)建:
ServletContext:主要監(jiān)聽servletContext的創(chuàng)建,需要實現(xiàn)ServeltContextListener接口。
ServletRequest:主要監(jiān)聽request的創(chuàng)建, 需要實現(xiàn)ServletRequestListener接口
HttpSession:主要監(jiān)聽session的創(chuàng)建,需要實現(xiàn)HttpSessionListener接口
2.監(jiān)聽屬性的改變:
ServletContext:主要監(jiān)聽servletContext屬性的更改、添加、刪除,需要實現(xiàn)ServeltContextAttrbuteListener接口。
ServletRequest:主要監(jiān)聽request屬性的更改、添加、刪除, 需要實現(xiàn)ServletRequestAttrbuteListener接口
HttpSession:主要監(jiān)聽session屬性的更改、添加、刪除,需要實現(xiàn)HttpSessionAttrbuteListener接口
3.監(jiān)聽session的活化與鈍化:
httpSessionActivationListener主要監(jiān)聽了session的活化與鈍化
4.監(jiān)聽session與對象的綁定:
httpSessionBindingListener監(jiān)聽了session與對象的綁定
在學(xué)習(xí)的時候應(yīng)當(dāng)著重于各種監(jiān)聽器的觸發(fā)條件。
活化與鈍化其實就是對反應(yīng)序列化與序列化,可以對session進行保存和恢復(fù)需要實現(xiàn)Serializable接口。
學(xué)習(xí)鏈接:
https://www.cnblogs.com/lukelook/p/11079113.html
https://blog.csdn.net/csdn19970806/article/details/80707271
JavaWeb四大域
以上說到監(jiān)聽三大域的監(jiān)聽器,那么現(xiàn)在就來聊聊四大域。
依照pageContext->request->session->servletContext的順序由小到大。
pageContext是JSP中特有的,如果使用靜態(tài)頁面則沒有,封裝了8大隱式對象。
servletContext則涵蓋了整個web應(yīng)用。
http://www.itdecent.cn/p/6c02951267d8
EL表達式
EL是JSP內(nèi)置的表達式語言,EL可以輸出的東西都在11個內(nèi)置對象中,11個內(nèi)置對象,其中10個是Map,只有pageContext不是(如上所述是四大域之一)。
https://blog.csdn.net/qq_17045385/article/details/54799998
HTTP協(xié)議
HTTP協(xié)議下屬于TPC/IP協(xié)議簇(TCP/IP協(xié)議不僅僅指的是TCP 和IP兩個協(xié)議,而是指一個由FTP、SMTP、TCP、UDP、IP等協(xié)議構(gòu)成的協(xié)議簇, 只是因為在TCP/IP協(xié)議中TCP協(xié)議和IP協(xié)議最具代表性,所以被稱為TCP/IP協(xié)議)中的應(yīng)用層協(xié)議。
協(xié)議就是指:網(wǎng)絡(luò)協(xié)議是通信計算機雙方必須共同遵從的一組約定。一般由Servlet提供規(guī)范接口。
HTTP包括GET,POST,PUT,DELETE,OPTIONS等請求方式,主流開發(fā)中一般使用GET,POST兩種請求方式。
他們都包含請求行、請求頭,但是POST請求參數(shù)放在了請求體內(nèi)。
一般來說純參數(shù)的請求使用GET也可以達到和POST一樣的效果但是由于瀏覽器和服務(wù)器對URL長度(8208字節(jié))的限制導(dǎo)致傳圖像和文件時只能使用POST請求。
https://www.cnblogs.com/xianlei/p/tcpip_http.html
HTTPS協(xié)議
HTTPS協(xié)議可以理解為HTTP協(xié)議的升級,就是在HTTP的基礎(chǔ)上增加了數(shù)據(jù)加密。在數(shù)據(jù)進行傳輸之前,對數(shù)據(jù)進行加密,然后再發(fā)送到服務(wù)器。這樣,就算數(shù)據(jù)被第三者所截獲,但是由于數(shù)據(jù)是加密的,所以你的個人信息讓然是安全的。這就是HTTP和HTTPS的最大區(qū)別。
2021.4.14補充:
HTTPS實際上就是利用了CA(也可能是自己)頒發(fā)的SSL證書利用其中的公鑰、私鑰,進而通過一系列加密算法實現(xiàn)對稱、非對稱加密等過程,實現(xiàn)對信息加密防止了信息傳輸過程中的偷窺以及中間人攻擊等可能危害信息安全的事件發(fā)生。