日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

复习资料整合

發(fā)布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 复习资料整合 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1、設(shè)計(jì)模式

代理模式--Spring中的AOP

工廠模式--BeanFactory所有的對象都是通過Factory

模板方法設(shè)計(jì)模式--RestTemplate --

策略設(shè)計(jì)模式---算法--策略--輪詢等--LoadBalancerClient--還有Ribbon

單例設(shè)計(jì)模式--Singleton--餓漢式:小對象,頻繁加載

懶漢式:用的時候加載對象,加鎖

門面設(shè)計(jì)模式--Slf4j 日志的查看

適配器模式--XxxAdapter 適配器接口繼承某個適配器,就不用實(shí)現(xiàn)全部的方法了,用哪個實(shí)現(xiàn)哪個就可以了

責(zé)任鏈模式--Interceptor 比如攔截器鏈等等

雙重校驗(yàn)的單例設(shè)計(jì)--很多地方都用到了

享元設(shè)計(jì)模式---所有的池都是,通過池減少對象的創(chuàng)建次數(shù)

建造者模式,過濾器模式,

2、常見的工廠模式

1、簡單工廠模式:每增加一款汽車都需要修改工廠類。違背開閉原則

2、工廠方法模式:只負(fù)責(zé)生產(chǎn)單一產(chǎn)品。避免簡單工廠模式的缺點(diǎn)。新增一款汽車只需要新建一家工廠即可。符合開閉原則

3、抽象工廠模式:工廠負(fù)責(zé)生產(chǎn)旗下多個產(chǎn)品(產(chǎn)品族)打比方:一個超級工廠里面分小工廠,工廠1生產(chǎn)輪胎、工廠2生產(chǎn)方向盤、工廠3生產(chǎn):底盤等等。新增汽車工廠實(shí)現(xiàn)此接口即可

抽象工廠模式會使用上:需要生產(chǎn)公司旗下的一系列產(chǎn)品(產(chǎn)品族)。同時其它公司旗下也會生產(chǎn)同樣一系列產(chǎn)品(可以理解成是共同抽象出來的部分)。只是兩個工廠生產(chǎn)出來品牌不一樣。

抽象工廠模式常見應(yīng)用:oracle和mysql數(shù)據(jù)庫應(yīng)用時,系統(tǒng)可以會替換數(shù)據(jù)庫,這時候抽象工廠模式就派上用場了。?

2、數(shù)據(jù)結(jié)構(gòu)

1、數(shù)組;是有序的元素序列,數(shù)組是在內(nèi)存中開辟一段連續(xù)的空間,并在此空間存放元素。查找快,增刪慢。

就像是一列火車,從1號車廂到最后一節(jié)車廂,每節(jié)車廂都有自己的固定編號,乘坐火車的人可以通過車廂號快速找到自己的位置。

2、鏈表,是一系列Node節(jié)點(diǎn)組成,每個節(jié)點(diǎn)包括兩部分(存儲數(shù)據(jù)元素的數(shù)據(jù)域,存儲下一個節(jié)點(diǎn)地址的指針域),多個節(jié)點(diǎn)之間通過地址進(jìn)行連接

一種遞歸的數(shù)據(jù)結(jié)構(gòu);單向鏈表和雙向鏈表兩種;不需要初始化容量、可以添加任意元素;

插入和刪除的時候只需要更新引用。

8、哈希表Hash Table:也叫散列表,通過(key-value)數(shù)據(jù)結(jié)構(gòu),可以快速實(shí)現(xiàn)查找、插入和刪除。

3、堆:可以被看做是一棵樹的數(shù)組對象

  • 堆中某個節(jié)點(diǎn)的值總是不大于或不小于其父節(jié)點(diǎn)的值;
  • 堆總是一棵完全二叉樹。

3、棧Stack,它是運(yùn)算受限的線性表,其限制是僅允許在標(biāo)的一端進(jìn)行插入和刪除操作,不允許在其他任何位置進(jìn)行添加、查找、刪除等操作。

按照“先進(jìn)后出”的原則來存儲數(shù)據(jù);像一個水桶 都是線性表

壓棧:就是存元素 彈棧:就是取元素。

4、隊(duì)列queue;先進(jìn)先出,也是一種運(yùn)算受限的線性表,其限制是僅允許在表的一端進(jìn)行插入,而在表的另一端進(jìn)行刪除。像一個水管,隊(duì)頭只允許刪除操作(出隊(duì)),隊(duì)尾只允許插入操作(入隊(duì))。

5、,是由 n(n>0)個有限節(jié)點(diǎn)組成的一個具有層次關(guān)系的集合;

分為:無序樹,二叉樹,滿二叉樹,二叉樹查找樹,紅黑樹

二叉樹BinaryTree:每個節(jié)點(diǎn)不超過2的有序樹(tree)

我們可以簡單理解成生活的樹的結(jié)構(gòu),只不過每個節(jié)點(diǎn)上都最多只能有兩個子節(jié)點(diǎn)。

頂上的叫根節(jié)點(diǎn),兩邊被稱作“左子樹”和“右子樹”。

紅黑樹:紅黑樹是最常見的平衡二叉樹(左右要實(shí)現(xiàn)“平衡,兩邊的子樹相等”)本身就是一顆二叉查找樹,平衡二叉樹的難點(diǎn)在于,當(dāng)刪除或者增加節(jié)點(diǎn)的情況下,如何通過左旋或者右旋的方式來保持左右平衡。

樹的鍵值仍然是有序的。紅黑樹的速度特別快,趨近平衡樹,查找葉子元素最少和最多次數(shù)不多于二倍

紅黑樹的特點(diǎn):

  • 節(jié)點(diǎn)可以是紅色的或者黑色的
  • 根節(jié)點(diǎn)是黑色的
  • 葉子節(jié)點(diǎn)(特指空節(jié)點(diǎn))是黑色的
  • 如果一個節(jié)點(diǎn)是紅色的,則它兩個子節(jié)點(diǎn)都是黑色的。也就是說在一條路徑上不能出現(xiàn)相鄰的兩個紅色節(jié)點(diǎn)。
  • 從任一節(jié)點(diǎn)到其每個葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。

7、圖;圖是一種復(fù)雜的非線性結(jié)構(gòu),有頂點(diǎn)和有窮非空集合和頂點(diǎn)之間的集合組成,通常表示為G(V,E) ,其中G表示一個圖,V是圖G中 頂點(diǎn)的集合,E是圖G中 邊的集合

3、代理

1.1代理對象

JDK動態(tài)代理

特點(diǎn):

1.要求被代理者必須實(shí)現(xiàn)(有)接口.

2.JDK代理是jdk默認(rèn)提供的.

默認(rèn)情況下采用jdk實(shí)現(xiàn)aop,也可以強(qiáng)制使用CGlib

CGLIB動態(tài)代理

特點(diǎn):

1.不管被代理者是否有接口,都可以為其創(chuàng)建代理對象. 代理對象是目標(biāo)對象的子類.

2.cglib需要手動導(dǎo)入jar包

3.spring為了創(chuàng)建代理對象方便,自身自動添加cglib依賴項(xiàng)

CGLIB的大部分類是直接對Java字節(jié)碼進(jìn)行操作,這樣生成的類會在Java的永久堆中。如果動態(tài)代理操作過多,容易造成永久堆滿,觸發(fā)OutOfMemory內(nèi)存溢出異常

spring默認(rèn)使用jdk動態(tài)代理,如果類沒有接口,則使用cglib。Spring會自動在JDK動態(tài)代理和cglib之間轉(zhuǎn)換

1.2代理機(jī)制

反向代理---nginx

nginx是 一個高性能的HTTP 和 反向代理web服務(wù)器

ngnix有wendows版本,也有l(wèi)inux版本

在高連接并發(fā)的情況下,Nginx是Apache服務(wù)器不錯的替代品。

  • 代理服務(wù)器介于用戶和服務(wù)器之間.
  • 用戶以為反向代理服務(wù)器就是目標(biāo)服務(wù)器.
  • 用戶不清楚真實(shí)的服務(wù)器到底是誰!
  • 反向代理服務(wù)器保護(hù)了目標(biāo)服務(wù)器的信息 所以也稱之為"服務(wù)器端代理".
  • 正向代理--路由vpn

  • 正向代理服務(wù)器 介于用戶和服務(wù)器之間
  • 用戶將請求發(fā)送給代理服務(wù)器,并且指定目標(biāo)服務(wù)器.
  • 目標(biāo)服務(wù)器以為是代理服務(wù)器訪問的,保護(hù)了用戶的信息,所以也稱之為 “客戶端代理”
  • 方法的重寫

    語法規(guī)則:兩同 兩小 一大

    兩同:方法名相同,參數(shù)列表相同

    一大:(子類方法的修飾符范圍>=父類的修飾符范圍--指的是訪問控制符)

    兩小:子類方法的返回值類型<=父類方法的返回值類型(是引用類型的返回值是繼承關(guān)系的大小)(void和基本數(shù)據(jù)類型,返回類型必須保持一致)
    子類方法拋出的異常類型<=父類的異常類型

    重寫和重載的區(qū)別

    方法的重載和重寫都是實(shí)現(xiàn)多態(tài)的方式,區(qū)別在于前者實(shí)現(xiàn)的是編譯時的多態(tài)性,而后者實(shí)現(xiàn)的是運(yùn)行時的多態(tài)性。

    重載發(fā)生在一個類中,同名的方法如果有不同的參數(shù)列表(參數(shù)類型不同、參數(shù)個數(shù)不同或者二者都不同)則視為重載;

    重寫發(fā)生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的參數(shù)列表,有兼容的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求,不能根據(jù)返回類型進(jìn)行區(qū)分。
    ?

    IO流

    序列化:ObjectOutputStream

    將對象轉(zhuǎn)化為字節(jié)/Json字符串 保存在磁盤文件中

    反序列化:ObjectInputStream

    反序列化流對象來讀取恢復(fù)對象

    將字節(jié)/Json字符串從磁盤文件中取出,重新恢復(fù)成對象

    *每次反序列化時,拿之前序列化生成的UID作比較,一致時才能反序列成功
    *解決方案1:一次序列化對應(yīng)一次反序列化操作
    *解決方案2:將UID寫死,無論怎么修改Student類,UID都不變*/

    private static final long serialVersionUID=1L;

    4.1輸入流、輸出流

    字節(jié)流:按8位傳輸以字節(jié) 為單位輸入輸出數(shù)據(jù)

    字節(jié)輸入流:FileInputStream文件字節(jié)輸入流 BufferedInputStream高效字節(jié)輸入流

    字節(jié)輸出流:FileOuputStream 、BufferOutputStream

    字符流按照16位傳輸以字符為單位 輸入輸出數(shù)據(jù)

    字符輸入流:FileReader文件字符輸入流、BufferedReader高效字符輸入流

    字符輸出流:FileWriter、BufferWriter

    Buffered為什么高效:Buffered實(shí)質(zhì)是通過內(nèi)部一個緩存數(shù)組來實(shí)現(xiàn)“緩沖區(qū)”的功能。BufferedInputStream是緩沖輸入流,它繼承于FilterInputStream。

    BufferedInputStream的read()里面就寫好了類似于byte[] buf = new byte[1024];的方法,以至于它可以一下子讀到的字節(jié)比FileInputStream一下子讀到的字節(jié)多

    BufferedInputStream 的好處之一就是能夠提高效率,是因?yàn)榇嬖诰彺婵臻g,就比如一個中轉(zhuǎn)倉庫,當(dāng)倉庫滿了的時候就把倉庫的物品一次搬移。緩存空間數(shù)據(jù)滿了之后就讀取一次把數(shù)據(jù)取走。

    FileInputStream是一直讀取流中數(shù)據(jù),這樣相比于BufferedInputStream更加耗費(fèi)CPU,效率也就下降了

    編碼轉(zhuǎn)換流:

    輸出流 outputStreamWriter

    輸入流 InputStreamReader

  • 大多數(shù)情況下使用字節(jié)流會更好,因?yàn)榇蠖鄶?shù)時候 IO 操作都是直接操作磁盤文件,所以這些流在傳輸時都是以字節(jié)的方式進(jìn)行的(圖片等都是按字節(jié)存儲的)
  • 如果對于操作需要通過 IO 在內(nèi)存中頻繁處理字符串的情況使用字符流會好些,因?yàn)?strong>字符流具備緩沖區(qū),提高了性能
  • 序列化就是一種用來處理對象流的機(jī)制,將對象的內(nèi)容進(jìn)行流化??梢詫α骰蟮膶ο筮M(jìn)行讀寫操作,可以將流化后的對象傳輸于網(wǎng)絡(luò)之間。序列化是為了解決在對象流讀寫操作時所引發(fā)的問題,對象--字節(jié)
  • 序列化的實(shí)現(xiàn):將需要被序列化的類實(shí)現(xiàn)Serialize接口,沒有需要實(shí)現(xiàn)的方法,此接口只是為了標(biāo)注對象可被序列化的,然后使用一個輸出流(如:FileOutputStream)來構(gòu)造一個ObjectOutputStream(對象流)對象,再使用ObjectOutputStream對象的write(Object obj)方法就可以將參數(shù)obj的對象寫
  • 流一旦打開就必須關(guān)閉,使用close方法
  • 放入finally語句塊中(finally 語句一定會執(zhí)行)
  • 調(diào)用的處理流就關(guān)閉處理流
  • 多個流互相調(diào)用只關(guān)閉最外層的流
  • 使用字符流:完全和輸入的字符保持一致

    字節(jié)流:結(jié)果是二進(jìn)制文件:a是00 00 00 05,int是四個字節(jié) b是01,布爾是一個字節(jié)
    c是00 47,char是兩個字節(jié)

    JVM

    java虛擬機(jī)

    • Java虛擬機(jī)JVM 管理的內(nèi)存包括5個運(yùn)行時數(shù)據(jù)內(nèi)存方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器,其中方法區(qū)和堆是由線程共享的數(shù)據(jù)區(qū),其他幾個是線程隔離的數(shù)據(jù)區(qū)
    • JVM垃圾回收,年青代(Young)(分為伊甸園區(qū),存活區(qū))、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法(復(fù)制算法、標(biāo)記清除算法、標(biāo)記整理算法)。

    內(nèi)存溢出和內(nèi)存泄露

    內(nèi)存泄漏 Memory Leak 是指程序申請內(nèi)存后,無法釋放已申請的資源,一次沒關(guān)系,但是內(nèi)存泄漏次數(shù)多了就會導(dǎo)致內(nèi)存溢出OutOfMemory。new了對象,但是不歸還delete,產(chǎn)生堆積;
    未清空對象的引用,或關(guān)流

    ②:垃圾回收

    ③:死循環(huán)問題,不結(jié)束

    ④:線程等待問題,線程溢出,無限遞歸調(diào)用

    堆溢出:堆和棧的設(shè)置過小,過程中不停的創(chuàng)建對象,都會引起堆溢出

    棧溢出:遞歸的調(diào)用過深

    1.內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);
    2.集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;
    3.代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;
    4.使用的第三方軟件中的BUG;
    5.啟動參數(shù)內(nèi)存值設(shè)定的過小

    6、本質(zhì)原因是創(chuàng)建了太多的線程,而能創(chuàng)建的線程數(shù)是有限制的,導(dǎo)致了這種異常的發(fā)生

    7、并行或者并發(fā)回收器在GC回收時間過長

    解決方法

  • 修改JVM虛擬機(jī)啟動參數(shù),直接增加內(nèi)存。(-Xms,-Xmx參數(shù)一定不要忘記加。)
  • 檢查錯誤日志,查看“OutOfMemory”錯誤前是否有其 它異常或錯誤。
  • 對代碼進(jìn)行走查和分析,找出可能發(fā)生內(nèi)存溢出的位置。
  • 使用內(nèi)存查看工具動態(tài)查看內(nèi)存使用情況
  • 異常

    FileNotFoundException-------文件找不到異常--編譯異常,需要在編寫時就聲明

    classNotFoundException------類無法加載異常 、類文件未找到異常

    java.lang.Stack Over flowError --棧溢出異常---遞歸的內(nèi)存泄漏異常

    Null Pointer Exception-----------空指針異常

    ClassCastException--------------類轉(zhuǎn)換異常 (強(qiáng)轉(zhuǎn)的時候)

    Index OutOf BoundsException---下標(biāo)越界異常

    Arithmetic Exception-------------算數(shù)異常

    SQLException-------操作數(shù)據(jù)庫異常

    IOException--------輸入輸出異常,是失敗或中斷的I/O操作生成的異常的通用類。

    NumberFormatException--------字符串格式轉(zhuǎn)換異常

    SecurityException----------------由安全管理器拋出的異常,指示存在安全侵犯。

    JDK JRE JVM 三者之間的關(guān)系

    JDK java開發(fā)工具包--包含JRE+開發(fā)工具

    開發(fā)java程序最小的環(huán)境為JDK,所以JDK是JAVA語言的核心,

    JRE java運(yùn)行環(huán)境--包含JVM+運(yùn)行java程序必須的環(huán)境

    運(yùn)行java程序最小的環(huán)境為JRE

    JVM java虛擬機(jī)--負(fù)責(zé)加載class并運(yùn)行.class文件

    將JAVA代碼轉(zhuǎn)換為對應(yīng)的操作系統(tǒng)可以理解的指令,可以運(yùn)行字節(jié)碼文件,可以跨平臺的核心部分

    ajax請求的參數(shù)

    url:發(fā)送請求的地址

    type:請求方式post/get。默認(rèn)是get

    contentType:文本類型

    data:

    dataType:傳輸?shù)臄?shù)據(jù)類

    success:請求成功要執(zhí)行的代碼

    error:

    timeout:設(shè)置請求超時時間

    async:默認(rèn)true--所有的請求都是異步,如果要同步請求,false,加鎖鎖住瀏覽器

    cache:會不會從瀏覽器緩存中加載請求信息

    VUE 生命周期函數(shù)

    概念: 生命周期函數(shù),是VUE針對與用戶提供的擴(kuò)展的功能.如果編輯了生命周期函數(shù),則vue對象自動執(zhí)行,無需手動調(diào)用.

    生命周期函數(shù)種類:

    1. 初始化階段 beforeCreate創(chuàng)建前 created實(shí)例創(chuàng)建完成后直接調(diào)用

    beforeMount掛載前 mounted--VUE對象真正的實(shí)例化,開始干活了

    2. 使用:Vue對象的修改 beforeUpdate, updated

    3. 對象銷毀 beforeDestroy destroyed

    2.2生命周期函數(shù)難點(diǎn)講解(了解)

    beforeCreate

    官網(wǎng)說明: 在實(shí)例初始化之后,數(shù)據(jù)觀測 (data observer) 和 event/watcher 事件配置之前被調(diào)用。

    解析: VUE對象被JS剛解析之后,實(shí)例化成功. 內(nèi)部的屬性暫時都為null.

    created

    官方說明: 在實(shí)例創(chuàng)建完成后被立即調(diào)用

    解析: VUE對象開始加載其中的屬性和屬性的值,當(dāng)加載完成,對象實(shí)例化成功!!! 僅限于創(chuàng)建不執(zhí)行業(yè)務(wù)操作.

    beforeMount

    官方說明: 在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用。

    解析: vue對象中 el: “#app”, 通過app指定的ID,將指定的區(qū)域交給Vue對象進(jìn)行管理.

    mounted

    官方說明: 實(shí)例被掛載后調(diào)用,這時 el 被新創(chuàng)建的 vm.$el 替換了。

    解析: 當(dāng)對象創(chuàng)建完成之后,并且指定區(qū)域開始 “渲染”,將區(qū)域中的標(biāo)簽/表達(dá)式進(jìn)行解析加載. 當(dāng)數(shù)據(jù)加載成功之后,這時mounted執(zhí)行完成.這時用戶可以看到解析后的頁面.

    事務(wù)

    事務(wù)就是將一堆的SQL語句(通常是增刪改操作)綁定在一起執(zhí)行,要么都執(zhí)行成功,要么都執(zhí)行失敗

    事務(wù)4個特性ACID

    原子性(Atomicity,或稱不可分割性)綁定到一起,要么全成功,要么全失敗。

    一致性(Consistency)多個系統(tǒng)中,保證數(shù)據(jù)是一致的

    隔離性(Isolation,又稱獨(dú)立性)保證性能的同時(高并發(fā)),隔離用戶操作

    持久性(Durability)持久影響的,對數(shù)據(jù)的修改是永久的,系統(tǒng)故障也不會丟失

    · 開啟事務(wù):start transaction;BEGIN;

    · 結(jié)束事務(wù):commit;(提交事務(wù))或rollback(回滾事務(wù)操作之前)。

    隔離級別

    1、讀未提交:Read uncommitted 效率高,安全性最差,可能發(fā)生并發(fā)數(shù)據(jù)問題,可以讀到比人未提交的數(shù)據(jù)

    2、讀提交(read committed) 犧牲了效率提高了安全性,Oracle默認(rèn)的隔離級別

    3、可重復(fù)讀(repeatable read)MySQL默認(rèn)的隔離級別,安全性較好,性能一般,產(chǎn)生緩存,不能看到你后開啟的事務(wù)內(nèi)容--多次讀同一數(shù)據(jù),讀到的數(shù)據(jù)是相同的。防止臟讀、不可重復(fù)讀,容易產(chǎn)生幻讀

    當(dāng)隔離級別設(shè)置為Repeatable read 時,可以避免不可重復(fù)讀。當(dāng)singo拿著工資卡去消費(fèi)時,一旦系統(tǒng)開始讀取工資卡信息(即事務(wù)開始),singo的老婆就不可能對該記錄進(jìn)行修改,也就是singo的老婆不能在此時轉(zhuǎn)賬。

    4、串行化(Serializable) 表級鎖,讀寫都加鎖,效率低下,安全性高,不能并發(fā)--解決幻讀

    mandatory

    never--從不創(chuàng)建實(shí)物

    not-supported

    supports--

    required--創(chuàng)建新事物

    required -new

    nested---

    事務(wù)注解@Transactional的參數(shù)

    BeanFactory和FactoryBean的區(qū)別

    區(qū)別:BeanFactory是個Factory,也就是IOC容器或?qū)ο蠊S, 它負(fù)責(zé)生產(chǎn)和管理bean的一個工廠

    FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進(jìn)行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產(chǎn)或者修飾對象生成的 工廠Bean,它的實(shí)現(xiàn)與設(shè)計(jì)模式中的工廠模式和修飾器模式類似

    Servlet

    tomcat(Web服務(wù)連接器) 里面有多個servlet模塊,servlet里面有xml文件中servlet name 然后,有個class name,通過反射實(shí)例化

    httpservlet是可以重寫,但是我們以后寫的所有的類都要繼承這個類,里面有HttpServletRequse 和 HttpServletResponse

    public String findUserByIds(HttpServletRequest request)

    {String id = request.getParameter("id");

    手動JDBC

    String url= "jdbc:mysql://localhost:3306/cgb2107?characterEncoding=utf8&serverTimezone=Asia/Shanghai";//解決中文亂碼

    jdbc:傳輸協(xié)議//本機(jī)的IP地址localhost:數(shù)據(jù)庫的端口號/數(shù)據(jù)庫的名稱

    注冊Driver驅(qū)動class.forName--通過反射

    獲取鏈接對象DriverManager.getConnection,訪問的url和用戶名、密碼傳進(jìn)來

    得到prepare Statement傳輸器,將sql骨架語句放進(jìn)來---用sql骨架,用問好代替參數(shù)

    執(zhí)行sql,更新;executeUpdate()

    處理結(jié)果集——查才處理,

    關(guān)閉流close

    描述AOP

    ①:AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來建立一種對象層次結(jié)構(gòu)。面向?qū)ο蠼鉀Q了層次的問題,比如下級繼承上級來增強(qiáng)上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點(diǎn)是通過代理方式對目標(biāo)類在運(yùn)行期間織入功能

    面向切面的編程(AOP)實(shí)現(xiàn)了橫切關(guān)注的模塊化 避免代碼纏繞 削除代碼分散

    ②:AOP的三要素:切面、通知、切點(diǎn)

    @Aspect切面:包括了切入點(diǎn)和advice---基本上是個類

    JoinPoint連接點(diǎn):程序中的一個點(diǎn),例如方法的調(diào)用或者排除異常,每一個方法都是一個連接點(diǎn),

    @pointcut 切點(diǎn):xml文件或者注解,選擇一個或者多個連接點(diǎn)表達(dá)式---在什么時候執(zhí)行

    Advice(通知):在選擇的每個連接點(diǎn)執(zhí)行的代碼--基本是個方法

    編織:將切面與主要代碼進(jìn)行結(jié)合的技術(shù),spring就提供了,就是把所有整合在了一起,將指定位置注入

    ③:通知的用法:

    日志與跟蹤 事務(wù)管理 安全 緩存 錯誤處理 性能監(jiān)測 自定義業(yè)務(wù)規(guī)則

    前置Advice

    @Before如果advice拋出異常,則目標(biāo)方法不被執(zhí)行---關(guān)心方法訪問權(quán)限限制

    后置Advice

    @After--無論怎么,在方法之后都調(diào)用Advice

    @AfterReturning--當(dāng)方法有返回值的時候才執(zhí)行Advice,

    如果目標(biāo)方法出現(xiàn)異常--不是返回失敗,則advice不被調(diào)用

    @AfterThrows--當(dāng)目標(biāo)方法拋出正確異常的時候才調(diào)用Advice,

    必須是指定異常,但是不會停止異常傳播(沒辦法try/cratch)(調(diào)用有異常的方法本身也有了異常,方法之間的調(diào)用)

    環(huán)繞Advice--阻止異常傳播

    @Around注解和proceedingJoinPoint 添加的proceed()

    1.spring bean是什么?

    Bean是Spring框架中最核心的兩個概念之一(另一個是面向切面編程AOP,bean是一個由Spring IoC容器實(shí)例化、組裝和管理的對象。我們的應(yīng)用程序由一個個bean構(gòu)成

    • 概念1:Bean容器,或稱spring ioc容器,主要用來管理對象和依賴,以及依賴的注入。
    • 概念2:bean是一個Java對象,根據(jù)bean規(guī)范編寫出來的類,并由bean容器生成的對象就是一個bean。
    • 概念3:bean規(guī)范。
  • 所有屬性為private
  • 提供默認(rèn)構(gòu)造方法
  • 提供getter和setter
  • 實(shí)現(xiàn)serializable接口

  • 2.bean的生命周期?

  • Spring啟動,解析xml配置或者@Bean注解配置的類,得到BeanDefinition
  • 通過BeanDefinition反射創(chuàng)建Bean對象,對bean對象屬性進(jìn)行填充
  • 如果Bean實(shí)現(xiàn)了BeanNameAware接口的話,Spring將Bean的Id傳遞給setBeanName()方法
  • 如果Bean實(shí)現(xiàn)了BeanFactoryAware接口的話,Spring將調(diào)用setBeanFactory()方法,將BeanFactory容器實(shí)例傳入
  • 如果Bean實(shí)現(xiàn)了ApplicationContextAware接口的話,Spring將調(diào)用Bean的setApplicationContext()方法,將bean所在應(yīng)用上下文引用傳入進(jìn)來。
  • 如果Bean實(shí)現(xiàn)了BeanPostProcessor接口,Spring就將調(diào)用他們的postProcess BeforeInitialization()初始化前方法。調(diào)用init初始化方法;
  • 如果Bean 實(shí)現(xiàn)了InitializingBean接口,Spring將調(diào)用他們的afterPropertiesSet()初始化后方法。類似的,如果bean使用init-method聲明了初始化方法,該方法也會被調(diào)用,此處會進(jìn)行Aop
  • 將創(chuàng)建的Bean對象放入一個Map中;
  • 此時,Bean已經(jīng)準(zhǔn)備就緒,可以被應(yīng)用程序使用了。他們將一直駐留在應(yīng)用上下文中,直到應(yīng)用上下文被銷毀。
  • spring容器關(guān)閉時調(diào)用DisposableBean接口中的destory()方法;
  • 最常用的作用域--五個

    一般用@Scope來標(biāo)識作用域

    單例(長命對象):singleton作用域:Spring 只會為每一個bean創(chuàng)建一個實(shí)例,并保持bean的引用,在spring Ioc容器中僅存在一個bean實(shí)例,Bean以單利方式存在---默認(rèn)值

    原型(短命對象):prototype作用域:一個bean定義對應(yīng)多個對象實(shí)例。每次從容器中調(diào)用Bean是,都返回一個新的實(shí)例,即每次調(diào)用getBean()時,相當(dāng)于new操作

    request作用域:每次Http request請求都會創(chuàng)建一個新的bean,該作用域僅適用于WebApplicationContext環(huán)境

    seesion作用域:同一個Http Session共享一個bean,不同session使用不同bean,僅WebApplicationContext環(huán)境

    globalSeesion作用域:一般用于Porlet應(yīng)用環(huán)境,也僅適用于web環(huán)境

    注解

    一、spring

    Spring是一個輕量級的開源框架,是為解決企業(yè)級應(yīng)用開發(fā)的復(fù)雜性而創(chuàng)建的,通過核心的Bean factory實(shí)現(xiàn)了底層的類的實(shí)例化和生命周期的管理。Spring的最根本使命是:簡化java開發(fā),整合第三方框架

    三大核心組件Bean、Context、Core

    Spring框架兩大核心:IoC和DI

    Spring框架重點(diǎn)提供的IOC DI AOP

    IOC:
    控制反轉(zhuǎn): 將對象創(chuàng)建的權(quán)利交給Spring容器管理,由Spring容器管理對象的生命周期
    DI: 依賴注入
    創(chuàng)建對象時,如果該對象中有需要依賴的屬性,Spring負(fù)責(zé)為屬性賦值.

    ------------------------------------------------------------------------------------------------------

    導(dǎo)入的包是org.springframework.beans.factory.annotation

    @Autowired 基于注解的依賴注入,可以被使用再屬性域,方法,構(gòu)造函數(shù)上--先根據(jù)type判斷,在判斷name

    @Qualifier @Autowired注解判斷多個bean類型相同時,就需要使用 @Qualifier("xxBean") 來指定依賴的bean的id:

    @Resource 也是依賴的注入,先根據(jù)name判斷注入哪個對象, 屬性域和方法上 ---這個是javax包

    @Scope bean的創(chuàng)建模式--單例和原型模式 singleton--只有一個實(shí)例 prototype--每一次都new

    @Component@Controller @Service 當(dāng)使用基于注解的配置和類路徑掃描的時候,這些類就會被實(shí)例化,把創(chuàng)建的對象交給spring容器管理

    @Configuration將配置類交給spring管理 添加在配置類config上面

    ---------------------------------------------------不熟悉的注解-----------------------------------------------------

    @PropertySource 加載指定的配置文件 將pro文件交給spring容器管理 @PropertySource(value = "classpath:/mysql.properties",encoding = "UTF-8"),

    @Value("${mysql.username}") springel表達(dá)式,簡稱spel,從spring容器內(nèi)獲取key,動態(tài)為屬性賦值

    @Repository

    @Repository和@Controller、@Service、@Component的作用差不多,都是把對象交給spring管理。@Repository用在持久層的接口上,這個注解是將接口的一個實(shí)現(xiàn)類交給spring管理。

    不使用@Repository注解,idea會報警告,提示找不到這個bean,直接忽略即可

    @Async 在方法上,這個注解描述的方法,底層會異步執(zhí)行--日志表數(shù)據(jù)量太大,不由web服務(wù)線程執(zhí)行,而是交給spring自帶的線程池中的線程去執(zhí)行;優(yōu)點(diǎn):不會長時間阻塞web服務(wù)(例如tomcat)線程

    @EnableAsync 需要在啟動類上啟動異步執(zhí)行

    @Cacheable

    @EnableCaching

    @CachePut

    @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm") 修改日期的格式

    ---------------------------------------------------AOP---------------------------------------------------------

    @Aspect切面聲明,標(biāo)注在類、接口(包括注解類型)或枚舉上。

    @Pointcut 切入點(diǎn)聲明,即切入到哪些目標(biāo)類的目標(biāo)方法。

    @Before 前置通知, 在目標(biāo)方法(切入點(diǎn))執(zhí)行之前執(zhí)行

    @After 后置通知, 在目標(biāo)方法(切入點(diǎn))執(zhí)行之后執(zhí)行,不管是否拋出異常都執(zhí)行

    @AfterReturning 返回通知, 在目標(biāo)方法(切入點(diǎn))返回結(jié)果之后執(zhí)行,在 @After 的后面執(zhí)行

    @AfterThrowing 異常通知, 在方法拋出異常之后執(zhí)行, 意味著跳過返回通知

    @Around 環(huán)繞通知:目標(biāo)方法執(zhí)行前后分別執(zhí)行一些代碼,發(fā)生異常的時候執(zhí)行另外一些代碼

    AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來建立一種對象層次結(jié)構(gòu)。面向?qū)ο蠼鉀Q了層次的問題,比如下級繼承上級來增強(qiáng)上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點(diǎn)是通過代理方式對目標(biāo)類在 運(yùn)行期間 織入功能 面向切面的編程(AOP)實(shí)現(xiàn)了橫切關(guān)注的模塊化 避免代碼纏繞 削除代碼分散

    ②:AOP的三要素:切面、通知、切點(diǎn)

    @Aspect切面:包括了切入點(diǎn)和advice---基本上是個類

    JoinPoint連接點(diǎn):程序中的一個點(diǎn),例如方法的調(diào)用或者排除異常,每一個方法都是一個連接點(diǎn),

    @pointcut 切點(diǎn):xml文件或者注解,選擇一個或者多個連接點(diǎn)表達(dá)式---在什么時候執(zhí)行

    Advice(通知):在選擇的每個連接點(diǎn)執(zhí)行的代碼--基本是個方法

    編織:將切面與主要代碼進(jìn)行結(jié)合的技術(shù),spring就提供了,就是把所有整合在了一起,將指定位置注入

    ③:通知的用法:

    日志與跟蹤 事務(wù)管理 安全 緩存 錯誤處理 性能監(jiān)測 自定義業(yè)務(wù)規(guī)則

    ------------------------------------------事務(wù)----------------------------------------

    @Transactional ---spring管理事務(wù)

    1.默認(rèn)條件下,只攔截運(yùn)行時異常 ,執(zhí)行成功則提交事務(wù),不成功則回滾

    * 2.rollbackFor: 指定異常的類型回滾 rollbackFor = RuntimeException.class

    * 3.noRollbackFor: 指定異常不回滾 noRollbackFor = RuntimeException.class

    添加的位置最好在增刪改方法上,不要直接添加在類上

    二、springBoot

    解決了spring的配置地獄的問題,內(nèi)嵌Servlet容器(tomcat),降低了對環(huán)境的要求; 提供一系列的starter pom啟動項(xiàng)簡化Maven配置,主要應(yīng)用了開箱即用的思想。

    1、簡化了maven的操作(用什么jar包,就要添加依賴(坐標(biāo)))

    2、內(nèi)嵌了Tomcat,可以訪問服務(wù)器里的程序

    3、springboot整合了Spring+SpringMVC+Mybatis--基于ORM的思想以對象的方式操作數(shù)據(jù)庫

    @RestControllerAdvice捕獲全局異常,都是對Controller進(jìn)行增強(qiáng)的,可以全局捕獲spring mvc拋的異常,指定遇到某種異常實(shí)現(xiàn)AOP處理.,返回值都是JSON串,通知是AOP中的技術(shù),解決特定問題。
    @ExceptionHandler(value = Exception.class)或者是捕獲({RuntimeException.class})運(yùn)行時異常,用來捕獲指定的異常。

    @SpringBootTest 單元測試類的注解

    @SpringBootApplication--主啟動類注解

    spring boot starter 這是啟動項(xiàng),

    spring boot starter-web是整合了mvc,開箱即用的思想,有啟動項(xiàng)starter 就可以直接使用其中的功能

    常用的啟動項(xiàng)/依賴

  • spring-boot-start-web
  • spring-boot-start-test
  • spring-boot-start-jdbc
  • spring-boot-start-aop
  • springboot的加載機(jī)制:開箱即用

    pom文件只是添加了jar包,需要被調(diào)用才能生效,被主啟動類上的注解SpringBootApplication調(diào)用

    springboot程序啟動,就是注解開始工作,

    注解的結(jié)構(gòu)

    1、元注解:@Inherited

    2、配置類:@SpringBootConfiguration

    3、自動配置:@EnableAutoConfiguration

    4、包掃描:@ComponentScan

    SpringbootApplication注解的說明

  • 元注解: 標(biāo)識注解的注解
  • @Target 作用位置類/方法

    @Retention 生命周期

    @Documented 是否動態(tài)生成文檔

    @Inherited 是否允許繼承

  • 配置類
  • @SpringBootConfiguration 加載其他小的配置文件

    >@Configration 標(biāo)識我是一個配置類

  • 自動配置
  • @EnableAutoConfiguration 開啟自動化配置

    >@AutoConfigurationPackage 動態(tài)獲取主啟動類的包路徑

    >@Import({AutoConfigurationImportSelector.class}) 開箱即用選擇器

  • 包掃描
  • @ComponentScan(excludeFilters排除某些類,如:影響項(xiàng)目的過濾器)

    springboot開箱即用也可以說是工作流程:選擇器加載web,選擇器加載jdbc,選擇器加載MQ資源很多選擇器。pom文件中有1.web啟動項(xiàng)2.jdbc啟動項(xiàng);

    web選擇器去掃描pom文件,發(fā)現(xiàn)web資源,線程開始工作,整個web項(xiàng)目中的包開始生效和加載,加載完成后,springMVC就有效了。程序返回繼續(xù)執(zhí)行下一個jdbc選擇器,找到j(luò)dbc的配置項(xiàng),開始整合JDBC,此時獲取資源從配置文件中。

    所以啟動項(xiàng)里面寫的東西,都要在配置文件中配好,否則就會啟動失敗

    選擇器沒找到啟動項(xiàng),就繼續(xù)向下執(zhí)行,直到所有的選擇器執(zhí)行完

    總結(jié):springboot在內(nèi)部有N個選擇器,當(dāng)springBoot程序啟動時,依次加載選擇器,如果選擇器匹配pom文件中的啟動項(xiàng),則自動配置程序開始運(yùn)

    三、springMvc

    概念:是spring框架的一個非常有名的產(chǎn)品,用來接收瀏覽器發(fā)來的請求,并返回?cái)?shù)據(jù),給瀏覽器做出響應(yīng)。遵循了MVC思想:主要是想要松耦合,實(shí)現(xiàn)代碼間的高內(nèi)聚,提高代碼的可維護(hù)性

    M:是model模型,用來封裝數(shù)據(jù)

    V:是view,視圖層,用來展示數(shù)據(jù)

    C:是controller,控制層,作用是用來,接收請求和給出相應(yīng)

    導(dǎo)入的包是org.springframework.web.bind.annotation

    @RestController只能用在類上,讓瀏覽器訪問,是@ResponseBody 把數(shù)據(jù)轉(zhuǎn)化成Json串和@Controller合并起來的

    @RequestMapping規(guī)定瀏覽器怎么訪問方法(類)

    @GetMapping是一個組合注解,等價于@RequestMapping(method = RequestMethod.Get ),用于簡化開發(fā),@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping

    @PathVariable 綁定到操作方法的入?yún)⒅?/p>

    @ResponseBody 返回對象利用jackson工具類轉(zhuǎn)換為json字符串--web提供就是mvc提供的

    @RequestParam 參數(shù)名和請求參數(shù)名稱不同時使用,可以設(shè)置默認(rèn)值

    @CrossOrigin//放行js的訪問請求,解決跨域問題---在controller中添加類注解--Ajax引擎--局部刷新,ajax是不支持跨域的

    五個核心的組件工作原理:

    1、前端控制器DispatcherServlet:接收請求,并分發(fā)請求

    2、處理器映射器HandlerMapping:根據(jù)請求,找到具體能處理請求的類名,方法名

    3、處理器適配器HandlerAdapter:正式開始調(diào)用當(dāng)方法處理請求,并返回結(jié)果

    4、視圖解析器ViewResolver:把頁面找到,并把數(shù)據(jù)進(jìn)行解析

    5、視圖渲染View:具體展示數(shù)據(jù)并返回給瀏覽器

    四、Mybatis

    MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射(ORM)。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。(mybatis在內(nèi)部將JDBC封裝).

    MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄。

    1、持久化 : 計(jì)算機(jī)在計(jì)算時,數(shù)據(jù)都在內(nèi)存中.如果斷電則數(shù)據(jù)清空,所以要求將內(nèi)存數(shù)據(jù)保存到磁盤中.--目標(biāo)--概念

    2、持久層: 程序通過Dao/Mapper 與數(shù)據(jù)庫進(jìn)行交互的層級代碼 (Controller層 Service層 Dao/Mapper層與數(shù)據(jù)庫交互)--流程--具體操作

    小結(jié): Mybatis是一個優(yōu)秀的持久層框架,基于ORM設(shè)計(jì)思想,實(shí)現(xiàn)了以對象的方式操作數(shù)據(jù)庫.

    Mybatis的ORM并不完全,只完成了結(jié)果集映射,但是Sql需要自己手寫.所以也稱之為半自動化的ORM映射框架.

    前身是apache的一個開源項(xiàng)目IBatis

    @Param 在mapper接口中,如果方法傳遞的參數(shù)有多個,則可以將多個參數(shù)使用注解@Param("sex") String sex 封裝為Map

    @Mapper注解

    @MapperScan

    #{}獲取數(shù)據(jù)時候

    ①:默認(rèn)存在預(yù)編譯效果,用?代替,防止sql注入攻擊

    ②:默認(rèn)為數(shù)據(jù)添加一對兒“”號

    ③:高效的

    ${}獲取數(shù)據(jù)

    ①:當(dāng)以字段名稱直接作為參數(shù)時候,使用${}

    ②:這樣的sql慎用,會有sql注入攻擊

    所以能用#就不要用$

    標(biāo)簽

    <select>

    <update>

    <insert>

    <delect>

    <foreach> mybatis為了參數(shù)取值方便,特意封裝了遍歷的標(biāo)簽<foreach>,屬性:collection

    1.如果傳遞的參數(shù)是數(shù)組, 則collection="array"

    2.如果傳遞的參數(shù)是list集合, 則collection="list"

    3.如果傳遞的參數(shù)是Map集合, 則collection="map中的key",item,open,close,index,separator

    --------------------------------------------優(yōu)化

    <typeAliases>別名包-有順序的,在<environment>上面配置<typeAliases>標(biāo)簽

    <sql>簡化sql語句,提取sql語句中的共性

    ---------------------------------------------動態(tài)sql

    <where>-<if>動態(tài)Sql要求根據(jù)對象中不為null的屬性,充當(dāng)where條件 實(shí)現(xiàn)動態(tài)查詢

    <set>-<if>根據(jù)對象中不為null的屬性當(dāng)做set條件,去除set條件中多余的 ,號,多個條件并列的時候

    <choose>-<when>/<otherwise>如果不想將全部的條件當(dāng)做if的判斷.則mybatis提供了分支結(jié)構(gòu) switch

    <choose>:代表分支結(jié)構(gòu),只有一個條件有效.

    <when test=" ">: 指定判斷的條件 和if類似.

    <otherwise>: 如果上述的條件都不滿足時,該行代碼有效.

    <resultMap>當(dāng)結(jié)果集中的字段名稱,與對象中的屬性不一致時,可以使用resultMap實(shí)現(xiàn)自定義的封裝.,resultType是<select>中的一個屬性,當(dāng)結(jié)果集中的字段名稱,如果與對象中的屬性的名稱一致時,才會實(shí)現(xiàn)自動的數(shù)據(jù)封裝

    屬性:id---sql語句中返回結(jié)果集resultMap中的名稱

    屬性:type---返回結(jié)果要封裝成的對象,應(yīng)該是全路徑,但是我們定義了別名包

    <!--自定義映射關(guān)系

    1.<id>標(biāo)簽代表主鍵 (每張表中都會有一個主鍵)

    標(biāo)簽屬性:column: 代表結(jié)果集(數(shù)據(jù)庫表)中的字段.--相當(dāng)于查詢的時候給列起的別名

    標(biāo)簽屬性:property: 對象中的屬性

    2.<result>標(biāo)簽 除了主鍵之外的配置信息

    -->

    <association>標(biāo)簽---關(guān)聯(lián)封裝,如果需要封裝單個的對象-----子查詢,一對一

    property 屬性名 封裝的是emp中的屬性dept
    javaType 屬性dept的類型 參數(shù)是類型的pojo全路徑,但是已經(jīng)別名包,所以直接寫Dept

    column=“子查詢的字段信息
    select=“sql的Id” 作用:根據(jù)column中的數(shù)據(jù) 實(shí)現(xiàn)子查詢

    <collection>標(biāo)簽: 封裝集合的固定寫法. ----一對多

    property: 指定屬性 ,要封裝的dept中集合對象的屬性名

    ofType: 封裝List集合的泛型中的對象

    <cache>:應(yīng)用二級緩存,在xml配置文件中

    前端常見錯誤編號

    2**--成功

    3**--重定向,轉(zhuǎn)移

    301--對象已永久移走,即永久重定向

    302--對象已臨時移動

    4**--客戶端錯誤--前端瀏覽器

    400--錯誤的請求:url中的參數(shù)類型不匹配 和服務(wù)需要的參數(shù)類型不同

    403--禁止訪問

    404--未找到,url路徑不存在

    401是認(rèn)證失敗--沒有攜帶令牌

    403是沒有訪問權(quán)限--某種請求方式?jīng)]有在數(shù)據(jù)庫中設(shè)置權(quán)限

    5**--服務(wù)器錯誤--后臺

    500--內(nèi)部服務(wù)器錯誤,看后臺報錯

    502--Web服務(wù)器用作網(wǎng)關(guān)或代理服務(wù)器時收到了無效響應(yīng)。

    504--網(wǎng)關(guān)超時

    505--HTTP版本不支持

    500:請求的url未綁定參數(shù)--一定看后臺--IDEA中拋出了IllegalStateException的異常

    Mybatis中的緩存機(jī)制

    解決是用戶的只是查詢,不能提高入庫的速度,緩存可以有效降低用戶訪問物理設(shè)備的頻次.提高用戶響應(yīng)速度.
    1.mybatis自身緩存 一級緩存/二級緩存
    2.Redis緩存 讀取10萬次/秒, 寫 8.6萬次/秒

    1-4-1一級緩存

    概念說明: Mybatis默認(rèn)開啟一級緩存, 一級緩存可以在同一個SqlSession對象中執(zhí)行相同的SQL查詢時,第一次去查詢數(shù)據(jù)庫,并寫在緩存中。第二次會直接從緩存中取。但是當(dāng)執(zhí)行兩次查詢中間發(fā)生了增刪改的操作,則sqlSeesion緩存會被清空,每次查詢會先去緩存中找,如果找不到就去查數(shù)據(jù)庫,并把數(shù)據(jù)寫在緩存中。

    Mybatis內(nèi)部緩存使用HashMap,key是sql語句+hashcode+statementId;

    value是查詢出來的結(jié)果集映射成的java對象

    1-4-2二級緩存

    說明: 二級緩存mybatis中默認(rèn)也是開啟的.但是需要手動標(biāo)識.

    同一個SqlSessionFactory內(nèi)部有效. 也就是不同的SqlSession都可以用,只要是同一個工廠

    全局配置:默認(rèn)是開的

    sqlSession查詢數(shù)據(jù)之后,會將緩存信息保存到一級緩存中.但是不會立即將 緩存交給二級緩存保管.如果需要使用二級緩存,則必須將sqlSession業(yè)務(wù)邏輯執(zhí)行成功之后關(guān)閉.sqlSession.close

    局部配置:直接在xml配置文件中加<cache>標(biāo)簽就可以了

    數(shù)據(jù)庫與緩存同步

    高并發(fā)(熱點(diǎn)數(shù)據(jù))的項(xiàng)目,實(shí)現(xiàn)讀寫一致性--緩存與數(shù)據(jù)庫的一致性

    當(dāng)我們在做數(shù)據(jù)庫與緩存數(shù)據(jù)同步時,究竟更新緩存,還是刪除緩存,究竟是先操作數(shù)據(jù)庫,還是先操作緩存?

    其實(shí)如果業(yè)務(wù)簡單,只是去數(shù)據(jù)庫拿一個值,寫入緩存,那么更新緩存也是可以的。但是,淘汰緩存(刪除緩存)操作簡單,并且?guī)淼母弊饔弥皇窃黾恿艘淮蝐ache miss,建議作為通用的處理方式。

    先操作緩存,還是先操作數(shù)據(jù)庫?

    那么問題就來了,我們是先刪除緩存,然后再更新數(shù)據(jù)庫,還是先更新數(shù)據(jù)庫,再刪緩存呢

    先更新數(shù)據(jù)庫,再刪緩存依然會有問題,不過,問題出現(xiàn)的可能性會因?yàn)樯厦嬲f的原因,變得比較低!數(shù)據(jù)庫的讀操作的速度遠(yuǎn)快于寫操作的因此產(chǎn)生臟數(shù)據(jù)的可能性會很小。(不然做讀寫分離干嘛,做讀寫分離的意義就是因?yàn)樽x操作比較快,耗資源少)

    Mybatis-plus

    以對象的方式操作數(shù)據(jù)庫

    Mybatis是半自動化的ORM映射框架.半自動: Sql是自己手寫的,但是結(jié)果集映射是自動的.
    MybatisPlus: 全自動的ORM 映射框架,對Mybatis的擴(kuò)展.在UserMapper上繼承BaseMapper<User>接口,baseMapper中的新增方法1個,修改方法是:2個?

    1、Mybatis操作的實(shí)質(zhì):sql把公共的部分提取出來

    調(diào)用步驟:

    1.用戶執(zhí)行userMapper.insert(user);

    2.根據(jù)繼承的關(guān)系 BaseMapper.insert(user);

    3.MP在內(nèi)部生成sql之后交給Mybatis調(diào)用 最終實(shí)現(xiàn)數(shù)據(jù)操作

    2、MP映射注解

    @TableName("demo_user") //對象與表名映射,如果相同可以不寫,添加在類上

    @TableId(type = IdType.AUTO)//主鍵自增,添加在屬性上

    @TableField("age") //實(shí)現(xiàn)屬性與字段映射

    @TableField(exist = false)//排除不必要的字段

    3、QueryWrapper條件構(gòu)造器,動態(tài)拼接where條件,默認(rèn)的關(guān)系連接符 and

    * 1. eq =

    * 2. gt >

    * 3. lt >

    * 4. ge >=

    * 5. le <=

    * 6. ne <>

    queryWrapper.gt("age",18).eq("sex","男");//利用條件構(gòu)造器自己構(gòu)建條件

    * 7.模糊查詢

    queryWrapper.like("name","君") //"%君%"

    queryWrapper.likeLeft("name","君") //"%君"

    * 8.in--多個相同參數(shù)--包裝類型

    Integer[] ids = {1,3,4,5};

    queryWrapper.in("id",ids).orderByDesc("age");//desc排序

    *9.動態(tài)sql

    動態(tài)sql實(shí)現(xiàn),根據(jù)不為null的屬性當(dāng)做where條件

    Spring提供的API StringUtils.hasLength(sex);---判斷字符串API

    對于字符串來說,要判斷是否第null和空串代替了:/boolean flag = sex !=null && "".equals(sex);

    對于數(shù)字來說,就直接判斷是否為空就可

    boolean flag = StringUtils.hasLength(sex);

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();

    queryWrapper.gt(age!=null, "age",age )//三個屬性,添加了判斷條件,條件成立則拼接

    .eq(flag,"sex",sex);

    4、MybatisPlus API介紹

    selectById---返回一個對象

    selectList--返回多個對象集合

    selectObjs--只想獲取第一列數(shù)據(jù)--做關(guān)聯(lián)查詢時可以使用

    五、lombok

    @Data //動態(tài)生成get/set/toString/equals等方法

    @Accessors(chain = true) //開啟鏈?zhǔn)郊虞d,換名就方便了,不用一直寫對象.方法 重寫set

    @NoArgsConstructor //無參構(gòu)造

    @AllArgsConstructor //有參構(gòu)造

    六、Servlet--控制器?

    socket 服務(wù)器

    springmvc底層就是servlet

    Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務(wù)程序或服務(wù)連接器主要功能在于交互式地瀏覽和生成數(shù)據(jù),生成動態(tài)Web內(nèi)容。

    狹義的Servlet是指Java語言實(shí)現(xiàn)的一個接口,廣義的Servlet是指任何實(shí)現(xiàn)了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運(yùn)行于支持Java的應(yīng)用服務(wù)器中。從原理上講,Servlet可以響應(yīng)任何類型的請求,但絕大多數(shù)情況下Servlet只用來擴(kuò)展基于HTTP協(xié)議的Web服務(wù)器。

    servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監(jiān)聽端口,請求過來后,根據(jù)URL信息,確定要將請求交給哪個servlet去處理,然后調(diào)用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。

    概括: Servlet是java后臺程序與用戶交互的機(jī)制(媒介).

    核心方法:request.getParameter--parameter是參數(shù)的意思

    規(guī)則:

    參數(shù)是取的 而不是傳的;請求的流程: 一個request對象返回response

    public String findUserByIds(HttpServletRequest request)

    {String id = request.getParameter("id");

    參數(shù)名稱必須相同、弊端無論什么樣的數(shù)據(jù),都是String數(shù)據(jù)類型,需要手動的轉(zhuǎn)化

    SpringMVC: 在內(nèi)部封裝了Servlet機(jī)制.并且可以根據(jù)用戶的參數(shù)類型,實(shí)現(xiàn)自動的數(shù)據(jù)類型的轉(zhuǎn)化

    第一階段

    ASCII碼表

    ‘0’對應(yīng)48‘A’對應(yīng)65 ‘a(chǎn)’對應(yīng)的97單引號對應(yīng)數(shù)值,雙引號是字符串

    Idea快捷鍵

    ctrl+alt+L 代碼對齊

    Shift +alt+↑向上移動

    Ctrl+d 復(fù)制當(dāng)前行

    Ctrl+y 刪除

    //特征 屬性 字段 成員變量是同一個東西
    //行為 功能 方法 成員方法 是同一個東西

    Math常用的方法

    Math.round()返回最接近參數(shù)的int,它表示"四舍五入"

    Math.rint()返回最接近參數(shù)并等于某一整數(shù)的 double 值,如果有2個數(shù)同樣接近,則返回偶數(shù)的那個

    Math.floor()返回最小的(最接近正無窮大)double 值,該值小于等于參數(shù),并等于某個整數(shù)

    Math.ceil()返回最大的(最接近負(fù)無窮大)double 值,該值大于等于參數(shù),并等于某個整數(shù)

    Math.cbrt()返回 double 值的立方根

    Math.sqrt()返回正確舍入的double 值的正平方根

    Math.pow()返回第一個參數(shù)的第二個參數(shù)次冪的值

    Math.max()返回兩個 double 值中較大的一個

    Math.min()返回兩個 double 值中較小的一個

    關(guān)鍵字

    String不是關(guān)鍵字

    Assert--------用來查找內(nèi)部程序錯誤、

    Enum---------枚舉類型

    Final----------修飾一個常量 finally----try塊中總會執(zhí)行的部分

    Instanceof-----測試一個對象是否是某個類的實(shí)例

    Native---------由宿主系統(tǒng)實(shí)現(xiàn)的一個方法

    被native關(guān)鍵字修飾的方法叫做本地方法,本地方法和其它方法不一樣,本地方法意味著和平臺有關(guān),因此使用了native的程序可移植性都不太高。另外native方法在JVM中運(yùn)行時數(shù)據(jù)區(qū)也和其它方法不一樣,它有專門的本地方法棧。native方法主要用于加載文件和動態(tài)鏈接庫,由于Java語言無法訪問操作系統(tǒng)底層信息(比如:底層硬件設(shè)備等),這時候就需要借助C語言來完成了。被native修飾的方法可以被C語言重寫。

    Java程序中聲明native修飾的方法,類似于abstract修飾的方法,只有方法簽名,沒有方法實(shí)現(xiàn)。編譯該java文件,會產(chǎn)生一個.class文件。

    Strictfp---------對浮點(diǎn)數(shù)計(jì)算使用嚴(yán)格的規(guī)則

    Throw----------拋出一個異常throws-------一個方法可能拋出的異常

    Transient-------標(biāo)志非永久性的數(shù)據(jù)

    Volatile---------標(biāo)記字段可能會被多個線程同時訪問(異步),而不做同步

    標(biāo)記其他線程可見,可見性和原子性,禁止指令重排序

    枚舉enum

    Java是一門面向?qū)ο蟮恼Z言,當(dāng)我們創(chuàng)建好一個類以后,可以創(chuàng)建這個類的多個對象
    但是一個類究竟創(chuàng)建多少個對象,并且對象代表的值我們是無法限制的
    所以,如果開發(fā)中需要一組值,的數(shù)據(jù)是明確的,就可以使用枚舉

    如果當(dāng)我們需要定義一組常量表示不同的狀態(tài)時,就建議使用枚舉類,枚舉類的對象個數(shù)是有限且明確

    JDK5以前是需要自定義枚舉類的,JDK5以后可以使用關(guān)鍵字enum來定義枚舉類,普通類定義class 類名,枚舉類eunm 類名

    枚舉類的構(gòu)造方法必須私有化,防止外界隨意創(chuàng)建本類對象,本類特有的私有屬性要加上final防止被篡改

    • 定義枚舉對象:不能像自定義枚舉類對象時new,要按照下面的語法枚舉名1(值1,值2),枚舉名2(值1,值2),枚舉名3(值1,值2);
    • 不需要生成toString,枚舉類繼承了java.lang.Enum * 在Enum中重寫了繼承自O(shè)bject的toString(),直接打印的就是枚舉名
    • 實(shí)現(xiàn)接口的枚舉類,在枚舉類每個枚舉對象后分別實(shí)現(xiàn)接口中的抽象方法
    • JDK5中擴(kuò)展了switch語句,除了可以接收byte short char int ,還可以接收枚舉類型

    1、面向?qū)ο蟮奶卣饔心男?/h2>

    1.封裝:

    通過private修飾符來封裝屬性與方法,封裝后需要外界提供公共的操作方式get/set。封裝是把過程和數(shù)據(jù)包裹起來,外界對于數(shù)據(jù)的操作僅限于我們提供的方式。

    2.繼承:

    繼承是一種聯(lián)結(jié)類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。我們可以從現(xiàn)有的類中派生出一個新的類,這個過程稱為類繼承。新類繼承了原始類的特性,被稱為派生類(子類),而原始類稱為基類(父類)。派生類可以從它的基類那里繼承方法和實(shí)例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。

    3.多態(tài):多態(tài)性是指可以盡量屏蔽不同類的差異性提供通用的解決方案。多態(tài)性語言具有靈活、抽象、行為共享、代碼共享的優(yōu)勢,很好的解決了應(yīng)用程序函數(shù)同名問題。我們所使用的有:

    向上造型【將子類對象統(tǒng)一看作父類型Parent p=new Child();,比如花木蘭替父從軍】

    向下造型【將之前看作父類型的子類對象再重新恢復(fù)成子類對象,比如花木蘭打仗結(jié)束回家化妝】

    4.抽象:抽象就是忽略一個主題中與當(dāng)前目標(biāo)無關(guān)的那些方面,以便更充分地注意與當(dāng)前目標(biāo)有關(guān)的方面。抽象并不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細(xì)節(jié)。抽象包括兩個方面,一是過程抽象,二是數(shù)據(jù)抽象。

    (3) 構(gòu)造方法,與類同名,但是沒有返回值類型(連void都沒有)

    一個類會默認(rèn)存在無參構(gòu)造方法(函數(shù));格式 public 名稱(){}

    2、抽象類與接口的異同:

    抽象類:

    == 抽象類和普通類的主要有三點(diǎn)區(qū)別:==

    1)抽象方法必須為public或者protected(因?yàn)槿绻麨閜rivate,則不能被子類繼承,子類便無法實(shí)現(xiàn)該方法),缺省情況下默認(rèn)為public。抽象類的存在就是為了被繼承,如果是private就沒有存在的意義了。

    2)抽象類不能用來創(chuàng)建對象;--有構(gòu)造方法,為了子類

    3)如果一個類繼承于一個抽象類,則子類必須實(shí)現(xiàn)父類的抽象方法。如果子類沒有實(shí)現(xiàn)父類的抽象方法,則必須將子類也定義為為abstract類。

    接口:

    接口中的變量默認(rèn)拼接public static final,接口中的方法必須都是抽象方法。

    抽象類和接口的區(qū)別:

    語法層面上的區(qū)別

    1)一個類只能繼承一個抽象類,而一個類卻可以實(shí)現(xiàn)多個接口

    設(shè)計(jì)層面上的區(qū)別

    1)抽象類是對一種事物的抽象,即對類抽象,而接口是對行為的抽象。所以抽象類是后天構(gòu)建的結(jié)果,接口是先天設(shè)計(jì)的

    2)設(shè)計(jì)層面不同,抽象類作為很多子類的父類,它是一種模板式設(shè)計(jì)。而接口是一種行為規(guī)范,它是一種輻射式設(shè)計(jì)。

    2、抽象類中有構(gòu)造方法 ,為了給子類創(chuàng)建對象時調(diào)用

    接口中沒有構(gòu)造方法,子類調(diào)用的是父類的(默認(rèn)object)的構(gòu)造方法

    3、抽象類 單繼承

    接口可以多繼承

    4、抽象類可以定義普通的成員變量,抽象類中可以有普通的方法

    接口只能定義靜態(tài)常量(static和final),只能有抽象方法

    5、相同:抽象類和接口均不可以實(shí)例化/創(chuàng)建對象

    6、抽象類是后天構(gòu)建的結(jié)果,接口是先天設(shè)計(jì)的

    3.、線程有幾種狀態(tài)?它們是怎么切換的?

    線程生命周期,主要有五種狀態(tài):

    1、新建狀態(tài)(New) : 當(dāng)線程對象創(chuàng)建后就進(jìn)入了新建狀態(tài).如:Thread t = new MyThread();

    2、就緒狀態(tài)(Runnable):當(dāng)調(diào)用線程對象的start()方法,線程即為進(jìn)入就緒狀態(tài).

    處于就緒(可運(yùn)行)狀態(tài)的線程,只是說明線程已經(jīng)做好準(zhǔn)備,隨時等待CPU調(diào)度執(zhí)行,并不是執(zhí)行了t.start()此線程立即就會執(zhí)行

    3、運(yùn)行狀態(tài)(Running):當(dāng)CPU調(diào)度了處于就緒狀態(tài)的線程時,此線程才是真正的執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)

    就緒狀態(tài)是進(jìn)入運(yùn)行狀態(tài)的唯一入口,也就是線程想要進(jìn)入運(yùn)行狀態(tài)狀態(tài)執(zhí)行,先得處于就緒狀態(tài)

    4、阻塞狀態(tài)(Blocked):處于運(yùn)狀態(tài)中的線程由于某種原因,暫時放棄對CPU的使用權(quán),停止執(zhí)行,此時進(jìn)入阻塞狀態(tài),直到其進(jìn)入就緒狀態(tài)才有機(jī)會被CPU選中再次執(zhí)行.

    根據(jù)阻塞狀態(tài)產(chǎn)生的原因不同,阻塞狀態(tài)又可以細(xì)分成三種:

    等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait()方法,本線程進(jìn)入到等待阻塞狀態(tài)

    同步阻塞:線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其他線程占用),它會進(jìn)入同步阻塞狀態(tài)

    其他阻塞:調(diào)用線程的sleep()或者join()或發(fā)出了I/O請求時,線程會進(jìn)入到阻塞狀態(tài).當(dāng)sleep()狀態(tài)超時.join()等待線程終止或者超時或者I/O處理完畢時線程重新轉(zhuǎn)入就緒狀態(tài)

    5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期

    就緒 → 執(zhí)行:為就緒線程分配CPU 即可變?yōu)閳?zhí)行狀態(tài)"

    執(zhí)行 → 就緒:正在執(zhí)行的線程由于時間片用完被剝奪CPU暫停執(zhí)行,就變?yōu)榫途w狀態(tài)

    執(zhí)行 → 阻塞:由于發(fā)生某事件,使正在執(zhí)行的線程受阻,無法執(zhí)行,則由執(zhí)行變?yōu)樽枞?/p>

    (例如線程正在訪問臨界資源,而資源正在被其他線程訪問)

    反之,如果獲得了之前需要的資源,則由阻塞變?yōu)榫途w狀態(tài),等待分配CPU再次執(zhí)行

    開啟線程:繼承thread類,重寫run方法;2.實(shí)現(xiàn)runnable接口,實(shí)現(xiàn)run方法,3.實(shí)現(xiàn)callable接口,實(shí)現(xiàn)call方法(有返回值),通過FutureTask創(chuàng)建一個線程,4.線程池

    4、什么是線程池?線程池有什么優(yōu)點(diǎn)?

    我們使用線程的時候就去創(chuàng)建一個線程,如果并發(fā)的線程數(shù)量很多,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時間。

    線程池:其實(shí)就是一個容納多個線程的容器,其中的線程可以反復(fù)使用,省去了頻繁創(chuàng)建線程對象的操作,無需反復(fù)創(chuàng)建線程而消耗過多資源。

    好處:

    • 降低資源消耗,減少創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用
    • 提高響應(yīng)速度,當(dāng)任務(wù)到達(dá)時,任務(wù)不需要等待線程的創(chuàng)建 立即執(zhí)行
    • 提高線程的可管理性,根據(jù)系統(tǒng)的承受能力調(diào)整線程池中的數(shù)目,防止消耗過多內(nèi)存--內(nèi)存溢出

    5、創(chuàng)建線程池有哪幾種方式?

    兩類:通過Excutors創(chuàng)建、另一類:通過ThreadPoolExecutor創(chuàng)建--最原始的創(chuàng)建線程池方式

    ①. new Fixed ThreadPool(int nThreads)創(chuàng)建一個固定長度的線程池,每當(dāng)提交一個任務(wù)就創(chuàng)建一個線程,直到達(dá)到線程池的最大數(shù)量,這時線程規(guī)模將不再變化,當(dāng)線程發(fā)生未預(yù)期的錯誤而結(jié)束時,線程池會補(bǔ)充一個新的線程。

    ②. new Cached ThreadPool()創(chuàng)建一個可緩存的線程池,如果線程池的規(guī)模超過了處理需求,將自動回收空閑線程,而當(dāng)需求增加時,則可以自動添加新線程,線程池的規(guī)模不存在任何限制。

    ③. new Single ThreadExe cutor()這是一個單線程的Executor,它創(chuàng)建單個工作線程來執(zhí)行任務(wù),如果這個線程異常結(jié)束,會創(chuàng)建一個新的來替代它;它的特點(diǎn)是能確保依照任務(wù)在隊(duì)列中的順序來串行執(zhí)行。

    ④. new Sche duled ThreadPool(int corePoolSize)創(chuàng)建了一個固定長度的線程池,而且以延遲或定時的方式來執(zhí)行任務(wù),類似于Timer。

    ⑤:new SingleThread ScheduledExecutor:創(chuàng)建一個單線程的可以執(zhí)行延遲任務(wù)的線程池;

    ⑥:new WorkStealing Pool:創(chuàng)建一個搶占式執(zhí)行的線程池(任務(wù)執(zhí)行順序不確定)JDK 1.8 添加

    ⑦:ThreadPoolExecutor:最原始的創(chuàng)建線程池的方式

    46.線程池的參數(shù):

    core Pool Size 線程池核心線程大小 一個最小的線程數(shù)量,

    maxi mum PoolSize 線程池最大線程數(shù)量 一個最大線程數(shù)量的限制

    workQueue 工作隊(duì)列 新任務(wù)被提交后,會先進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時再從隊(duì)列中取出任務(wù)

    46. 線程池中 submit()和 execute()方法有什么區(qū)別?

    接收的參數(shù)不一樣submit有返回值,而execute沒有,submit方便Exception處理

    47.怎么保證線程安全:

    加鎖:jvm提供的synchoronized關(guān)鍵字鎖

    jdk提供的各種鎖Lock

    5、volatile關(guān)鍵字和Synchronized關(guān)鍵字

    Synchronized,用來枷鎖

    volatile只是保持變量的線程可見性,通常適用于一個線程寫,多個線程讀

    volatile不能保證原子性,只能保證線程可見性

    多個線程的時候,多個副本線程從主線程里面拿取數(shù)據(jù),副本線程的更改,只有當(dāng)提交到主線程后,才能被其他福線程感知到。volatile就是實(shí)現(xiàn)復(fù)線程更改后 馬上感知到這個變化

    volatile防止指令重排序

    6、Java虛擬機(jī)管理的內(nèi)存分配--5個

    JVM運(yùn)行時數(shù)據(jù)區(qū)

    Java虛擬機(jī)JVM 管理的內(nèi)存包括5個運(yùn)行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器,其中方法區(qū)和堆是由線程共享的數(shù)據(jù)區(qū),其他幾個是線程隔離的數(shù)據(jù)區(qū)

    1、程序計(jì)數(shù)器ProgramConute

    程序計(jì)數(shù)器是一塊較小的內(nèi)存,他可以看做是當(dāng)前線程所執(zhí)行的行號指示器。每一個線程都有自己的寄存器,PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的地址

    分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)基礎(chǔ)功能都需要依賴這個計(jì)數(shù)器來完成。

    如果線程執(zhí)行的是Java方法,這個計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;

    如果正在執(zhí)行的是Native方法,這個計(jì)數(shù)器則為空

    此內(nèi)存區(qū)域是唯一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemotyError(內(nèi)存溢出)情況的區(qū)域

    2、Java虛擬機(jī)棧

    虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于儲存局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每個方法從調(diào)用直至完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧中入棧到出棧的過程。

    棧內(nèi)存就是虛擬機(jī)棧,或者說是虛擬機(jī)棧中局部變量表的部分

    Java虛擬機(jī)規(guī)范對這個區(qū)域規(guī)定了兩種異常狀況

    • 如果線程請求的棧深度大于虛擬機(jī)所允許的深度,拋出StackOverflowError棧溢出異常
    • 如果虛擬機(jī)擴(kuò)展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError內(nèi)存溢出異常

    3、本地方法棧

    本地方法棧虛擬機(jī)棧發(fā)揮的作用是非常類似的,他們的區(qū)別是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)

    本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryErroy異常

    4、Java堆(Heap)

    堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,是被所有線程共享的一塊內(nèi)存區(qū)域,此內(nèi)存區(qū)域的唯一目的是存放對象實(shí)例,幾乎所有的對象實(shí)例和數(shù)組都在這里分配內(nèi)存,堆中的對象內(nèi)存需要等待GC進(jìn)行回收。

    Java堆細(xì)分為新生代和老年代

    5、方法區(qū)(Method Area)

    方法區(qū)它用于儲存已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)

    跟Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外,還可以不實(shí)現(xiàn)垃圾收集。這個區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載
    當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryErroy異常

    6、JVM垃圾回收

    Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法。(基于對對象生命周期分析)

    GC的基本原理:將內(nèi)存中不再被使用的對象進(jìn)行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,Java在對對象的生命周期特征進(jìn)行分析后,按照新生代、舊生代的方式來對對象進(jìn)行收集,以盡可能的縮短GC對應(yīng)用造成的暫停

    • Young(年輕代)

    三個區(qū)。一個Eden區(qū),兩個Survivor區(qū)。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時,還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個),當(dāng)這個Survivor區(qū)滿時,此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū),當(dāng)這個Survivor去也滿了的時候,從第一個Survivor區(qū)復(fù)制過來的并且此時還存活的對象,將被復(fù)制年老區(qū)(Tenured)

    • Tenured(年老代)

    年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。

    • Perm(持久代)

    用于存放靜態(tài)文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時候需要設(shè)置一個比較大的持久代空間來存放這些運(yùn)行過程中新增的類。持久代大小通過-XX:Max PermSize=進(jìn)行設(shè)置。

    自動GC,由jvm系統(tǒng)內(nèi)存不足的時候,自動釋放

    判斷策略:1引用計(jì)數(shù)法 2可達(dá)性分析

    算法:

    1、復(fù)制算法:伊甸園區(qū)(判斷)→存活區(qū)(伊甸園就空了)分成S0和S1

    S0滿了用復(fù)制算法→S1(二者總有一個是空的)

    S1滿了用復(fù)制算法→S0(默認(rèn)是15輪,后仍然存在→老生帶區(qū))

    所以新生代區(qū)和老生代區(qū)GC的頻率是不同的,內(nèi)存縮小代價高,代碼存活率太高

    2、標(biāo)記清除算法:老生帶的對象,標(biāo)記后,直接刪除不復(fù)制了,但是此時區(qū)域變成了碎片(內(nèi)存碎片)》優(yōu)化算法

    3、標(biāo)記整理算法,解決內(nèi)存碎片問題

    7、垃圾回收機(jī)制GC

    基于特定的算法,釋放垃圾對象縮占用的內(nèi)存空間,是進(jìn)軍大規(guī)模應(yīng)用開發(fā)的前提

    手動GC,手動內(nèi)存分配與釋放,如果忘記釋放對應(yīng)的內(nèi)存不被使用(容易內(nèi)存泄露)System.gc

    自動GC,由jvm系統(tǒng)內(nèi)存不足的時候,自動釋放

    判斷策略:1計(jì)數(shù) 2可達(dá)性

    算法:

    1、復(fù)制算法:伊甸園區(qū)(判斷)→存活區(qū)(伊甸園就空了)分成S0和S1

    S0滿了用復(fù)制算法→S1(二者總有一個是空的)

    S1滿了用復(fù)制算法→S0(默認(rèn)是15輪,后仍然存在→老生帶區(qū))

    所以新生代區(qū)和老生代區(qū)GC的頻率是不同的,內(nèi)存縮小代價高,代碼存活率太高

    2、標(biāo)記清除算法:老生帶的對象,標(biāo)記后,直接刪除不復(fù)制了,但是此時區(qū)域變成了碎片(內(nèi)存碎片)》優(yōu)化算法

    3、標(biāo)記整理算法,解決內(nèi)存碎片問題

    6、談?wù)勀銓tatic的了解

    static是java中的一個關(guān)鍵字,可以用來修飾方法、變量、代碼塊、內(nèi)部類,還可以使用靜態(tài)導(dǎo)包

    • static方法:不依賴于對象就可以直接訪問。不能與this合用--因?yàn)樗灰栏接谌魏螌ο?#xff0c;沒有對象就談不上this了。靜態(tài)方法中只能訪問靜態(tài)變量和靜態(tài)方法。非靜態(tài)方法中可以訪問靜態(tài)方法
    • static變量:靜態(tài)變量被所有對象共享,在內(nèi)存中只有一個副本,類加載時候初始化
    • static代碼塊:靜態(tài)代碼塊優(yōu)化程序性能,只加載一次,靜態(tài)代碼塊》構(gòu)造代碼塊》構(gòu)造函數(shù)
    • 靜態(tài)內(nèi)部類:在定義內(nèi)部類的時候加上static權(quán)限修飾符,靜態(tài)內(nèi)部類又稱為嵌套類,此時不需要外部類,就能創(chuàng)建內(nèi)部類的對象。不能從內(nèi)部類訪問外部類的非靜態(tài)資源。

    *修飾方法一般寫在權(quán)限修飾符public之后

    *可以修飾變量、方法、代碼塊、內(nèi)部類

    *靜態(tài)資源優(yōu)先于對象進(jìn)行加載,他隨著類的加載而加載的,比對象先加載入內(nèi)存,所以在沒創(chuàng)建對象的時候,靜態(tài)資源可以通過類名直接調(diào)用。類名.變量/方法

    *由于靜態(tài)比對象先加載,所以static不能與this/super共用。因?yàn)檫€沒有對象

    *靜態(tài)資源被全局所有對象(多個對象)共享,屬于類資源,值只有一份

    *普可調(diào)普、普可調(diào)靜、靜可調(diào)靜、靜不可調(diào)普(只能調(diào)靜)

    *靜態(tài)代碼塊:static{},類里方法外;靜態(tài)代碼塊也屬于靜態(tài)資源,優(yōu)先于對象加載(創(chuàng)建對象的時候先執(zhí)行靜態(tài)代碼塊→構(gòu)造代碼塊→構(gòu)造方法),并且只加載一次;作用:用于加載需要第一時間就加載,并且只加載一次(初始化)

    7、談?wù)勀銓Ψ椒ǖ睦斫?#xff1a;

    方法是具有一定功能的代碼塊,可以把重復(fù)多次使用的功能提取成一個方法,減少代碼的冗余

    • 格式:權(quán)限修飾符 返回值類型 方法名(參數(shù)列表){ 方法體 }

    方法簽名:方法名(參數(shù)列表)

    • 方法的重載和重寫

    重載:方法名相同,但是參數(shù)列表不同

    重寫:兩同(方法名,參數(shù)列表相同)兩小(返回值,異常)一大(子的訪問權(quán)限控制符>=父類的)

    • 方法的遞歸:在方法中調(diào)用自己本身,遞歸次數(shù)過多時,會出現(xiàn)棧溢出異常,求數(shù)字階乘--遞歸

    8、內(nèi)部類有哪些種類?又有哪些使用場景?

    1、成員內(nèi)部類--在類的內(nèi)部

    成員內(nèi)部類可以無條件的訪問外部類的成員屬性和成員方法(包括 private 和 static 類型的成員)

    使用場景:當(dāng)類 A 需要使用類 B ,同時 B 需要訪問 A 的成員/方法時,可以將 B 作為 A 的成員內(nèi)部類。同時我們可以利用 private 內(nèi)部類禁止其他類訪問該內(nèi)部類,從而做到將具體的實(shí)現(xiàn)細(xì)節(jié)完全隱藏。

    2、靜態(tài)內(nèi)部類--成員內(nèi)部類被靜態(tài)修飾

    不持有外部類的引用,想在靜態(tài)內(nèi)部類中訪問外部類的成員只能 new 一個外部類的對象,否則只能訪問外部類的靜態(tài)屬性和靜態(tài)方法。

    使用場景:當(dāng)類 A 需要使用類 B,而 B 不需要直接訪問外部類 A 的成員變量和方法時,可以將 B 作為 A 的靜態(tài)內(nèi)部類。

    3、局部內(nèi)部類--在代碼塊或方法中創(chuàng)建的類

    局部內(nèi)部類的作用域只能在其所在的代碼塊或者方法內(nèi),在其它地方無法創(chuàng)建該類的對象。
    我們可以把局部內(nèi)部類理解為作用域很小的成員內(nèi)部類。

    使用場景:局部內(nèi)部類只用于當(dāng)前方法或者代碼塊中創(chuàng)建、使用,屬于一次性產(chǎn)品,使用場景少。

    4、匿名內(nèi)部類--

    1.必須繼承一個父類或?qū)崿F(xiàn)一個接口,并不需要增加額外的方法,只是對繼承方法的實(shí)現(xiàn)或是覆蓋。

    2.只是為了獲得一個對象實(shí)例,并不需要知道其實(shí)際的類型。

    3.匿名內(nèi)部類的匿名指的是沒有類名,而不是沒有引用指向它。

    4.匿名內(nèi)部類不能有構(gòu)造方法,只能有初始化代碼塊。因?yàn)榫幾g器會幫我們生成一個構(gòu)造方法然后調(diào)用。

    5.匿名內(nèi)部類中使用到的參數(shù)是需要聲明為 final 的,否則編譯器會報錯。

    使用場景: 當(dāng)我們需要實(shí)現(xiàn)一個接口,但不需要持有它的引用,僅需要使用一次它的某一個資源時使用

    9、談?wù)劥?、并行、并發(fā)、分布式?

    串行---A和B兩個任務(wù)運(yùn)行在單核【1個CPU】線程上

    并行---A和B兩個任務(wù)在同一時刻發(fā)生在多核【多個CPU】線程上

    并發(fā)---多個線程在宏觀時間上看似同時執(zhí)行,但實(shí)際上是輪流穿插執(zhí)行的效果。實(shí)際上是一個cpu在若干程序間的多路復(fù)用。有串行并發(fā)、并行并發(fā)兩種

    分布式---在并行處理的基礎(chǔ)上,強(qiáng)調(diào)正在執(zhí)行的物理設(shè)備處理器、內(nèi)存等 是分開的,并行是計(jì)算機(jī)上的計(jì)算,不是物理上的分開。

    10、String中常用的api

    s.charAt(0) //a,獲取指定下標(biāo)處的字符(元素)

    s.concat("dfg") //用于拼接字符串,不會改變原串

    s.indexOf("b") //判斷指定元素第一次出現(xiàn)的索引(下標(biāo))

    s.lastIndexOf("b")//判斷指定元素最后一次出現(xiàn)的索引

    s.substring(3)//從指定下標(biāo)處截取子串,到結(jié)束
    s.substring(1,5)//截取指定范圍的子串[) 含頭不含尾

    s.length()//獲取字符串的長度是字符串的方法,但length是數(shù)組的屬性無括號

    s.split(" ")//regex是正則表達(dá)式(你定義的規(guī)則)按照正則表達(dá)式拆分字符串

    s.toUpperCase()//全大寫
    s.toLowerCase()//全小寫

    s.startsWith("a")//判斷字符串是否以指定元素開頭

    s.endsWith("c") //判斷字符串是否以指定元素結(jié)尾

    s.trim()//去除字符串兩端多余的空格

    String.valueof()//通過類型調(diào)用,不用名字,把輸入的類型轉(zhuǎn)換成String
    System.out.println(String.valueOf(80)+10);//8010 因?yàn)榍懊娴?0已經(jīng)成字符串了,兩者是拼接的關(guān)系

    long t1=System.currentTimeMillis();獲取系統(tǒng)當(dāng)前時間,使用的類:System

    11、Regex

    定義一個string類型的字符串---規(guī)則

    要判斷的.matches(string正則表達(dá)式)---返回值是布爾類型

    12、包裝類常用API

    Integer.valueof()---byte范圍內(nèi)高效的創(chuàng)建包裝類,也可以把string轉(zhuǎn)換成Integer

    Integer.parseInt()----把string類型的數(shù)據(jù)轉(zhuǎn)成 String---int

    泛型

    < ? >的部分,它就是泛型

    1、引入泛型

    泛型想要模擬數(shù)組的數(shù)據(jù)類型檢查,因?yàn)榉盒徒?jīng)常與集合一起使用,不檢查就啥都能傳進(jìn)去,引入泛型--主要目的檢查約束集合中元素的類型,可以把報錯的時機(jī)提前,在編譯器就報錯。是一顆語法糖,編譯后刪除,不會出現(xiàn)在.class源碼中。

    List list=new ArrayList();//原來的集合對象,任何數(shù)據(jù)都可以

    List<String> list2=new ArrayList();//引入泛型,約束成String類型

    <引用類型>中必須使用基本數(shù)據(jù)類型的包裝類(引用類型)

    13、多個集合(帶all)

    c.addAll(c2)//把c2集合中的所有元素添加到c中,c2本身沒有改變

    c.containsAll(c2)//true判斷c集合是否包含c2的所有元素

    c.removeAll(c2)//刪除c中屬于c2集合的所有元素,若有重復(fù),都刪

    c.retainAll(c2)//取c和c2的交集,c2不受影響,刪c

    迭代(遍歷)集合

    *迭代步驟:1.獲取工具--集合的迭代器c.iterator()

    Iterator<Integer> it = c2.iterator();//c2定義的泛型是Integer類型
    * 2.判斷集合中是否有下一個元素可以迭代 it.hasNext(),用在while循環(huán)中
    * 3.獲取當(dāng)前迭代器迭代到的元素it.next()

    ①:System.out.println(it.next());//直接打印本次迭代到的元素

    ②:Integer num = it.next();//來接一下元素,在打印
    System.out.println("本次跌倒到的元素"+num);

    14、List接口

    特點(diǎn):元素都有下標(biāo),有序的,允許存放重復(fù)的元素

    list接口中獨(dú)有的方法

    list.add(1,"蛇精");//在指定索引位置增加元素,并且可以存放重復(fù)的元素

    System.out.println(list.indexOf("小蝴蝶"));//3 元素第一次出現(xiàn)的位置
    System.out.println(list.lastIndexOf("小蝴蝶"))//8 最后出現(xiàn)的位置

    System.out.println(list.remove(5))//根據(jù)下標(biāo)刪除,并打印刪除的元素

    通過remove()直接刪除元素300
    list.remove(Integer.valueOf(300));//把int自動封裝成Integer

    System.out.println(list.get(3));//小蝴蝶 獲取指定位置的元素

    System.out.println(list.set(6,"蝎子精"));
    //重置,把6位置的葫蘆娃 換成了蝎子精 會打印出來6位置的元素

    List<E> subList(int fromIndex,int toIndex)截取子集合,含頭不含尾

    15、集合的四種迭代方式:

    * 1.for循環(huán)--因?yàn)閘ist集合是有序的,元素有下邊,所以可以根據(jù)下邊來遍歷

    for (int i = 0; i <list.size() ; i++) {
    System.out.println(list.get(i));//根據(jù)循環(huán)次數(shù) 獲取對應(yīng)的集合元素
    }
    * 2.高效for循環(huán)

    for(String s:list){
    System.out.println(s);//:后面是要遍歷的對象,前面是類型
    }
    * 3.iterator

    Iterator<String> it = list.iterator();//獲取迭代器
    while(it.hasNext()){//判斷集合是否仍然由下一個元素可以迭代
    System.out.println(it.next());//可以直接打印
    }
    * 4.listIterator

    listIterator屬于list接口特有的迭代器Iterator<E>--父接口,ListIterator<E>--子接口(不常用,可以逆序迭代)

    ListIterator<String> it2 = list.listIterator();
    while(it2.hasNext()){
    System.out.println(it2.next());
    }

    16、map集合的迭代

    1、map的迭代(沒有迭代器,只能去找Set接口)

    方式一:KeySet()

    遍歷map中的數(shù)據(jù),Set<Key>:把map中所有的key取出來放入到當(dāng)前的set集合中,Key的類型放入到<>中。

    ①:轉(zhuǎn)Set集合Set<Integer> keySet = map.keySet();

    ②:獲取迭代器Iterator<Integer> it = keySet.iterator();

    while(it.hasNext()){//3是否有下一個元素
    //4獲取當(dāng)前循環(huán)迭代到的元素 ,每輪獲取的是map中的key
    Integer key = it.next();
    //5根據(jù)剛剛獲取到的key從map中獲取對應(yīng)的value map.get();
    String value = map.get(key);
    //6拼接打印key與value
    System.out.println("{"+key+"="+value+"}");
    }

    方式二:entrySet()

    此方案是把map中的每一對鍵值對看做是一個個的Entry<k,v> 放入set集合中

    最后在獲取key和value

    ①:轉(zhuǎn)entry

    Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

    ②:獲取迭代器

    Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();

    while(it2.hasNext()){
    //獲取當(dāng)前迭代的元素,這里是一個entry------
    Map.Entry<Integer, String> entry = it2.next();//是一個entry
    //通過entry.getKey();通過entry方法獲取
    System.out.println("["+entry.getKey()+"="+entry.getValue()+"]");
    }

    17、HashMap

    HashMap數(shù)組和鏈表的結(jié)合,數(shù)組的初始容量是16,默認(rèn)的加載因子是0.75,我們用key的hash值%初始容量,存在對應(yīng)的位置上。當(dāng)計(jì)算出來的數(shù)重復(fù),則形成鏈的結(jié)構(gòu)(>8是轉(zhuǎn)成紅黑樹,<6時在轉(zhuǎn)回鏈)。所以Map是無序的。

    JDK1.8 以后在解決哈希沖突時有了較大的變化,當(dāng)鏈表長度大于閾值(或者紅黑樹的邊界值,默認(rèn)為 8)并且當(dāng)前數(shù)組的長度大于64時,此時此索引位置上的所有數(shù)據(jù)改為使用紅黑樹存儲。

    HashMap的特點(diǎn):

    1.存取無序的

    2.鍵和值位置都可以是null,但是鍵位置只能是一個null

    3.鍵位置是唯一的,底層的數(shù)據(jù)結(jié)構(gòu)控制鍵的

    4.jdk1.8前數(shù)據(jù)結(jié)構(gòu)是:鏈表 + 數(shù)組 jdk1.8之后是 : 鏈表 + 數(shù)組 + 紅黑樹

    5.閾值(邊界值) > 8 并且數(shù)組長度大于64,才將鏈表轉(zhuǎn)換為紅黑樹,變?yōu)榧t黑樹的目的是為了高效的查詢。


    ?

    18、ConcurrentHashMap

    --線程安全的-效率更高--采用的分段鎖

    hashTable在所有的上面都加了synchronize--全局鎖--保證線程安全的

    ConcurrentHashMap:如何實(shí)現(xiàn)線程安全

    在JDK1.7中----分段鎖

    ConcurrentHashMap采用了ReentrantLock重的+Segment+HahEntry。一個Segment中包含一個HashEntry數(shù)組,每個HashEntry又是一個鏈表結(jié)構(gòu)。--數(shù)組+鏈表的結(jié)構(gòu),外層加了Segment分段鎖。

    Segment分段鎖 繼承了ReentrantLock ,key對應(yīng)的是某個Segment,value對應(yīng)的是某段,這樣就是只鎖住某一段。不影響其他段的操作。并發(fā)度:就是segment有多少個,可以同時支持多少個線程來操作--可以通過構(gòu)造函數(shù)來設(shè)計(jì)。

    數(shù)組擴(kuò)容不影響其他的segment

    元素的查詢:兩次hash,第一次hash是定位到哪個Segment中包含了數(shù)組,第二次hash定位到元素所在的鏈表的頭部

    get方式無需加鎖,不會產(chǎn)生臟讀,因?yàn)樵乇旧硗ㄟ^volatile 保證的--一個線程寫,多個線程讀。標(biāo)記這個可能會被異步訪問。

    在JDK1.8中---synchronize+CAS(樂觀鎖)+Node+紅黑樹,

    查找、替換、賦值都使用CAS---保證不了線程安全 擴(kuò)容等等,所以才加的synchronize

    鎖:是鏈表的head節(jié)點(diǎn),鎖的細(xì)粒度更高,擴(kuò)容時阻塞所有的讀寫操作,讀操作無鎖。

    取消了Segment分段鎖的數(shù)據(jù)結(jié)構(gòu),取而代之的是Node,Node的value和next都是由volatile關(guān)鍵字進(jìn)行修飾,可以保證可見性。采用CAS+Synchronized替代Segment分段鎖。

    18、反射

    1、獲取字節(jié)碼對象

    Class.forName(“全路徑”);//全路徑--包名.類名 可能會拋出異常

    類名.class 不能alt+回車獲取返回值,這是一個屬性

    New對象.getClass();

    Class<?> student1 = Class.forName("cn.tedu.reflection.Student");
    Class<?> student2 = Student.class;
    Class<?> student3 = new Student().getClass();

    通過反射我們可以獲取到//class 類的全路徑//字節(jié)碼對象全路徑名:包名.類名//獲取對應(yīng)的字節(jié)碼的類名

    獲取構(gòu)造方法/獲取成員方法//獲取成員變量

    暴力反射:

    通過反射的方式創(chuàng)建對象
    Object obj = clazz.newInstance();

    //4.2!!!暴力反射!!需要設(shè)置權(quán)限私有可見
    field.setAccessible(true);

    19、設(shè)計(jì)單例模式

    1、單例設(shè)計(jì)方式一:餓漢式,自己吧對象都創(chuàng)建好了

    //0.創(chuàng)建自己的單例程序(類)

    class MySingle{

    //1.構(gòu)造方法私有化,不讓外界隨意調(diào)用本方法創(chuàng)建對象(實(shí)例化)

    private MySingle(){}

    //2.創(chuàng)建本類對象,并私有化

    private static MySingle sin=new MySingle();

    //3給外界提供一個公共的全局訪問點(diǎn)(類似get和set方法)。把創(chuàng)建好的對象返回,返回值類型是MySingle

    public static MySingle getSin(){

    return sin;}//4.將本類中剛創(chuàng)建好的對象通過return返回到調(diào)用位置

    // 5.get方法設(shè)置為靜態(tài),直接通過類名調(diào)用,不用創(chuàng)建對象;靜態(tài)方法只能調(diào)靜態(tài),所以創(chuàng)建出來的對象new MySingle()也需要改成靜態(tài)的

    MySingle sin1 = MySingle.getSin();
    MySingle sin2 = MySingle.getSin();

    //都只是執(zhí)行了類中自己創(chuàng)建的那一個對象(單例),地址值是一樣的

    sin1==sin2

    二、單例設(shè)計(jì)方式二:懶漢式

    先不創(chuàng)建對象,等你需要才創(chuàng)建--延遲加載

    //1.構(gòu)造方法私有化

    //2.在類的內(nèi)部創(chuàng)建引用類型變量成員變量(延遲加載的思想)---注意私有化

    private static MySingle2(引用類型類名) sin2;

    //3.創(chuàng)建一個公共的方法供外界調(diào)用,用來獲取本類唯一的方法

    public static MySingle2 getSin2(){

    //3.1在返回對象之前,需要判斷sin2是否保存有地址值
    if (sin2==null){//如果是默認(rèn)值null,說明之前從來沒有創(chuàng)建過本類對象
    //3.2創(chuàng)建一個對象并賦給sin2
    sin2=new MySingle2();
    }//3.3如果sin2不為空(之前創(chuàng)建過對象)就跳過if,直接返回sin2

    return sin2;

    //4.1方法設(shè)置成靜態(tài)
    //4.2成員變量設(shè)置成靜態(tài)
    //5.獲取本類對象

    MySingle2 s1 = MySingle2.getSin2(); 類名.方法名;

    20、JDK自帶注解(5個)

    @Override:標(biāo)識重寫方法

    @SuppressWarnings(“deprecation”) 忽略警告

    ·元注解(5個),用來定義其他注解 的注解

    @Target 注解用在哪里:類上、方法上、屬性上等等,可以使用的位置

    @Retention 注解的生命周期:源文件中、字節(jié)碼文件中、運(yùn)行中

    @interface注解名--自定義注解之前要用上兩個元注解表示作用位置和生命周期

    第二階段

    1、表的操作

    創(chuàng)建表:

    先使用數(shù)據(jù)庫 use 數(shù)據(jù)庫名

    create table 表名(字段1名字 字段類型(字段最大長度),字段2,字段3)

    id int primary key auto_increment, 可以寫id int (5),

    door_name varchar(100), 下劃線分隔

    tel varchar(50)

    );

    查看表的結(jié)構(gòu):desc 表名;--展示表的字段名和數(shù)據(jù)類型

    添加表的列(字段):alter table 表名 add column money numeric(7,2);

    2、SQL語句

    結(jié)構(gòu)化查詢語言(Structured Query Language),專門用來操作數(shù)據(jù)庫的語言

    • 對數(shù)據(jù)(記錄)的操作

    查詢表中的數(shù)據(jù):select*from表名;*代表通配符,表示查所有

    添加表中的數(shù)據(jù):insert into 表名values(null,'varchar的數(shù)據(jù)',666);

    修改表中的數(shù)據(jù):update 表名 set 字段名= 555 where id=1;

    刪除表中的數(shù)據(jù):Delete from 表名 where id=2;

    • 基礎(chǔ)函數(shù)

    Lower、Upper、Length、Substr截取、Concat拼接、Replace替換、Ifnull、Round/ceil/floor對小數(shù)的特殊處理、now、Year/Month/day

    • 條件查詢

    distinct(去重)、where、and/or/in 、like、null/is not null、between and、limit、order by desc降序asc升序

    limit 1,3; 從下標(biāo)1(第二行)開始記錄3行

    select * from emp limit 2; 從頭開始展示2行

    orderby和limit搭配使用--可以用來查詢最大,最小,第幾大

    若不用order by來排序

    where salaries.salary=( select max(s2.salary) from salaries s2

    where s2.salary< (SELECT max(salary) from salaries));兩次max()

    • 聚合函數(shù)

    max(),min(),sum(),avg(),count(),count(1),group By

    count(列名)--只統(tǒng)計(jì)非空的數(shù)列,非聚合列和聚合列不能一起查,只能分組。

    Having跟where 一樣,但是用在group by之后。不能再從表中讀取數(shù)據(jù)了,數(shù)據(jù)的查詢都是在from 表名之前進(jìn)行的。所以having后面跟的條件一定是之前select過的。

    Group by 非聚合列 having 過濾條件;

    Where 用在分組之前更高效,但是where不能用在聚合函數(shù)之中。

    3、字段約束

    主鍵約束“primary key”、唯一約束“unique”、非空約束“not null”、外鍵約束“foreign key”、默認(rèn)值約束“Default”,檢查約束(年齡大于0,小于200)

    4、創(chuàng)建索引index

    索引是一種排好序的快速查找的數(shù)據(jù)結(jié)構(gòu),它幫助數(shù)據(jù)庫高效的進(jìn)行數(shù)據(jù)的檢索。設(shè)計(jì)索引通常都是背后操作。但是索引本身就是龐大的表--采用BTree樹方式構(gòu)建。

    分類: 單值索引:一個索引只包含一個列(字段),

    唯一索引:索引列的值必須唯一,允許有空值;主鍵會自動創(chuàng)建唯一索引

    復(fù)合索引:一個索引包含多個列

    創(chuàng)建索引:create index 索引名字 on 表名(字段名)

    創(chuàng)建唯一索引:create unique index 索引名 on 表名(字段名);

    查看索引 show index from 表名;

    刪除索引:alter table 表名 drop index 索引名;

    創(chuàng)建復(fù)合索引:最左特性(應(yīng)用索引時候的查詢條件必須按照從左到右的順序),否則索引失效

    模糊查詢導(dǎo)致索引失效:

    5、多表聯(lián)查

    1、笛卡爾積Cartesian product又稱直積

    SELECT * FROM dept,emp where dept . deptno=emp.deptno;直接先查兩個表--慎用

    2、連接查詢

    · 內(nèi)連接 inner join 兩個的交集。

    · 左(外)連接 left join 左表所有的和右表滿足條件的,不滿足的是null,以左邊表為基礎(chǔ),

    · 右(外)連接 right join 右表的所有和左表滿足的,左表不滿足的是null,

    3、子查詢subquery

    也叫嵌套查詢(兩個select)效率非常低,把上次查詢的結(jié)果當(dāng)做第二次查詢的條件

    6、sql優(yōu)化

    1、查詢SQL盡量不要使用select *,而是具體字段,盡量避免返回大量數(shù)據(jù)

    2、在where子句中不使用or來連接條件 盡量用and

    使用varchar代替char

    不要使用in和not in,用between和exists代替

    3、盡量使用數(shù)值替代字符串類型 --0表示女 1 表示男 數(shù)字內(nèi)存小,還有讀寫性能

    4、給常用來查詢條件的字段設(shè)計(jì)索引,但是索引控制在5個以內(nèi),不適合建在有大量重復(fù)數(shù)據(jù)的字段上

    ##使用explain分析你SQL執(zhí)行計(jì)劃,分析性能是否使用了索引

    創(chuàng)建name字段的索引 where name=””; name索引才生效

    5、優(yōu)化like語句,盡量避免%號在前面,但是like很可能讓你的索引失效--從開始位置比較高效

    6、where限定查詢的數(shù)據(jù),避免在where子句中使用!=或<>操作符,在等號左側(cè)進(jìn)行表達(dá)式、函數(shù)操作

    7、INSERT INTO dept (deptno,dname) VALUES(4,"abcs");#給指定的列插入值,

    INSERT INTO student (id,NAME) VALUES(4,'齊雷'),(5,'劉昱江');批量提交

    8、偽刪除設(shè)計(jì)

    商品狀態(tài)(state):1-上架、2-下架、3-刪除

    通過where state=1或者where state=2過濾掉數(shù)據(jù),這樣偽刪除的數(shù)據(jù)用戶就看不到了,從而不影響用戶的使用,操作速度快,特別數(shù)據(jù)量很大情況下

    9、 count 聚合函數(shù)

    沒有主建: 只有一列count(*)多列count(1)

    有主鍵 count(主鍵)

    所以實(shí)際業(yè)務(wù)中一般用count(1)比較普遍,但是如果需要聚合多個列,則用count(列名)比較合適。

    7、HTML--超文本標(biāo)記語言

    1、列表標(biāo)簽<ul type=”circle”> <li> 有序列表 ol -li(自動編號) /無序列表 ul -li

    2、<input type="radio"/>男 單選框

    <input type="number" /> 數(shù)字值

    <input type="week" /> 日歷

    <input type="checkbox" />楊冪 復(fù)選框

    3、表格標(biāo)簽 table <table>表里 <tr> 行 里包含 <td> 列

    參數(shù)border=邊框 cellspacing=間距 bgcolor="顏色" width="寬度" align="center"

    5、下拉框:<select> <option>aaa</option></select>

    6、文字框:<textarea >請輸入描述信息</textarea>

    7、添加音頻<audio controls="controls"> 添加音頻的控制屬性

    <source src="位置"></source></audio>

    8、CSS-使用標(biāo)簽<style>--層疊樣式表

    CSS語法:元素的選擇器{ 修飾的具體樣式 屬性名:屬性值;}

    CSS使用的位置: <style> CSS代碼 </style>標(biāo)簽

    • 行內(nèi)CSS(給正在用的標(biāo)簽,添加style的屬性) <div style="text-align: center;">大家好</div>
    • 內(nèi)部CSS(使用html提供的<style>標(biāo)簽,把css代碼包裹起來) <head>里加<style> 選擇器
    • 外部CSS(在網(wǎng)頁里,引入一個外部CSS文件)

    選擇器:簡單選擇器(標(biāo)簽名,id,類class選擇器)分組選擇器 屬性選擇器--選擇好了,可以設(shè)置我們特殊的樣式了
    CSS選擇器

    1、簡單選擇器

    • 標(biāo)簽名選擇器 自己給標(biāo)簽起個名字,選擇的太多
    • 類Class選擇器 ①:加class屬性, 精確的選擇 ②:點(diǎn)class的值{ } <span class="a b"> 如果a 和b中有相同的設(shè)置,b會覆蓋a的
    • Id選擇器:①:按照id屬性的值(唯一) ②:#id的值 ---更高效 唯一

    1、分組選擇器

    將多個選擇器選中的元素組合在一起,用,號隔開 div,span{

    2、屬性選擇器

    通過標(biāo)簽的屬性選中元素input [ type='text' ]{ } //a [href="!"]{} //a[href] 不寫值也可

    3、引用外部的CSS,在同一級目錄

    <link href="yonghuzuce.css" rel="stylesheet"/> 可以直接寫名字 stylesheet:樣式表

    9、JS--使用標(biāo)簽<script>

    1、JS概述

    JavaScript 是 web 前端開發(fā)者必學(xué)的三種語言之一:

    · HTML 定義網(wǎng)頁的內(nèi)容 H5

    · CSS 規(guī)定網(wǎng)頁的布局 CSS3

    · JavaScript 實(shí)現(xiàn)網(wǎng)站的交互--人機(jī)互動--動態(tài) ES6

    JS是一門 基于對象事件驅(qū)動腳本語言 ,通常用來提高網(wǎng)頁與用戶的交互性。

    2、名詞解釋

    基于對象:它不僅可以創(chuàng)建對象,也能使用現(xiàn)有的對象。JS沒有類的概念,也沒有編譯的過程。是一邊解釋一邊執(zhí)行。

    事件驅(qū)動在JS中,大部分情況下都是通事件觸發(fā)驅(qū)動函數(shù)執(zhí)行的,從而實(shí)現(xiàn)特定的功能。(比如點(diǎn)擊div將內(nèi)容替換為時間、當(dāng)鼠標(biāo)滑過元素,元素就有翻轉(zhuǎn)的動態(tài)。)

    腳本語言:在網(wǎng)絡(luò)前端開發(fā)環(huán)境下,用于嵌入在客戶端瀏覽器中的一段小程序。

    3、特點(diǎn)和優(yōu)勢

    特點(diǎn):

    (1)JS是一門直譯式的語言,直接執(zhí)行的就是源代碼.

    是一邊解釋一邊執(zhí)行,沒有編譯的過程(不像Java需要提前編譯為class文件再運(yùn)行).

    (2) JS是一門弱類型的語言,沒有嚴(yán)格的數(shù)據(jù)類型.

    優(yōu)勢:

    (1)良好的交互性

    (2)一定的安全性(JS被強(qiáng)制的要求,不能訪問瀏覽器以外的東西,只能訪問瀏覽器和瀏覽器內(nèi)部的資源)

    (3)跨平臺性(Java語言具有跨平臺性,是因?yàn)橛刑摂M機(jī))

    只要有瀏覽器的地方都能執(zhí)行JS

    4、引入JS代碼

    1、行內(nèi)JS

    <div οnclick="alert(100)">我是div</div> onclick點(diǎn)擊事件 alert 是彈出窗口的

    <div οndblclick=--雙擊

    <div οnmοuseenter=--鼠標(biāo)移入

    2、內(nèi)部JS 在<head>里 <script>alert('嘟嘟嘟嘟~~~')</script> //執(zhí)行代碼

    3、外部js <script src="1.js"></script>//引入外部的js文件

    不要同時通過一個script標(biāo)簽引入JS代碼和JS文件,會導(dǎo)致代碼不會執(zhí)行

    5、js的數(shù)據(jù)類型

    數(shù)據(jù)類型包括:number / string / boolean / null / undefined

    數(shù)字類型運(yùn)算自動轉(zhuǎn)換類型 alert(2.4+3.6) 直接輸出6不是6.0

    字符串類型中用單引號和雙引號均可

    6、js的變量--常量

    var 定義變量,沒有嚴(yán)格的區(qū)分變量類型

    alter(a==b);//比較的是值

    alter(a===b);//比較值和類型

    const常量:const定義常量且必須初始化

    typeof運(yùn)算符: 用于返回變量或者表達(dá)式 的數(shù)據(jù)類型

    JS數(shù)組:單個的變量中存儲多個值(個容器)。例如:數(shù)值、字符串、布爾值、undefined、null、對象、函數(shù)等

    JS函數(shù):類似java中的方法

    方式一:function 聲明函數(shù)

    聲明:function 函數(shù)名稱( 參數(shù)列表 ){ 函數(shù)體 } 沒有用變量來接受創(chuàng)建好的函數(shù)

    調(diào)用: 函數(shù)名稱( 參數(shù)列表 );

    function add(a , b){//定義含參函數(shù) alert(x+y); }//不用寫變量類型

    Add(1,2);//調(diào)用add方法

    方式二:var 函數(shù)名稱 = function (x,y)-----這種注意不能先調(diào)用。因?yàn)楹竺娼o名字

    JS對象:window對象可以提供alert/confrm/prompt輸入框/document

    string對象

    number對象

    自定義對象:

    1、方式一;

    聲明對象:function Person(){}-----P要大寫

    ------------------用創(chuàng)建出的p對象 更改Person對象---------------------

    創(chuàng)建對象:var p1 = new Person();

    設(shè)置屬性:p1.name = "張飛"; p1.age = 18;-----創(chuàng)建出來的對象 直接添加值

    設(shè)置方法:p1.run = function( ){ }-----在Person中添加run函數(shù)

    訪問p1對象:

    它的屬性可以邊寫邊創(chuàng)建

    2、方式二:

    定義對象的名稱 var p2={

    “變量名”:values, "pname":"張三",

    "page":20,

    綁定函數(shù) "psay":function(){

    console.log(this.pname+this.page);

    }}

    打印對象 console.log(p2);----{pname: '張三', page: 20, psay: ?}

    調(diào)用對象的方法 p2.psay();

    10、document對象

    1、獲取方式window.document;先寫一句這個

    2、調(diào)用方法 document..getElementById("id屬性的值")--返回1個元素,根據(jù)id找

    Document..getElementsByName("name屬性的值")--返回多個元素(用數(shù)組)

    getElementsByClassName("class屬性的值")--返回多個元素(用數(shù)組)

    11、JSON--JavaScript 對象表示法

    JavaScript Object Notation

    1、JSON數(shù)據(jù):本身是字符串

    Var a=’”fristname”:”josn”’ ---外層有引號 內(nèi)層的key和values都要有引號

    不同于創(chuàng)建對象時候key的引號可以省略

    2、JSON對象---多個屬性

    var a = '{ "firstName":"John" , "lastName":"Doe" }'

    3、JSON 數(shù)組:

    var a = '[ { "firstName":"Bill" , "lastName":"Gates" },{ "firstName":"George" , "lastName":"Bush" } ] ';

    給服務(wù)器發(fā)送數(shù)據(jù): 將JS對象轉(zhuǎn)成JSON字符串 JSON.stringify(Js對象)

    接受服務(wù)器的數(shù)據(jù): JSON字符串轉(zhuǎn)成JS對象 JSON.parse("json字符串")

    轉(zhuǎn)換成js對象后,直接 .name 屬性就能獲取name的值

    var js=JSON.parse(a); console.log(js.name);

    轉(zhuǎn)成字符串后 就可以直接對字符串操作,拼接、求長度等

    12、vue

    · 一個輕量級的mvvm框架雙向綁定,數(shù)據(jù)動態(tài)更新,gzip后大小只有20k+

    · 是一個漸進(jìn)式框架,其核心思想是數(shù)據(jù)驅(qū)動、組件化的前端開發(fā)

    · 原生html頁面是通過js 操作的是dom,而vue.js操作的是數(shù)據(jù)

    3、使用vue準(zhǔn)備數(shù)據(jù) <script>標(biāo)簽里面創(chuàng)建vue對象

    <script>new Vue({ vue的兩個屬性,用{}包起來,里面寫的是js對象})</script>

    el 屬性 掛載點(diǎn) element的簡稱,即將把準(zhǔn)備好的數(shù)據(jù) 渲染到指定區(qū)域

    data屬性 準(zhǔn)備要顯示的數(shù)據(jù),傳遞數(shù)據(jù)

    第三階段

    一、linux命令--在Mobaxter

    1.1 cd命令集

    ifconfig/ip addr 檢查IP地址

    pwd 檢查當(dāng)前的位置

    tab鍵 自動補(bǔ)齊(注意唯一性)

    cd命令是linux中最基本的命令語句,必須熟練掌握

    cd / 返回根目錄

    cd ~ 用戶主目錄

    cd . 當(dāng)前目錄

    cd ..返回到上一級目錄

    cd /usr/ 進(jìn)入到usr目錄

    cd – 返回上一個目錄

    cd 直接回家

    1.2 ls目錄和文件

    ls –l 詳細(xì)格式,文件權(quán)限,時間

    ll 和ls –l作用相同

    ls *.txt 查看所有的txt類型文檔

    1.3 目錄操作

    mkdir 創(chuàng)建目錄

    mkdir a 創(chuàng)建 a目錄

    mkdir a/aa/aa 創(chuàng)建多級目錄

    mkdir -p a/b 創(chuàng)建 a目錄,并在a目錄里創(chuàng)建b目錄

    mkdir -m 777 c 創(chuàng)建一個權(quán)限為777的C目錄

    rmdir 刪除目錄(如果目錄里有文件,則不能用此命令)

    1.4 Vi/vim創(chuàng)建/查看/編輯文件

    命令行:Esc切換到命令行模式。

    編輯模式:先按一下切換模式

    按i,在光標(biāo)前開始編輯

    按a,在光標(biāo)后開始編輯

    按o,在當(dāng)前行的下一行開始編輯

    按u, 撤銷之前的操作---基本上是撤銷一行

    底行模式:按 shift+:冒號。

    :q! 不保存退出

    :wq 保存退出

    :/world 從當(dāng)前光標(biāo)處,向上查找world關(guān)鍵字

    :?world 從當(dāng)前光標(biāo)處,向后查找world關(guān)鍵字

    1.5 刪除文件

    rm 刪除文件

    rm n.txt 提示y刪除n放棄

    rm –f n.txt 不提示

    rm –rf dirname 不提示遞歸刪除目錄下所以內(nèi)容

    rm –rf * 刪除所有文件

    rm –rf /* 刪除所有子目錄所有和文件

    1.6 復(fù)制和移動文件

    cp復(fù)制文件

    cp nginx.conf n.txt

    cp –R tomcat1 tomcat2 #復(fù)制整個目錄

    mv 修改文件名,移動文件

    mv n.txt m.txt 修改文件名稱

    1.7瀏覽文件

    cat 輸出文件所有的內(nèi)容

    more 輸出文檔所有的內(nèi)容,分頁輸出,空格瀏覽下一屏,q退出

    less 用法和more相同,只是通過PgUp、PgOn鍵來控制

    tail 用于顯示文件后幾號,使用頻繁

    tail -10 nginx.conf 查看nginx.conf的最后10行

    tail –f nginx.conf 動態(tài)查看日志,方便查看日志新增的信息

    ctrl+c 結(jié)束查看

    1.8 打包命令

    tar命令位于/bin目錄下,它能夠?qū)⒂脩羲付ǖ奈募蚰夸洿虬梢粋€文件,但不做壓縮。一般Linux上常用的壓縮方式是選用tar將許多文件打包成一個文件,再以gzip壓縮命令壓縮成name.tar.gz的文件。

    -c 創(chuàng)建一個新的tar文件

    -v 顯示運(yùn)行過程的信息

    -f 指定文件名

    -z 調(diào)用gzip壓縮命令進(jìn)行壓縮

    -t 查看壓縮文件的內(nèi)容

    -x 解開tar文件

    tar –cvf n.tar ./* 壓縮當(dāng)前目錄下的所有文件和目錄,文件名為n.tar

    tar –xvf n.tar 解壓壓縮包中的文件到當(dāng)前目錄(如果長時間未解壓成功 Ctrl+C推出)

    tar –cvzf m.tar.gz ./* 壓縮文件

    tar -zxvf m.tar.gz 解壓m.tar文件到當(dāng)前目錄

    1.9grep命令

    grep root /etc/passwd 在文件中查找關(guān)鍵字root

    grep root /etc/passwd –-color 高亮顯示

    grep root /etc/passwd –A5 –B5 高亮顯示,A后5行,B前5行

    grep -n root /etc/passwd 查找并顯示行數(shù)

    grep -v root /etc/passwd 取反,查出不含root的數(shù)據(jù)

    第四階段-----微服務(wù)

    知識基礎(chǔ):

    1、RestTemplate--客戶端

    Spring RestTemplate 是 Spring 提供的用于訪問 Rest 服務(wù)的客戶端,RestTemplate 提供了多種便捷訪問遠(yuǎn)程Http服務(wù)的方法,第三方服務(wù)商都是使用 RestTemplate 請求 restful 服務(wù)。


    @Autowired//從spring容器獲取一個RestTemplate對象,基于此對象實(shí)現(xiàn)遠(yuǎn)端服務(wù)
    private RestTemplate restTemplate;

    public String doRestEcho1(){
    //1、定義要調(diào)用的遠(yuǎn)端服務(wù)的url
    String url="http://localhost:8081/provider/echo/"+server;
    //2、基于restTemplate對象中的相關(guān)方法進(jìn)行服務(wù)調(diào)用
    return restTemplate.getForObject(url,String.class );
    }

    服務(wù)消費(fèi)方是如何調(diào)用服務(wù)提供方的服務(wù)的?(RestTemplate)--客戶端

    服務(wù)之間進(jìn)行服務(wù)調(diào)用時,使用了什么API?(RestTemplate)

    2、Socket/ServerSocket對象

    * 1.網(wǎng)絡(luò)服務(wù)端(ServerSocket) ---用tomcat實(shí)現(xiàn)
    * 2.網(wǎng)絡(luò)客戶端(Socket)--用Browser

    //網(wǎng)絡(luò)中計(jì)算機(jī)的唯一標(biāo)識是:ip
    //計(jì)算機(jī)中應(yīng)用程序的唯一標(biāo)識:端口port

    3、讀寫鎖--ReadWriteLock

    寫鎖--悲觀寫,自能自己寫,別人不能寫

    讀鎖--樂觀讀,拿讀鎖后還能被別人讀

    創(chuàng)建讀寫鎖ReentrantReadWriteLock

    4、過濾器Filter

    過濾器鏈:FilterChain

    一般和servlet控制器 (請求控制邏輯) 組合使用,控制請求的分發(fā)等等

    5、Servlet控制器

    Servlet是java后臺程序與用戶交互的機(jī)制(媒介).

    Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務(wù)程序或服務(wù)連接器主要功能在于交互式地瀏覽和生成數(shù)據(jù),生成動態(tài)Web內(nèi)容。

    狹義的Servlet是指Java語言實(shí)現(xiàn)的一個接口,廣義的Servlet是指任何實(shí)現(xiàn)了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運(yùn)行于支持Java的應(yīng)用服務(wù)器中。從原理上講,Servlet可以響應(yīng)任何類型的請求,但絕大多數(shù)情況下Servlet只用來擴(kuò)展基于HTTP協(xié)議的Web服務(wù)器

    servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監(jiān)聽端口,請求過來后,根據(jù)URL信息,確定要將請求交給哪個servlet去處理,然后調(diào)用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。

    核心方法:request.getParameter--parameter是參數(shù)的意思,參數(shù)是自己取的,根據(jù)request對象取值

    public String findUserByIds(HttpServletRequest request)

    {String id = request.getParameter("id"); //自己從request對象中取出信息

    參數(shù)名稱必須相同、弊端無論什么樣的數(shù)據(jù),都是String數(shù)據(jù)類型,需要手動的轉(zhuǎn)化

    SpringMVC: 在內(nèi)部封裝了Servlet機(jī)制.并且可以根據(jù)用戶的參數(shù)類型,實(shí)現(xiàn)自動的數(shù)據(jù)類型的轉(zhuǎn)化

    6、tomcat處理請求流程

    tomcat 處理客戶端的請求時的一個簡易流程分析

    關(guān)鍵組件:

    服務(wù)注冊 與發(fā)現(xiàn)---Nacos 服務(wù)的配置--配置中心

    負(fù)載均衡-Ribbon

    遠(yuǎn)程服務(wù)調(diào)用-Feign

    限流降級--Sentinel

    訪問入口管理--網(wǎng)關(guān)GateWay

    分布式事務(wù)管理

    一、Nacos注冊中心

    1.1應(yīng)用背景、配置

    不停機(jī)就可以動態(tài)刷新服務(wù)內(nèi)部的配置項(xiàng)--調(diào)整日志級別

    例如,在生產(chǎn)環(huán)境上日志級別error ,系統(tǒng)出問題對它 debug 的時候,日志級別調(diào)整為 debug 級別。

    服務(wù)注冊與發(fā)現(xiàn),一般需要添加的依賴有兩個spring-cloud-starter-alibaba-nacos-discovery/config--這是配置中心的

    • 實(shí)現(xiàn)Nacos服務(wù)注冊需要添加什么依賴?(兩個spring-boot-starter-web,spring-cloud-starter-alibaba-nacos-discovery)
    • 實(shí)現(xiàn)Nacos服務(wù)注冊時,必須做哪些配置?(服務(wù)名,假如是本機(jī)服務(wù)注冊可以省略服務(wù)地址)
    • Nacos如何檢查服務(wù)狀態(tài)?(通過心跳包實(shí)現(xiàn))
    • 服務(wù)之間進(jìn)行服務(wù)調(diào)用時,使用了什么API?(RestTemplate)

    要想下載的nacos能夠管理對應(yīng)的數(shù)據(jù)庫表--找到nacos文件的conf(配置)中的application.properties,更改里面的表名,數(shù)據(jù)庫連接密碼等。

    啟動Nacos:在bin目錄下打開cmd輸入:startup.cmd -m standalone

    訪問Nacos:運(yùn)行成功后會顯示訪問端口號和路徑http://localhost:8848/nacos

    • 什么是配置中心?(存儲項(xiàng)目配置信息的一個服務(wù))
    • 為什么要使用配置中心?(集中管理配置信息動態(tài)發(fā)布配置信息)
    • 配置中心一般都會配置什么內(nèi)容?(可能會經(jīng)常變化的配置信息,例如連接池,日志、線程池、限流熔斷規(guī)則)
    • 什么信息一般不會寫到配置中心?(服務(wù)端口,服務(wù)名,服務(wù)的注冊地址,配置中心)
    • 市場上有哪些主流的配置中心?(Apollo,nacos,Spring Cloud全家桶……)

    1.2nacos動態(tài)刷新

    我們系統(tǒng)在瀏覽器中能看到日志級別的變化

    * 配置中心的level--日志級別 發(fā)生了變化,但屬性的值不變:屬性的值是對象初始化的時候讀取的 *

    @RefreshScope--在controller類上添加注解--創(chuàng)建監(jiān)聽器--監(jiān)聽配置中心,只要配置中心發(fā)生變化,就重新創(chuàng)建對象

    日志級別:error>warn>info>debug>trace

    當(dāng)我們后臺設(shè)計(jì)的日志級別是info,trace/debug 等級別小的能在后臺打印,warn/error比info大的不打印

    • 你項(xiàng)目中使用的日志規(guī)范是什么?(SLF4J)
    • 項(xiàng)目中使用的日志API是什么:(org:slf4j.Logger
    • Nacos配置中心宕機(jī)了,我們的服務(wù)還可以讀取到配置信息嗎?(可以從內(nèi)存,客戶端獲取了配置中心的配置信息以后,會將配置信息在本地內(nèi)存中存儲一份.)
    • 微服務(wù)應(yīng)用中我們的客戶端如何獲取配置中心的信息?(我們的服務(wù)一般首先會從內(nèi)存讀取配置信息,同時我們的微服務(wù)還可以定時向nacos配置中心發(fā)請求拉取(pull)更新的配置信息)
    • 微服務(wù)應(yīng)用中客戶端如何感知配置中心數(shù)據(jù)變化?(當(dāng)數(shù)據(jù)發(fā)生變化時,nacos找到它維護(hù)的客戶端,然后通知客戶端去獲取更新的數(shù)據(jù),客戶端獲取數(shù)據(jù)以后更新本地內(nèi)存,并在下次訪問資源時,刷新@Value注解描述的屬性值,但是需要借助@RefreshScope注解對屬性所在的類進(jìn)行描述)(nacos客戶端會基于長輪詢機(jī)制,從nacos獲取配置信息)
    • 長輪詢:如果沒有配置更新時 就在nacos服務(wù)端的對列 等待 短輪詢:如果沒有就離開

    1.3定時任務(wù)Timer/ScheduledExecuteService

    • 基于Timer對象實(shí)現(xiàn)定時任務(wù)調(diào)度(單線程有順序的任務(wù)調(diào)度)---最大缺陷是多個任務(wù)不能并發(fā)執(zhí)行

    //Timer對象創(chuàng)建時會創(chuàng)建一個線程(單線程用Timer),并未線程分配一個對列(多任務(wù)排隊(duì))
    Timer timer=new Timer();

    //構(gòu)建任務(wù)對象 TimerTask 是接口類型
    TimerTask task1=new TimerTask()

    //3.定時執(zhí)行任務(wù)
    timer.schedule(task1,1000,1000);

    • 基于ScheduledExecuteService實(shí)現(xiàn)多線程任務(wù)調(diào)度(Nacos中定時心跳,配置長輪詢都基于此對象)
      //構(gòu)建一個負(fù)責(zé)任務(wù)調(diào)度的線程池對象,池中最多有5個核心線程
      ScheduledExecutorService ses=Executors.newScheduledThreadPool(5);
      //構(gòu)建任務(wù)對象
      Runnable task=new Runnable() {//重寫里面的方法--要執(zhí)行的代碼
      //執(zhí)行任務(wù)對象(定時任務(wù)調(diào)度)
      ses.scheduleWithFixedDelay(task, initialDelay:1,//初始延遲 delay:1, //每隔一秒執(zhí)行一次(與任務(wù)是否結(jié)束無關(guān))TimeUnit.SECONDS);//每秒刷新一次

    2、基于雙重校驗(yàn)機(jī)制,實(shí)現(xiàn)類的單實(shí)例設(shè)計(jì)--懶漢式

    1.4多線程從數(shù)據(jù)庫獲取數(shù)據(jù)

    問題1:數(shù)據(jù)緩存問題

    問題2:多線程安全問題(不能取null值),和并發(fā)問題

    安全問題:

    list集合用 CopyOnWriteArrayList<>()

    Vector<>是悲觀鎖,別人不允許更新,值允許一個人更新

    CopyOnWriteArrayList 是一個線程安全的list集合,允許多個線程并發(fā)更新,但是只能有一個更新成功

    并發(fā)問題:

    雙重校驗(yàn)+加鎖

    1.5共享配置文件--點(diǎn)單登錄

    在同一個namespace的工作空間中,提取相同的配置,存儲到指定配置文件

    單點(diǎn)登錄:商城中不同的模塊,不需要多次登錄,相當(dāng)于登錄的通行證(加密的)--需要校驗(yàn)--秘鑰

    應(yīng)用jwt.io技術(shù)網(wǎng)站,生產(chǎn)令牌,包含用戶信息--后期需要解密

    日志的級別、秘鑰token、可以當(dāng)做共享配置

    • 項(xiàng)目中為什么使用org.slf4j.Logger對象記錄日志?此對象是日志規(guī)范,對外面的門面,可以更好降低耦合
    • @Slf4j注解的作用是什么?(此注解屬于lombok,用于描述類,在類中創(chuàng)建org.slf4j.Logger對象)
    • Nacos配置中心依賴對項(xiàng)目中配置文件的名字有要求嗎?(bootstrap.yml)
    • Nacos中@RefreshScope注解的應(yīng)用場景以及作用.:動態(tài)創(chuàng)建對象,當(dāng)數(shù)據(jù)更新時就創(chuàng)建一次對象

    二、負(fù)載均衡--Ribbon

    2.1LoadBalancerClient應(yīng)用

    client--客戶端、用戶端、客戶、委托人

    LoadBalancerClient對象可以從nacos中基于服務(wù)名獲取服務(wù)實(shí)例,在工程中基于算法實(shí)現(xiàn)負(fù)載均衡方式的調(diào)用。spring幫我們創(chuàng)建好了LoadBalancerClient對象,直接注入就可以了,

    loadBalancerClient.choose("sca-provider");//choose綁定負(fù)載均衡地址 serviceId--服務(wù)的id

    2.2Ribbon負(fù)載均衡--優(yōu)化

    Netfilx公司進(jìn)行維護(hù)的

    在啟動類中創(chuàng)建RestTemplate對象,使用@loadBalanced注解描述RestTemplate對象時,假如發(fā)起遠(yuǎn)程服務(wù)調(diào)用,底層會對這個請求進(jìn)行攔截,攔截到此請求后會基于LoadBalancerClient對象獲取服務(wù)實(shí)例,然后進(jìn)行負(fù)載均衡方式的調(diào)用。簡化了loadBalancerClient對象獲取實(shí)例信息的過程。

    為RestTemplate對象注入攔截器,在底層攔截器中實(shí)現(xiàn)服務(wù)實(shí)例的獲取

    • 何為服務(wù)發(fā)現(xiàn)?(從nacos獲取服務(wù)實(shí)例)
    • LoadBalancerClient的作用?(從nacos獲取服務(wù)實(shí)例列表,然后本地基于負(fù)載均衡算法 獲取服務(wù)實(shí)例)
    • @Loadbalanced作用?(描述RestTemplate對象,讓系統(tǒng)底層為RestTemplate對象賦能--簡化)

    三、遠(yuǎn)程服務(wù)調(diào)用--Feign

    3.1feign的注解

    Feign 是一種聲明式Web服務(wù)客戶端,底層封裝了對Rest技術(shù)的應(yīng)用,通過Feign可以簡化服務(wù)消費(fèi)方對遠(yuǎn)程服務(wù)提供方法的調(diào)用實(shí)現(xiàn)。(不用自己拼url了)

    1、添加依賴

    <!--feign 中的api封裝了遠(yuǎn)程服務(wù)調(diào)用方式以及錯誤機(jī)制--> 可以不用 直接用RestTemplate

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    2、@EnableFeignClients--啟動類上添加注解

    使用@EnableFeignClients注解配置啟動類時,主要用于告訴spring框架,要對使用@FeignClient接口創(chuàng)建實(shí)現(xiàn)類以及對象

    實(shí)現(xiàn)Feign的時候 注意有代理對象,然后實(shí)現(xiàn)的http:url的訪問遠(yuǎn)程服務(wù)

    3、@FeignClient--接口上,定義遠(yuǎn)程調(diào)用規(guī)范

    @FeignClient(name="sca-provider")--name屬性是遠(yuǎn)端服務(wù)名,同時也會將這個名字作為接口實(shí)現(xiàn)類的 Bean對象名字

    4、@GetMapping等注解--在接口中的方法上添加

    Feign接口是基于方法上的@GetMapping等注解中的value(默認(rèn)不寫)值,調(diào)用遠(yuǎn)端服務(wù)的某個具體方法
    @GetMapping("/provider/echo/{msg}")--調(diào)用sca-provider中的provider類中的echo方法
    String echoMsg(@PathVariable("msg") String msg);//jdk版本中只有參數(shù)arg0,arg1,所以要特定指定參數(shù)
    5、創(chuàng)建controller類,定義方法,基于怎么樣的額方式調(diào)用遠(yuǎn)程服務(wù)

    外界輸入的url:http://localhost:8090/consumer/echo4/aaa先訪問consumer中的方法,然后跳轉(zhuǎn)到feign接口中

    3.2Feign配置進(jìn)階

    防止同一個服務(wù)提供了很多服務(wù)調(diào)用方法,并添加容錯方案

    1、添加唯一標(biāo)識屬性屬性

    為遠(yuǎn)程調(diào)用服務(wù)接口指定一個contextId屬性,作為遠(yuǎn)程調(diào)用服務(wù)的唯一標(biāo)識即可

    @FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider服務(wù)提供名稱

    interface RemoteProviderService{

    @GetMapping("/provider/echo/{string}")//前提是遠(yuǎn)端需要有這個服務(wù)

    public String echoMessage(@PathVariable("string") String string);

    }

    2、實(shí)現(xiàn)類做相關(guān)處理

    創(chuàng)建實(shí)現(xiàn)類--調(diào)用feign.hystrix提供的FallbackFactory接口

    @Component
    public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService前面的接口> {
    /**此方法會在RemoteProviderService接口服務(wù)調(diào)用時,出現(xiàn)了異常后執(zhí)行.*/
    @Override
    public RemoteProviderService create(Throwable throwable) {//自動重寫的方法

    //JDK8中l(wèi)ambda表達(dá)式--匿名函數(shù)--用箭頭操作符--左側(cè) : Lambda 表達(dá)式的參數(shù)列表
    --右側(cè) : Lambda 表達(dá)式中所需執(zhí)行的功能, 即 Lambda 體
    return (msg) -> {
    return "服務(wù)維護(hù)中,請稍等";
    };
    }


    3、在@FeignClient注解的接口中,添加fallbackFactory屬性=實(shí)現(xiàn)類的名字;在Feign訪問接口中應(yīng)用FallbackFactory對象

    4、服務(wù)中斷處理機(jī)制

    3.3調(diào)用流程

    1)通過 @EnableFeignCleints 注解告訴springcloud,啟動 Feign Starter 組件。

    2) Feign Starter 在項(xiàng)目啟動過程中注冊全局配置,掃描包下所由@FeignClient注解描述的接口,然后由系統(tǒng)底層創(chuàng)建接口實(shí)現(xiàn)類(JDK代理類),并構(gòu)建類的對象,然后交給spring管理(注冊 IOC 容器)。

    3) 接口被調(diào)用時被動態(tài)代理類邏輯攔截, @FeignClient 請求信息通過編碼器生成 Request請求對象,基于此對象進(jìn)行遠(yuǎn)程過程調(diào)用。

    4) 請求對象經(jīng)Ribbon進(jìn)行負(fù)載均衡,挑選出一個健康的 Server 實(shí)例(instance)

    5) 通過 Client(客戶端) 攜帶 Request 調(diào)用遠(yuǎn)端服務(wù)返回請求響應(yīng)

    6) 通過解碼器生成 Response 返回客戶端,將信息流解析成為接口返回?cái)?shù)據(jù)。

    • Feign是什么?(Spring Cloud微服務(wù)規(guī)范中的一組遠(yuǎn)程調(diào)用API)
    • 為什么使用Feign?(優(yōu)化服務(wù)調(diào)用結(jié)構(gòu))
    • 如何使用Feign實(shí)現(xiàn)服務(wù)調(diào)用?(加入依賴,@EnableFeignClients啟動類上,@FeignClient加在接口上)
    • Feign方式的服務(wù)調(diào)用原理是怎樣的?(底層基于代理對象實(shí)現(xiàn))

    四、Sentinel限流和熔斷

    Sentinel (分布式系統(tǒng)的流量防衛(wèi)兵) 是阿里開源的一套用于服務(wù)容錯的綜合性解決方案

    以流量為切入點(diǎn), 從流量控制、熔斷降級、系統(tǒng)負(fù)載保護(hù)等多個維度來保護(hù)服務(wù)的穩(wěn)定性。

    sentinel的核心:核心庫(java客戶端)、控制臺(Dashboard)儀表盤--整合了很多第三方的框架,圖表等等

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    1、添加了此依賴以后,會在項(xiàng)目中添加一個攔截器對象,這個對象會對此服務(wù)發(fā)出的請求進(jìn)行攔截,會與sentinel控制臺定義的限流規(guī)則進(jìn)行比對,在允許范圍內(nèi)則繼續(xù)訪問,否則則拋出處理后的異常。

    stringMVC中有Handler Interceptor這個類提供的攔截器prehandle---在controller類之前攔截,如果服務(wù)重啟了,規(guī)則消失--需要改源碼

    2、修改yml

    sentinel:
    transport:
    dashboard: localhost:8180 #這里描述的是sentinel控制臺的地址

    eager: true #服務(wù)啟動后就與sentimel控制臺注冊 而不用瀏覽器訪問請求了

    4.1sentinel限流

    三種限流模式:直接限流,關(guān)聯(lián)限流,鏈路限流(@SentinelResource("資源名"))

    三種流控效果:快速失敗,預(yù)熱(WarmUp),排隊(duì)等待

    • Sentinel是什么?(阿里推出一個流量控制平臺,防衛(wèi)兵)
    • 類似Sentinel的產(chǎn)品你知道有什么?(hystrix-一代微服務(wù)產(chǎn)品spring-cloud微服務(wù))
    • Sentinel是如何對請求進(jìn)行限流的?(基于sentinel依賴提供的攔截器)
    • 為什么要進(jìn)行限流? (系統(tǒng)處理能力有限,可以通過限流方式,保證系統(tǒng)可靠運(yùn)行)
    • Sentinel限流的基本原理?(底層對請求進(jìn)行攔截,然后通過流控規(guī)則限定對資源訪問)
    • Sentinel限流有哪些算法? (計(jì)數(shù)器,令牌桶,漏桶,滑動窗口算法--sentinel默認(rèn))
    • 你了解sentinel中的閾值應(yīng)用類型嗎?(兩種-QPS(每秒請求次數(shù)),線程數(shù))

    4.2sentinel降級熔斷

    鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級---調(diào)用超時或異常比例升高,讓請求快速失敗,避免影響到其它的資源而導(dǎo)致級聯(lián)錯誤。當(dāng)資源被降級后,在接下來的降級時間窗口之內(nèi),對該資源的調(diào)用都自動熔斷(默認(rèn)行為是拋出 DegradeException)

    4.3sentinel異常處理

    自己進(jìn)行定義異常處理機(jī)制,直接或間接實(shí)現(xiàn)BlockExceptionHandler接口,并將對象交給spring管理。用于處理BlockException類型以及子類類型異常

    4.4熱點(diǎn)限流

    @SentinelResource("resource")--方法上-基于參數(shù)或參數(shù)值進(jìn)行限流

    熱點(diǎn)規(guī)則的限流模式只有QPS模式(這才叫熱點(diǎn))。sentinel中心設(shè)置的參數(shù)索引為@SentinelResource注解的參數(shù)的下標(biāo),0代表第一個參數(shù),1代表第二個參數(shù)

    4.5授權(quán)規(guī)則(黑白名單)

    自己創(chuàng)建類實(shí)現(xiàn)implements RequestOriginParser,交給spring管理加@Component注解,當(dāng)設(shè)置了授權(quán)規(guī)則后,系統(tǒng)底層攔截請求,會調(diào)用此方法,對請求數(shù)據(jù)進(jìn)行解析

    • Sentinel 的降級(熔斷)策略有哪些?(慢調(diào)用-響應(yīng)時長,異常比例-異常占比,異常數(shù))
    • Sentinel 的熱點(diǎn)規(guī)則中的熱點(diǎn)數(shù)據(jù)?(熱賣商品,微博大咖,新上映的電影)
    • 為什么要進(jìn)行異常處理?(提高用戶體驗(yàn))
    • Sentinel中限流,降級的父類異常類型是什么?(BlockException)
    • Sentinel中默認(rèn)的BlockException處理對象是誰?(DefaultBlockException)
    • 如何自己定義Sentinel的異常處理對象?(直接或間接繼承BlockExceptionHandler)
    • 如何理解sentinel中的系統(tǒng)規(guī)則?(全局限流規(guī)則,基于QPS,CPU,…)
    • 如何理解Sentinel中的授權(quán)規(guī)則?(黑白名單)

    五、網(wǎng)關(guān)GateWay

    訪問入口管理--API網(wǎng)關(guān)--就是URL的入口管理

    網(wǎng)關(guān)是訪問內(nèi)部服務(wù)的入口,隱藏真實(shí)地址,對請求中的數(shù)據(jù)過濾--用Filter過濾器

    Spring Cloud Gateway提供了權(quán)限認(rèn)證,監(jiān)控、限流等功能,--單點(diǎn)登錄認(rèn)證

    • 優(yōu)點(diǎn):
  • 性能強(qiáng)勁:是第一代網(wǎng)關(guān)Zuul(是spring cloud中應(yīng)用的網(wǎng)關(guān))的1.6倍。
  • 功能強(qiáng)大:內(nèi)置了很多功能,例如轉(zhuǎn)發(fā)、監(jiān)控、限流等
  • 設(shè)計(jì)優(yōu)雅,容易擴(kuò)展。
    • 缺點(diǎn):
  • 依賴Netty(網(wǎng)絡(luò)編程框架)與WebFlux(Spring5.0),不是SpringMVC提供的,學(xué)習(xí)成本高。
  • (SpringMvc的并發(fā)處理能力不高)不是傳統(tǒng)的Servlet編程模型(Spring MVC就是基于此模型實(shí)現(xiàn))
  • 需要Spring Boot 2.0及以上的版本,才支持
  • Netty中NIO的設(shè)計(jì),需要二次加工
  • 5.1配置

    <dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-gateway</artifactId>

    </dependency>

    API網(wǎng)關(guān)在微服務(wù)架構(gòu)中也是一個web服務(wù),但這個web服務(wù)的啟動不是依賴于tomcat
    而是依賴于網(wǎng)絡(luò)編程框架Netty,添加依賴后,系統(tǒng)會自動幫我們關(guān)聯(lián)下載一個netty框架

    server:

    port: 9000

    spring:

    cloud:

    gateway:

    routes: #配置網(wǎng)關(guān)路由規(guī)則

    - id: route01 #路由標(biāo)識符id,自己指定一個唯一值即可

    uri: http://localhost:8081/ #網(wǎng)關(guān)幫我們轉(zhuǎn)發(fā)的url,轉(zhuǎn)發(fā)的目的地(微服務(wù)) url是屬于uri的子級

    predicates: ###斷言(謂此):匹配請求規(guī)則進(jìn)行條件判斷,只有斷言都返回真,才會執(zhí)行路由

    - Path=/nacos/provider/echo/** #請求路徑定義,此路徑對應(yīng)uri中的資源

    filters: ##網(wǎng)關(guān)過濾器,用于對謂詞中的內(nèi)容進(jìn)行判斷分析以及處理

    - StripPrefix=1 #轉(zhuǎn)發(fā)之前去掉path中第一層路徑,例如nacos

    啟動gateway、啟動nacos,啟動provider類,在類中找/provider/echo/*這個方法,如果有就能跳轉(zhuǎn)

    訪問http://localhost:9000/nacos/provider/echo/dakun

    5.2負(fù)載均衡設(shè)計(jì)

    在訪問服務(wù)時,要基于服務(wù)serivce id(服務(wù)名)去查找對應(yīng)的服務(wù),讓請求從網(wǎng)關(guān)層進(jìn)行均衡轉(zhuǎn)發(fā)

    1、我們需要將網(wǎng)關(guān)作為一個服務(wù)在nacos中進(jìn)行注冊--添加spring-cloud-alibaba-nacos-discovery

    2、yml中配置, uri: lb://sca-provider lb實(shí)現(xiàn)負(fù)載均衡,后面加服務(wù)名

    • 網(wǎng)關(guān)中沒有配置,是底層自己實(shí)現(xiàn)負(fù)載均衡,底層的LoadBalance Client Filter這個攔截器調(diào)用 Ribbon LoadBalanceClient對象中的choose方法實(shí)現(xiàn)負(fù)載均衡
    • Ribbon是自己來實(shí)現(xiàn)Ribbon LoadBalancerClient對象實(shí)現(xiàn)的負(fù)載均衡。

    網(wǎng)關(guān)請求轉(zhuǎn)發(fā)流程分析:

    客戶端向Spring Cloud Gateway(網(wǎng)關(guān))發(fā)出請求。 如果Gateway Handler Mapping(處理器映射器) 通過斷言predicates(predicates)的集合確定請求與路由(Routers)匹配,則將其發(fā)送到Gateway Web Handler(web處理器)。 Gateway Web Handler 通過確定的路由中所配置的過濾鏈調(diào)用過濾器Filters。找到指定的攔截器(LoadBalanceClientFilter),調(diào)用Ribbon LoadBalanceClient對象實(shí)現(xiàn)負(fù)載均衡

    Filter由虛線分隔的原因是, Filter可以在發(fā)送代理請求之前和之后運(yùn)行邏輯。處理的邏輯是 在處理請求時 排在前面的過濾器先執(zhí)行,而處理返回相應(yīng)的時候,排在后面的過濾器先執(zhí)行。

    5.3限流設(shè)計(jì)

    1、添加依賴:限流sentinel,sentinel-gateway(因?yàn)檫@個不是mvc的)
    2、修改yml:

    3、啟動網(wǎng)關(guān):-Dcsp.sentinel.app.type=1

    1、自定義API維度限流(重點(diǎn))

    是一種更細(xì)粒度的限流規(guī)則定義,它允許我們利用sentinel提供的API,將請求路徑進(jìn)行分組,然后在組上設(shè)置限流規(guī)則

    1、新建API分組,定義匹配的字符串--請求路徑

    2、根據(jù)API分組來限流

    2、定制流控網(wǎng)關(guān)返回值

    現(xiàn)在用的網(wǎng)關(guān)gateWay不是mvc了,不用寫那么多層,寫一個網(wǎng)關(guān)配置類,并用@Configuration注解標(biāo)識
    一般都是用JSON串來返回響應(yīng)結(jié)果

    • 新建一個BlockRequest Handler攔截器,
    • 創(chuàng)建一個hashMap<>集合,put放狀態(tài)碼,和信息msg
    • 把map轉(zhuǎn)換成json格式的字符串:String jsonStr=JSON.toJSONString(map);
    • 返回服務(wù)端響應(yīng): return ServerResponse.ok().body();

    5.4常見問題:

    • 為什么使用網(wǎng)關(guān)?(服務(wù)安全,統(tǒng)一服務(wù)入口管理,負(fù)載均衡,限流,鑒權(quán))
    • Gateway 服務(wù)的啟動 底層是通過誰去實(shí)現(xiàn)的?(Netty網(wǎng)絡(luò)編程框架--ServerSocket網(wǎng)絡(luò)服務(wù)端)
    • 網(wǎng)關(guān)層面服務(wù)的映射方式怎樣的?(謂詞-path,…,服務(wù)名/服務(wù)實(shí)例)
    • 何為謂詞?(網(wǎng)關(guān)中封裝了判斷邏輯的一個對象)
    • 謂詞邏輯的設(shè)計(jì)是怎樣的?(謂詞判斷邏輯返回值為true則進(jìn)行請求轉(zhuǎn)發(fā))
    • 你了解哪些謂詞邏輯?(path,請求參數(shù),請求方式,請求頭,….)
    • 我們可以自己定義謂詞工廠對象嗎?(可以的)

    • 網(wǎng)關(guān)層如何記錄服務(wù)的映射?(通過map,并要考慮讀寫鎖的應(yīng)用)
    • 網(wǎng)關(guān)層面是如何實(shí)現(xiàn)負(fù)載均衡的?(通過服務(wù)id查找具體的服務(wù)實(shí)例,底層調(diào)用RibbonLoadBalanceClient對象中的choose方法實(shí)現(xiàn)負(fù)載均衡)
    • 網(wǎng)關(guān)層面是如何通過服務(wù)名查找服務(wù)實(shí)例的?(Ribbon)

    • 網(wǎng)關(guān)層面結(jié)合sentinel實(shí)現(xiàn)限流,其限流的類型有幾種?(兩種-Route id,api分組)

    • 網(wǎng)關(guān)層面可以自定義限流后的異常處理結(jié)果嗎?(可以)
    • 你知道Sentinel底層限流的算法有哪些?(滑動窗口,令牌桶,漏斗,。。。)

    • 網(wǎng)關(guān)過濾器的作用是什么?(對請求和響應(yīng)數(shù)據(jù)做一個預(yù)處理)
    • 網(wǎng)關(guān)過濾器的類型有哪些?(局部過濾器,全局過濾器--作用于所有請求鏈路,不用定義)
    • 如何自己定義全局過濾器?(直接或間接實(shí)現(xiàn)Globa lFilter接口)

    -------------------單點(diǎn)登錄系統(tǒng)SSO--------------

    Single sign on

    解決方案:

    解決方案1:用戶登陸成功以后,將用戶登陸狀態(tài)存儲到redis數(shù)據(jù)庫,例如:弊端:多次操作數(shù)據(jù)庫,高并發(fā)的時候,性能差

    解決方案2:用戶登陸成功以后,將用戶信息存儲到token(令牌),然后寫到客戶端進(jìn)行存儲。(本次設(shè)計(jì)方案),方案一的以前的token就是一個隨機(jī)的UUID,沒有實(shí)際意義;JWT token是攜帶了信息的令牌

    關(guān)鍵技術(shù):

    redis數(shù)據(jù)庫

    auth認(rèn)證/授權(quán)

    Spring Security技術(shù)(認(rèn)證,授權(quán))

    JWT技術(shù) JWT token攜帶信息的令牌

    也要用到服務(wù)的注冊、發(fā)現(xiàn)nacos,web服務(wù),mybatis-plus

    生成序列化id?

    工程的結(jié)構(gòu):用到的模塊

    system系統(tǒng)模塊中涉及到的表

    • tb_user_roles--用戶角色關(guān)系表,可以在此表中基于用戶id找到用戶角色
    • tb_role_menus--角色菜單關(guān)系表,可以基于角色id找到菜單id
    • tb_menus菜單表,為資源的外部表現(xiàn)形式,在此表中可以根據(jù)id找到權(quán)限標(biāo)識

    一、auth2認(rèn)證服務(wù)中心

    第三方應(yīng)用授權(quán)登錄,允許用戶授權(quán)第三方移動應(yīng)用訪問 另外服務(wù)提供者上的信息,不需要將用戶名/密碼提供給第三方移動應(yīng)用或分享他們數(shù)據(jù)的所有內(nèi)容

    1、添加依賴、配置文件、啟動類

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>

    <!-- 用feign的方式調(diào)用,調(diào)用另外服務(wù)提供者-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    加載的spring-cloud-starter-oauth2依賴中,包括Spring Security(內(nèi)置了一個頁面)+JWT+oauth2,所以我們一訪問localhost:8071,就會找到登錄頁面,登錄后默認(rèn)跳轉(zhuǎn)到index.html,并產(chǎn)生一個password

    簡單的配置后已將可以登陸了

    二、簡單的單體登錄

    2.1遠(yuǎn)程信息調(diào)用

    1、啟動類上添加@EnableFeignClients--啟動feign掃描,使用feign實(shí)現(xiàn)遠(yuǎn)程調(diào)用

    2、創(chuàng)建service接口中@FeignClient遠(yuǎn)程調(diào)用 遠(yuǎn)程用戶 信息 ---直接sso-system中controller中的方法

    @FeignClient(value = "sso-system",contextId = "remoteUserService")

    2.2遠(yuǎn)程服務(wù)調(diào)用/認(rèn)證管理

    3、創(chuàng)建業(yè)務(wù)邏輯處理對象,接口的實(shí)現(xiàn)類,實(shí)現(xiàn)遠(yuǎn)程 服務(wù) 調(diào)用 ,交給認(rèn)證管理器(AutnenticationManager)根據(jù)獲取到的信息去完成密碼的比對操作 (

    • 根據(jù)用戶名查找用戶信息,判斷用戶是否存在,不存在則拋出異常
    • 根據(jù)用戶id查詢用戶 是否有權(quán)限)
    • 和封裝返回 用戶信息和權(quán)限對象,此對象最終會交給認(rèn)證管理器

    request-->filer過濾器-->servlet控制器-->handler interceptor攔截器-->controller

    2.3Security配置類--密碼加密

    4、定義密碼加密對象Security配置類(要不然登錄會報錯)

    @Configuration
    public class SecurityConfig {
    /*構(gòu)建密碼加密對象,比MD5更強(qiáng),登錄時,系統(tǒng)底層會基于此對象進(jìn)行密碼加密*/
    @Bean
    public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
    }
    }//返回的是一個password的編碼器對象

    2.4配置認(rèn)證規(guī)則(攔截規(guī)則)

    繼承WebSecurityConfigurerAdapter

    登陸請求url的放行(不需要認(rèn)證)和攔截(需要認(rèn)證)

    2.5自定義處理方法--可省

    2.6總結(jié)

    單體登錄基本完成,但是上述設(shè)計(jì)所有的頁面都在一個項(xiàng)目中,只有一個tomcat——我們要完成分布式(多個服務(wù)器)。以上用戶的認(rèn)證操作,其實(shí)現(xiàn)主要基于Spring Security框架

    設(shè)置cookies緩存,單體登錄都是存在session中的,servlet服務(wù)器端的Session,向客戶端響應(yīng)一些cookies(有效期就是會話時間)

    還有生成JWT token給用戶的相關(guān)信息進(jìn)行加密——分布式架構(gòu),多個服務(wù)器

    用auth認(rèn)證和授權(quán)

    三、分布式單點(diǎn)登錄系統(tǒng)

    3.0總結(jié):

    Spring Security是基礎(chǔ),OAuth和Jwt是具體實(shí)現(xiàn),在security上生效

    OAuth2.0是規(guī)范、一種授權(quán)協(xié)議,不是實(shí)現(xiàn)

    jwt是token的實(shí)現(xiàn)

    如果我們的系統(tǒng)要給第三方做授權(quán),就實(shí)現(xiàn)OAuth2.0

    如果我們要做前后端分離,就實(shí)現(xiàn)token就可以了,jwt(JSON WEB Token)僅僅是token的一種實(shí)現(xiàn)方式

    • 1)SpringSecurity (提供認(rèn)證和授權(quán)的實(shí)現(xiàn))
    • 2)TokenConfig(提供了令牌的生成,存儲,校驗(yàn))
    • 3)Oauth2(定義了一套認(rèn)證規(guī)范,例如為誰發(fā)令牌,都發(fā)什么,...)

    3.0Spring Security企業(yè)級安全框架

    對軟件系統(tǒng)中的認(rèn)證,授權(quán),加密等功能進(jìn)行封裝,Spring Security 在企業(yè)中實(shí)現(xiàn)認(rèn)證和授權(quán)業(yè)務(wù)時,底層構(gòu)建了大量的過濾器,在過濾連中定義Oauth2和JWT的具體實(shí)現(xiàn)

    3.1定義令牌的存儲對象--三種

    * 構(gòu)建令牌配置對象,在微服務(wù)架構(gòu)中登陸成功后,可以將用戶信息進(jìn)行存儲,

    常用存儲方式
    * 1、產(chǎn)生一個隨機(jī)字符串(token),然后基于此字符串將用戶信息存儲到關(guān)系數(shù)據(jù)庫(mysql)
    * 2、產(chǎn)生一個隨機(jī)字符串(token),然后基于此字符串將用戶信息存儲到內(nèi)存數(shù)據(jù)庫(redis)
    * 3、基于Jwt創(chuàng)建令牌,存儲用戶信息,不需要寫到數(shù)據(jù)庫,在客戶端存儲即可
    * 基于上述設(shè)計(jì)方案,Oauth2協(xié)議給了具體的API實(shí)現(xiàn)對象,例如:
    * 1、JdbcTokenStore(用的較少)
    * 2、RedisTokenStore(中型應(yīng)用,比jdbc快)
    * 3、JwtTokenStore(對性能要求比較高的分布式架構(gòu))

    Jwt數(shù)據(jù)規(guī)范--令牌生成

    JWT(JSON WEB Token),是基于token 的認(rèn)證協(xié)議的實(shí)現(xiàn)

    有三部分構(gòu)成:

    Header頭部:是一個JSON對象 描述JWT的元數(shù)據(jù) 包含算法、令牌token的類型

    {

    "alg": "HS256",//alg屬性表示簽名的算法(algorithm)

    "typ": "JWT" //令牌的類型(type)

    }

    Payload:實(shí)際需要傳遞的數(shù)據(jù),JWT規(guī)范中規(guī)定了7個官方字段,供選用

    Signature:簽名部分,防止數(shù)據(jù)被串改,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道

    算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點(diǎn)"(.)分隔,就可以返回給用戶。

    3.2定義Oauth2認(rèn)證授權(quán)配置類

    1、創(chuàng)建配置類,添加@EnableAuthorizationServer 啟動授權(quán)和認(rèn)證,@Configuration和全參構(gòu)造

    繼承AuthorizationServer Configurer Adapter 類(Adapter適配器模式)

    2、配置認(rèn)證規(guī)則:

    • 配置由誰完成認(rèn)證(認(rèn)證管理器)
    • 配置由誰負(fù)責(zé)查詢用戶業(yè)務(wù)數(shù)據(jù),訪問數(shù)據(jù)庫(認(rèn)證時需要兩部分信息:客戶端,數(shù)據(jù)庫)
    • 配置可以處理的認(rèn)證請求方式(默認(rèn)支持post方式)
    • 配置token生成及存儲策略(默認(rèn)令牌生成UUID,存儲方式為內(nèi)存)

    3、修改令牌方法,主要是令牌增強(qiáng),配置令牌有效時間、是否支持刷新令牌、另設(shè)置刷新有效時長

    下面解決兩個問題:

    • 對誰頒發(fā)令牌(對客戶端有沒有要求,需要進(jìn)行配置)
    • 訪問哪個路徑時幫你頒發(fā)令牌,需要對外暴露認(rèn)證路徑(定義頒發(fā)、解析、效驗(yàn)令牌的路徑)

    4、在內(nèi)存中配置,定義客戶端的id(client_id 客戶端提交用戶信息認(rèn)證時需要這個id),定義客戶端秘鑰(client_secret 客戶端提交用戶信息時候需要攜帶這個秘鑰),定義作用范圍(符合規(guī)則的客戶端),允許客戶端基于密碼方式,刷新令牌實(shí)現(xiàn)認(rèn)證(grant_type=password)

    5、我們登陸時要對哪個url發(fā)起請求,通過哪個url可以加載令牌,配置對外暴露的認(rèn)證url,刷新令牌的url,檢查令牌的url,

    6、postMan訪問

    7、401是認(rèn)證失敗--沒有攜帶令牌

    403是沒有訪問權(quán)限--某種請求方式?jīng)]有在數(shù)據(jù)庫中設(shè)置權(quán)限

    3.3略過的內(nèi)容

    資源服務(wù)工程的設(shè)計(jì)與實(shí)現(xiàn)--sso-resource模塊--不做認(rèn)證,只做資源管理

    網(wǎng)關(guān)工程--sso-gateway

    客戶端UI工程--sso-ui--前后端分離項(xiàng)目,添加login.html頁面

    網(wǎng)關(guān)層面添加限流

    資源服務(wù)resource中日志的獲取---AOP方式獲取日志---Feign方式將日志傳遞給系統(tǒng)服務(wù)

    --------------Docker虛擬引擎----簡化運(yùn)維-----------

    一、Go語言和CS架構(gòu)

    Docker是一個虛擬引擎(基于現(xiàn)有的資源虛擬出一個容器)、容器化技術(shù)平臺,基于 Google 公司的 Go 語言進(jìn)行實(shí)現(xiàn),基于這種方式,可更快地打包、測試以及部署應(yīng)用程序

    有了docker,一個安裝配置好的mysql容器,可以直接拿到另一臺主機(jī)上啟動,而不必重新安裝mysql

    誕生背景:服務(wù)多了維護(hù)困難,簡化部署運(yùn)維,提高服務(wù)的可維護(hù)性

    docker平臺基本架構(gòu)CS(Client/Server)

    核心對象:鏡像Image 容器Container

    二、docker的操作命令

    登錄 · 語雀

    三、Docker數(shù)據(jù)管理

    • 數(shù)據(jù)卷(Volumes):可供一個或多個容器使用的特殊目錄,可以在容器之間共享和重用,默認(rèn)會一直存在,即使容器被刪除。多個容器共用一個數(shù)據(jù)卷
    • 掛在主機(jī)目錄(Bind mounts):

    四、Dockerfile鏡像制作

    Jdk鏡像、

    基于JDK鏡像sentinel鏡像、

    Mysql數(shù)據(jù)庫鏡像、

    Redis數(shù)據(jù)庫鏡像、

    Nginx代理鏡像、

    nacos容器

    五、常見問題

    什么是數(shù)據(jù)卷(Volume)?(docker中的一個數(shù)據(jù)管理對象)

    常用數(shù)據(jù)卷操作有哪些?(創(chuàng)建,查看,應(yīng)用,刪除)

    如何理解數(shù)據(jù)卷和目錄掛載(Mount),兩者的不同(是否由Docker管理)?

    鏡像的制作過程是怎樣的?(app+Dockerfile->Build)

    鏡像文件可以被多個容器共享嗎?(可以,可以基于同一個鏡像文件啟動多個容器)

    常用鏡像文件的下載以及容器的啟動?(MySql,Redis,Nginx,Naocs等)

    退出容器后想再進(jìn)入容器怎么辦? 首先docker ps查看容器是否在運(yùn)行,假如沒有運(yùn)行要start啟動

    Docker平臺下容器互聯(lián)操作(創(chuàng)建網(wǎng)絡(luò),查看網(wǎng)絡(luò)信息,應(yīng)用網(wǎng)絡(luò))

    -----------Redis數(shù)據(jù)庫--緩存技術(shù)--------

    要求掌握:

    • redis的核心api--Jedis --RedisTemplate
    • 存儲數(shù)據(jù)的方式--五重
    • 投票系統(tǒng)、購物車、秒殺、單點(diǎn)登錄系統(tǒng)
    • redis的持久化(兩種)

    redis--分布式緩存數(shù)據(jù)庫,是一個key-value存儲系統(tǒng)

    我們現(xiàn)在的項(xiàng)目基本上是web服務(wù)器(Tomcat)和數(shù)據(jù)庫獨(dú)立部署的,獨(dú)占資源,隨著用戶的增多,高并發(fā)讀寫數(shù)據(jù)庫會增大數(shù)據(jù)庫的壓力,性能下降。如果我們tomcat服務(wù)器上增加本地緩存,并在外部增加分布式緩存,緩存熱門數(shù)據(jù)。通過緩存就能把絕大多數(shù)請求在讀寫數(shù)據(jù)庫前攔截掉,降低壓力

    一、redis操作指令

    docker start redis01 ---啟動redis01鏡像

    docker exec -it redis01 bash---進(jìn)入到容器

    redis-cli ---進(jìn)入redis數(shù)據(jù)庫

    redis-cli -p 6379--進(jìn)入指定端口號的數(shù)據(jù)庫

    exit--退出

    shutdown--安全關(guān)閉redis服務(wù),保存數(shù)據(jù)

    keys*---查看redis中的額所有key

    set key value--基于key/value的形式存儲數(shù)據(jù)

    get key

    flushdb--清除當(dāng)前數(shù)據(jù)庫數(shù)據(jù)

    flushall---清除所有數(shù)據(jù)庫數(shù)據(jù)

    expire key seconds---控制key的有效時長

    二、Redis數(shù)據(jù)類型

    2.1String類型操作--散列方式

    命令:

    incr/incrby key value---自增,并返回遞增后的值

    decr/decrby key value--自減/自減多個---可以實(shí)現(xiàn)博客的點(diǎn)贊操作

    append key---向尾部追加值,如果鍵不存在則創(chuàng)建

    strlen key---字?jǐn)?shù)統(tǒng)計(jì)

    mset/mget---同時設(shè)置/獲取多個鍵值對

    2.2Hash類型--方便存儲對象

    大哈希(rides中的key value)中的小哈希(value 中的key和value)

    命令:

    hset KEY key value key2 value2 ---可以設(shè)置多個值

    hget KEY key ---獲取value

    hmget---可以獲取多個key對應(yīng)的值

    hmset=hset

    hincrby---正數(shù)加 負(fù)數(shù)減

    hgetall---獲取所有的key value

    hexists---是否存在

    hdel---刪除

    hkeys--獲取所有的key

    hvals---獲取所有的value

    2.3List類型--可重復(fù)、有序

    redis中的list相當(dāng)于java中的linkedList,是一個雙向鏈表,支持正向、反向查找和遍歷。經(jīng)常用于實(shí)現(xiàn)熱銷榜,最新評論等設(shè)計(jì)

    lpush--每次都從左面放 A B C D 的存放是D C B A

    rpush--從右面放

    rpop--從右面取出先進(jìn)先出--對列結(jié)構(gòu) 取出A B C D --比如秒殺活動

    lpop--左面出--先進(jìn)后出--結(jié)構(gòu) 取出 D C B A --手機(jī)中的頁面,最新評論

    del--刪除key中的所有元素-clean

    llen--集合的長度

    lrange key 0 -1 --查看從第一個取到最后一個數(shù)據(jù) 不會移除數(shù)據(jù),只是讀,pop是取出

    linsert--在指定位置插入 linsert lst1 after/before B C --不論左右都是直接在B后面插入C--結(jié)果是BCA

    lrem--移除幾個元素 lrem lst1 2 C --list中允許元素重復(fù),從左邊移除

    ltrim--保留指定位置間的元素,其他的移除,以前是去空格

    lindex--查看指定下標(biāo)位置的元素

    rpoplpush ONE TWO--從ONE右邊拿,放到TWO左邊,也可以兩個集合

    brpop--阻塞式對列,如果沒有數(shù)據(jù)給拿出來,會等待時間結(jié)束后在取(釋放cpu),b代表阻塞

    如何基于redis實(shí)現(xiàn)一個隊(duì)列結(jié)構(gòu)?(lpush/rpop)

    如何基于redis實(shí)現(xiàn)一個棧結(jié)構(gòu)?(lpush/lpop)

    如何基于redis實(shí)現(xiàn)一個阻塞式隊(duì)列?(lpush/brpop)

    如何實(shí)現(xiàn)秒殺活動的公平性?(先進(jìn)先出--對列)

    用戶注冊時的郵件發(fā)送功能如何提高其效率?(郵件發(fā)送是要調(diào)用三方服務(wù),底層通過隊(duì)列優(yōu)化其效率,隊(duì)列一般是list結(jié)構(gòu))

    如何動態(tài)更新商品的銷量列表?(賣的好的排名靠前一些,linsert)

    商家的粉絲列表使用什么結(jié)構(gòu)實(shí)現(xiàn)呢?(list結(jié)構(gòu)--有先后順序)

    2.4set類型--不重復(fù)、無序

    Redis中Set集合是通過哈希表實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是O(1)。

    sadd--添加元素,重復(fù)元素添加失敗,返回0--去重

    smembers--獲取內(nèi)容,成員

    sismember--是否存在元素,返回值是布爾類型

    spop--移除隨機(jī)幾個元素,并返回移除元素

    scard--獲取元素的個數(shù)--投票系統(tǒng) 計(jì)數(shù) 點(diǎn)贊

    smove--移動元素 smove set1 set2 C 把set1中的C 移動到set2

    sunion--合并的顯示,并不是創(chuàng)建新的集合

    srem--指定要刪除的元素

    2.5zset類型--不重復(fù)、有序

    zadd--添加元素

    zrange--獲取索引區(qū)間內(nèi)的元素

    zrangebyscore--獲取分?jǐn)?shù)區(qū)間內(nèi)的元素

    zrem--刪除元素

    zcard--獲取集合中元素的個數(shù)

    zincrby--增減,正數(shù)增

    zcount--獲取分?jǐn)?shù)區(qū)間內(nèi)元素個數(shù)

    zrank--獲取項(xiàng)在zset中的索引

    zrevrank--獲取在zset中的倒序索引

    2.6常見問題:

    • Redis 簡介(分布式緩存數(shù)據(jù)庫,非關(guān)系型數(shù)據(jù)庫,NoSQL數(shù)據(jù)庫)
    • Redis 服務(wù)的線程模型(6.0之前都是單線程,6.0之后網(wǎng)絡(luò)io操作引入了多線程)
    • Redis 數(shù)據(jù)庫的基本操作(服務(wù)的啟動,停止,redis的登入,登出)
    • 為什么使用redis?(解決分布式系統(tǒng)下數(shù)據(jù)緩存的問題)
    • 如何理解Redis數(shù)據(jù)庫的大小哈希(hash)操作?(全局是大哈希,局部value是小哈希)
    • Redis數(shù)據(jù)中常用的數(shù)據(jù)類型的應(yīng)用場景?(

    string類型--方便字?jǐn)?shù)統(tǒng)計(jì),日志追加

    hash類--方便存儲對象

    list類型--實(shí)現(xiàn)熱銷排行榜,最新評論(棧結(jié)構(gòu)-先進(jìn)后出),秒殺活動(對列結(jié)構(gòu)-先進(jìn)先出)

    set類型--投票系統(tǒng),計(jì)數(shù),點(diǎn)贊功能)

    三、Java中操作redis--Jedis

    java中操作json 三劍客 jackson fastjson gson(谷歌提供的)

    創(chuàng)建springboot工程自動就能應(yīng)用jackson

    Jedis是Java中操作redis的一個客戶端,類似通過jdbc訪問mysql數(shù)據(jù)庫----spring

    頻繁的對象創(chuàng)建和銷毀對象,性能降低--底層是TCP協(xié)議(三次握手,四次揮手)

    • Redis 客戶端API(Jedis)的基本應(yīng)用(對象的創(chuàng)建,對象的銷毀,常用方法的應(yīng)用)

    redis的API就是在java中怎么取代虛擬機(jī)中的操作命令,基本上都差不多。.set .expire設(shè)置key的有效時長 .pipelined--獲取管道 .exists(key) .type(key) 返回值的類型

    3.1JedisPool連接池

    Jedis連接池的測試--享元設(shè)計(jì)模式--設(shè)計(jì)思想通過池減少對象的創(chuàng)建次數(shù),實(shí)現(xiàn)對象的可重用性,所有池的設(shè)計(jì)都有這個設(shè)計(jì)模式的應(yīng)用(整數(shù)池,字符串池,線程池,連接池)

    創(chuàng)建線程池:

    public static Jedis getConnection02(){
    if(jedisPool==null){
    synchronized (JedisDataSource.class){//保證線程安全
    if(jedisPool==null){ //雙重校驗(yàn)
    JedisPoolConfig config=new JedisPoolConfig();
    config.setMaxTotal(16);
    config.setMaxIdle(8);//最大空間
    jedisPool=new JedisPool(config,IP, PORT);//創(chuàng)建對象
    }
    }
    }
    return jedisPool.getResource();//獲得資源
    }

    volatile:關(guān)鍵字--多線程可見性、禁止指令重排序、不保證原子性

    四、項(xiàng)目實(shí)踐

    4.1分布式id

    在分布式系統(tǒng)中,數(shù)據(jù)量將越來越大時,就需要對數(shù)據(jù)進(jìn)行分表操作,但是,分表后,每個表中的數(shù)據(jù)都會按自己的節(jié)奏進(jìn)行自增,很有可能出現(xiàn)ID沖突。這時就需要一個單獨(dú)的機(jī)制來負(fù)責(zé)生成唯一ID,生成出來的ID也可以叫做 分布式ID,這里我們借助redis實(shí)現(xiàn)一個簡易的分布式id進(jìn)行實(shí)現(xiàn),

    4.2單點(diǎn)登錄--hash

    之前單點(diǎn)登錄用的是認(rèn)證授權(quán),沒存儲到數(shù)據(jù)庫,這里借助redis類存儲用戶信息

    流程:

    1、執(zhí)行登錄認(rèn)證,將來寫到認(rèn)證服務(wù)器中

    • 檢驗(yàn)數(shù)據(jù)的合法性(判斷用戶名,密碼是否為空,密碼的長度是否有數(shù)字字母等)
    • 基于用戶名查詢用戶信息,并判定密碼是否正確
    • 用戶存在且密碼正確,將用戶信息寫到redis
    • 將token返回給客戶端

    2、攜帶token訪問資源服務(wù)器

    • 效驗(yàn)token是否為空(為空說明未登陸)
    • 基于token查詢redis數(shù)據(jù),假如有對應(yīng)數(shù)據(jù)說明用戶登錄了
    • 檢查用戶是否有訪問資源的權(quán)限,有權(quán)限則可以訪問
    • 返回要訪問的資源

    4.3秒殺隊(duì)列--list

    將商品搶購信息先寫到redis(以隊(duì)列形式進(jìn)行存儲),因?yàn)閷憆edis內(nèi)存數(shù)據(jù)庫要比寫mysql數(shù)據(jù)庫快很多倍

    算法:先進(jìn)先出(FIFO)-體現(xiàn)公平性lpush--rpop

    4.4投票系統(tǒng)--set

    * 1.投票數(shù)據(jù)存儲到redis(key:活動id value:多個用戶id的集合)
    * 2.同一個用戶不能執(zhí)行多次投票--用sismember來判斷返回的布爾值
    * 3.業(yè)務(wù)操作(投票,獲取總票數(shù),檢查是否投過票,取消投票,投票人)

    4.5購物車系統(tǒng)--hash

    1.向購物車添加商品--hset每次執(zhí)行都是覆蓋,不是追加
    2.查看購物車商品--hgetall
    3.刪除購物車商品----
    4.改變購物車某個商品的購買數(shù)量---
    需要的參數(shù):用戶的id,商品id,總數(shù)

    五、redis的API--RedisTemplate

    RedisTemplate是springboot工程中操作redis數(shù)據(jù)庫的一個Java對象

    模板方法設(shè)計(jì)模式

    特性:在springBoot中RedisTemplate就不用像Jedis那樣創(chuàng)建連接池了,springboot中默認(rèn)使用lettuce連接池


    ValueOperations<String,String> vo=redisTemplate.opsForValue();//獲取字符串操作對象

    5.1RedisTemplate 應(yīng)用--jdk

    基于jdk序列化和反序列化是SpringRedisTemplate的父類

    源碼中默認(rèn)序列化 Jdk Serialization RedisSerializer

    需求:key序列化方式采用StringRedis方式-----setKeySerializer(RedisSerializer.string())

    set---序列化 給redis設(shè)置值

    get---反序列化,從redis拿出值,并解析成數(shù)據(jù)顯示在控制臺

    • StringRedisTemplate和RedisTemplate兩個應(yīng)用時有什么不同?

    StringRedisTemplate基于spring序列化,傳的參數(shù)必須是string類型的。RedisTemplate是jdk方式序列化的,存在redis數(shù)據(jù)的形式不同,是StringRedisTemplate的父類

    六、redis與AOP整合應(yīng)用

    基于AOP方式操作redis緩存

    @EnableCaching--在啟動類上,啟動aop緩存應(yīng)用

    當(dāng)我們在調(diào)用一個緩存方法時會把該方法參數(shù)和返回結(jié)果作為一個鍵值對存在緩存中 redis中

    @Cacheable--spring--方法上--此注解描述的方法為切入點(diǎn)方法--一般用于存儲

    此方法執(zhí)行時,底層會通過AOP機(jī)制,先從緩存取數(shù)據(jù),緩存有則直接返回,緩存沒有則查數(shù)據(jù),最后將查詢的數(shù)據(jù),還會向redis存儲一份

    @Cacheable--更新緩存

    屬性:CacheNames用來指定緩存組件的名字,將方法的返回結(jié)果放在哪個緩存中,可以是數(shù)組的方式,支持指定多個緩存。

    使用緩存的時候要注意:緩存的雪崩、穿透;要注意緩存的更新,比如定時,然后訪問mysql

    七、redis的持久化-rdb和aof

    Redis是一種內(nèi)存數(shù)據(jù)庫,斷電時,數(shù)據(jù)可能會丟失。如果通過持久化將數(shù)據(jù)搞一份兒到磁盤上去,然后再定期同步到一些云存儲服務(wù)上去,那么就可以保證一些數(shù)據(jù)不丟失,保證數(shù)據(jù)的可靠性。

    7.1Rdb方式持久化

    redis data base 宿主機(jī)中用來存儲數(shù)據(jù)的磁盤數(shù)據(jù)--redis的默認(rèn)的數(shù)據(jù)持久化方式.系統(tǒng)啟動時會自動開啟這種方式的持久化機(jī)制

    Rdb方式是通過手動:三種方式保存redis中的key/value--生成快照的方式持久化

    save-是一種阻塞式方式必須所有的保存操作都執(zhí)行完了,

    bgsave-異步,保存的數(shù)據(jù)交給后臺(又啟動了一個進(jìn)程,不是線程),自己可以接受別的命令

    周期性方式保存(如多久的時間數(shù)據(jù)發(fā)生了變化,去保存--生成快照)

    常見問題:

    1、Redis中的save和bgsave有什么不同?

    Redis Save 命令執(zhí)行一個同步保存操作,將當(dāng)前 Redis 實(shí)例的所有數(shù)據(jù)快照(snapshot)以 RDB 文件的形式保存到硬盤。

    BGSAVE 命令執(zhí)行之后立即返回 OK ,然后 Redis fork 出一個新子進(jìn)程,原來的 Redis 進(jìn)程(父進(jìn)程)繼續(xù)處理客戶端請求,而子進(jìn)程則負(fù)責(zé)將數(shù)據(jù)保存到磁盤,然后退出。

    2、RDB持久化機(jī)制有哪些優(yōu)點(diǎn)?

    第一:RDB會生成多個數(shù)據(jù)文件,每個數(shù)據(jù)文件都代表某一個時刻中redis的數(shù)據(jù),非常適合做冷備

    第二:RDB對redis對外提供的讀寫服務(wù),影響非常小,可以讓redis保持高性能,因?yàn)閞edis主進(jìn)程只需要一個子進(jìn)程執(zhí)行磁盤IO操作來進(jìn)行RDB持久化。

    第三:相對于AOF持久化機(jī)制來說,直接基于RDB數(shù)據(jù)文件來重啟和恢復(fù)redis進(jìn)程,更加快速。存儲是內(nèi)存中的數(shù)據(jù)aof中存儲的數(shù)命令

    3、缺點(diǎn):數(shù)據(jù)丟失比較嚴(yán)重

    它都是每隔5分鐘或更長時間做一次快照,這個時候一旦redis進(jìn)程宕機(jī),那么會丟失最近幾分鐘的數(shù)據(jù)

    7.2aof方式持久化

    默認(rèn)是關(guān)閉的:appendonly no 可能會影響性能:寫一次指令 保存一次磁盤

    Aof方式是通過記錄寫操作日志的方式,記錄redis數(shù)據(jù)的一種持久化機(jī)制,這個機(jī)制默認(rèn)是關(guān)閉的。

    Redis支持三種刷寫模式:何時刷新指令

    • #appendfsync always #每次收到寫命令就立即強(qiáng)制寫入磁盤,是最安全的,速度也是最慢的,一般不推薦。
    • appendfsync everysec #每秒鐘強(qiáng)制寫入磁盤一次,在性能和持久化方面做平衡,推薦該方式。
    • #appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,性能最好但是持久化最沒有保證,不推薦。

    日志的重寫:何時啟動重寫

    • 當(dāng)前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。
    • 當(dāng)前AOF文件啟動新的 設(shè)置日志重寫過程的最小值,避免剛剛啟動Reids時由于文件尺寸較小導(dǎo)致頻繁的重寫。

    常見問題:

    1、如何理解AOF方式中的rewrite 重寫 操作?

    redis中的可以存儲的數(shù)據(jù)是有限的,很多數(shù)據(jù)可能會自動過期,也可能會被用戶刪除或被redis用緩存清除的算法清理掉。只有一部分常用的數(shù)據(jù)會被自動保留在redis內(nèi)存中,所以可能很多之前的已經(jīng)被清理掉的數(shù)據(jù),對應(yīng)的寫日志還停留在AOF中,AOF日志文件就一個,會不斷的膨脹,最好導(dǎo)致文件很大。

    所以,AOF會自動在后臺每隔一定時間做rewrite操作,比如當(dāng)前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。當(dāng)上次文件過小時,不啟動重寫,覆蓋之前的老日志,從而,確保AOF日志文件不會過大,保持跟redis內(nèi)存數(shù)據(jù)量一致.

    2、AOF持久化機(jī)制有哪些優(yōu)點(diǎn)?

    第一:更好的保護(hù)數(shù)據(jù)不丟失,AOF會每隔1秒,執(zhí)行一次fsync刷寫操作,最多丟失1秒鐘的數(shù)據(jù).

    第二:AOF日志文件通常以append-only模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,并且文件不容易破損,即使文件尾部破損,也很容易修復(fù)。

    第三:AOF日志文件過大的時候,出現(xiàn)后臺重寫操作,也不會影響客戶端的讀寫

    第四:AOF日志文件的命令通過易讀的方式進(jìn)行記錄,非常適合做災(zāi)難性的誤刪除的緊急恢復(fù),只要這個時候后臺rewrite還沒有發(fā)生,那么就可以立即拷貝AOF文件,將最后一條flushall命令給刪了,然后再將該AOF文件放回去,就可以通過恢復(fù)機(jī)制,自動恢復(fù)所有數(shù)據(jù).

    3、AOF持久化機(jī)制有哪些缺點(diǎn)?

    第一:對于同一份數(shù)據(jù)來說,AOF日志文件通常比RDB數(shù)據(jù)快照文件更大。

    第二:AOF開啟后,支持的寫QPS會比RDB支持的寫QPS低,因?yàn)锳OF一般會配置成每秒fsync一次日志文件,當(dāng)然,每秒一次fsync,性能也還是很高的。性能比rdb低

    第三:AOF這種基于命令日志方式,比基于RDB每次持久化一份完整的數(shù)據(jù)快照文件的方式,更加脆弱一些,容易有bug。不過AOF為了避免rewrite過程導(dǎo)致的bug,因此每次rewrite并不是基于舊的指令日志進(jìn)行merge的,而是基于當(dāng)時內(nèi)存中的數(shù)據(jù)進(jìn)行指令的重新構(gòu)建,這樣健壯性會好很多

    不適合做冷備

    4、如何選擇redis的持久化方式?

    第一:不要僅僅使用RDB,因?yàn)槟菢訒?dǎo)致你丟失很多數(shù)據(jù)。

    第二:也不要僅僅使用AOF,因?yàn)锳OF做冷備沒有RDB做冷備進(jìn)行數(shù)據(jù)恢復(fù)的速度快,并且RDB簡單粗暴的數(shù)據(jù)快照方式更加健壯。

    第三:綜合使用AOF和RDB兩種持久化機(jī)制,用AOF來保證數(shù)據(jù)不丟失,作為數(shù)據(jù)恢復(fù)的第一選擇; 用RDB來做不同程度的冷備。

    八、redis事務(wù)處理--樂觀鎖

    mysql中大多數(shù)都是悲觀鎖,redis中采用樂觀鎖

    它使用watch命令監(jiān)視給定的key,當(dāng)exec(提交事務(wù))的時候,如果監(jiān)視的key從調(diào)用watch后發(fā)生過變化,則整個事務(wù)會失敗--多個客戶端之間,都操作,但是只有一個成功了,多個客戶端都沒有阻塞。

    也可以調(diào)用watch多次監(jiān)視多個key。注意watch的key是對整個連接有效的,如果連接斷開,監(jiān)視和事務(wù)都會被自動清除。當(dāng)然exec,discard,unwatch命令都會清除連接中的所有監(jiān)視。

    redis的原子性:分怎么說,有的版本當(dāng)發(fā)生錯誤的時候,還會繼續(xù)輸入,但是后來又改了

    redis如何保證數(shù)據(jù)的可靠性:1.持久化,數(shù)據(jù)不容易丟 2.事務(wù)保證提交的數(shù)據(jù) 3.架構(gòu)的設(shè)計(jì),多個redis

    8.1基本指令

    redis進(jìn)行事務(wù)控制時,通常是基于如下指令進(jìn)行實(shí)現(xiàn),例如:

    • multi 開啟事務(wù)
    • exec 提交事務(wù)
    • discard 取消事務(wù)
    • watch 監(jiān)控,如果監(jiān)控的值發(fā)生變化,則提交事務(wù)時會失敗--使用樂觀鎖
    • unwatch 去掉監(jiān)控

    exec提交事務(wù):先開啟multi事務(wù)后,把指令放到一個隊(duì)列里面,只有提交exec的時候才會執(zhí)行指

    discard取消事務(wù):redis事務(wù)太簡單,沒有回滾,不會回到原來的數(shù)據(jù),而只有取消(遇到錯誤也取消),不執(zhí)行對列中的指令

    8.2秒殺、搶票--樂觀鎖

    基于一個秒殺,搶購案例,演示redis樂觀鎖方式

    客戶端1:

    127.0.0.1:6379> set ticket 1

    OK

    127.0.0.1:6379> set money 0

    OK

    127.0.0.1:6379> watch ticket #樂觀鎖,對值進(jìn)行觀察,改變則事務(wù)失敗

    OK

    127.0.0.1:6379> multi #開啟事務(wù)

    OK

    127.0.0.1:6379> decr ticket //票數(shù)-1

    QUEUED

    127.0.0.1:6379> incrby money 100 //沒有提交事務(wù)

    QUEUED

    第二步:打開客戶端2,執(zhí)行如下操作,演示還沒等客戶端1提交事務(wù),此時客戶端2把票買到了。

    127.0.0.1:6379> get ticket

    "1"

    127.0.0.1:6379> decr ticket

    (integer) 0//票搶到了 ticker變成了0 默認(rèn)自動提交事務(wù)

    第三步,回到客戶端1:提交事務(wù),檢查ticket的值

    127.0.0.1:6379> exec
    (nil) #提交事務(wù),失敗
    127.0.0.1:6379> get ticket
    “0”//只有一個人成功了,客戶端1和2都減1了,但是只有一個成功了,兩個執(zhí)行都沒有阻塞,樂觀鎖
    127.0.0.1:6379> unwatch #取消監(jiān)控

    九、多個redis架構(gòu)設(shè)計(jì)--主從、哨兵、集群高可用

    9.1Redis主從架構(gòu)(Master/Slave)

    單個Redis支持的讀寫能力還是有限的(redis便于查詢 讀),此時我們可以使用多個redis來提高redis的并發(fā)處理能力,這些redis如何協(xié)同,就需要有一定的架構(gòu)設(shè)計(jì)(讀寫架構(gòu)的設(shè)計(jì),讀多或者寫少),這里我們首先從

    主從架構(gòu)(Master/Slave)(一個master和多個slave)架構(gòu)進(jìn)行分析和實(shí)現(xiàn).,master負(fù)責(zé)讀寫,并將數(shù)據(jù)同步到salve,從節(jié)點(diǎn)負(fù)責(zé)讀操作.

    當(dāng)向master寫數(shù)據(jù),master扔一個rdb冷備給slave(全量同步--初始化時),第二次寫master就是發(fā)送aof--日志了,只是增量同步實(shí)現(xiàn)數(shù)據(jù)的同步(只備份更新的部分)

    9.2Redis哨兵模式

    master宕機(jī)了,把slave升級成master

    哨兵(Sentinel)是Redis的主從架構(gòu)模式下,實(shí)現(xiàn)高可用性(high availability)的一種機(jī)制。

    由一個或多個Sentinel實(shí)例(instance)組成的Sentinel系統(tǒng)(system)可以監(jiān)視任意多個主服務(wù)器,以及這些主服務(wù)器屬下的所有從服務(wù)器,并在被監(jiān)視的主服務(wù)器進(jìn)入下線狀態(tài)時,自動將下線主服務(wù)器屬下的某個從服務(wù)器升級為新的主服務(wù)器,然后由新的主服務(wù)器代替已下線的主服務(wù)器繼續(xù)處理命令請求。

    9.3Redis集群高可用

    面試題

    1、基礎(chǔ)

    14.接口和抽象類有什么區(qū)別?

    抽象類可以有 main 方法,并且我們能運(yùn)行它;接口不能有 main 方法。

    16.BIO、NIO、AIO 有什么區(qū)別?

    • BIO:Block IO 同步 阻塞式 IO,就是我們平常使用的傳統(tǒng) IO,它的特點(diǎn)是模式簡單使用方便,并發(fā)處理能力低。
    • NIO:New IO 同步 非阻塞 IO,是傳統(tǒng) IO 的升級,客戶端和服務(wù)器端通過 Channel(通道)通訊,實(shí)現(xiàn)了多路復(fù)用。
    • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實(shí)現(xiàn)了異步 非堵塞 IO ,異步 IO 的操作基于事件和回調(diào)機(jī)制。

    17、配置文件

    1、bootstrap的配置文件優(yōu)先加載application

    2、如果在不同的目錄中存在多個配置文件,它的讀取順序是:

    1、config/application.properties(項(xiàng)目根目錄中config目錄下)

    2、config/application.yml

    3、application.properties(項(xiàng)目根目錄下)

    4、application.yml

    如果同一個目錄下,有application.yml也有application.properties,默認(rèn)先讀取application.properties。但是yml的可讀性更高些,yml使用key:value 和縮進(jìn)形式,properties文件必須寫全路徑

    2、集合

    19. Collection 和 Collections 有什么區(qū)別?

    • java.util.Collection 是一個集合接口集合類的一個頂級接口)。
    • Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態(tài)方法,用于對集合中元素進(jìn)行排序、搜索以及線程安全等各種操作。

    21. HashMap 和 Hashtable 有什么區(qū)別?

    • hashTable同步的、安全的,而HashMap是非同步的,效率上比hashTable要高。
    • hashMap允許空鍵值(不安全),而hashTable不允許。
    • hashMap是數(shù)組+鏈表的結(jié)構(gòu)

    22. 如何決定使用 HashMap 還是 TreeMap?

    HashMap在Map中插入、刪除和定位元素這類操作

    TreeMap對一個有序的key集合進(jìn)行遍歷,是更好的選擇。

    24. 說一下 HashSet 的實(shí)現(xiàn)原理?

    • HashSet底層由HashMap實(shí)現(xiàn)
    • HashSet的值存放于HashMap的key上
    • HashMap的value統(tǒng)一為PRESENT

    25. ArrayList 和 LinkedList 的區(qū)別是什么?

    ArrrayList底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機(jī)訪問,

    LinkedList 的底層數(shù)據(jù)結(jié)構(gòu)是雙向循環(huán)鏈表,不支持隨機(jī)訪問。使用下標(biāo)訪問一個元素,ArrayList 的時間復(fù)雜度是 O(1),而 LinkedList 是 O(n)。

    26. 如何實(shí)現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?

    • List轉(zhuǎn)換成為數(shù)組:調(diào)用ArrayListtoArray方法。
    • 數(shù)組轉(zhuǎn)換成為List:調(diào)用Arrays的asList方法。

    27. ArrayList 和 Vector 的區(qū)別是什么?

    • Vector是同步的、安全的
    • ArrayList比Vector快,它因?yàn)橛型?#xff0c;不會過載。
    • ArrayList更加通用,因?yàn)槲覀兛梢允褂肅ollections工具類輕易地獲取同步列表和只讀列表。

    28. Array 和 ArrayList 有何區(qū)別?

    • Array可以容納基本類型和對象,而ArrayList只能容納對象。
    • Array是指定大小后不可變的,而ArrayList大小是可變的。
    • Array沒有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

    3、線程

    線程和進(jìn)程的區(qū)別:

    進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單元

    線程是操作系統(tǒng)進(jìn)行任務(wù)分配的最小單元,線程隸屬于進(jìn)程

    41. sleep() 和 wait() 有什么區(qū)別?

    sleep():方法是線程類(Thread)的靜態(tài)方法,讓調(diào)用線程進(jìn)入睡眠狀態(tài),不釋放鎖機(jī)制,其他線程依然無法訪問這個對象。

    wait():wait()是Object類的方法,當(dāng)一個線程執(zhí)行到wait方法時,它就進(jìn)入到一個和該對象相關(guān)的等待池,釋放對象的機(jī)鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程

    42. notify()和 notifyAll()有什么區(qū)別?--都是object中的方法

    當(dāng)有線程調(diào)用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機(jī)喚醒一個 wait 線程),被喚醒的的線程便會進(jìn)入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。

    假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調(diào)用 wait()方法,它才會重新回到等待池中。

    43. 線程的 run()和 start()有什么區(qū)別?

    start()方法來啟動一個線程,真正實(shí)現(xiàn)了多線程運(yùn)行。這時無需等待run方法體代碼執(zhí)行完畢,可以直接繼續(xù)執(zhí)行下面的代碼;

    用run()方法必須等待run()方法執(zhí)行完畢才能執(zhí)行下面的代碼,所以執(zhí)行路徑還是只有一條,根本就沒有線程的特征

    44. 創(chuàng)建線程池有哪幾種方式?

    兩類:通過Excutors創(chuàng)建、另一類:通過ThreadPoolExecutor創(chuàng)建--最原始的創(chuàng)建線程池方式

    ①. new Fixed ThreadPool(int nThreads)創(chuàng)建一個固定長度的線程池,每當(dāng)提交一個任務(wù)就創(chuàng)建一個線程,直到達(dá)到線程池的最大數(shù)量,這時線程規(guī)模將不再變化,當(dāng)線程發(fā)生未預(yù)期的錯誤而結(jié)束時,線程池會補(bǔ)充一個新的線程。

    ②. new Cached ThreadPool()創(chuàng)建一個可緩存的線程池,如果線程池的規(guī)模超過了處理需求,將自動回收空閑線程,而當(dāng)需求增加時,則可以自動添加新線程,線程池的規(guī)模不存在任何限制。

    ③. new Single ThreadExecutor()這是一個單線程的Executor,它創(chuàng)建單個工作線程來執(zhí)行任務(wù),如果這個線程異常結(jié)束,會創(chuàng)建一個新的來替代它;它的特點(diǎn)是能確保依照任務(wù)在隊(duì)列中的順序來串行執(zhí)行。

    ④. new Scheduled ThreadPool(int corePoolSize)創(chuàng)建了一個固定長度的線程池,而且以延遲或定時的方式來執(zhí)行任務(wù),類似于Timer。

    ⑤:new SingleThread ScheduledExecutor:創(chuàng)建一個單線程的可以執(zhí)行延遲任務(wù)的線程池;

    ⑥:new WorkStealing Pool:創(chuàng)建一個搶占式執(zhí)行的線程池(任務(wù)執(zhí)行順序不確定)JDK 1.8 添加

    ⑦:ThreadPoolExecutor:最原始的創(chuàng)建線程池的方式

    45. 線程池都有哪些狀態(tài)?

    線程池有5種狀態(tài):Running、ShutDown、Stop、Tidying、Terminated。

    線程池各個狀態(tài)切換框架圖:

    46.線程池的參數(shù):

    core Pool Size 線程池核心線程大小 一個最小的線程數(shù)量,

    maxi mum PoolSize 線程池最大線程數(shù)量 一個最大線程數(shù)量的限制

    workQueue 工作隊(duì)列 新任務(wù)被提交后,會先進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時再從隊(duì)列中取出任務(wù)

    keepAliveTime 空閑線程存活時間

    unit 空閑線程存活時間單位

    threadFactory 線程工廠

    handler 拒絕策略

    46. 線程池中 submit()和 execute()方法有什么區(qū)別?

    接收的參數(shù)不一樣submit有返回值,而execute沒有,submit方便Exception處理

    47. 在 java 程序中怎么保證多線程的運(yùn)行安全?

    線程安全在三個方面體現(xiàn):

    原子性:提供互斥訪問,同一時刻只能有一個線程對數(shù)據(jù)進(jìn)行操作,(atomic,synchronized);

    可見性:一個線程對主內(nèi)存的修改可以及時地被其他線程看到,(synchronized,volatile);

    有序性:一個線程觀察其他線程中的指令執(zhí)行順序,由于指令重排序,該觀察結(jié)果一般雜亂無序,(happens-before原則)。

    48. 多線程鎖的升級原理是什么?

    在Java中,鎖共有4種狀態(tài),級別從低到高依次為:無狀態(tài)鎖,偏向鎖,輕量級鎖和重量級鎖狀態(tài),這幾個狀態(tài)會隨著競爭情況逐漸升級。鎖可以升級但不能降級。

    49. 什么是死鎖?

    死鎖是指兩個或兩個以上的進(jìn)程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。

    50. 怎么防止死鎖?

    死鎖的四個必要條件:

    互斥條件:進(jìn)程對所分配到的資源不允許其他進(jìn)程進(jìn)行訪問,若其他進(jìn)程訪問該資源只能等待,直至占有該資源的進(jìn)程使用完成后釋放該資源

    請求和保持條件:進(jìn)程獲得一定的資源之后,又對其他資源發(fā)出請求,但是該資源可能被其他進(jìn)程占有,此事請求阻塞,但又對自己獲得的資源保持不放

    不可剝奪條件:是指進(jìn)程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放

    環(huán)路等待條件:是指進(jìn)程發(fā)生死鎖后,若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系

    這四個條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發(fā)生死鎖。

    52.說一下 synchronized 底層實(shí)現(xiàn)原理?

    synchronized保證方法或者代碼塊在運(yùn)行時,同一時刻只有一個方法可以進(jìn)入到臨界區(qū),同時它還可以保證共享變量的內(nèi)存可見性(只有一個人看)。

    Java中每一個對象都可以作為鎖,synchronized實(shí)現(xiàn)同步的基礎(chǔ):

    普通同步方法,鎖是當(dāng)前實(shí)例對象

    靜態(tài)同步方法,鎖是當(dāng)前類的class對象

    同步方法塊,鎖是括號里面的對象

    53. synchronized 和 volatile 的區(qū)別是什么?

    volatile關(guān)鍵字,只能修飾屬性,標(biāo)記字段可能被多個線程訪問(異步),保證線程可見性,不保證原子性

    synchronized則是鎖定當(dāng)前變量、方法、類,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。

    保證變量的修改可見性和原子性。

    volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。

    54. synchronized 和 Lock 有什么區(qū)別?

    首先synchronized是java內(nèi)置關(guān)鍵字,在jvm層面,Lock是個java類

    synchronized無法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖;

    synchronized會自動釋放鎖(a?線程執(zhí)行完同步代碼會釋放鎖 ;b 線程執(zhí)行過程中發(fā)生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖

    用synchronized關(guān)鍵字的兩個線程1和線程2,如果當(dāng)前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不一直等待就結(jié)束了;

    synchronized的鎖可重入、不可中斷、非公平,Lock鎖可重入、可判斷、可公平(兩者皆可);

    Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。

    55. synchronized 和 ReentrantLock 區(qū)別是什么?

    synchronized是關(guān)鍵字,ReentrantLock是類

    ReentrantLock可以對獲取鎖的等待時間進(jìn)行設(shè)置,這樣就避免了死鎖?

    ReentrantLock可以獲取各種鎖的信息,可以靈活地實(shí)現(xiàn)多路通知?

    鎖機(jī)制不同:ReentrantLock底層調(diào)用的是Unsafe的park方法加鎖,synchronized操作的應(yīng)該是對象頭中mark word。

    4、反射

    通過字節(jié)碼對象,通過反射,獲取構(gòu)造方法,成員方法,成員變量等信息。

    還可以通過反射創(chuàng)建對象

    暴力反射Declared獲取私有屬性,私有方法

    57. 什么是反射?

    反射主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力

    Java反射:

    在Java運(yùn)行時環(huán)境中,對于任意一個類,知道這個類有哪些屬性和方法,對于任意一個對象,能否調(diào)用它的任意一個方法

    Java反射機(jī)制主要提供了以下功能

    在運(yùn)行時判斷任意一個對象所屬的類,類所具有的成員變量和方法。調(diào)用任意一個對象的方法。

    58. 什么是 java 序列化?什么情況下需要序列化?

    分為序列化和反序列化,將我們的對象保存在磁盤當(dāng)中,然后再從磁盤中讀取對象。

    基于序列化和反序列化的一個應(yīng)用-對象的克隆

    什么情況下需要序列化:

    a)當(dāng)你想把的內(nèi)存中的對象狀態(tài)保存到一個文件中或者數(shù)據(jù)庫中時候;

    b)當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時候;c)當(dāng)你想通過RMI傳輸對象的時候;

    59. 動態(tài)代理是什么?有哪些應(yīng)用?

    動態(tài)代理:當(dāng)想要給實(shí)現(xiàn)了某個接口的類中的方法,加一些額外的處理。比如說加日志,加事務(wù)等。就是創(chuàng)建一個新的類,這個類不僅包含原來類方法的功能,還添加了額外處理的。這個代理類并不是定義好的,是動態(tài)生成的。具有解耦意義,靈活,擴(kuò)展性強(qiáng)。

    動代理的應(yīng)用:Spring的AOP、加事務(wù),加權(quán)限,加日志

    60. 怎么實(shí)現(xiàn)動態(tài)代理?

    首先必須定義一個接口,還要有一個InvocationHandler(將實(shí)現(xiàn)接口的類的對象傳遞給它)處理類。再有一個工具類Proxy(習(xí)慣性將其稱為代理類,因?yàn)檎{(diào)用他的newInstance()可以產(chǎn)生代理對象,其實(shí)他只是一個產(chǎn)生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進(jìn)制碼,利用加載器加載,并將其實(shí)例化產(chǎn)生代理對象,最后返回。

    5、Cookies、Session

    67. session 和 cookie 有什么區(qū)別?

    Session.典型的場景比如購物車,當(dāng)你點(diǎn)擊下單按鈕時,由于HTTP協(xié)議無狀態(tài),并不知道是哪個用戶操作的,所以服務(wù)端創(chuàng)建了特定的Session,用用于標(biāo)識這個用戶,并且跟蹤用戶,這樣才知道購物車?yán)锩嬗袔妆緯?。這個Session是保存在服務(wù)端的,有一個唯一標(biāo)識sessionId。Session 信息都是放在內(nèi)存的。

    思考一下服務(wù)端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發(fā)送相應(yīng)的Cookie信息到服務(wù)端。實(shí)際上大多數(shù)的應(yīng)用都是用 Cookie 來實(shí)現(xiàn)Session跟蹤的第一次創(chuàng)建Session的時候,服務(wù)端會在HTTP協(xié)議中告訴客戶端,需要在 Cookie 里面記錄一個Session ID,以后每次請求把這個會話ID發(fā)送到服務(wù)器,我就知道你是誰了。

    用戶登錄信息賬號可以寫到Cookie里面,方便登錄,Session是在服務(wù)端保存的一個數(shù)據(jù)結(jié)構(gòu),用來跟蹤用戶的狀態(tài),這個數(shù)據(jù)可以保存在集群、數(shù)據(jù)庫、文件中;

    Cookie是客戶端保存用戶信息的一種機(jī)制,用來記錄用戶的一些信息,也是實(shí)現(xiàn)Session的一種方式。

    session存儲的是在服務(wù)器上面的 cookie是存放在客戶端上的(也就是所謂的瀏覽器上)

    1. Session稱為“會話控制"

    2. Session可以存儲用戶信息.

    3. Session數(shù)據(jù)的生命周期是整個會話,如果會話關(guān)閉 則數(shù)據(jù)清空.

    4. Session的數(shù)據(jù)結(jié)構(gòu) 是key-value結(jié)構(gòu).

    1. Cookie是一個小型文本文件,存儲到本地終端上.

    2. Cookie可以存儲用戶信息.

    3. Cookie的數(shù)據(jù)類型key-value

    4. Cookie中的數(shù)據(jù)一般采用加密的方式保存

    5. Cookie的數(shù)據(jù)可以"永久"保存.

    Session和Cookie選擇

    1.如果數(shù)據(jù)需要臨時保存,則選用Session, 如果數(shù)據(jù)需要長時間存儲選用Cookie.

    2.如果對于安全性要求較高的數(shù)據(jù),選用Session,如果安全性要求不高選用Cookie.

    問題: 財(cái)務(wù)系統(tǒng)用戶信息選用什么技術(shù)保存信息? 選用session.

    68. 說一下 session 的工作原理?

    session是一個存在服務(wù)器上的類似于一個散列表格的文件。類似于一個大號的map,里面的鍵存儲的是用戶的sessionid,用戶向服務(wù)器發(fā)送請求的時候會帶上這個sessionid。這時就可以從中取出對應(yīng)的值了。

    69. 如果客戶端禁止 cookie 能實(shí)現(xiàn) session 還能用嗎?不能

    Cookie與 Session,一般認(rèn)為是兩個獨(dú)立的東西,Session采用的是在服務(wù)器端保持狀態(tài)的方案,Cookie采用的是在客戶端保持狀態(tài)的方案。但因?yàn)镾ession是用Session ID來確定當(dāng)前對話所對應(yīng)的服務(wù)器Session,而Session ID是通過Cookie來傳遞的,禁用Cookie相當(dāng)于失去了Session ID,也就得不到Session了。

    71. 如何避免 sql 注入?

    sql語句中出現(xiàn)了特殊符號#,改變了SQL語句

    獲取新的傳輸器:Prepare Statement(簡單又有效的方法)

    使用正則表達(dá)式過濾傳入的參數(shù)

    字符串過濾

    6、異常

    74. throw 和 throws 的區(qū)別?

    throw則是指拋出的一個具體的異常類型。

    throws是用來聲明一個方法可能拋出的所有異常信息,不處理異常、異常往上傳,誰調(diào)誰處理

    75. final、finally、finalize 有什么區(qū)別?

    • final關(guān)鍵字用來修飾類、變量、方法--表示最終
    • finally關(guān)鍵字一般作用在try-catch代碼塊中,將一定要執(zhí)行的代碼方法finally代碼塊中,
    • finalize是一個方法,屬于Object類的一個方法,一般由垃圾回收器來調(diào)用,當(dāng)我們調(diào)用System的gc()方法的時候,由垃圾回收器調(diào)用finalize(),回收垃圾。

    76. try-catch-finally 中哪個部分可以省略?

    答:catch 可以省略

    原因:

    定義普通異常的時候需要捕獲catch,進(jìn)行進(jìn)一步處理。

    運(yùn)行時異常,catch就可以省略

    至于加上finally,則是在不管有沒捕獲異常,都要進(jìn)行的“掃尾”處理。

    77、Error和Exception都是繼承于Throwable

    error是系統(tǒng)錯誤

    • Error類一般是指與虛擬機(jī)相關(guān)的問題,如系統(tǒng)崩潰,虛擬機(jī)錯誤,內(nèi)存空間不足,方法調(diào)用棧溢出等。如java.lang.StackOverFlowError和Java.lang.OutOfMemoryError。對于這類錯誤,Java編譯器不去檢查他們。對于這類錯誤的導(dǎo)致的應(yīng)用程序中斷,僅靠程序本身無法恢復(fù)和預(yù)防,遇到這樣的錯誤,建議讓程序終止
    • Exception類表示程序可以處理的異常,可以捕獲且可能恢復(fù)。遇到這類異常,應(yīng)該盡可能處理異常,使程序恢復(fù)運(yùn)行,而不應(yīng)該隨意終止異常。
    • Exception又分為運(yùn)行時異常(Runtime Exception)和受檢查的異常(Checked Exception )。
    • RuntimeException:其特點(diǎn)是Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時,即使沒有用try……catch捕獲,也沒有用throws拋出,還是會編譯通過,如除數(shù)為零的ArithmeticException、錯誤的類型轉(zhuǎn)換、數(shù)組越界訪問和試圖訪問空指針等。處理RuntimeException的原則是:如果出現(xiàn)RuntimeException,那么一定是程序員的錯誤。
    • 受檢查的異常(IOException等):這類異常如果沒有try……catch也沒有throws拋出,編譯是通不過的。這類異常一般是外部錯誤,例如文件找不到、試圖從文件尾后讀取數(shù)據(jù)等,這并不是程序本身的錯誤,而是在應(yīng)用環(huán)境中出現(xiàn)的外部錯誤。

    數(shù)據(jù)庫

    78、數(shù)據(jù)庫中存儲的數(shù)據(jù)類型有哪些

    varchar、int、date、text

    Mysql中的

    整數(shù)類型:int、big int、small int、Tiny int 、bit、bool、medium int

    int(m)里的m是表示SELECT查詢結(jié)果集中的顯示寬度,并不影響實(shí)際的取值范圍

    浮點(diǎn)數(shù)類型:float、double、decimal

    浮點(diǎn)型在數(shù)據(jù)庫中存放的是近似值,而定點(diǎn)類型在數(shù)據(jù)庫中存放的是精確值。

    字符串類型:char、varchar、text、tinytext、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB二進(jìn)制數(shù)據(jù)(_Blob)TEXT以文本方式存儲,英文存儲區(qū)分大小寫,而_Blob是以二進(jìn)制方式存儲,不分大小寫。

    日期類型:Date、DateTime、TimeStamp、Time、Year

    其他數(shù)據(jù)類型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等

    redis數(shù)據(jù)庫中的數(shù)據(jù)類型

    string字符串:統(tǒng)計(jì)網(wǎng)站訪問數(shù)量,當(dāng)前在線人數(shù),文章字?jǐn)?shù)

    散列hash:是一個鍵值(key=>value)對集合,存儲對象,讀取、修改用戶屬性(name,age,pwd等)

    列表list:可重復(fù)、有序,簡單的字符串列表實(shí)現(xiàn)熱銷榜,最新評論

    集合set :不可重復(fù)、無序,利用交集求共同好友。利用唯一性,可以統(tǒng)計(jì)訪問網(wǎng)站的所有獨(dú)立IP

    有序集合zset:不可重復(fù),zset 和 set 一樣也是string類型元素的集合,大型在線游戲的積分排行榜

    79.Redis的五種數(shù)據(jù)類型和適用的場景

    1.String(數(shù)據(jù)緩存)

    2.Hash(購物車 單點(diǎn)登錄 用戶信息 登錄信息)

    3.List(評論 評分 有序可重)

    4.Set(共同好友 無序不重)

    5.Zset(最熱商品)

    80.什么是緩存穿透?如何避免?什么是緩存雪崩?何如避免?

    緩存穿透?一般的緩存系統(tǒng),都是按照key去緩存查詢,如果不存在對應(yīng)的value,就應(yīng)該去后端系統(tǒng)查找(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對后端系統(tǒng)造成很大的壓力。這就叫做緩存穿透。

    如何避免?對查詢結(jié)果為空的情況也進(jìn)行緩存,緩存時間設(shè)置短一點(diǎn),或者該key對應(yīng)的數(shù)據(jù)insert了之后清理緩存。 2:對一定不存在的key進(jìn)行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

    緩存雪崩?當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給后端系統(tǒng)帶來很大壓力。導(dǎo)致系統(tǒng)崩潰。

    如何避免?

    1:在緩存失效后,通過加鎖或者隊(duì)列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。
    2:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設(shè)置為短期,A2設(shè)置為長期
    3:不同的key,設(shè)置不同的過期時間,讓緩存失效的時間點(diǎn)盡量均勻。

    81.Redis實(shí)現(xiàn)分布式鎖

    Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關(guān)系Redis中可以使用SETNX命令實(shí)現(xiàn)分布式鎖。
    將 key 的值設(shè)為 value ,當(dāng)且僅當(dāng) key 不存在。 若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作

    82.有沒有嘗試進(jìn)行多機(jī)redis 的部署?如何保證數(shù)據(jù)一致的?

    主從復(fù)制,讀寫分離
    一類是主數(shù)據(jù)庫(master)一類是從數(shù)據(jù)庫(slave),主數(shù)據(jù)庫可以進(jìn)行讀寫操作,當(dāng)發(fā)生寫操作的時候自動將數(shù)據(jù)同步到從數(shù)據(jù)庫,而從數(shù)據(jù)庫一般是只讀的,并接收主數(shù)據(jù)庫同步過來的數(shù)據(jù),一個主數(shù)據(jù)庫可以有多個從數(shù)據(jù)庫,而一個從數(shù)據(jù)庫只能有一個主數(shù)據(jù)庫。
    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的复习资料整合的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。