复习资料整合
1、設計模式
代理模式--Spring中的AOP
工廠模式--BeanFactory所有的對象都是通過Factory
模板方法設計模式--RestTemplate --
策略設計模式---算法--策略--輪詢等--LoadBalancerClient--還有Ribbon
單例設計模式--Singleton--餓漢式:小對象,頻繁加載
懶漢式:用的時候加載對象,加鎖
門面設計模式--Slf4j 日志的查看
適配器模式--XxxAdapter 適配器接口繼承某個適配器,就不用實現全部的方法了,用哪個實現哪個就可以了
責任鏈模式--Interceptor 比如攔截器鏈等等
雙重校驗的單例設計--很多地方都用到了
享元設計模式---所有的池都是,通過池減少對象的創建次數
建造者模式,過濾器模式,
2、常見的工廠模式
1、簡單工廠模式:每增加一款汽車都需要修改工廠類。違背開閉原則
2、工廠方法模式:只負責生產單一產品。避免簡單工廠模式的缺點。新增一款汽車只需要新建一家工廠即可。符合開閉原則
3、抽象工廠模式:工廠負責生產旗下多個產品(產品族)打比方:一個超級工廠里面分小工廠,工廠1生產輪胎、工廠2生產方向盤、工廠3生產:底盤等等。新增汽車工廠實現此接口即可
抽象工廠模式會使用上:需要生產公司旗下的一系列產品(產品族)。同時其它公司旗下也會生產同樣一系列產品(可以理解成是共同抽象出來的部分)。只是兩個工廠生產出來品牌不一樣。
抽象工廠模式常見應用:oracle和mysql數據庫應用時,系統可以會替換數據庫,這時候抽象工廠模式就派上用場了。?
2、數據結構
1、數組;是有序的元素序列,數組是在內存中開辟一段連續的空間,并在此空間存放元素。查找快,增刪慢。
就像是一列火車,從1號車廂到最后一節車廂,每節車廂都有自己的固定編號,乘坐火車的人可以通過車廂號快速找到自己的位置。
2、鏈表,是一系列Node節點組成,每個節點包括兩部分(存儲數據元素的數據域,存儲下一個節點地址的指針域),多個節點之間通過地址進行連接
一種遞歸的數據結構;單向鏈表和雙向鏈表兩種;不需要初始化容量、可以添加任意元素;
插入和刪除的時候只需要更新引用。
8、哈希表Hash Table:也叫散列表,通過(key-value)數據結構,可以快速實現查找、插入和刪除。
3、堆:可以被看做是一棵樹的數組對象
- 堆中某個節點的值總是不大于或不小于其父節點的值;
- 堆總是一棵完全二叉樹。
3、棧Stack,它是運算受限的線性表,其限制是僅允許在標的一端進行插入和刪除操作,不允許在其他任何位置進行添加、查找、刪除等操作。
按照“先進后出”的原則來存儲數據;像一個水桶 都是線性表
壓棧:就是存元素 彈棧:就是取元素。
4、隊列queue;先進先出,也是一種運算受限的線性表,其限制是僅允許在表的一端進行插入,而在表的另一端進行刪除。像一個水管,隊頭只允許刪除操作(出隊),隊尾只允許插入操作(入隊)。
5、樹,是由 n(n>0)個有限節點組成的一個具有層次關系的集合;
分為:無序樹,二叉樹,滿二叉樹,二叉樹查找樹,紅黑樹
二叉樹BinaryTree:每個節點不超過2的有序樹(tree)
我們可以簡單理解成生活的樹的結構,只不過每個節點上都最多只能有兩個子節點。
頂上的叫根節點,兩邊被稱作“左子樹”和“右子樹”。
紅黑樹:紅黑樹是最常見的平衡二叉樹(左右要實現“平衡,兩邊的子樹相等”)本身就是一顆二叉查找樹,平衡二叉樹的難點在于,當刪除或者增加節點的情況下,如何通過左旋或者右旋的方式來保持左右平衡。
樹的鍵值仍然是有序的。紅黑樹的速度特別快,趨近平衡樹,查找葉子元素最少和最多次數不多于二倍
紅黑樹的特點:
- 節點可以是紅色的或者黑色的
- 根節點是黑色的
- 葉子節點(特指空節點)是黑色的
- 如果一個節點是紅色的,則它兩個子節點都是黑色的。也就是說在一條路徑上不能出現相鄰的兩個紅色節點。
- 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
7、圖;圖是一種復雜的非線性結構,有頂點和有窮非空集合和頂點之間的集合組成,通常表示為G(V,E) ,其中G表示一個圖,V是圖G中 頂點的集合,E是圖G中 邊的集合
3、代理
1.1代理對象
JDK動態代理
特點:
1.要求被代理者必須實現(有)接口.
2.JDK代理是jdk默認提供的.
默認情況下采用jdk實現aop,也可以強制使用CGlib
CGLIB動態代理
特點:
1.不管被代理者是否有接口,都可以為其創建代理對象. 代理對象是目標對象的子類.
2.cglib需要手動導入jar包
3.spring為了創建代理對象方便,自身自動添加cglib依賴項
CGLIB的大部分類是直接對Java字節碼進行操作,這樣生成的類會在Java的永久堆中。如果動態代理操作過多,容易造成永久堆滿,觸發OutOfMemory內存溢出異常
spring默認使用jdk動態代理,如果類沒有接口,則使用cglib。Spring會自動在JDK動態代理和cglib之間轉換
1.2代理機制
反向代理---nginx
nginx是 一個高性能的HTTP 和 反向代理web服務器
ngnix有wendows版本,也有linux版本
在高連接并發的情況下,Nginx是Apache服務器不錯的替代品。
正向代理--路由vpn
方法的重寫
語法規則:兩同 兩小 一大
兩同:方法名相同,參數列表相同
一大:(子類方法的修飾符范圍>=父類的修飾符范圍--指的是訪問控制符)
兩小:子類方法的返回值類型<=父類方法的返回值類型(是引用類型的返回值是繼承關系的大小)(void和基本數據類型,返回類型必須保持一致)
 子類方法拋出的異常類型<=父類的異常類型
重寫和重載的區別
方法的重載和重寫都是實現多態的方式,區別在于前者實現的是編譯時的多態性,而后者實現的是運行時的多態性。
重載發生在一個類中,同名的方法如果有不同的參數列表(參數類型不同、參數個數不同或者二者都不同)則視為重載;
重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的參數列表,有兼容的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求,不能根據返回類型進行區分。
 ?
IO流
序列化:ObjectOutputStream
將對象轉化為字節/Json字符串 保存在磁盤文件中
反序列化:ObjectInputStream
反序列化流對象來讀取恢復對象
將字節/Json字符串從磁盤文件中取出,重新恢復成對象
*每次反序列化時,拿之前序列化生成的UID作比較,一致時才能反序列成功
 *解決方案1:一次序列化對應一次反序列化操作
 *解決方案2:將UID寫死,無論怎么修改Student類,UID都不變*/
private static final long serialVersionUID=1L;
4.1輸入流、輸出流
字節流:按8位傳輸以字節 為單位輸入輸出數據
字節輸入流:FileInputStream文件字節輸入流 BufferedInputStream高效字節輸入流
字節輸出流:FileOuputStream 、BufferOutputStream
字符流按照16位傳輸以字符為單位 輸入輸出數據
字符輸入流:FileReader文件字符輸入流、BufferedReader高效字符輸入流
字符輸出流:FileWriter、BufferWriter
Buffered為什么高效:Buffered實質是通過內部一個緩存數組來實現“緩沖區”的功能。BufferedInputStream是緩沖輸入流,它繼承于FilterInputStream。
BufferedInputStream的read()里面就寫好了類似于byte[] buf = new byte[1024];的方法,以至于它可以一下子讀到的字節比FileInputStream一下子讀到的字節多
BufferedInputStream 的好處之一就是能夠提高效率,是因為存在緩存空間,就比如一個中轉倉庫,當倉庫滿了的時候就把倉庫的物品一次搬移。緩存空間數據滿了之后就讀取一次把數據取走。
FileInputStream是一直讀取流中數據,這樣相比于BufferedInputStream更加耗費CPU,效率也就下降了
編碼轉換流:
輸出流 outputStreamWriter
輸入流 InputStreamReader
使用字符流:完全和輸入的字符保持一致
字節流:結果是二進制文件:a是00 00 00 05,int是四個字節 b是01,布爾是一個字節
 c是00 47,char是兩個字節
JVM
java虛擬機
- Java虛擬機JVM 管理的內存包括5個運行時數據內存:方法區、虛擬機棧、本地方法棧、堆、程序計數器,其中方法區和堆是由線程共享的數據區,其他幾個是線程隔離的數據區
- JVM垃圾回收,年青代(Young)(分為伊甸園區,存活區)、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法(復制算法、標記清除算法、標記整理算法)。
內存溢出和內存泄露
內存泄漏 Memory Leak 是指程序申請內存后,無法釋放已申請的資源,一次沒關系,但是內存泄漏次數多了就會導致內存溢出OutOfMemory。new了對象,但是不歸還delete,產生堆積;
 未清空對象的引用,或關流
②:垃圾回收
③:死循環問題,不結束
④:線程等待問題,線程溢出,無限遞歸調用
堆溢出:堆和棧的設置過小,過程中不停的創建對象,都會引起堆溢出
棧溢出:遞歸的調用過深
1.內存中加載的數據量過于龐大,如一次從數據庫取出過多數據;
 2.集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;
 3.代碼中存在死循環或循環產生過多重復的對象實體;
 4.使用的第三方軟件中的BUG;
 5.啟動參數內存值設定的過小
6、本質原因是創建了太多的線程,而能創建的線程數是有限制的,導致了這種異常的發生
7、并行或者并發回收器在GC回收時間過長
解決方法
異常
FileNotFoundException-------文件找不到異常--編譯異常,需要在編寫時就聲明
classNotFoundException------類無法加載異常 、類文件未找到異常
java.lang.Stack Over flowError --棧溢出異常---遞歸的內存泄漏異常
Null Pointer Exception-----------空指針異常
ClassCastException--------------類轉換異常 (強轉的時候)
Index OutOf BoundsException---下標越界異常
Arithmetic Exception-------------算數異常
SQLException-------操作數據庫異常
IOException--------輸入輸出異常,是失敗或中斷的I/O操作生成的異常的通用類。
NumberFormatException--------字符串格式轉換異常
SecurityException----------------由安全管理器拋出的異常,指示存在安全侵犯。
JDK JRE JVM 三者之間的關系
JDK java開發工具包--包含JRE+開發工具
開發java程序最小的環境為JDK,所以JDK是JAVA語言的核心,
JRE java運行環境--包含JVM+運行java程序必須的環境
運行java程序最小的環境為JRE
JVM java虛擬機--負責加載class并運行.class文件
將JAVA代碼轉換為對應的操作系統可以理解的指令,可以運行字節碼文件,可以跨平臺的核心部分
ajax請求的參數
url:發送請求的地址
type:請求方式post/get。默認是get
contentType:文本類型
data:
dataType:傳輸的數據類
success:請求成功要執行的代碼
error:
timeout:設置請求超時時間
async:默認true--所有的請求都是異步,如果要同步請求,false,加鎖鎖住瀏覽器
cache:會不會從瀏覽器緩存中加載請求信息
VUE 生命周期函數
概念: 生命周期函數,是VUE針對與用戶提供的擴展的功能.如果編輯了生命周期函數,則vue對象自動執行,無需手動調用.
生命周期函數種類:
1. 初始化階段 beforeCreate創建前 created實例創建完成后直接調用
beforeMount掛載前 mounted--VUE對象真正的實例化,開始干活了
2. 使用:Vue對象的修改 beforeUpdate, updated
3. 對象銷毀 beforeDestroy destroyed
2.2生命周期函數難點講解(了解)
beforeCreate
官網說明: 在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用。
解析: VUE對象被JS剛解析之后,實例化成功. 內部的屬性暫時都為null.
created
官方說明: 在實例創建完成后被立即調用
解析: VUE對象開始加載其中的屬性和屬性的值,當加載完成,對象實例化成功!!! 僅限于創建不執行業務操作.
beforeMount
官方說明: 在掛載開始之前被調用:相關的 render 函數首次被調用。
解析: vue對象中 el: “#app”, 通過app指定的ID,將指定的區域交給Vue對象進行管理.
mounted
官方說明: 實例被掛載后調用,這時 el 被新創建的 vm.$el 替換了。
解析: 當對象創建完成之后,并且指定區域開始 “渲染”,將區域中的標簽/表達式進行解析加載. 當數據加載成功之后,這時mounted執行完成.這時用戶可以看到解析后的頁面.
事務
事務就是將一堆的SQL語句(通常是增刪改操作)綁定在一起執行,要么都執行成功,要么都執行失敗
事務4個特性ACID
原子性(Atomicity,或稱不可分割性)綁定到一起,要么全成功,要么全失敗。
一致性(Consistency)多個系統中,保證數據是一致的
隔離性(Isolation,又稱獨立性)保證性能的同時(高并發),隔離用戶操作
持久性(Durability)持久影響的,對數據的修改是永久的,系統故障也不會丟失
· 開啟事務:start transaction;BEGIN;
· 結束事務:commit;(提交事務)或rollback(回滾事務操作之前)。
隔離級別
1、讀未提交:Read uncommitted 效率高,安全性最差,可能發生并發數據問題,可以讀到比人未提交的數據
2、讀提交(read committed) 犧牲了效率提高了安全性,Oracle默認的隔離級別
3、可重復讀(repeatable read)MySQL默認的隔離級別,安全性較好,性能一般,產生緩存,不能看到你后開啟的事務內容--多次讀同一數據,讀到的數據是相同的。防止臟讀、不可重復讀,容易產生幻讀
當隔離級別設置為Repeatable read 時,可以避免不可重復讀。當singo拿著工資卡去消費時,一旦系統開始讀取工資卡信息(即事務開始),singo的老婆就不可能對該記錄進行修改,也就是singo的老婆不能在此時轉賬。
4、串行化(Serializable) 表級鎖,讀寫都加鎖,效率低下,安全性高,不能并發--解決幻讀
mandatory
never--從不創建實物
not-supported
supports--
required--創建新事物
required -new
nested---
事務注解@Transactional的參數
BeanFactory和FactoryBean的區別
區別:BeanFactory是個Factory,也就是IOC容器或對象工廠, 它負責生產和管理bean的一個工廠
FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的 工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似
Servlet
tomcat(Web服務連接器) 里面有多個servlet模塊,servlet里面有xml文件中servlet name 然后,有個class name,通過反射實例化
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:傳輸協議//本機的IP地址localhost:數據庫的端口號/數據庫的名稱
注冊Driver驅動class.forName--通過反射
獲取鏈接對象DriverManager.getConnection,訪問的url和用戶名、密碼傳進來
得到prepare Statement傳輸器,將sql骨架語句放進來---用sql骨架,用問好代替參數
執行sql,更新;executeUpdate()
處理結果集——查才處理,
關閉流close
描述AOP
①:AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構。面向對象解決了層次的問題,比如下級繼承上級來增強上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點是通過代理方式對目標類在運行期間織入功能
面向切面的編程(AOP)實現了橫切關注的模塊化 避免代碼纏繞 削除代碼分散
②:AOP的三要素:切面、通知、切點
@Aspect切面:包括了切入點和advice---基本上是個類
JoinPoint連接點:程序中的一個點,例如方法的調用或者排除異常,每一個方法都是一個連接點,
@pointcut 切點:xml文件或者注解,選擇一個或者多個連接點表達式---在什么時候執行
Advice(通知):在選擇的每個連接點執行的代碼--基本是個方法
編織:將切面與主要代碼進行結合的技術,spring就提供了,就是把所有整合在了一起,將指定位置注入
③:通知的用法:
日志與跟蹤 事務管理 安全 緩存 錯誤處理 性能監測 自定義業務規則
前置Advice
@Before如果advice拋出異常,則目標方法不被執行---關心方法訪問權限限制
后置Advice
@After--無論怎么,在方法之后都調用Advice
@AfterReturning--當方法有返回值的時候才執行Advice,
如果目標方法出現異常--不是返回失敗,則advice不被調用
@AfterThrows--當目標方法拋出正確異常的時候才調用Advice,
必須是指定異常,但是不會停止異常傳播(沒辦法try/cratch)(調用有異常的方法本身也有了異常,方法之間的調用)
環繞Advice--阻止異常傳播
@Around注解和proceedingJoinPoint 添加的proceed()
1.spring bean是什么?
Bean是Spring框架中最核心的兩個概念之一(另一個是面向切面編程AOP,bean是一個由Spring IoC容器實例化、組裝和管理的對象。我們的應用程序由一個個bean構成
- 概念1:Bean容器,或稱spring ioc容器,主要用來管理對象和依賴,以及依賴的注入。
- 概念2:bean是一個Java對象,根據bean規范編寫出來的類,并由bean容器生成的對象就是一個bean。
- 概念3:bean規范。
 2.bean的生命周期?
 
最常用的作用域--五個
一般用@Scope來標識作用域
單例(長命對象):singleton作用域:Spring 只會為每一個bean創建一個實例,并保持bean的引用,在spring Ioc容器中僅存在一個bean實例,Bean以單利方式存在---默認值
原型(短命對象):prototype作用域:一個bean定義對應多個對象實例。每次從容器中調用Bean是,都返回一個新的實例,即每次調用getBean()時,相當于new操作
request作用域:每次Http request請求都會創建一個新的bean,該作用域僅適用于WebApplicationContext環境
seesion作用域:同一個Http Session共享一個bean,不同session使用不同bean,僅WebApplicationContext環境
globalSeesion作用域:一般用于Porlet應用環境,也僅適用于web環境
注解
一、spring
Spring是一個輕量級的開源框架,是為解決企業級應用開發的復雜性而創建的,通過核心的Bean factory實現了底層的類的實例化和生命周期的管理。Spring的最根本使命是:簡化java開發,整合第三方框架
三大核心組件Bean、Context、Core
Spring框架兩大核心:IoC和DI
Spring框架重點提供的IOC DI AOP
IOC:
 控制反轉: 將對象創建的權利交給Spring容器管理,由Spring容器管理對象的生命周期
 DI: 依賴注入
 創建對象時,如果該對象中有需要依賴的屬性,Spring負責為屬性賦值.
------------------------------------------------------------------------------------------------------
導入的包是org.springframework.beans.factory.annotation
@Autowired 基于注解的依賴注入,可以被使用再屬性域,方法,構造函數上--先根據type判斷,在判斷name
@Qualifier @Autowired注解判斷多個bean類型相同時,就需要使用 @Qualifier("xxBean") 來指定依賴的bean的id:
@Resource 也是依賴的注入,先根據name判斷注入哪個對象, 屬性域和方法上 ---這個是javax包
@Scope bean的創建模式--單例和原型模式 singleton--只有一個實例 prototype--每一次都new
@Component@Controller @Service 當使用基于注解的配置和類路徑掃描的時候,這些類就會被實例化,把創建的對象交給spring容器管理
@Configuration將配置類交給spring管理 添加在配置類config上面
---------------------------------------------------不熟悉的注解-----------------------------------------------------
@PropertySource 加載指定的配置文件 將pro文件交給spring容器管理 @PropertySource(value = "classpath:/mysql.properties",encoding = "UTF-8"),
@Value("${mysql.username}") springel表達式,簡稱spel,從spring容器內獲取key,動態為屬性賦值
@Repository
@Repository和@Controller、@Service、@Component的作用差不多,都是把對象交給spring管理。@Repository用在持久層的接口上,這個注解是將接口的一個實現類交給spring管理。
不使用@Repository注解,idea會報警告,提示找不到這個bean,直接忽略即可
@Async 在方法上,這個注解描述的方法,底層會異步執行--日志表數據量太大,不由web服務線程執行,而是交給spring自帶的線程池中的線程去執行;優點:不會長時間阻塞web服務(例如tomcat)線程
@EnableAsync 需要在啟動類上啟動異步執行
@Cacheable
@EnableCaching
@CachePut
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm") 修改日期的格式
---------------------------------------------------AOP---------------------------------------------------------
@Aspect切面聲明,標注在類、接口(包括注解類型)或枚舉上。
@Pointcut 切入點聲明,即切入到哪些目標類的目標方法。
@Before 前置通知, 在目標方法(切入點)執行之前執行
@After 后置通知, 在目標方法(切入點)執行之后執行,不管是否拋出異常都執行
@AfterReturning 返回通知, 在目標方法(切入點)返回結果之后執行,在 @After 的后面執行
@AfterThrowing 異常通知, 在方法拋出異常之后執行, 意味著跳過返回通知
@Around 環繞通知:目標方法執行前后分別執行一些代碼,發生異常的時候執行另外一些代碼
AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構。面向對象解決了層次的問題,比如下級繼承上級來增強上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點是通過代理方式對目標類在 運行期間 織入功能 面向切面的編程(AOP)實現了橫切關注的模塊化 避免代碼纏繞 削除代碼分散
②:AOP的三要素:切面、通知、切點
@Aspect切面:包括了切入點和advice---基本上是個類
JoinPoint連接點:程序中的一個點,例如方法的調用或者排除異常,每一個方法都是一個連接點,
@pointcut 切點:xml文件或者注解,選擇一個或者多個連接點表達式---在什么時候執行
Advice(通知):在選擇的每個連接點執行的代碼--基本是個方法
編織:將切面與主要代碼進行結合的技術,spring就提供了,就是把所有整合在了一起,將指定位置注入
③:通知的用法:
日志與跟蹤 事務管理 安全 緩存 錯誤處理 性能監測 自定義業務規則
------------------------------------------事務----------------------------------------
@Transactional ---spring管理事務
1.默認條件下,只攔截運行時異常 ,執行成功則提交事務,不成功則回滾
* 2.rollbackFor: 指定異常的類型回滾 rollbackFor = RuntimeException.class
* 3.noRollbackFor: 指定異常不回滾 noRollbackFor = RuntimeException.class
添加的位置最好在增刪改方法上,不要直接添加在類上
二、springBoot
解決了spring的配置地獄的問題,內嵌Servlet容器(tomcat),降低了對環境的要求; 提供一系列的starter pom啟動項簡化Maven配置,主要應用了開箱即用的思想。
1、簡化了maven的操作(用什么jar包,就要添加依賴(坐標))
2、內嵌了Tomcat,可以訪問服務器里的程序
3、springboot整合了Spring+SpringMVC+Mybatis--基于ORM的思想以對象的方式操作數據庫
@RestControllerAdvice捕獲全局異常,都是對Controller進行增強的,可以全局捕獲spring mvc拋的異常,指定遇到某種異常實現AOP處理.,返回值都是JSON串,通知是AOP中的技術,解決特定問題。
@ExceptionHandler(value = Exception.class)或者是捕獲({RuntimeException.class})運行時異常,用來捕獲指定的異常。
@SpringBootTest 單元測試類的注解
@SpringBootApplication--主啟動類注解
spring boot starter 這是啟動項,
spring boot starter-web是整合了mvc,開箱即用的思想,有啟動項starter 就可以直接使用其中的功能
常用的啟動項/依賴
springboot的加載機制:開箱即用
pom文件只是添加了jar包,需要被調用才能生效,被主啟動類上的注解SpringBootApplication調用
springboot程序啟動,就是注解開始工作,
注解的結構
1、元注解:@Inherited
2、配置類:@SpringBootConfiguration
3、自動配置:@EnableAutoConfiguration
4、包掃描:@ComponentScan
SpringbootApplication注解的說明
@Target 作用位置類/方法
@Retention 生命周期
@Documented 是否動態生成文檔
@Inherited 是否允許繼承
@SpringBootConfiguration 加載其他小的配置文件
>@Configration 標識我是一個配置類
@EnableAutoConfiguration 開啟自動化配置
>@AutoConfigurationPackage 動態獲取主啟動類的包路徑
>@Import({AutoConfigurationImportSelector.class}) 開箱即用選擇器
@ComponentScan(excludeFilters排除某些類,如:影響項目的過濾器)
springboot開箱即用也可以說是工作流程:選擇器加載web,選擇器加載jdbc,選擇器加載MQ資源很多選擇器。pom文件中有1.web啟動項2.jdbc啟動項;
web選擇器去掃描pom文件,發現web資源,線程開始工作,整個web項目中的包開始生效和加載,加載完成后,springMVC就有效了。程序返回繼續執行下一個jdbc選擇器,找到jdbc的配置項,開始整合JDBC,此時獲取資源從配置文件中。
所以啟動項里面寫的東西,都要在配置文件中配好,否則就會啟動失敗
選擇器沒找到啟動項,就繼續向下執行,直到所有的選擇器執行完
總結:springboot在內部有N個選擇器,當springBoot程序啟動時,依次加載選擇器,如果選擇器匹配pom文件中的啟動項,則自動配置程序開始運行
三、springMvc
概念:是spring框架的一個非常有名的產品,用來接收瀏覽器發來的請求,并返回數據,給瀏覽器做出響應。遵循了MVC思想:主要是想要松耦合,實現代碼間的高內聚,提高代碼的可維護性
M:是model模型,用來封裝數據
V:是view,視圖層,用來展示數據
C:是controller,控制層,作用是用來,接收請求和給出相應
導入的包是org.springframework.web.bind.annotation
@RestController只能用在類上,讓瀏覽器訪問,是@ResponseBody 把數據轉化成Json串和@Controller合并起來的
@RequestMapping規定瀏覽器怎么訪問方法(類)
@GetMapping是一個組合注解,等價于@RequestMapping(method = RequestMethod.Get ),用于簡化開發,@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
@PathVariable 綁定到操作方法的入參中
@ResponseBody 返回對象利用jackson工具類轉換為json字符串--web提供就是mvc提供的
@RequestParam 參數名和請求參數名稱不同時使用,可以設置默認值
@CrossOrigin//放行js的訪問請求,解決跨域問題---在controller中添加類注解--Ajax引擎--局部刷新,ajax是不支持跨域的
五個核心的組件工作原理:
1、前端控制器DispatcherServlet:接收請求,并分發請求
2、處理器映射器HandlerMapping:根據請求,找到具體能處理請求的類名,方法名
3、處理器適配器HandlerAdapter:正式開始調用當方法處理請求,并返回結果
4、視圖解析器ViewResolver:把頁面找到,并把數據進行解析
5、視圖渲染View:具體展示數據并返回給瀏覽器
四、Mybatis
MyBatis 是一款優秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射(ORM)。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。(mybatis在內部將JDBC封裝).
MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。
1、持久化 : 計算機在計算時,數據都在內存中.如果斷電則數據清空,所以要求將內存數據保存到磁盤中.--目標--概念
2、持久層: 程序通過Dao/Mapper 與數據庫進行交互的層級代碼 (Controller層 Service層 Dao/Mapper層與數據庫交互)--流程--具體操作
小結: Mybatis是一個優秀的持久層框架,基于ORM設計思想,實現了以對象的方式操作數據庫.
Mybatis的ORM并不完全,只完成了結果集映射,但是Sql需要自己手寫.所以也稱之為半自動化的ORM映射框架.
前身是apache的一個開源項目IBatis
@Param 在mapper接口中,如果方法傳遞的參數有多個,則可以將多個參數使用注解@Param("sex") String sex 封裝為Map
@Mapper注解
@MapperScan
#{}獲取數據時候
①:默認存在預編譯效果,用?代替,防止sql注入攻擊
②:默認為數據添加一對兒“”號
③:高效的
${}獲取數據
①:當以字段名稱直接作為參數時候,使用${}
②:這樣的sql慎用,會有sql注入攻擊
所以能用#就不要用$
標簽
<select>
<update>
<insert>
<delect>
<foreach> mybatis為了參數取值方便,特意封裝了遍歷的標簽<foreach>,屬性:collection
1.如果傳遞的參數是數組, 則collection="array"
2.如果傳遞的參數是list集合, 則collection="list"
3.如果傳遞的參數是Map集合, 則collection="map中的key",item,open,close,index,separator
--------------------------------------------優化
<typeAliases>別名包-有順序的,在<environment>上面配置<typeAliases>標簽
<sql>簡化sql語句,提取sql語句中的共性
---------------------------------------------動態sql
<where>-<if>動態Sql要求根據對象中不為null的屬性,充當where條件 實現動態查詢
<set>-<if>根據對象中不為null的屬性當做set條件,去除set條件中多余的 ,號,多個條件并列的時候
<choose>-<when>/<otherwise>如果不想將全部的條件當做if的判斷.則mybatis提供了分支結構 switch
<choose>:代表分支結構,只有一個條件有效.
<when test=" ">: 指定判斷的條件 和if類似.
<otherwise>: 如果上述的條件都不滿足時,該行代碼有效.
<resultMap>當結果集中的字段名稱,與對象中的屬性不一致時,可以使用resultMap實現自定義的封裝.,resultType是<select>中的一個屬性,當結果集中的字段名稱,如果與對象中的屬性的名稱一致時,才會實現自動的數據封裝
屬性:id---sql語句中返回結果集resultMap中的名稱
屬性:type---返回結果要封裝成的對象,應該是全路徑,但是我們定義了別名包
<!--自定義映射關系
1.<id>標簽代表主鍵 (每張表中都會有一個主鍵)
標簽屬性:column: 代表結果集(數據庫表)中的字段.--相當于查詢的時候給列起的別名
標簽屬性:property: 對象中的屬性
2.<result>標簽 除了主鍵之外的配置信息
-->
<association>標簽---關聯封裝,如果需要封裝單個的對象-----子查詢,一對一
property 屬性名 封裝的是emp中的屬性dept
javaType 屬性dept的類型 參數是類型的pojo全路徑,但是已經別名包,所以直接寫Dept
column=“子查詢的字段信息”
select=“sql的Id” 作用:根據column中的數據 實現子查詢
<collection>標簽: 封裝集合的固定寫法. ----一對多
property: 指定屬性 ,要封裝的dept中集合對象的屬性名
ofType: 封裝List集合的泛型中的對象
<cache>:應用二級緩存,在xml配置文件中
前端常見錯誤編號
2**--成功
3**--重定向,轉移
301--對象已永久移走,即永久重定向
302--對象已臨時移動
4**--客戶端錯誤--前端瀏覽器
400--錯誤的請求:url中的參數類型不匹配 和服務需要的參數類型不同
403--禁止訪問
404--未找到,url路徑不存在
401是認證失敗--沒有攜帶令牌
403是沒有訪問權限--某種請求方式沒有在數據庫中設置權限
5**--服務器錯誤--后臺
500--內部服務器錯誤,看后臺報錯
502--Web服務器用作網關或代理服務器時收到了無效響應。
504--網關超時
505--HTTP版本不支持
500:請求的url未綁定參數--一定看后臺--IDEA中拋出了IllegalStateException的異常
Mybatis中的緩存機制
解決是用戶的只是查詢,不能提高入庫的速度,緩存可以有效降低用戶訪問物理設備的頻次.提高用戶響應速度.
 1.mybatis自身緩存 一級緩存/二級緩存
 2.Redis緩存 讀取10萬次/秒, 寫 8.6萬次/秒
1-4-1一級緩存
概念說明: Mybatis默認開啟一級緩存, 一級緩存可以在同一個SqlSession對象中執行相同的SQL查詢時,第一次去查詢數據庫,并寫在緩存中。第二次會直接從緩存中取。但是當執行兩次查詢中間發生了增刪改的操作,則sqlSeesion緩存會被清空,每次查詢會先去緩存中找,如果找不到就去查數據庫,并把數據寫在緩存中。
Mybatis內部緩存使用HashMap,key是sql語句+hashcode+statementId;
value是查詢出來的結果集映射成的java對象
1-4-2二級緩存
說明: 二級緩存mybatis中默認也是開啟的.但是需要手動標識.
同一個SqlSessionFactory內部有效. 也就是不同的SqlSession都可以用,只要是同一個工廠
全局配置:默認是開的
sqlSession查詢數據之后,會將緩存信息保存到一級緩存中.但是不會立即將 緩存交給二級緩存保管.如果需要使用二級緩存,則必須將sqlSession業務邏輯執行成功之后關閉.sqlSession.close
局部配置:直接在xml配置文件中加<cache>標簽就可以了
數據庫與緩存同步
高并發(熱點數據)的項目,實現讀寫一致性--緩存與數據庫的一致性
當我們在做數據庫與緩存數據同步時,究竟更新緩存,還是刪除緩存,究竟是先操作數據庫,還是先操作緩存?
其實如果業務簡單,只是去數據庫拿一個值,寫入緩存,那么更新緩存也是可以的。但是,淘汰緩存(刪除緩存)操作簡單,并且帶來的副作用只是增加了一次cache miss,建議作為通用的處理方式。
先操作緩存,還是先操作數據庫?
那么問題就來了,我們是先刪除緩存,然后再更新數據庫,還是先更新數據庫,再刪緩存呢?
先更新數據庫,再刪緩存依然會有問題,不過,問題出現的可能性會因為上面說的原因,變得比較低!數據庫的讀操作的速度遠快于寫操作的因此產生臟數據的可能性會很小。(不然做讀寫分離干嘛,做讀寫分離的意義就是因為讀操作比較快,耗資源少)
Mybatis-plus
以對象的方式操作數據庫
Mybatis是半自動化的ORM映射框架.半自動: Sql是自己手寫的,但是結果集映射是自動的.
 MybatisPlus: 全自動的ORM 映射框架,對Mybatis的擴展.在UserMapper上繼承BaseMapper<User>接口,baseMapper中的新增方法1個,修改方法是:2個?
1、Mybatis操作的實質:sql把公共的部分提取出來
調用步驟:
1.用戶執行userMapper.insert(user);
2.根據繼承的關系 BaseMapper.insert(user);
3.MP在內部生成sql之后交給Mybatis調用 最終實現數據操作
2、MP映射注解
@TableName("demo_user") //對象與表名映射,如果相同可以不寫,添加在類上
@TableId(type = IdType.AUTO)//主鍵自增,添加在屬性上
@TableField("age") //實現屬性與字段映射
@TableField(exist = false)//排除不必要的字段
3、QueryWrapper條件構造器,動態拼接where條件,默認的關系連接符 and
* 1. eq =
* 2. gt >
* 3. lt >
* 4. ge >=
* 5. le <=
* 6. ne <>
queryWrapper.gt("age",18).eq("sex","男");//利用條件構造器自己構建條件
* 7.模糊查詢
queryWrapper.like("name","君") //"%君%"
queryWrapper.likeLeft("name","君") //"%君"
* 8.in--多個相同參數--包裝類型
Integer[] ids = {1,3,4,5};
queryWrapper.in("id",ids).orderByDesc("age");//desc排序
*9.動態sql
動態sql實現,根據不為null的屬性當做where條件
Spring提供的API StringUtils.hasLength(sex);---判斷字符串API
對于字符串來說,要判斷是否第null和空串代替了:/boolean flag = sex !=null && "".equals(sex);
對于數字來說,就直接判斷是否為空就可
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--只想獲取第一列數據--做關聯查詢時可以使用
五、lombok
@Data //動態生成get/set/toString/equals等方法
@Accessors(chain = true) //開啟鏈式加載,換名就方便了,不用一直寫對象.方法 重寫set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
六、Servlet--控制器?
socket 服務器
springmvc底層就是servlet
Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務程序或服務連接器,主要功能在于交互式地瀏覽和生成數據,生成動態Web內容。
狹義的Servlet是指Java語言實現的一個接口,廣義的Servlet是指任何實現了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運行于支持Java的應用服務器中。從原理上講,Servlet可以響應任何類型的請求,但絕大多數情況下Servlet只用來擴展基于HTTP協議的Web服務器。
servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監聽端口,請求過來后,根據URL信息,確定要將請求交給哪個servlet去處理,然后調用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。
概括: Servlet是java后臺程序與用戶交互的機制(媒介).
核心方法:request.getParameter--parameter是參數的意思
規則:
參數是取的 而不是傳的;請求的流程: 一個request對象返回response
public String findUserByIds(HttpServletRequest request)
{String id = request.getParameter("id");
參數名稱必須相同、弊端無論什么樣的數據,都是String數據類型,需要手動的轉化
SpringMVC: 在內部封裝了Servlet機制.并且可以根據用戶的參數類型,實現自動的數據類型的轉化
第一階段
ASCII碼表
‘0’對應48‘A’對應65 ‘a’對應的97單引號對應數值,雙引號是字符串
Idea快捷鍵
ctrl+alt+L 代碼對齊
Shift +alt+↑向上移動
Ctrl+d 復制當前行
Ctrl+y 刪除
//特征 屬性 字段 成員變量是同一個東西
 //行為 功能 方法 成員方法 是同一個東西
Math常用的方法
Math.round()返回最接近參數的int,它表示"四舍五入"
Math.rint()返回最接近參數并等于某一整數的 double 值,如果有2個數同樣接近,則返回偶數的那個
Math.floor()返回最小的(最接近正無窮大)double 值,該值小于等于參數,并等于某個整數
Math.ceil()返回最大的(最接近負無窮大)double 值,該值大于等于參數,并等于某個整數
Math.cbrt()返回 double 值的立方根
Math.sqrt()返回正確舍入的double 值的正平方根
Math.pow()返回第一個參數的第二個參數次冪的值
Math.max()返回兩個 double 值中較大的一個
Math.min()返回兩個 double 值中較小的一個
關鍵字
String不是關鍵字
Assert--------用來查找內部程序錯誤、
Enum---------枚舉類型
Final----------修飾一個常量 finally----try塊中總會執行的部分
Instanceof-----測試一個對象是否是某個類的實例
Native---------由宿主系統實現的一個方法
被native關鍵字修飾的方法叫做本地方法,本地方法和其它方法不一樣,本地方法意味著和平臺有關,因此使用了native的程序可移植性都不太高。另外native方法在JVM中運行時數據區也和其它方法不一樣,它有專門的本地方法棧。native方法主要用于加載文件和動態鏈接庫,由于Java語言無法訪問操作系統底層信息(比如:底層硬件設備等),這時候就需要借助C語言來完成了。被native修飾的方法可以被C語言重寫。
Java程序中聲明native修飾的方法,類似于abstract修飾的方法,只有方法簽名,沒有方法實現。編譯該java文件,會產生一個.class文件。
Strictfp---------對浮點數計算使用嚴格的規則
Throw----------拋出一個異常throws-------一個方法可能拋出的異常
Transient-------標志非永久性的數據
Volatile---------標記字段可能會被多個線程同時訪問(異步),而不做同步
標記其他線程可見,可見性和原子性,禁止指令重排序
枚舉enum
Java是一門面向對象的語言,當我們創建好一個類以后,可以創建這個類的多個對象
 但是一個類究竟創建多少個對象,并且對象代表的值我們是無法限制的
 所以,如果開發中需要一組值,的數據是明確的,就可以使用枚舉
如果當我們需要定義一組常量表示不同的狀態時,就建議使用枚舉類,枚舉類的對象個數是有限且明確的
JDK5以前是需要自定義枚舉類的,JDK5以后可以使用關鍵字enum來定義枚舉類,普通類定義class 類名,枚舉類eunm 類名
枚舉類的構造方法必須私有化,防止外界隨意創建本類對象,本類特有的私有屬性要加上final防止被篡改
- 定義枚舉對象:不能像自定義枚舉類對象時new,要按照下面的語法枚舉名1(值1,值2),枚舉名2(值1,值2),枚舉名3(值1,值2);
- 不需要生成toString,枚舉類繼承了java.lang.Enum * 在Enum中重寫了繼承自Object的toString(),直接打印的就是枚舉名
- 實現接口的枚舉類,在枚舉類每個枚舉對象后分別實現接口中的抽象方法
- JDK5中擴展了switch語句,除了可以接收byte short char int ,還可以接收枚舉類型
1、面向對象的特征有哪些
1.封裝:
通過private修飾符來封裝屬性與方法,封裝后需要外界提供公共的操作方式get/set。封裝是把過程和數據包裹起來,外界對于數據的操作僅限于我們提供的方式。
2.繼承:
繼承是一種聯結類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。我們可以從現有的類中派生出一個新的類,這個過程稱為類繼承。新類繼承了原始類的特性,被稱為派生類(子類),而原始類稱為基類(父類)。派生類可以從它的基類那里繼承方法和實例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。
3.多態:多態性是指可以盡量屏蔽不同類的差異性,提供通用的解決方案。多態性語言具有靈活、抽象、行為共享、代碼共享的優勢,很好的解決了應用程序函數同名問題。我們所使用的有:
向上造型【將子類對象統一看作父類型Parent p=new Child();,比如花木蘭替父從軍】
向下造型【將之前看作父類型的子類對象再重新恢復成子類對象,比如花木蘭打仗結束回家化妝】
4.抽象:抽象就是忽略一個主題中與當前目標無關的那些方面,以便更充分地注意與當前目標有關的方面。抽象并不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細節。抽象包括兩個方面,一是過程抽象,二是數據抽象。
(3) 構造方法,與類同名,但是沒有返回值類型(連void都沒有);
一個類會默認存在無參構造方法(函數);格式 public 名稱(){}
2、抽象類與接口的異同:
抽象類:
== 抽象類和普通類的主要有三點區別:==
1)抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),缺省情況下默認為public。抽象類的存在就是為了被繼承,如果是private就沒有存在的意義了。
2)抽象類不能用來創建對象;--有構造方法,為了子類
3)如果一個類繼承于一個抽象類,則子類必須實現父類的抽象方法。如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。
接口:
接口中的變量默認拼接public static final,接口中的方法必須都是抽象方法。
抽象類和接口的區別:
語法層面上的區別
1)一個類只能繼承一個抽象類,而一個類卻可以實現多個接口
設計層面上的區別
1)抽象類是對一種事物的抽象,即對類抽象,而接口是對行為的抽象。所以抽象類是后天構建的結果,接口是先天設計的
2)設計層面不同,抽象類作為很多子類的父類,它是一種模板式設計。而接口是一種行為規范,它是一種輻射式設計。
2、抽象類中有構造方法 ,為了給子類創建對象時調用
接口中沒有構造方法,子類調用的是父類的(默認object)的構造方法
3、抽象類 單繼承
接口可以多繼承
4、抽象類可以定義普通的成員變量,抽象類中可以有普通的方法
接口只能定義靜態常量(static和final),只能有抽象方法
5、相同:抽象類和接口均不可以實例化/創建對象
6、抽象類是后天構建的結果,接口是先天設計的
3.、線程有幾種狀態?它們是怎么切換的?
線程生命周期,主要有五種狀態:
1、新建狀態(New) : 當線程對象創建后就進入了新建狀態.如:Thread t = new MyThread();
2、就緒狀態(Runnable):當調用線程對象的start()方法,線程即為進入就緒狀態.
處于就緒(可運行)狀態的線程,只是說明線程已經做好準備,隨時等待CPU調度執行,并不是執行了t.start()此線程立即就會執行
3、運行狀態(Running):當CPU調度了處于就緒狀態的線程時,此線程才是真正的執行,即進入到運行狀態
就緒狀態是進入運行狀態的唯一入口,也就是線程想要進入運行狀態狀態執行,先得處于就緒狀態
4、阻塞狀態(Blocked):處于運狀態中的線程由于某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入就緒狀態才有機會被CPU選中再次執行.
根據阻塞狀態產生的原因不同,阻塞狀態又可以細分成三種:
等待阻塞:運行狀態中的線程執行wait()方法,本線程進入到等待阻塞狀態
同步阻塞:線程在獲取synchronized同步鎖失敗(因為鎖被其他線程占用),它會進入同步阻塞狀態
其他阻塞:調用線程的sleep()或者join()或發出了I/O請求時,線程會進入到阻塞狀態.當sleep()狀態超時.join()等待線程終止或者超時或者I/O處理完畢時線程重新轉入就緒狀態
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期
就緒 → 執行:為就緒線程分配CPU 即可變為執行狀態"
執行 → 就緒:正在執行的線程由于時間片用完被剝奪CPU暫停執行,就變為就緒狀態
執行 → 阻塞:由于發生某事件,使正在執行的線程受阻,無法執行,則由執行變為阻塞
(例如線程正在訪問臨界資源,而資源正在被其他線程訪問)
反之,如果獲得了之前需要的資源,則由阻塞變為就緒狀態,等待分配CPU再次執行
開啟線程:繼承thread類,重寫run方法;2.實現runnable接口,實現run方法,3.實現callable接口,實現call方法(有返回值),通過FutureTask創建一個線程,4.線程池
4、什么是線程池?線程池有什么優點?
我們使用線程的時候就去創建一個線程,如果并發的線程數量很多,并且每個線程都是執行一個時間很短的任務就結束了,這樣頻繁創建線程就會大大降低系統的效率,因為頻繁創建線程和銷毀線程需要時間。
線程池:其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。
好處:
- 降低資源消耗,減少創建和銷毀線程的次數,每個工作線程都可以被重復利用
- 提高響應速度,當任務到達時,任務不需要等待線程的創建 立即執行
- 提高線程的可管理性,根據系統的承受能力調整線程池中的數目,防止消耗過多內存--內存溢出
5、創建線程池有哪幾種方式?
兩類:通過Excutors創建、另一類:通過ThreadPoolExecutor創建--最原始的創建線程池方式
①. new Fixed ThreadPool(int nThreads)創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。
②. new Cached ThreadPool()創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閑線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。
③. new Single ThreadExe cutor()這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串行執行。
④. new Sche duled ThreadPool(int corePoolSize)創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似于Timer。
⑤:new SingleThread ScheduledExecutor:創建一個單線程的可以執行延遲任務的線程池;
⑥:new WorkStealing Pool:創建一個搶占式執行的線程池(任務執行順序不確定)JDK 1.8 添加
⑦:ThreadPoolExecutor:最原始的創建線程池的方式
46.線程池的參數:
core Pool Size 線程池核心線程大小 一個最小的線程數量,
maxi mum PoolSize 線程池最大線程數量 一個最大線程數量的限制
workQueue 工作隊列 新任務被提交后,會先進入到此工作隊列中,任務調度時再從隊列中取出任務
46. 線程池中 submit()和 execute()方法有什么區別?
接收的參數不一樣submit有返回值,而execute沒有,submit方便Exception處理
47.怎么保證線程安全:
加鎖:jvm提供的synchoronized關鍵字鎖
jdk提供的各種鎖Lock
5、volatile關鍵字和Synchronized關鍵字
Synchronized,用來枷鎖
volatile只是保持變量的線程可見性,通常適用于一個線程寫,多個線程讀
volatile不能保證原子性,只能保證線程可見性
多個線程的時候,多個副本線程從主線程里面拿取數據,副本線程的更改,只有當提交到主線程后,才能被其他福線程感知到。volatile就是實現復線程更改后 馬上感知到這個變化
volatile防止指令重排序
6、Java虛擬機管理的內存分配--5個
JVM運行時數據區
Java虛擬機JVM 管理的內存包括5個運行時數據內存:方法區、虛擬機棧、本地方法棧、堆、程序計數器,其中方法區和堆是由線程共享的數據區,其他幾個是線程隔離的數據區
1、程序計數器ProgramConute
程序計數器是一塊較小的內存,他可以看做是當前線程所執行的行號指示器。每一個線程都有自己的寄存器,PC寄存器的內容總是指向下一條將被執行指令的地址
分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
如果線程執行的是Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;
如果正在執行的是Native方法,這個計數器則為空
此內存區域是唯一個在Java虛擬機規范中沒有規定任何OutOfMemotyError(內存溢出)情況的區域
2、Java虛擬機棧
虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用于儲存局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
棧內存就是虛擬機棧,或者說是虛擬機棧中局部變量表的部分
Java虛擬機規范對這個區域規定了兩種異常狀況:
- 如果線程請求的棧深度大于虛擬機所允許的深度,拋出StackOverflowError棧溢出異常
- 如果虛擬機擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError內存溢出異常
3、本地方法棧
本地方法棧和虛擬機棧發揮的作用是非常類似的,他們的區別是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則為虛擬機使用到的Native方法服務
本地方法棧區域也會拋出StackOverflowError和OutOfMemoryErroy異常
4、Java堆(Heap)
堆是Java虛擬機所管理的內存中最大的一塊,是被所有線程共享的一塊內存區域,此內存區域的唯一目的是存放對象實例,幾乎所有的對象實例和數組都在這里分配內存,堆中的對象內存需要等待GC進行回收。
Java堆細分為新生代和老年代
5、方法區(Method Area)
方法區它用于儲存已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據
跟Java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以不實現垃圾收集。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載
 當方法區無法滿足內存分配需求時,將拋出OutOfMemoryErroy異常
6、JVM垃圾回收
Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法。(基于對對象生命周期分析)
GC的基本原理:將內存中不再被使用的對象進行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,Java在對對象的生命周期特征進行分析后,按照新生代、舊生代的方式來對對象進行收集,以盡可能的縮短GC對應用造成的暫停
- Young(年輕代)
三個區。一個Eden區,兩個Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區復制過來的并且此時還存活的對象,將被復制年老區(Tenured)
- Tenured(年老代)
年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。
- Perm(持久代)
用于存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:Max PermSize=進行設置。
自動GC,由jvm系統內存不足的時候,自動釋放
判斷策略:1引用計數法 2可達性分析
算法:
1、復制算法:伊甸園區(判斷)→存活區(伊甸園就空了)分成S0和S1
S0滿了用復制算法→S1(二者總有一個是空的)
S1滿了用復制算法→S0(默認是15輪,后仍然存在→老生帶區)
所以新生代區和老生代區GC的頻率是不同的,內存縮小代價高,代碼存活率太高
2、標記清除算法:老生帶的對象,標記后,直接刪除不復制了,但是此時區域變成了碎片(內存碎片)》優化算法
3、標記整理算法,解決內存碎片問題
7、垃圾回收機制GC
基于特定的算法,釋放垃圾對象縮占用的內存空間,是進軍大規模應用開發的前提
手動GC,手動內存分配與釋放,如果忘記釋放對應的內存不被使用(容易內存泄露)System.gc
自動GC,由jvm系統內存不足的時候,自動釋放
判斷策略:1計數 2可達性
算法:
1、復制算法:伊甸園區(判斷)→存活區(伊甸園就空了)分成S0和S1
S0滿了用復制算法→S1(二者總有一個是空的)
S1滿了用復制算法→S0(默認是15輪,后仍然存在→老生帶區)
所以新生代區和老生代區GC的頻率是不同的,內存縮小代價高,代碼存活率太高
2、標記清除算法:老生帶的對象,標記后,直接刪除不復制了,但是此時區域變成了碎片(內存碎片)》優化算法
3、標記整理算法,解決內存碎片問題
6、談談你對static的了解
static是java中的一個關鍵字,可以用來修飾方法、變量、代碼塊、內部類,還可以使用靜態導包
- static方法:不依賴于對象就可以直接訪問。不能與this合用--因為他不依附于任何對象,沒有對象就談不上this了。靜態方法中只能訪問靜態變量和靜態方法。非靜態方法中可以訪問靜態方法
- static變量:靜態變量被所有對象共享,在內存中只有一個副本,類加載時候初始化
- static代碼塊:靜態代碼塊優化程序性能,只加載一次,靜態代碼塊》構造代碼塊》構造函數
- 靜態內部類:在定義內部類的時候加上static權限修飾符,靜態內部類又稱為嵌套類,此時不需要外部類,就能創建內部類的對象。不能從內部類訪問外部類的非靜態資源。
*修飾方法一般寫在權限修飾符public之后
*可以修飾變量、方法、代碼塊、內部類
*靜態資源優先于對象進行加載,他隨著類的加載而加載的,比對象先加載入內存,所以在沒創建對象的時候,靜態資源可以通過類名直接調用。類名.變量/方法
*由于靜態比對象先加載,所以static不能與this/super共用。因為還沒有對象
*靜態資源被全局所有對象(多個對象)共享,屬于類資源,值只有一份
*普可調普、普可調靜、靜可調靜、靜不可調普(只能調靜)
*靜態代碼塊:static{},類里方法外;靜態代碼塊也屬于靜態資源,優先于對象加載(創建對象的時候先執行靜態代碼塊→構造代碼塊→構造方法),并且只加載一次;作用:用于加載需要第一時間就加載,并且只加載一次(初始化)
7、談談你對方法的理解:
方法是具有一定功能的代碼塊,可以把重復多次使用的功能提取成一個方法,減少代碼的冗余
- 格式:權限修飾符 返回值類型 方法名(參數列表){ 方法體 }
方法簽名:方法名(參數列表)
- 方法的重載和重寫:
重載:方法名相同,但是參數列表不同
重寫:兩同(方法名,參數列表相同)兩小(返回值,異常)一大(子的訪問權限控制符>=父類的)
- 方法的遞歸:在方法中調用自己本身,遞歸次數過多時,會出現棧溢出異常,求數字階乘--遞歸
8、內部類有哪些種類?又有哪些使用場景?
1、成員內部類--在類的內部
成員內部類可以無條件的訪問外部類的成員屬性和成員方法(包括 private 和 static 類型的成員)
使用場景:當類 A 需要使用類 B ,同時 B 需要訪問 A 的成員/方法時,可以將 B 作為 A 的成員內部類。同時我們可以利用 private 內部類禁止其他類訪問該內部類,從而做到將具體的實現細節完全隱藏。
2、靜態內部類--成員內部類被靜態修飾
不持有外部類的引用,想在靜態內部類中訪問外部類的成員只能 new 一個外部類的對象,否則只能訪問外部類的靜態屬性和靜態方法。
使用場景:當類 A 需要使用類 B,而 B 不需要直接訪問外部類 A 的成員變量和方法時,可以將 B 作為 A 的靜態內部類。
3、局部內部類--在代碼塊或方法中創建的類
局部內部類的作用域只能在其所在的代碼塊或者方法內,在其它地方無法創建該類的對象。
 我們可以把局部內部類理解為作用域很小的成員內部類。
使用場景:局部內部類只用于當前方法或者代碼塊中創建、使用,屬于一次性產品,使用場景少。
4、匿名內部類--
1.必須繼承一個父類或實現一個接口,并不需要增加額外的方法,只是對繼承方法的實現或是覆蓋。
2.只是為了獲得一個對象實例,并不需要知道其實際的類型。
3.匿名內部類的匿名指的是沒有類名,而不是沒有引用指向它。
4.匿名內部類不能有構造方法,只能有初始化代碼塊。因為編譯器會幫我們生成一個構造方法然后調用。
5.匿名內部類中使用到的參數是需要聲明為 final 的,否則編譯器會報錯。
使用場景: 當我們需要實現一個接口,但不需要持有它的引用,僅需要使用一次它的某一個資源時使用
9、談談串行、并行、并發、分布式?
串行---A和B兩個任務運行在單核【1個CPU】線程上
并行---A和B兩個任務在同一時刻發生在多核【多個CPU】線程上
并發---多個線程在宏觀時間上看似同時執行,但實際上是輪流穿插執行的效果。實際上是一個cpu在若干程序間的多路復用。有串行并發、并行并發兩種
分布式---在并行處理的基礎上,強調正在執行的物理設備處理器、內存等 是分開的,并行是計算機上的計算,不是物理上的分開。
10、String中常用的api
s.charAt(0) //a,獲取指定下標處的字符(元素)
s.concat("dfg") //用于拼接字符串,不會改變原串
s.indexOf("b") //判斷指定元素第一次出現的索引(下標)
s.lastIndexOf("b")//判斷指定元素最后一次出現的索引
s.substring(3)//從指定下標處截取子串,到結束
 s.substring(1,5)//截取指定范圍的子串[) 含頭不含尾
s.length()//獲取字符串的長度是字符串的方法,但length是數組的屬性無括號
s.split(" ")//regex是正則表達式(你定義的規則)按照正則表達式拆分字符串
s.toUpperCase()//全大寫
 s.toLowerCase()//全小寫
s.startsWith("a")//判斷字符串是否以指定元素開頭
s.endsWith("c") //判斷字符串是否以指定元素結尾
s.trim()//去除字符串兩端多余的空格
String.valueof()//通過類型調用,不用名字,把輸入的類型轉換成String
 System.out.println(String.valueOf(80)+10);//8010 因為前面的80已經成字符串了,兩者是拼接的關系
long t1=System.currentTimeMillis();獲取系統當前時間,使用的類:System
11、Regex
定義一個string類型的字符串---規則
要判斷的.matches(string正則表達式)---返回值是布爾類型
12、包裝類常用API
Integer.valueof()---byte范圍內高效的創建包裝類,也可以把string轉換成Integer
Integer.parseInt()----把string類型的數據轉成 String---int
泛型
< ? >的部分,它就是泛型
1、引入泛型
泛型想要模擬數組的數據類型檢查,因為泛型經常與集合一起使用,不檢查就啥都能傳進去,引入泛型--主要目的檢查約束集合中元素的類型,可以把報錯的時機提前,在編譯器就報錯。是一顆語法糖,編譯后刪除,不會出現在.class源碼中。
List list=new ArrayList();//原來的集合對象,任何數據都可以
List<String> list2=new ArrayList();//引入泛型,約束成String類型
<引用類型>中必須使用基本數據類型的包裝類(引用類型)
13、多個集合(帶all)
c.addAll(c2)//把c2集合中的所有元素添加到c中,c2本身沒有改變
c.containsAll(c2)//true判斷c集合是否包含c2的所有元素
c.removeAll(c2)//刪除c中屬于c2集合的所有元素,若有重復,都刪
c.retainAll(c2)//取c和c2的交集,c2不受影響,刪c
迭代(遍歷)集合
*迭代步驟:1.獲取工具--集合的迭代器c.iterator()
Iterator<Integer> it = c2.iterator();//c2定義的泛型是Integer類型
 * 2.判斷集合中是否有下一個元素可以迭代 it.hasNext(),用在while循環中
 * 3.獲取當前迭代器迭代到的元素it.next()
①:System.out.println(it.next());//直接打印本次迭代到的元素
②:Integer num = it.next();//來接一下元素,在打印
 System.out.println("本次跌倒到的元素"+num);
14、List接口
特點:元素都有下標,有序的,允許存放重復的元素
list接口中獨有的方法
list.add(1,"蛇精");//在指定索引位置增加元素,并且可以存放重復的元素
System.out.println(list.indexOf("小蝴蝶"));//3 元素第一次出現的位置
 System.out.println(list.lastIndexOf("小蝴蝶"))//8 最后出現的位置
System.out.println(list.remove(5))//根據下標刪除,并打印刪除的元素
通過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循環--因為list集合是有序的,元素有下邊,所以可以根據下邊來遍歷
for (int i = 0; i <list.size() ; i++) {
 System.out.println(list.get(i));//根據循環次數 獲取對應的集合元素
 }
 * 2.高效for循環
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中的數據,Set<Key>:把map中所有的key取出來放入到當前的set集合中,Key的類型放入到<>中。
①:轉Set集合Set<Integer> keySet = map.keySet();
②:獲取迭代器Iterator<Integer> it = keySet.iterator();
while(it.hasNext()){//3是否有下一個元素
 //4獲取當前循環迭代到的元素 ,每輪獲取的是map中的key
 Integer key = it.next();
 //5根據剛剛獲取到的key從map中獲取對應的value map.get();
 String value = map.get(key);
 //6拼接打印key與value
 System.out.println("{"+key+"="+value+"}");
 }
方式二:entrySet()
此方案是把map中的每一對鍵值對看做是一個個的Entry<k,v> 放入set集合中
最后在獲取key和value
①:轉entry
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
②:獲取迭代器
Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();
while(it2.hasNext()){
 //獲取當前迭代的元素,這里是一個entry------
 Map.Entry<Integer, String> entry = it2.next();//是一個entry
 //通過entry.getKey();通過entry方法獲取
 System.out.println("["+entry.getKey()+"="+entry.getValue()+"]");
 }
17、HashMap
HashMap數組和鏈表的結合,數組的初始容量是16,默認的加載因子是0.75,我們用key的hash值%初始容量,存在對應的位置上。當計算出來的數重復,則形成鏈的結構(>8是轉成紅黑樹,<6時在轉回鏈)。所以Map是無序的。
JDK1.8 以后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值(或者紅黑樹的邊界值,默認為 8)并且當前數組的長度大于64時,此時此索引位置上的所有數據改為使用紅黑樹存儲。
HashMap的特點:
1.存取無序的
2.鍵和值位置都可以是null,但是鍵位置只能是一個null
3.鍵位置是唯一的,底層的數據結構控制鍵的
4.jdk1.8前數據結構是:鏈表 + 數組 jdk1.8之后是 : 鏈表 + 數組 + 紅黑樹
5.閾值(邊界值) > 8 并且數組長度大于64,才將鏈表轉換為紅黑樹,變為紅黑樹的目的是為了高效的查詢。
 ?
18、ConcurrentHashMap
--線程安全的-效率更高--采用的分段鎖
hashTable在所有的上面都加了synchronize--全局鎖--保證線程安全的
ConcurrentHashMap:如何實現線程安全
在JDK1.7中----分段鎖
ConcurrentHashMap采用了ReentrantLock重的+Segment+HahEntry。一個Segment中包含一個HashEntry數組,每個HashEntry又是一個鏈表結構。--數組+鏈表的結構,外層加了Segment分段鎖。
Segment分段鎖 繼承了ReentrantLock ,key對應的是某個Segment,value對應的是某段,這樣就是只鎖住某一段。不影響其他段的操作。并發度:就是segment有多少個,可以同時支持多少個線程來操作--可以通過構造函數來設計。
數組擴容不影響其他的segment
元素的查詢:兩次hash,第一次hash是定位到哪個Segment中包含了數組,第二次hash定位到元素所在的鏈表的頭部
get方式無需加鎖,不會產生臟讀,因為元素本身通過volatile 保證的--一個線程寫,多個線程讀。標記這個可能會被異步訪問。
在JDK1.8中---synchronize+CAS(樂觀鎖)+Node+紅黑樹,
查找、替換、賦值都使用CAS---保證不了線程安全 擴容等等,所以才加的synchronize
鎖:是鏈表的head節點,鎖的細粒度更高,擴容時阻塞所有的讀寫操作,讀操作無鎖。
取消了Segment分段鎖的數據結構,取而代之的是Node,Node的value和next都是由volatile關鍵字進行修飾,可以保證可見性。采用CAS+Synchronized替代Segment分段鎖。
18、反射
1、獲取字節碼對象
Class.forName(“全路徑”);//全路徑--包名.類名 可能會拋出異常
類名.class 不能alt+回車獲取返回值,這是一個屬性
New對象.getClass();
Class<?> student1 = Class.forName("cn.tedu.reflection.Student");
 Class<?> student2 = Student.class;
 Class<?> student3 = new Student().getClass();
通過反射我們可以獲取到//class 類的全路徑//字節碼對象全路徑名:包名.類名//獲取對應的字節碼的類名
獲取構造方法/獲取成員方法//獲取成員變量
暴力反射:
通過反射的方式創建對象
 Object obj = clazz.newInstance();
//4.2!!!暴力反射!!需要設置權限私有可見
 field.setAccessible(true);
19、設計單例模式
1、單例設計方式一:餓漢式,自己吧對象都創建好了
//0.創建自己的單例程序(類)
class MySingle{
//1.構造方法私有化,不讓外界隨意調用本方法創建對象(實例化)
private MySingle(){}
//2.創建本類對象,并私有化
private static MySingle sin=new MySingle();
//3給外界提供一個公共的全局訪問點(類似get和set方法)。把創建好的對象返回,返回值類型是MySingle
public static MySingle getSin(){
return sin;}//4.將本類中剛創建好的對象通過return返回到調用位置
// 5.get方法設置為靜態,直接通過類名調用,不用創建對象;靜態方法只能調靜態,所以創建出來的對象new MySingle()也需要改成靜態的
MySingle sin1 = MySingle.getSin();
 MySingle sin2 = MySingle.getSin();
//都只是執行了類中自己創建的那一個對象(單例),地址值是一樣的
sin1==sin2
二、單例設計方式二:懶漢式
先不創建對象,等你需要才創建--延遲加載
//1.構造方法私有化
//2.在類的內部創建引用類型變量成員變量(延遲加載的思想)---注意私有化
private static MySingle2(引用類型類名) sin2;
//3.創建一個公共的方法供外界調用,用來獲取本類唯一的方法
public static MySingle2 getSin2(){
//3.1在返回對象之前,需要判斷sin2是否保存有地址值
 if (sin2==null){//如果是默認值null,說明之前從來沒有創建過本類對象
 //3.2創建一個對象并賦給sin2
sin2=new MySingle2();
 }//3.3如果sin2不為空(之前創建過對象)就跳過if,直接返回sin2
return sin2;
//4.1方法設置成靜態
 //4.2成員變量設置成靜態
 //5.獲取本類對象
MySingle2 s1 = MySingle2.getSin2(); 類名.方法名;
20、JDK自帶注解(5個)
@Override:標識重寫方法
@SuppressWarnings(“deprecation”) 忽略警告
·元注解(5個),用來定義其他注解 的注解
@Target 注解用在哪里:類上、方法上、屬性上等等,可以使用的位置
@Retention 注解的生命周期:源文件中、字節碼文件中、運行中
@interface注解名--自定義注解之前要用上兩個元注解表示作用位置和生命周期
第二階段
1、表的操作
創建表:
先使用數據庫 use 數據庫名
create table 表名(字段1名字 字段類型(字段最大長度),字段2,字段3)
id int primary key auto_increment, 可以寫id int (5),
door_name varchar(100), 下劃線分隔
tel varchar(50)
);
查看表的結構:desc 表名;--展示表的字段名和數據類型
添加表的列(字段):alter table 表名 add column money numeric(7,2);
2、SQL語句
結構化查詢語言(Structured Query Language),專門用來操作數據庫的語言
- 對數據(記錄)的操作
查詢表中的數據:select*from表名;*代表通配符,表示查所有
添加表中的數據:insert into 表名values(null,'varchar的數據',666);
修改表中的數據:update 表名 set 字段名= 555 where id=1;
刪除表中的數據:Delete from 表名 where id=2;
- 基礎函數
Lower、Upper、Length、Substr截取、Concat拼接、Replace替換、Ifnull、Round/ceil/floor對小數的特殊處理、now、Year/Month/day
- 條件查詢
distinct(去重)、where、and/or/in 、like、null/is not null、between and、limit、order by desc降序asc升序
limit 1,3; 從下標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()
- 聚合函數
max(),min(),sum(),avg(),count(),count(1),group By
count(列名)--只統計非空的數列,非聚合列和聚合列不能一起查,只能分組。
Having跟where 一樣,但是用在group by之后。不能再從表中讀取數據了,數據的查詢都是在from 表名之前進行的。所以having后面跟的條件一定是之前select過的。
Group by 非聚合列 having 過濾條件;
Where 用在分組之前更高效,但是where不能用在聚合函數之中。
3、字段約束
主鍵約束“primary key”、唯一約束“unique”、非空約束“not null”、外鍵約束“foreign key”、默認值約束“Default”,檢查約束(年齡大于0,小于200)
4、創建索引index
索引是一種排好序的快速查找的數據結構,它幫助數據庫高效的進行數據的檢索。設計索引通常都是背后操作。但是索引本身就是龐大的表--采用BTree樹方式構建。
分類: 單值索引:一個索引只包含一個列(字段),
唯一索引:索引列的值必須唯一,允許有空值;主鍵會自動創建唯一索引
復合索引:一個索引包含多個列
創建索引:create index 索引名字 on 表名(字段名)
創建唯一索引:create unique index 索引名 on 表名(字段名);
查看索引 show index from 表名;
刪除索引:alter table 表名 drop index 索引名;
創建復合索引:最左特性(應用索引時候的查詢條件必須按照從左到右的順序),否則索引失效
模糊查詢導致索引失效:
5、多表聯查
1、笛卡爾積Cartesian product又稱直積
SELECT * FROM dept,emp where dept . deptno=emp.deptno;直接先查兩個表--慎用
2、連接查詢
· 內連接 inner join 兩個的交集。
· 左(外)連接 left join 左表所有的和右表滿足條件的,不滿足的是null,以左邊表為基礎,
· 右(外)連接 right join 右表的所有和左表滿足的,左表不滿足的是null,
3、子查詢subquery
也叫嵌套查詢(兩個select)效率非常低,把上次查詢的結果當做第二次查詢的條件
6、sql優化
1、查詢SQL盡量不要使用select *,而是具體字段,盡量避免返回大量數據
2、在where子句中不使用or來連接條件 盡量用and
使用varchar代替char
不要使用in和not in,用between和exists代替
3、盡量使用數值替代字符串類型 --0表示女 1 表示男 數字內存小,還有讀寫性能
4、給常用來查詢條件的字段設計索引,但是索引控制在5個以內,不適合建在有大量重復數據的字段上
##使用explain分析你SQL執行計劃,分析性能是否使用了索引
創建name字段的索引 where name=””; name索引才生效
5、優化like語句,盡量避免%號在前面,但是like很可能讓你的索引失效--從開始位置比較高效
6、where限定查詢的數據,避免在where子句中使用!=或<>操作符,在等號左側進行表達式、函數操作
7、INSERT INTO dept (deptno,dname) VALUES(4,"abcs");#給指定的列插入值,
INSERT INTO student (id,NAME) VALUES(4,'齊雷'),(5,'劉昱江');批量提交
8、偽刪除設計
商品狀態(state):1-上架、2-下架、3-刪除
通過where state=1或者where state=2過濾掉數據,這樣偽刪除的數據用戶就看不到了,從而不影響用戶的使用,操作速度快,特別數據量很大情況下
9、 count 聚合函數
沒有主建: 只有一列count(*)多列count(1)
有主鍵 count(主鍵)
所以實際業務中一般用count(1)比較普遍,但是如果需要聚合多個列,則用count(列名)比較合適。
7、HTML--超文本標記語言
1、列表標簽<ul type=”circle”> <li> 有序列表 ol -li(自動編號) /無序列表 ul -li
2、<input type="radio"/>男 單選框
<input type="number" /> 數字值
<input type="week" /> 日歷
<input type="checkbox" />楊冪 復選框
3、表格標簽 table <table>表里 <tr> 行 里包含 <td> 列
參數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-使用標簽<style>--層疊樣式表
CSS語法:元素的選擇器{ 修飾的具體樣式 屬性名:屬性值;}
CSS使用的位置: <style> CSS代碼 </style>標簽
- 行內CSS(給正在用的標簽,添加style的屬性) <div style="text-align: center;">大家好</div>
- 內部CSS(使用html提供的<style>標簽,把css代碼包裹起來) <head>里加<style> 選擇器
- 外部CSS(在網頁里,引入一個外部CSS文件)
選擇器:簡單選擇器(標簽名,id,類class選擇器)分組選擇器 屬性選擇器--選擇好了,可以設置我們特殊的樣式了
CSS選擇器
1、簡單選擇器
- 標簽名選擇器 自己給標簽起個名字,選擇的太多
- 類Class選擇器 ①:加class屬性, 精確的選擇 ②:點class的值{ } <span class="a b"> 如果a 和b中有相同的設置,b會覆蓋a的
- Id選擇器:①:按照id屬性的值(唯一) ②:#id的值 ---更高效 唯一
1、分組選擇器
將多個選擇器選中的元素組合在一起,用,號隔開 div,span{
2、屬性選擇器
通過標簽的屬性選中元素input [ type='text' ]{ } //a [href="!"]{} //a[href] 不寫值也可
3、引用外部的CSS,在同一級目錄
<link href="yonghuzuce.css" rel="stylesheet"/> 可以直接寫名字 stylesheet:樣式表
9、JS--使用標簽<script>
1、JS概述
JavaScript 是 web 前端開發者必學的三種語言之一:
· HTML 定義網頁的內容 H5
· CSS 規定網頁的布局 CSS3
· JavaScript 實現網站的交互--人機互動--動態 ES6
JS是一門 基于對象 和 事件驅動 的 腳本語言 ,通常用來提高網頁與用戶的交互性。
2、名詞解釋
基于對象:它不僅可以創建對象,也能使用現有的對象。JS沒有類的概念,也沒有編譯的過程。是一邊解釋一邊執行。
事件驅動:在JS中,大部分情況下都是通過事件觸發驅動函數執行的,從而實現特定的功能。(比如點擊div將內容替換為時間、當鼠標滑過元素,元素就有翻轉的動態。)
腳本語言:在網絡前端開發環境下,用于嵌入在客戶端瀏覽器中的一段小程序。
3、特點和優勢
特點:
(1)JS是一門直譯式的語言,直接執行的就是源代碼.
是一邊解釋一邊執行,沒有編譯的過程(不像Java需要提前編譯為class文件再運行).
(2) JS是一門弱類型的語言,沒有嚴格的數據類型.
優勢:
(1)良好的交互性
(2)一定的安全性(JS被強制的要求,不能訪問瀏覽器以外的東西,只能訪問瀏覽器和瀏覽器內部的資源)
(3)跨平臺性(Java語言具有跨平臺性,是因為有虛擬機)
只要有瀏覽器的地方都能執行JS
4、引入JS代碼
1、行內JS
<div οnclick="alert(100)">我是div</div> onclick點擊事件 alert 是彈出窗口的
<div οndblclick=--雙擊
<div οnmοuseenter=--鼠標移入
2、內部JS 在<head>里 <script>alert('嘟嘟嘟嘟~~~')</script> //執行代碼
3、外部js <script src="1.js"></script>//引入外部的js文件
不要同時通過一個script標簽引入JS代碼和JS文件,會導致代碼不會執行
5、js的數據類型
數據類型包括:number / string / boolean / null / undefined
數字類型運算自動轉換類型 alert(2.4+3.6) 直接輸出6不是6.0
字符串類型中用單引號和雙引號均可
6、js的變量--常量
var 定義變量,沒有嚴格的區分變量類型
alter(a==b);//比較的是值
alter(a===b);//比較值和類型
const常量:const定義常量且必須初始化
typeof運算符: 用于返回變量或者表達式 的數據類型
JS數組:單個的變量中存儲多個值(個容器)。例如:數值、字符串、布爾值、undefined、null、對象、函數等
JS函數:類似java中的方法
方式一:function 聲明函數
聲明:function 函數名稱( 參數列表 ){ 函數體 } 沒有用變量來接受創建好的函數
調用: 函數名稱( 參數列表 );
function add(a , b){//定義含參函數 alert(x+y); }//不用寫變量類型
Add(1,2);//調用add方法
方式二:var 函數名稱 = function (x,y)-----這種注意不能先調用。因為后面給名字
JS對象:window對象可以提供alert/confrm/prompt輸入框/document
string對象
number對象
自定義對象:
1、方式一;
聲明對象:function Person(){}-----P要大寫
------------------用創建出的p對象 更改Person對象---------------------
創建對象:var p1 = new Person();
設置屬性:p1.name = "張飛"; p1.age = 18;-----創建出來的對象 直接添加值
設置方法:p1.run = function( ){ }-----在Person中添加run函數
訪問p1對象:
它的屬性可以邊寫邊創建
2、方式二:
定義對象的名稱 var p2={
“變量名”:values, "pname":"張三",
"page":20,
綁定函數 "psay":function(){
console.log(this.pname+this.page);
}}
打印對象 console.log(p2);----{pname: '張三', page: 20, psay: ?}
調用對象的方法 p2.psay();
10、document對象
1、獲取方式window.document;先寫一句這個
2、調用方法 document..getElementById("id屬性的值")--返回1個元素,根據id找
Document..getElementsByName("name屬性的值")--返回多個元素(用數組)
getElementsByClassName("class屬性的值")--返回多個元素(用數組)
11、JSON--JavaScript 對象表示法
JavaScript Object Notation
1、JSON數據:本身是字符串
Var a=’”fristname”:”josn”’ ---外層有引號 內層的key和values都要有引號
不同于創建對象時候key的引號可以省略
2、JSON對象---多個屬性
var a = '{ "firstName":"John" , "lastName":"Doe" }'
3、JSON 數組:
var a = '[ { "firstName":"Bill" , "lastName":"Gates" },{ "firstName":"George" , "lastName":"Bush" } ] ';
給服務器發送數據: 將JS對象轉成JSON字符串 JSON.stringify(Js對象)
接受服務器的數據: JSON字符串轉成JS對象 JSON.parse("json字符串")
轉換成js對象后,直接 .name 屬性就能獲取name的值
var js=JSON.parse(a); console.log(js.name);
轉成字符串后 就可以直接對字符串操作,拼接、求長度等
12、vue
· 一個輕量級的mvvm框架,雙向綁定,數據動態更新,gzip后大小只有20k+
· 是一個漸進式框架,其核心思想是數據驅動、組件化的前端開發
· 原生html頁面是通過js 操作的是dom,而vue.js操作的是數據。
3、使用vue準備數據 <script>標簽里面創建vue對象
<script>new Vue({ vue的兩個屬性,用{}包起來,里面寫的是js對象})</script>
el 屬性 掛載點 element的簡稱,即將把準備好的數據 渲染到指定區域
data屬性 準備要顯示的數據,傳遞數據
第三階段
一、linux命令--在Mobaxter
1.1 cd命令集
ifconfig/ip addr 檢查IP地址
pwd 檢查當前的位置
tab鍵 自動補齊(注意唯一性)
cd命令是linux中最基本的命令語句,必須熟練掌握
cd / 返回根目錄
cd ~ 用戶主目錄
cd . 當前目錄
cd ..返回到上一級目錄
cd /usr/ 進入到usr目錄
cd – 返回上一個目錄
cd 直接回家
1.2 ls目錄和文件
ls –l 詳細格式,文件權限,時間
ll 和ls –l作用相同
ls *.txt 查看所有的txt類型文檔
1.3 目錄操作
mkdir 創建目錄
mkdir a 創建 a目錄
mkdir a/aa/aa 創建多級目錄
mkdir -p a/b 創建 a目錄,并在a目錄里創建b目錄
mkdir -m 777 c 創建一個權限為777的C目錄
rmdir 刪除目錄(如果目錄里有文件,則不能用此命令)
1.4 Vi/vim創建/查看/編輯文件
命令行:Esc切換到命令行模式。
編輯模式:先按一下切換模式
按i,在光標前開始編輯
按a,在光標后開始編輯
按o,在當前行的下一行開始編輯
按u, 撤銷之前的操作---基本上是撤銷一行
底行模式:按 shift+:冒號。
:q! 不保存退出
:wq 保存退出
:/world 從當前光標處,向上查找world關鍵字
:?world 從當前光標處,向后查找world關鍵字
1.5 刪除文件
rm 刪除文件
rm n.txt 提示y刪除n放棄
rm –f n.txt 不提示
rm –rf dirname 不提示遞歸刪除目錄下所以內容
rm –rf * 刪除所有文件
rm –rf /* 刪除所有子目錄所有和文件
1.6 復制和移動文件
cp復制文件
cp nginx.conf n.txt
cp –R tomcat1 tomcat2 #復制整個目錄
mv 修改文件名,移動文件
mv n.txt m.txt 修改文件名稱
1.7瀏覽文件
cat 輸出文件所有的內容
more 輸出文檔所有的內容,分頁輸出,空格瀏覽下一屏,q退出
less 用法和more相同,只是通過PgUp、PgOn鍵來控制
tail 用于顯示文件后幾號,使用頻繁
tail -10 nginx.conf 查看nginx.conf的最后10行
tail –f nginx.conf 動態查看日志,方便查看日志新增的信息
ctrl+c 結束查看
1.8 打包命令
tar命令位于/bin目錄下,它能夠將用戶所指定的文件或目錄打包成一個文件,但不做壓縮。一般Linux上常用的壓縮方式是選用tar將許多文件打包成一個文件,再以gzip壓縮命令壓縮成name.tar.gz的文件。
-c 創建一個新的tar文件
-v 顯示運行過程的信息
-f 指定文件名
-z 調用gzip壓縮命令進行壓縮
-t 查看壓縮文件的內容
-x 解開tar文件
tar –cvf n.tar ./* 壓縮當前目錄下的所有文件和目錄,文件名為n.tar
tar –xvf n.tar 解壓壓縮包中的文件到當前目錄(如果長時間未解壓成功 Ctrl+C推出)
tar –cvzf m.tar.gz ./* 壓縮文件
tar -zxvf m.tar.gz 解壓m.tar文件到當前目錄
1.9grep命令
grep root /etc/passwd 在文件中查找關鍵字root
grep root /etc/passwd –-color 高亮顯示
grep root /etc/passwd –A5 –B5 高亮顯示,A后5行,B前5行
grep -n root /etc/passwd 查找并顯示行數
grep -v root /etc/passwd 取反,查出不含root的數據
第四階段-----微服務
知識基礎:
1、RestTemplate--客戶端
Spring RestTemplate 是 Spring 提供的用于訪問 Rest 服務的客戶端,RestTemplate 提供了多種便捷訪問遠程Http服務的方法,第三方服務商都是使用 RestTemplate 請求 restful 服務。
 @Autowired//從spring容器獲取一個RestTemplate對象,基于此對象實現遠端服務
 private RestTemplate restTemplate;
public String doRestEcho1(){
 //1、定義要調用的遠端服務的url
 String url="http://localhost:8081/provider/echo/"+server;
 //2、基于restTemplate對象中的相關方法進行服務調用
 return restTemplate.getForObject(url,String.class );
 }
服務消費方是如何調用服務提供方的服務的?(RestTemplate)--客戶端
服務之間進行服務調用時,使用了什么API?(RestTemplate)
2、Socket/ServerSocket對象
* 1.網絡服務端(ServerSocket) ---用tomcat實現
 * 2.網絡客戶端(Socket)--用Browser
//網絡中計算機的唯一標識是:ip
 //計算機中應用程序的唯一標識:端口port
3、讀寫鎖--ReadWriteLock
寫鎖--悲觀寫,自能自己寫,別人不能寫
讀鎖--樂觀讀,拿讀鎖后還能被別人讀
創建讀寫鎖ReentrantReadWriteLock
4、過濾器Filter
過濾器鏈:FilterChain
一般和servlet控制器 (請求控制邏輯) 組合使用,控制請求的分發等等
5、Servlet控制器
Servlet是java后臺程序與用戶交互的機制(媒介).
Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務程序或服務連接器,主要功能在于交互式地瀏覽和生成數據,生成動態Web內容。
狹義的Servlet是指Java語言實現的一個接口,廣義的Servlet是指任何實現了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運行于支持Java的應用服務器中。從原理上講,Servlet可以響應任何類型的請求,但絕大多數情況下Servlet只用來擴展基于HTTP協議的Web服務器。
servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監聽端口,請求過來后,根據URL信息,確定要將請求交給哪個servlet去處理,然后調用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。
核心方法:request.getParameter--parameter是參數的意思,參數是自己取的,根據request對象取值
public String findUserByIds(HttpServletRequest request)
{String id = request.getParameter("id"); //自己從request對象中取出信息
參數名稱必須相同、弊端無論什么樣的數據,都是String數據類型,需要手動的轉化
SpringMVC: 在內部封裝了Servlet機制.并且可以根據用戶的參數類型,實現自動的數據類型的轉化
6、tomcat處理請求流程
tomcat 處理客戶端的請求時的一個簡易流程分析
關鍵組件:
服務注冊 與發現---Nacos 服務的配置--配置中心
負載均衡-Ribbon
遠程服務調用-Feign
限流降級--Sentinel
訪問入口管理--網關GateWay
分布式事務管理
一、Nacos注冊中心
1.1應用背景、配置
不停機就可以動態刷新服務內部的配置項--調整日志級別
例如,在生產環境上日志級別error ,系統出問題對它 debug 的時候,日志級別調整為 debug 級別。
服務注冊與發現,一般需要添加的依賴有兩個spring-cloud-starter-alibaba-nacos-discovery/config--這是配置中心的
- 實現Nacos服務注冊需要添加什么依賴?(兩個spring-boot-starter-web,spring-cloud-starter-alibaba-nacos-discovery)
- 實現Nacos服務注冊時,必須做哪些配置?(服務名,假如是本機服務注冊可以省略服務地址)
- Nacos如何檢查服務狀態?(通過心跳包實現)
- 服務之間進行服務調用時,使用了什么API?(RestTemplate)
要想下載的nacos能夠管理對應的數據庫表--找到nacos文件的conf(配置)中的application.properties,更改里面的表名,數據庫連接密碼等。
啟動Nacos:在bin目錄下打開cmd輸入:startup.cmd -m standalone
訪問Nacos:運行成功后會顯示訪問端口號和路徑http://localhost:8848/nacos
- 什么是配置中心?(存儲項目配置信息的一個服務)
- 為什么要使用配置中心?(集中管理配置信息,動態發布配置信息)
- 配置中心一般都會配置什么內容?(可能會經常變化的配置信息,例如連接池,日志、線程池、限流熔斷規則)
- 什么信息一般不會寫到配置中心?(服務端口,服務名,服務的注冊地址,配置中心)
- 市場上有哪些主流的配置中心?(Apollo,nacos,Spring Cloud全家桶……)
1.2nacos動態刷新
我們系統在瀏覽器中能看到日志級別的變化
* 配置中心的level--日志級別 發生了變化,但屬性的值不變:屬性的值是對象初始化的時候讀取的 *
@RefreshScope--在controller類上添加注解--創建監聽器--監聽配置中心,只要配置中心發生變化,就重新創建對象
日志級別:error>warn>info>debug>trace
當我們后臺設計的日志級別是info,trace/debug 等級別小的能在后臺打印,warn/error比info大的不打印
- 你項目中使用的日志規范是什么?(SLF4J)
- 項目中使用的日志API是什么:(org:slf4j.Logger)
- Nacos配置中心宕機了,我們的服務還可以讀取到配置信息嗎?(可以從內存,客戶端獲取了配置中心的配置信息以后,會將配置信息在本地內存中存儲一份.)
- 微服務應用中我們的客戶端如何獲取配置中心的信息?(我們的服務一般首先會從內存讀取配置信息,同時我們的微服務還可以定時向nacos配置中心發請求拉取(pull)更新的配置信息)
- 微服務應用中客戶端如何感知配置中心數據變化?(當數據發生變化時,nacos找到它維護的客戶端,然后通知客戶端去獲取更新的數據,客戶端獲取數據以后更新本地內存,并在下次訪問資源時,刷新@Value注解描述的屬性值,但是需要借助@RefreshScope注解對屬性所在的類進行描述)(nacos客戶端會基于長輪詢機制,從nacos獲取配置信息)
- 長輪詢:如果沒有配置更新時 就在nacos服務端的對列 等待 短輪詢:如果沒有就離開
1.3定時任務Timer/ScheduledExecuteService
- 基于Timer對象實現定時任務調度(單線程有順序的任務調度)---最大缺陷是多個任務不能并發執行
//Timer對象創建時會創建一個線程(單線程用Timer),并未線程分配一個對列(多任務排隊)
Timer timer=new Timer();
//構建任務對象 TimerTask 是接口類型
 TimerTask task1=new TimerTask()
//3.定時執行任務
 timer.schedule(task1,1000,1000);
- 基于ScheduledExecuteService實現多線程任務調度(Nacos中定時心跳,配置長輪詢都基于此對象)
 //構建一個負責任務調度的線程池對象,池中最多有5個核心線程
 ScheduledExecutorService ses=Executors.newScheduledThreadPool(5);
 //構建任務對象
 Runnable task=new Runnable() {//重寫里面的方法--要執行的代碼
 //執行任務對象(定時任務調度)
 ses.scheduleWithFixedDelay(task, initialDelay:1,//初始延遲 delay:1, //每隔一秒執行一次(與任務是否結束無關)TimeUnit.SECONDS);//每秒刷新一次
2、基于雙重校驗機制,實現類的單實例設計--懶漢式
1.4多線程從數據庫獲取數據
問題1:數據緩存問題
問題2:多線程安全問題(不能取null值),和并發問題
安全問題:
list集合用 CopyOnWriteArrayList<>()
Vector<>是悲觀鎖,別人不允許更新,值允許一個人更新
CopyOnWriteArrayList 是一個線程安全的list集合,允許多個線程并發更新,但是只能有一個更新成功
并發問題:
雙重校驗+加鎖
1.5共享配置文件--點單登錄
在同一個namespace的工作空間中,提取相同的配置,存儲到指定配置文件
單點登錄:商城中不同的模塊,不需要多次登錄,相當于登錄的通行證(加密的)--需要校驗--秘鑰
應用jwt.io技術網站,生產令牌,包含用戶信息--后期需要解密
日志的級別、秘鑰token、可以當做共享配置
- 項目中為什么使用org.slf4j.Logger對象記錄日志?此對象是日志規范,對外面的門面,可以更好降低耦合
- @Slf4j注解的作用是什么?(此注解屬于lombok,用于描述類,在類中創建org.slf4j.Logger對象)
- Nacos配置中心依賴對項目中配置文件的名字有要求嗎?(bootstrap.yml)
- Nacos中@RefreshScope注解的應用場景以及作用.:動態創建對象,當數據更新時就創建一次對象
二、負載均衡--Ribbon
2.1LoadBalancerClient應用
client--客戶端、用戶端、客戶、委托人
LoadBalancerClient對象可以從nacos中基于服務名獲取服務實例,在工程中基于算法實現負載均衡方式的調用。spring幫我們創建好了LoadBalancerClient對象,直接注入就可以了,
loadBalancerClient.choose("sca-provider");//choose綁定負載均衡地址 serviceId--服務的id
2.2Ribbon負載均衡--優化
Netfilx公司進行維護的
在啟動類中創建RestTemplate對象,使用@loadBalanced注解描述RestTemplate對象時,假如發起遠程服務調用,底層會對這個請求進行攔截,攔截到此請求后會基于LoadBalancerClient對象獲取服務實例,然后進行負載均衡方式的調用。簡化了loadBalancerClient對象獲取實例信息的過程。
為RestTemplate對象注入攔截器,在底層攔截器中實現服務實例的獲取
- 何為服務發現?(從nacos獲取服務實例)
- LoadBalancerClient的作用?(從nacos獲取服務實例列表,然后本地基于負載均衡算法 獲取服務實例)
- @Loadbalanced作用?(描述RestTemplate對象,讓系統底層為RestTemplate對象賦能--簡化)
三、遠程服務調用--Feign
3.1feign的注解
Feign 是一種聲明式Web服務客戶端,底層封裝了對Rest技術的應用,通過Feign可以簡化服務消費方對遠程服務提供方法的調用實現。(不用自己拼url了)
1、添加依賴
<!--feign 中的api封裝了遠程服務調用方式以及錯誤機制--> 可以不用 直接用RestTemplate
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
2、@EnableFeignClients--啟動類上添加注解
使用@EnableFeignClients注解配置啟動類時,主要用于告訴spring框架,要對使用@FeignClient的接口創建實現類以及對象
實現Feign的時候 注意有代理對象,然后實現的http:url的訪問遠程服務
3、@FeignClient--接口上,定義遠程調用規范
@FeignClient(name="sca-provider")--name屬性是遠端服務名,同時也會將這個名字作為接口實現類的 Bean對象名字
4、@GetMapping等注解--在接口中的方法上添加
Feign接口是基于方法上的@GetMapping等注解中的value(默認不寫)值,調用遠端服務的某個具體方法
 @GetMapping("/provider/echo/{msg}")--調用sca-provider中的provider類中的echo方法
 String echoMsg(@PathVariable("msg") String msg);//jdk版本中只有參數arg0,arg1,所以要特定指定參數
 5、創建controller類,定義方法,基于怎么樣的額方式調用遠程服務
外界輸入的url:http://localhost:8090/consumer/echo4/aaa先訪問consumer中的方法,然后跳轉到feign接口中
3.2Feign配置進階
防止同一個服務提供了很多服務調用方法,并添加容錯方案
1、添加唯一標識屬性屬性
為遠程調用服務接口指定一個contextId屬性,作為遠程調用服務的唯一標識即可
@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider服務提供名稱
interface RemoteProviderService{
@GetMapping("/provider/echo/{string}")//前提是遠端需要有這個服務
public String echoMessage(@PathVariable("string") String string);
}
2、實現類做相關處理
創建實現類--調用feign.hystrix提供的FallbackFactory接口
@Component
 public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService前面的接口> {
/**此方法會在RemoteProviderService接口服務調用時,出現了異常后執行.*/
 @Override
 public RemoteProviderService create(Throwable throwable) {//自動重寫的方法
//JDK8中lambda表達式--匿名函數--用箭頭操作符--左側 : Lambda 表達式的參數列表
 --右側 : Lambda 表達式中所需執行的功能, 即 Lambda 體
return (msg) -> {
return "服務維護中,請稍等";
};
 }
 3、在@FeignClient注解的接口中,添加fallbackFactory屬性=實現類的名字;在Feign訪問接口中應用FallbackFactory對象
4、服務中斷處理機制
3.3調用流程
1)通過 @EnableFeignCleints 注解告訴springcloud,啟動 Feign Starter 組件。
2) Feign Starter 在項目啟動過程中注冊全局配置,掃描包下所由@FeignClient注解描述的接口,然后由系統底層創建接口實現類(JDK代理類),并構建類的對象,然后交給spring管理(注冊 IOC 容器)。
3) 接口被調用時被動態代理類邏輯攔截, @FeignClient 請求信息通過編碼器生成 Request請求對象,基于此對象進行遠程過程調用。
4) 請求對象經Ribbon進行負載均衡,挑選出一個健康的 Server 實例(instance)
5) 通過 Client(客戶端) 攜帶 Request 調用遠端服務返回請求響應。
6) 通過解碼器生成 Response 返回客戶端,將信息流解析成為接口返回數據。
- Feign是什么?(Spring Cloud微服務規范中的一組遠程調用API)
- 為什么使用Feign?(優化服務調用結構)
- 如何使用Feign實現服務調用?(加入依賴,@EnableFeignClients啟動類上,@FeignClient加在接口上)
- Feign方式的服務調用原理是怎樣的?(底層基于代理對象實現)
四、Sentinel限流和熔斷
Sentinel (分布式系統的流量防衛兵) 是阿里開源的一套用于服務容錯的綜合性解決方案
以流量為切入點, 從流量控制、熔斷降級、系統負載保護等多個維度來保護服務的穩定性。
sentinel的核心:核心庫(java客戶端)、控制臺(Dashboard)儀表盤--整合了很多第三方的框架,圖表等等
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
 </dependency>
1、添加了此依賴以后,會在項目中添加一個攔截器對象,這個對象會對此服務發出的請求進行攔截,會與sentinel控制臺定義的限流規則進行比對,在允許范圍內則繼續訪問,否則則拋出處理后的異常。
stringMVC中有Handler Interceptor這個類提供的攔截器prehandle---在controller類之前攔截,如果服務重啟了,規則消失--需要改源碼
2、修改yml
sentinel:
 transport:
 dashboard: localhost:8180 #這里描述的是sentinel控制臺的地址
eager: true #服務啟動后就與sentimel控制臺注冊 而不用瀏覽器訪問請求了
4.1sentinel限流
三種限流模式:直接限流,關聯限流,鏈路限流(@SentinelResource("資源名"))
三種流控效果:快速失敗,預熱(WarmUp),排隊等待
- Sentinel是什么?(阿里推出一個流量控制平臺,防衛兵)
- 類似Sentinel的產品你知道有什么?(hystrix-一代微服務產品spring-cloud微服務)
- Sentinel是如何對請求進行限流的?(基于sentinel依賴提供的攔截器)
- 為什么要進行限流? (系統處理能力有限,可以通過限流方式,保證系統可靠運行)
- Sentinel限流的基本原理?(底層對請求進行攔截,然后通過流控規則限定對資源訪問)
- Sentinel限流有哪些算法? (計數器,令牌桶,漏桶,滑動窗口算法--sentinel默認)
- 你了解sentinel中的閾值應用類型嗎?(兩種-QPS(每秒請求次數),線程數)
4.2sentinel降級熔斷
鏈路中不穩定的資源進行熔斷降級---調用超時或異常比例升高,讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤。當資源被降級后,在接下來的降級時間窗口之內,對該資源的調用都自動熔斷(默認行為是拋出 DegradeException)
4.3sentinel異常處理
自己進行定義異常處理機制,直接或間接實現BlockExceptionHandler接口,并將對象交給spring管理。用于處理BlockException類型以及子類類型異常
4.4熱點限流
@SentinelResource("resource")--方法上-基于參數或參數值進行限流
熱點規則的限流模式只有QPS模式(這才叫熱點)。sentinel中心設置的參數索引為@SentinelResource注解的參數的下標,0代表第一個參數,1代表第二個參數
4.5授權規則(黑白名單)
自己創建類實現implements RequestOriginParser,交給spring管理加@Component注解,當設置了授權規則后,系統底層攔截請求,會調用此方法,對請求數據進行解析
- Sentinel 的降級(熔斷)策略有哪些?(慢調用-響應時長,異常比例-異常占比,異常數)
- Sentinel 的熱點規則中的熱點數據?(熱賣商品,微博大咖,新上映的電影)
- 為什么要進行異常處理?(提高用戶體驗)
- Sentinel中限流,降級的父類異常類型是什么?(BlockException)
- Sentinel中默認的BlockException處理對象是誰?(DefaultBlockException)
- 如何自己定義Sentinel的異常處理對象?(直接或間接繼承BlockExceptionHandler)
- 如何理解sentinel中的系統規則?(全局限流規則,基于QPS,CPU,…)
- 如何理解Sentinel中的授權規則?(黑白名單)
五、網關GateWay
訪問入口管理--API網關--就是URL的入口管理
網關是訪問內部服務的入口,隱藏真實地址,對請求中的數據過濾--用Filter過濾器
Spring Cloud Gateway提供了權限認證,監控、限流等功能,--單點登錄認證
- 優點:
- 缺點:
5.1配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
API網關在微服務架構中也是一個web服務,但這個web服務的啟動不是依賴于tomcat
 而是依賴于網絡編程框架Netty,添加依賴后,系統會自動幫我們關聯下載一個netty框架
server:
port: 9000
spring:
cloud:
gateway:
routes: #配置網關路由規則
- id: route01 #路由標識符id,自己指定一個唯一值即可
uri: http://localhost:8081/ #網關幫我們轉發的url,轉發的目的地(微服務) url是屬于uri的子級
predicates: ###斷言(謂此):匹配請求規則,進行條件判斷,只有斷言都返回真,才會執行路由
- Path=/nacos/provider/echo/** #請求路徑定義,此路徑對應uri中的資源
filters: ##網關過濾器,用于對謂詞中的內容進行判斷分析以及處理
- StripPrefix=1 #轉發之前去掉path中第一層路徑,例如nacos
啟動gateway、啟動nacos,啟動provider類,在類中找/provider/echo/*這個方法,如果有就能跳轉
訪問http://localhost:9000/nacos/provider/echo/dakun
5.2負載均衡設計
在訪問服務時,要基于服務serivce id(服務名)去查找對應的服務,讓請求從網關層進行均衡轉發
1、我們需要將網關作為一個服務在nacos中進行注冊--添加spring-cloud-alibaba-nacos-discovery
2、yml中配置, uri: lb://sca-provider lb實現負載均衡,后面加服務名
- 網關中沒有配置,是底層自己實現負載均衡,底層的LoadBalance Client Filter這個攔截器調用 Ribbon LoadBalanceClient對象中的choose方法實現負載均衡
- Ribbon是自己來實現Ribbon LoadBalancerClient對象實現的負載均衡。
網關請求轉發流程分析:
客戶端向Spring Cloud Gateway(網關)發出請求。 如果Gateway Handler Mapping(處理器映射器) 通過斷言predicates(predicates)的集合確定請求與路由(Routers)匹配,則將其發送到Gateway Web Handler(web處理器)。 Gateway Web Handler 通過確定的路由中所配置的過濾鏈調用過濾器Filters。找到指定的攔截器(LoadBalanceClientFilter),調用Ribbon LoadBalanceClient對象實現負載均衡
Filter由虛線分隔的原因是, Filter可以在發送代理請求之前和之后運行邏輯。處理的邏輯是 在處理請求時 排在前面的過濾器先執行,而處理返回相應的時候,排在后面的過濾器先執行。
5.3限流設計
1、添加依賴:限流sentinel,sentinel-gateway(因為這個不是mvc的)
 2、修改yml:
3、啟動網關:-Dcsp.sentinel.app.type=1
1、自定義API維度限流(重點)
是一種更細粒度的限流規則定義,它允許我們利用sentinel提供的API,將請求路徑進行分組,然后在組上設置限流規則
1、新建API分組,定義匹配的字符串--請求路徑
2、根據API分組來限流
2、定制流控網關返回值
現在用的網關gateWay不是mvc了,不用寫那么多層,寫一個網關配置類,并用@Configuration注解標識
 一般都是用JSON串來返回響應結果
- 新建一個BlockRequest Handler攔截器,
- 創建一個hashMap<>集合,put放狀態碼,和信息msg
- 把map轉換成json格式的字符串:String jsonStr=JSON.toJSONString(map);
- 返回服務端響應: return ServerResponse.ok().body();
5.4常見問題:
- 為什么使用網關?(服務安全,統一服務入口管理,負載均衡,限流,鑒權)
- Gateway 服務的啟動 底層是通過誰去實現的?(Netty網絡編程框架--ServerSocket網絡服務端)
- 網關層面服務的映射方式怎樣的?(謂詞-path,…,服務名/服務實例)
- 何為謂詞?(網關中封裝了判斷邏輯的一個對象)
- 謂詞邏輯的設計是怎樣的?(謂詞判斷邏輯返回值為true則進行請求轉發)
- 你了解哪些謂詞邏輯?(path,請求參數,請求方式,請求頭,….)
- 我們可以自己定義謂詞工廠對象嗎?(可以的)
- 網關層如何記錄服務的映射?(通過map,并要考慮讀寫鎖的應用)
- 網關層面是如何實現負載均衡的?(通過服務id查找具體的服務實例,底層調用RibbonLoadBalanceClient對象中的choose方法實現負載均衡)
- 網關層面是如何通過服務名查找服務實例的?(Ribbon)
- 網關層面結合sentinel實現限流,其限流的類型有幾種?(兩種-Route id,api分組)
- 網關層面可以自定義限流后的異常處理結果嗎?(可以)
- 你知道Sentinel底層限流的算法有哪些?(滑動窗口,令牌桶,漏斗,。。。)
- 網關過濾器的作用是什么?(對請求和響應數據做一個預處理)
- 網關過濾器的類型有哪些?(局部過濾器,全局過濾器--作用于所有請求鏈路,不用定義)
- 如何自己定義全局過濾器?(直接或間接實現Globa lFilter接口)
-------------------單點登錄系統SSO--------------
Single sign on
解決方案:
解決方案1:用戶登陸成功以后,將用戶登陸狀態存儲到redis數據庫,例如:弊端:多次操作數據庫,高并發的時候,性能差
解決方案2:用戶登陸成功以后,將用戶信息存儲到token(令牌),然后寫到客戶端進行存儲。(本次設計方案),方案一的以前的token就是一個隨機的UUID,沒有實際意義;JWT token是攜帶了信息的令牌
關鍵技術:
redis數據庫
auth認證/授權
Spring Security技術(認證,授權)
JWT技術 JWT token攜帶信息的令牌
也要用到服務的注冊、發現nacos,web服務,mybatis-plus
生成序列化id?
工程的結構:用到的模塊
system系統模塊中涉及到的表
- tb_user_roles--用戶角色關系表,可以在此表中基于用戶id找到用戶角色
- tb_role_menus--角色菜單關系表,可以基于角色id找到菜單id
- tb_menus菜單表,為資源的外部表現形式,在此表中可以根據id找到權限標識
一、auth2認證服務中心
第三方應用授權登錄,允許用戶授權第三方移動應用訪問 另外服務提供者上的信息,不需要將用戶名/密碼提供給第三方移動應用或分享他們數據的所有內容
1、添加依賴、配置文件、啟動類
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-oauth2</artifactId>
 </dependency>
<!--  用feign的方式調用,調用另外服務提供者-->
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
加載的spring-cloud-starter-oauth2依賴中,包括Spring Security(內置了一個頁面)+JWT+oauth2,所以我們一訪問localhost:8071,就會找到登錄頁面,登錄后默認跳轉到index.html,并產生一個password
簡單的配置后已將可以登陸了
二、簡單的單體登錄
2.1遠程信息調用
1、啟動類上添加@EnableFeignClients--啟動feign掃描,使用feign實現遠程調用
2、創建service接口中@FeignClient遠程調用 遠程用戶 信息 ---直接sso-system中controller中的方法
@FeignClient(value = "sso-system",contextId = "remoteUserService")
2.2遠程服務調用/認證管理
3、創建業務邏輯處理對象,接口的實現類,實現遠程 服務 調用 ,交給認證管理器(AutnenticationManager)根據獲取到的信息去完成密碼的比對操作 (
- 根據用戶名查找用戶信息,判斷用戶是否存在,不存在則拋出異常
- 根據用戶id查詢用戶 是否有權限)
- 和封裝返回 用戶信息和權限對象,此對象最終會交給認證管理器
request-->filer過濾器-->servlet控制器-->handler interceptor攔截器-->controller
2.3Security配置類--密碼加密
4、定義密碼加密對象Security配置類(要不然登錄會報錯)
@Configuration
 public class SecurityConfig {
/*構建密碼加密對象,比MD5更強,登錄時,系統底層會基于此對象進行密碼加密*/
@Bean
 public PasswordEncoder passwordEncoder(){
 return new BCryptPasswordEncoder();
 }
 }//返回的是一個password的編碼器對象
2.4配置認證規則(攔截規則)
繼承WebSecurityConfigurerAdapter
登陸請求url的放行(不需要認證)和攔截(需要認證)
2.5自定義處理方法--可省
2.6總結
單體登錄基本完成,但是上述設計所有的頁面都在一個項目中,只有一個tomcat——我們要完成分布式(多個服務器)。以上用戶的認證操作,其實現主要基于Spring Security框架
設置cookies緩存,單體登錄都是存在session中的,servlet服務器端的Session,向客戶端響應一些cookies(有效期就是會話時間)
還有生成JWT token給用戶的相關信息進行加密——分布式架構,多個服務器
用auth認證和授權
三、分布式單點登錄系統
3.0總結:
Spring Security是基礎,OAuth和Jwt是具體實現,在security上生效
OAuth2.0是規范、一種授權協議,不是實現
jwt是token的實現
如果我們的系統要給第三方做授權,就實現OAuth2.0
如果我們要做前后端分離,就實現token就可以了,jwt(JSON WEB Token)僅僅是token的一種實現方式
- 1)SpringSecurity (提供認證和授權的實現)
- 2)TokenConfig(提供了令牌的生成,存儲,校驗)
- 3)Oauth2(定義了一套認證規范,例如為誰發令牌,都發什么,...)
3.0Spring Security企業級安全框架
對軟件系統中的認證,授權,加密等功能進行封裝,Spring Security 在企業中實現認證和授權業務時,底層構建了大量的過濾器,在過濾連中定義Oauth2和JWT的具體實現
3.1定義令牌的存儲對象--三種
* 構建令牌配置對象,在微服務架構中登陸成功后,可以將用戶信息進行存儲,
常用存儲方式
 * 1、產生一個隨機字符串(token),然后基于此字符串將用戶信息存儲到關系數據庫(mysql)
 * 2、產生一個隨機字符串(token),然后基于此字符串將用戶信息存儲到內存數據庫(redis)
 * 3、基于Jwt創建令牌,存儲用戶信息,不需要寫到數據庫,在客戶端存儲即可
 * 基于上述設計方案,Oauth2協議給了具體的API實現對象,例如:
 * 1、JdbcTokenStore(用的較少)
 * 2、RedisTokenStore(中型應用,比jdbc快)
 * 3、JwtTokenStore(對性能要求比較高的分布式架構)
Jwt數據規范--令牌生成
JWT(JSON WEB Token),是基于token 的認證協議的實現
有三部分構成:
Header頭部:是一個JSON對象 描述JWT的元數據 包含算法、令牌token的類型
{
"alg": "HS256",//alg屬性表示簽名的算法(algorithm)
"typ": "JWT" //令牌的類型(type)
}
Payload:實際需要傳遞的數據,JWT規范中規定了7個官方字段,供選用
Signature:簽名部分,防止數據被串改,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道
算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
3.2定義Oauth2認證授權配置類
1、創建配置類,添加@EnableAuthorizationServer 啟動授權和認證,@Configuration和全參構造
繼承AuthorizationServer Configurer Adapter 類(Adapter適配器模式)
2、配置認證規則:
- 配置由誰完成認證(認證管理器)
- 配置由誰負責查詢用戶業務數據,訪問數據庫(認證時需要兩部分信息:客戶端,數據庫)
- 配置可以處理的認證請求方式(默認支持post方式)
- 配置token生成及存儲策略(默認令牌生成UUID,存儲方式為內存)
3、修改令牌方法,主要是令牌增強,配置令牌有效時間、是否支持刷新令牌、另設置刷新有效時長
下面解決兩個問題:
- 對誰頒發令牌(對客戶端有沒有要求,需要進行配置)
- 訪問哪個路徑時幫你頒發令牌,需要對外暴露認證路徑(定義頒發、解析、效驗令牌的路徑)
4、在內存中配置,定義客戶端的id(client_id 客戶端提交用戶信息認證時需要這個id),定義客戶端秘鑰(client_secret 客戶端提交用戶信息時候需要攜帶這個秘鑰),定義作用范圍(符合規則的客戶端),允許客戶端基于密碼方式,刷新令牌實現認證(grant_type=password)
5、我們登陸時要對哪個url發起請求,通過哪個url可以加載令牌,配置對外暴露的認證url,刷新令牌的url,檢查令牌的url,
6、postMan訪問
7、401是認證失敗--沒有攜帶令牌
403是沒有訪問權限--某種請求方式沒有在數據庫中設置權限
3.3略過的內容
資源服務工程的設計與實現--sso-resource模塊--不做認證,只做資源管理
網關工程--sso-gateway
客戶端UI工程--sso-ui--前后端分離項目,添加login.html頁面
網關層面添加限流
資源服務resource中日志的獲取---AOP方式獲取日志---Feign方式將日志傳遞給系統服務
--------------Docker虛擬引擎----簡化運維-----------
一、Go語言和CS架構
Docker是一個虛擬引擎(基于現有的資源虛擬出一個容器)、容器化技術平臺,基于 Google 公司的 Go 語言進行實現,基于這種方式,可更快地打包、測試以及部署應用程序
有了docker,一個安裝配置好的mysql容器,可以直接拿到另一臺主機上啟動,而不必重新安裝mysql
誕生背景:服務多了維護困難,簡化部署運維,提高服務的可維護性
docker平臺基本架構CS(Client/Server)
核心對象:鏡像Image 容器Container
二、docker的操作命令
登錄 · 語雀
三、Docker數據管理
- 數據卷(Volumes):可供一個或多個容器使用的特殊目錄,可以在容器之間共享和重用,默認會一直存在,即使容器被刪除。多個容器共用一個數據卷
- 掛在主機目錄(Bind mounts):
四、Dockerfile鏡像制作
Jdk鏡像、
基于JDK鏡像sentinel鏡像、
Mysql數據庫鏡像、
Redis數據庫鏡像、
Nginx代理鏡像、
nacos容器
五、常見問題
什么是數據卷(Volume)?(docker中的一個數據管理對象)
常用數據卷操作有哪些?(創建,查看,應用,刪除)
如何理解數據卷和目錄掛載(Mount),兩者的不同(是否由Docker管理)?
鏡像的制作過程是怎樣的?(app+Dockerfile->Build)
鏡像文件可以被多個容器共享嗎?(可以,可以基于同一個鏡像文件啟動多個容器)
常用鏡像文件的下載以及容器的啟動?(MySql,Redis,Nginx,Naocs等)
退出容器后想再進入容器怎么辦? 首先docker ps查看容器是否在運行,假如沒有運行要start啟動
Docker平臺下容器互聯操作(創建網絡,查看網絡信息,應用網絡)
-----------Redis數據庫--緩存技術--------
要求掌握:
- redis的核心api--Jedis --RedisTemplate
- 存儲數據的方式--五重
- 投票系統、購物車、秒殺、單點登錄系統
- redis的持久化(兩種)
redis--分布式緩存數據庫,是一個key-value存儲系統
我們現在的項目基本上是web服務器(Tomcat)和數據庫獨立部署的,獨占資源,隨著用戶的增多,高并發讀寫數據庫會增大數據庫的壓力,性能下降。如果我們在tomcat服務器上增加本地緩存,并在外部增加分布式緩存,緩存熱門數據。通過緩存就能把絕大多數請求在讀寫數據庫前攔截掉,降低壓力
一、redis操作指令
docker start redis01 ---啟動redis01鏡像
docker exec -it redis01 bash---進入到容器
redis-cli ---進入redis數據庫
redis-cli -p 6379--進入指定端口號的數據庫
exit--退出
shutdown--安全關閉redis服務,保存數據
keys*---查看redis中的額所有key
set key value--基于key/value的形式存儲數據
get key
flushdb--清除當前數據庫數據
flushall---清除所有數據庫數據
expire key seconds---控制key的有效時長
二、Redis數據類型
2.1String類型操作--散列方式
命令:
incr/incrby key value---自增,并返回遞增后的值
decr/decrby key value--自減/自減多個---可以實現博客的點贊操作
append key---向尾部追加值,如果鍵不存在則創建
strlen key---字數統計
mset/mget---同時設置/獲取多個鍵值對
2.2Hash類型--方便存儲對象
大哈希(rides中的key value)中的小哈希(value 中的key和value)
命令:
hset KEY key value key2 value2 ---可以設置多個值
hget KEY key ---獲取value
hmget---可以獲取多個key對應的值
hmset=hset
hincrby---正數加 負數減
hgetall---獲取所有的key value
hexists---是否存在
hdel---刪除
hkeys--獲取所有的key
hvals---獲取所有的value
2.3List類型--可重復、有序
redis中的list相當于java中的linkedList,是一個雙向鏈表,支持正向、反向查找和遍歷。經常用于實現熱銷榜,最新評論等設計
lpush--每次都從左面放 A B C D 的存放是D C B A
rpush--從右面放
rpop--從右面取出,先進先出--對列結構 取出A B C D --比如秒殺活動
lpop--左面出--先進后出--棧結構 取出 D C B A --手機中的頁面,最新評論
del--刪除key中的所有元素-clean
llen--集合的長度
lrange key 0 -1 --查看從第一個取到最后一個數據 不會移除數據,只是讀,pop是取出
linsert--在指定位置插入 linsert lst1 after/before B C --不論左右都是直接在B后面插入C--結果是BCA
lrem--移除幾個元素 lrem lst1 2 C --list中允許元素重復,從左邊移除
ltrim--保留指定位置間的元素,其他的移除,以前是去空格
lindex--查看指定下標位置的元素
rpoplpush ONE TWO--從ONE右邊拿,放到TWO左邊,也可以兩個集合
brpop--阻塞式對列,如果沒有數據給拿出來,會等待時間結束后在取(釋放cpu),b代表阻塞
如何基于redis實現一個隊列結構?(lpush/rpop)
如何基于redis實現一個棧結構?(lpush/lpop)
如何基于redis實現一個阻塞式隊列?(lpush/brpop)
如何實現秒殺活動的公平性?(先進先出--對列)
用戶注冊時的郵件發送功能如何提高其效率?(郵件發送是要調用三方服務,底層通過隊列優化其效率,隊列一般是list結構)
如何動態更新商品的銷量列表?(賣的好的排名靠前一些,linsert)
商家的粉絲列表使用什么結構實現呢?(list結構--有先后順序)
2.4set類型--不重復、無序
Redis中Set集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是O(1)。
sadd--添加元素,重復元素添加失敗,返回0--去重
smembers--獲取內容,成員
sismember--是否存在元素,返回值是布爾類型
spop--移除隨機幾個元素,并返回移除元素
scard--獲取元素的個數--投票系統 計數 點贊
smove--移動元素 smove set1 set2 C 把set1中的C 移動到set2
sunion--合并的顯示,并不是創建新的集合
srem--指定要刪除的元素
2.5zset類型--不重復、有序
zadd--添加元素
zrange--獲取索引區間內的元素
zrangebyscore--獲取分數區間內的元素
zrem--刪除元素
zcard--獲取集合中元素的個數
zincrby--增減,正數增
zcount--獲取分數區間內元素個數
zrank--獲取項在zset中的索引
zrevrank--獲取在zset中的倒序索引
2.6常見問題:
- Redis 簡介(分布式緩存數據庫,非關系型數據庫,NoSQL數據庫)
- Redis 服務的線程模型(6.0之前都是單線程,6.0之后網絡io操作引入了多線程)
- Redis 數據庫的基本操作(服務的啟動,停止,redis的登入,登出)
- 為什么使用redis?(解決分布式系統下數據緩存的問題)
- 如何理解Redis數據庫的大小哈希(hash)操作?(全局是大哈希,局部value是小哈希)
- Redis數據中常用的數據類型的應用場景?(
string類型--方便字數統計,日志追加
hash類--方便存儲對象
list類型--實現熱銷排行榜,最新評論(棧結構-先進后出),秒殺活動(對列結構-先進先出)
set類型--投票系統,計數,點贊功能)
三、Java中操作redis--Jedis
java中操作json 三劍客 jackson fastjson gson(谷歌提供的)
創建springboot工程自動就能應用jackson
Jedis是Java中操作redis的一個客戶端,類似通過jdbc訪問mysql數據庫----spring
頻繁的對象創建和銷毀對象,性能降低--底層是TCP協議(三次握手,四次揮手)
- Redis 客戶端API(Jedis)的基本應用(對象的創建,對象的銷毀,常用方法的應用)
redis的API就是在java中怎么取代虛擬機中的操作命令,基本上都差不多。.set .expire設置key的有效時長 .pipelined--獲取管道 .exists(key) .type(key) 返回值的類型
3.1JedisPool連接池
Jedis連接池的測試--享元設計模式--設計思想通過池減少對象的創建次數,實現對象的可重用性,所有池的設計都有這個設計模式的應用(整數池,字符串池,線程池,連接池)
創建線程池:
public static Jedis getConnection02(){
 if(jedisPool==null){
 synchronized (JedisDataSource.class){//保證線程安全
 if(jedisPool==null){ //雙重校驗
 JedisPoolConfig config=new JedisPoolConfig();
 config.setMaxTotal(16);
 config.setMaxIdle(8);//最大空間
jedisPool=new JedisPool(config,IP, PORT);//創建對象
 }
 }
 }
 return jedisPool.getResource();//獲得資源
 }
volatile:關鍵字--多線程可見性、禁止指令重排序、不保證原子性
四、項目實踐
4.1分布式id
在分布式系統中,數據量將越來越大時,就需要對數據進行分表操作,但是,分表后,每個表中的數據都會按自己的節奏進行自增,很有可能出現ID沖突。這時就需要一個單獨的機制來負責生成唯一ID,生成出來的ID也可以叫做 分布式ID,這里我們借助redis實現一個簡易的分布式id進行實現,
4.2單點登錄--hash
之前單點登錄用的是認證授權,沒存儲到數據庫,這里借助redis類存儲用戶信息
流程:
1、執行登錄認證,將來寫到認證服務器中
- 檢驗數據的合法性(判斷用戶名,密碼是否為空,密碼的長度是否有數字字母等)
- 基于用戶名查詢用戶信息,并判定密碼是否正確
- 用戶存在且密碼正確,將用戶信息寫到redis
- 將token返回給客戶端
2、攜帶token訪問資源服務器
- 效驗token是否為空(為空說明未登陸)
- 基于token查詢redis數據,假如有對應數據說明用戶登錄了
- 檢查用戶是否有訪問資源的權限,有權限則可以訪問
- 返回要訪問的資源
4.3秒殺隊列--list
將商品搶購信息先寫到redis(以隊列形式進行存儲),因為寫redis內存數據庫要比寫mysql數據庫快很多倍
算法:先進先出(FIFO)-體現公平性lpush--rpop
4.4投票系統--set
* 1.投票數據存儲到redis(key:活動id value:多個用戶id的集合)
 * 2.同一個用戶不能執行多次投票--用sismember來判斷返回的布爾值
 * 3.業務操作(投票,獲取總票數,檢查是否投過票,取消投票,投票人)
4.5購物車系統--hash
1.向購物車添加商品--hset每次執行都是覆蓋,不是追加
 2.查看購物車商品--hgetall
 3.刪除購物車商品----
 4.改變購物車某個商品的購買數量---
 需要的參數:用戶的id,商品id,總數
五、redis的API--RedisTemplate
RedisTemplate是springboot工程中操作redis數據庫的一個Java對象
模板方法設計模式
特性:在springBoot中RedisTemplate就不用像Jedis那樣創建連接池了,springboot中默認使用lettuce連接池
ValueOperations<String,String> vo=redisTemplate.opsForValue();//獲取字符串操作對象
5.1RedisTemplate 應用--jdk
基于jdk序列化和反序列化,是SpringRedisTemplate的父類
源碼中默認序列化 Jdk Serialization RedisSerializer
需求:key序列化方式采用StringRedis方式-----setKeySerializer(RedisSerializer.string())
set---序列化 給redis設置值
get---反序列化,從redis拿出值,并解析成數據顯示在控制臺
- StringRedisTemplate和RedisTemplate兩個應用時有什么不同?
StringRedisTemplate基于spring序列化,傳的參數必須是string類型的。RedisTemplate是jdk方式序列化的,存在redis數據的形式不同,是StringRedisTemplate的父類
六、redis與AOP整合應用
基于AOP方式操作redis緩存
@EnableCaching--在啟動類上,啟動aop緩存應用
當我們在調用一個緩存方法時會把該方法參數和返回結果作為一個鍵值對存在緩存中 redis中
@Cacheable--spring--方法上--此注解描述的方法為切入點方法--一般用于存儲
此方法執行時,底層會通過AOP機制,先從緩存取數據,緩存有則直接返回,緩存沒有則查數據,最后將查詢的數據,還會向redis存儲一份
@Cacheable--更新緩存
屬性:CacheNames用來指定緩存組件的名字,將方法的返回結果放在哪個緩存中,可以是數組的方式,支持指定多個緩存。
使用緩存的時候要注意:緩存的雪崩、穿透;要注意緩存的更新,比如定時,然后訪問mysql
七、redis的持久化-rdb和aof
Redis是一種內存數據庫,斷電時,數據可能會丟失。如果通過持久化將數據搞一份兒到磁盤上去,然后再定期同步到一些云存儲服務上去,那么就可以保證一些數據不丟失,保證數據的可靠性。
7.1Rdb方式持久化
redis data base 宿主機中用來存儲數據的磁盤數據--redis的默認的數據持久化方式.系統啟動時會自動開啟這種方式的持久化機制
Rdb方式是通過手動:三種方式保存redis中的key/value--生成快照的方式持久化
save-是一種阻塞式方式必須所有的保存操作都執行完了,
bgsave-異步,保存的數據交給后臺(又啟動了一個進程,不是線程),自己可以接受別的命令
周期性方式保存(如多久的時間數據發生了變化,去保存--生成快照)
常見問題:
1、Redis中的save和bgsave有什么不同?
Redis Save 命令執行一個同步保存操作,將當前 Redis 實例的所有數據快照(snapshot)以 RDB 文件的形式保存到硬盤。
BGSAVE 命令執行之后立即返回 OK ,然后 Redis fork 出一個新子進程,原來的 Redis 進程(父進程)繼續處理客戶端請求,而子進程則負責將數據保存到磁盤,然后退出。
2、RDB持久化機制有哪些優點?
第一:RDB會生成多個數據文件,每個數據文件都代表某一個時刻中redis的數據,非常適合做冷備
第二:RDB對redis對外提供的讀寫服務,影響非常小,可以讓redis保持高性能,因為redis主進程只需要一個子進程,執行磁盤IO操作來進行RDB持久化。
第三:相對于AOF持久化機制來說,直接基于RDB數據文件來重啟和恢復redis進程,更加快速。存儲是內存中的數據,aof中存儲的數命令
3、缺點:數據丟失比較嚴重
它都是每隔5分鐘或更長時間做一次快照,這個時候一旦redis進程宕機,那么會丟失最近幾分鐘的數據
7.2aof方式持久化
默認是關閉的:appendonly no 可能會影響性能:寫一次指令 保存一次磁盤
Aof方式是通過記錄寫操作日志的方式,記錄redis數據的一種持久化機制,這個機制默認是關閉的。
Redis支持三種刷寫模式:何時刷新指令
- #appendfsync always #每次收到寫命令就立即強制寫入磁盤,是最安全的,速度也是最慢的,一般不推薦。
- appendfsync everysec #每秒鐘強制寫入磁盤一次,在性能和持久化方面做平衡,推薦該方式。
- #appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,性能最好但是持久化最沒有保證,不推薦。
日志的重寫:何時啟動重寫
- 當前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。
- 當前AOF文件啟動新的 設置日志重寫過程的最小值,避免剛剛啟動Reids時由于文件尺寸較小導致頻繁的重寫。
常見問題:
1、如何理解AOF方式中的rewrite 重寫 操作?
redis中的可以存儲的數據是有限的,很多數據可能會自動過期,也可能會被用戶刪除或被redis用緩存清除的算法清理掉。只有一部分常用的數據會被自動保留在redis內存中,所以可能很多之前的已經被清理掉的數據,對應的寫日志還停留在AOF中,AOF日志文件就一個,會不斷的膨脹,最好導致文件很大。
所以,AOF會自動在后臺每隔一定時間做rewrite操作,比如當前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。當上次文件過小時,不啟動重寫,覆蓋之前的老日志,從而,確保AOF日志文件不會過大,保持跟redis內存數據量一致.
2、AOF持久化機制有哪些優點?
第一:更好的保護數據不丟失,AOF會每隔1秒,執行一次fsync刷寫操作,最多丟失1秒鐘的數據.
第二:AOF日志文件通常以append-only模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,并且文件不容易破損,即使文件尾部破損,也很容易修復。
第三:AOF日志文件過大的時候,出現后臺重寫操作,也不會影響客戶端的讀寫
第四:AOF日志文件的命令通過易讀的方式進行記錄,非常適合做災難性的誤刪除的緊急恢復,只要這個時候后臺rewrite還沒有發生,那么就可以立即拷貝AOF文件,將最后一條flushall命令給刪了,然后再將該AOF文件放回去,就可以通過恢復機制,自動恢復所有數據.
3、AOF持久化機制有哪些缺點?
第一:對于同一份數據來說,AOF日志文件通常比RDB數據快照文件更大。
第二:AOF開啟后,支持的寫QPS會比RDB支持的寫QPS低,因為AOF一般會配置成每秒fsync一次日志文件,當然,每秒一次fsync,性能也還是很高的。性能比rdb低
第三:AOF這種基于命令日志方式,比基于RDB每次持久化一份完整的數據快照文件的方式,更加脆弱一些,容易有bug。不過AOF為了避免rewrite過程導致的bug,因此每次rewrite并不是基于舊的指令日志進行merge的,而是基于當時內存中的數據進行指令的重新構建,這樣健壯性會好很多
不適合做冷備
4、如何選擇redis的持久化方式?
第一:不要僅僅使用RDB,因為那樣會導致你丟失很多數據。
第二:也不要僅僅使用AOF,因為AOF做冷備沒有RDB做冷備進行數據恢復的速度快,并且RDB簡單粗暴的數據快照方式更加健壯。
第三:綜合使用AOF和RDB兩種持久化機制,用AOF來保證數據不丟失,作為數據恢復的第一選擇; 用RDB來做不同程度的冷備。
八、redis事務處理--樂觀鎖
mysql中大多數都是悲觀鎖,redis中采用樂觀鎖
它使用watch命令監視給定的key,當exec(提交事務)的時候,如果監視的key從調用watch后發生過變化,則整個事務會失敗--多個客戶端之間,都操作,但是只有一個成功了,多個客戶端都沒有阻塞。
也可以調用watch多次監視多個key。注意watch的key是對整個連接有效的,如果連接斷開,監視和事務都會被自動清除。當然exec,discard,unwatch命令都會清除連接中的所有監視。
redis的原子性:分怎么說,有的版本當發生錯誤的時候,還會繼續輸入,但是后來又改了
redis如何保證數據的可靠性:1.持久化,數據不容易丟 2.事務保證提交的數據 3.架構的設計,多個redis
8.1基本指令
redis進行事務控制時,通常是基于如下指令進行實現,例如:
- multi 開啟事務
- exec 提交事務
- discard 取消事務
- watch 監控,如果監控的值發生變化,則提交事務時會失敗--使用樂觀鎖
- unwatch 去掉監控
exec提交事務:先開啟multi事務后,把指令放到一個隊列里面,只有提交exec的時候才會執行指令
discard取消事務:redis事務太簡單,沒有回滾,不會回到原來的數據,而只有取消(遇到錯誤也取消),不執行對列中的指令
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 #樂觀鎖,對值進行觀察,改變則事務失敗
OK
127.0.0.1:6379> multi #開啟事務
OK
127.0.0.1:6379> decr ticket //票數-1
QUEUED
127.0.0.1:6379> incrby money 100 //沒有提交事務
QUEUED
第二步:打開客戶端2,執行如下操作,演示還沒等客戶端1提交事務,此時客戶端2把票買到了。
127.0.0.1:6379> get ticket
"1"
127.0.0.1:6379> decr ticket
(integer) 0//票搶到了 ticker變成了0 默認自動提交事務
第三步,回到客戶端1:提交事務,檢查ticket的值
127.0.0.1:6379> exec
 (nil) #提交事務,失敗
 127.0.0.1:6379> get ticket
 “0”//只有一個人成功了,客戶端1和2都減1了,但是只有一個成功了,兩個執行都沒有阻塞,樂觀鎖
 127.0.0.1:6379> unwatch #取消監控
九、多個redis架構設計--主從、哨兵、集群高可用
9.1Redis主從架構(Master/Slave)
單個Redis支持的讀寫能力還是有限的(redis便于查詢 讀),此時我們可以使用多個redis來提高redis的并發處理能力,這些redis如何協同,就需要有一定的架構設計(讀寫架構的設計,讀多或者寫少),這里我們首先從
主從架構(Master/Slave)(一個master和多個slave)架構進行分析和實現.,master負責讀寫,并將數據同步到salve,從節點負責讀操作.
當向master寫數據,master扔一個rdb冷備給slave(全量同步--初始化時),第二次寫master就是發送aof--日志了,只是增量同步實現數據的同步(只備份更新的部分)
9.2Redis哨兵模式
master宕機了,把slave升級成master
哨兵(Sentinel)是Redis的主從架構模式下,實現高可用性(high availability)的一種機制。
由一個或多個Sentinel實例(instance)組成的Sentinel系統(system)可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,并在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續處理命令請求。
9.3Redis集群高可用
面試題
1、基礎
14.接口和抽象類有什么區別?
抽象類可以有 main 方法,并且我們能運行它;接口不能有 main 方法。
16.BIO、NIO、AIO 有什么區別?
- BIO:Block IO 同步 阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,并發處理能力低。
- NIO:New IO 同步 非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實現了多路復用。
- AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步 非堵塞 IO ,異步 IO 的操作基于事件和回調機制。
17、配置文件
1、bootstrap的配置文件優先加載application
2、如果在不同的目錄中存在多個配置文件,它的讀取順序是:
1、config/application.properties(項目根目錄中config目錄下)
2、config/application.yml
3、application.properties(項目根目錄下)
4、application.yml
如果同一個目錄下,有application.yml也有application.properties,默認先讀取application.properties。但是yml的可讀性更高些,yml使用key:value 和縮進形式,properties文件必須寫全路徑
2、集合
19. Collection 和 Collections 有什么區別?
- java.util.Collection 是一個集合接口(集合類的一個頂級接口)。
- Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用于對集合中元素進行排序、搜索以及線程安全等各種操作。
21. HashMap 和 Hashtable 有什么區別?
- hashTable同步的、安全的,而HashMap是非同步的,效率上比hashTable要高。
- hashMap允許空鍵值(不安全),而hashTable不允許。
- hashMap是數組+鏈表的結構
22. 如何決定使用 HashMap 還是 TreeMap?
HashMap在Map中插入、刪除和定位元素這類操作
TreeMap對一個有序的key集合進行遍歷,是更好的選擇。
24. 說一下 HashSet 的實現原理?
- HashSet底層由HashMap實現
- HashSet的值存放于HashMap的key上
- HashMap的value統一為PRESENT
25. ArrayList 和 LinkedList 的區別是什么?
ArrrayList底層的數據結構是數組,支持隨機訪問,
LinkedList 的底層數據結構是雙向循環鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間復雜度是 O(1),而 LinkedList 是 O(n)。
26. 如何實現數組和 List 之間的轉換?
- List轉換成為數組:調用ArrayList的toArray方法。
- 數組轉換成為List:調用Arrays的asList方法。
27. ArrayList 和 Vector 的區別是什么?
- Vector是同步的、安全的
- ArrayList比Vector快,它因為有同步,不會過載。
- ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表。
28. Array 和 ArrayList 有何區別?
- Array可以容納基本類型和對象,而ArrayList只能容納對象。
- Array是指定大小后不可變的,而ArrayList大小是可變的。
- Array沒有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
3、線程
線程和進程的區別:
進程是操作系統進行資源分配的最小單元
線程是操作系統進行任務分配的最小單元,線程隸屬于進程
41. sleep() 和 wait() 有什么區別?
sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,不釋放鎖機制,其他線程依然無法訪問這個對象。
wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程
42. notify()和 notifyAll()有什么區別?--都是object中的方法
當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。
假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它才會重新回到等待池中。
43. 線程的 run()和 start()有什么區別?
start()方法來啟動一個線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,可以直接繼續執行下面的代碼;
用run()方法必須等待run()方法執行完畢才能執行下面的代碼,所以執行路徑還是只有一條,根本就沒有線程的特征
44. 創建線程池有哪幾種方式?
兩類:通過Excutors創建、另一類:通過ThreadPoolExecutor創建--最原始的創建線程池方式
①. new Fixed ThreadPool(int nThreads)創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。
②. new Cached ThreadPool()創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閑線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。
③. new Single ThreadExecutor()這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串行執行。
④. new Scheduled ThreadPool(int corePoolSize)創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似于Timer。
⑤:new SingleThread ScheduledExecutor:創建一個單線程的可以執行延遲任務的線程池;
⑥:new WorkStealing Pool:創建一個搶占式執行的線程池(任務執行順序不確定)JDK 1.8 添加
⑦:ThreadPoolExecutor:最原始的創建線程池的方式
45. 線程池都有哪些狀態?
線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。
線程池各個狀態切換框架圖:
46.線程池的參數:
core Pool Size 線程池核心線程大小 一個最小的線程數量,
maxi mum PoolSize 線程池最大線程數量 一個最大線程數量的限制
workQueue 工作隊列 新任務被提交后,會先進入到此工作隊列中,任務調度時再從隊列中取出任務
keepAliveTime 空閑線程存活時間
unit 空閑線程存活時間單位
threadFactory 線程工廠
handler 拒絕策略
46. 線程池中 submit()和 execute()方法有什么區別?
接收的參數不一樣submit有返回值,而execute沒有,submit方便Exception處理
47. 在 java 程序中怎么保證多線程的運行安全?
線程安全在三個方面體現:
原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。
48. 多線程鎖的升級原理是什么?
在Java中,鎖共有4種狀態,級別從低到高依次為:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨著競爭情況逐漸升級。鎖可以升級但不能降級。
49. 什么是死鎖?
死鎖是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
50. 怎么防止死鎖?
死鎖的四個必要條件:
互斥條件:進程對所分配到的資源不允許其他進程進行訪問,若其他進程訪問該資源只能等待,直至占有該資源的進程使用完成后釋放該資源
請求和保持條件:進程獲得一定的資源之后,又對其他資源發出請求,但是該資源可能被其他進程占有,此事請求阻塞,但又對自己獲得的資源保持不放
不可剝奪條件:是指進程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放
環路等待條件:是指進程發生死鎖后,若干進程之間形成一種頭尾相接的循環等待資源關系
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發生死鎖。
52.說一下 synchronized 底層實現原理?
synchronized保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性(只有一個人看)。
Java中每一個對象都可以作為鎖,synchronized實現同步的基礎:
普通同步方法,鎖是當前實例對象
靜態同步方法,鎖是當前類的class對象
同步方法塊,鎖是括號里面的對象
53. synchronized 和 volatile 的區別是什么?
volatile關鍵字,只能修飾屬性,標記字段可能被多個線程訪問(異步),保證線程可見性,不保證原子性
synchronized則是鎖定當前變量、方法、類,只有當前線程可以訪問該變量,其他線程被阻塞住。
保證變量的修改可見性和原子性。
volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
54. synchronized 和 Lock 有什么區別?
首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;
synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;
synchronized會自動釋放鎖(a?線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
用synchronized關鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不一直等待就結束了;
synchronized的鎖可重入、不可中斷、非公平,Lock鎖可重入、可判斷、可公平(兩者皆可);
Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
55. synchronized 和 ReentrantLock 區別是什么?
synchronized是關鍵字,ReentrantLock是類
ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖?
ReentrantLock可以獲取各種鎖的信息,可以靈活地實現多路通知?
鎖機制不同:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word。
4、反射
通過字節碼對象,通過反射,獲取構造方法,成員方法,成員變量等信息。
還可以通過反射創建對象
暴力反射Declared獲取私有屬性,私有方法
57. 什么是反射?
反射主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力
Java反射:
在Java運行時環境中,對于任意一個類,知道這個類有哪些屬性和方法,對于任意一個對象,能否調用它的任意一個方法
Java反射機制主要提供了以下功能
在運行時判斷任意一個對象所屬的類,類所具有的成員變量和方法。調用任意一個對象的方法。
58. 什么是 java 序列化?什么情況下需要序列化?
分為序列化和反序列化,將我們的對象保存在磁盤當中,然后再從磁盤中讀取對象。
基于序列化和反序列化的一個應用-對象的克隆
什么情況下需要序列化:
a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;c)當你想通過RMI傳輸對象的時候;
59. 動態代理是什么?有哪些應用?
動態代理:當想要給實現了某個接口的類中的方法,加一些額外的處理。比如說加日志,加事務等。就是創建一個新的類,這個類不僅包含原來類方法的功能,還添加了額外處理的。這個代理類并不是定義好的,是動態生成的。具有解耦意義,靈活,擴展性強。
動代理的應用:Spring的AOP、加事務,加權限,加日志
60. 怎么實現動態代理?
首先必須定義一個接口,還要有一個InvocationHandler(將實現接口的類的對象傳遞給它)處理類。再有一個工具類Proxy(習慣性將其稱為代理類,因為調用他的newInstance()可以產生代理對象,其實他只是一個產生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進制碼,利用加載器加載,并將其實例化產生代理對象,最后返回。
5、Cookies、Session
67. session 和 cookie 有什么區別?
Session.典型的場景比如購物車,當你點擊下單按鈕時,由于HTTP協議無狀態,并不知道是哪個用戶操作的,所以服務端創建了特定的Session,用用于標識這個用戶,并且跟蹤用戶,這樣才知道購物車里面有幾本書。這個Session是保存在服務端的,有一個唯一標識sessionId。Session 信息都是放在內存的。
思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候,服務端會在HTTP協議中告訴客戶端,需要在 Cookie 里面記錄一個Session ID,以后每次請求把這個會話ID發送到服務器,我就知道你是誰了。
用戶登錄信息賬號可以寫到Cookie里面,方便登錄,Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集群、數據庫、文件中;
Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。
session存儲的是在服務器上面的 cookie是存放在客戶端上的(也就是所謂的瀏覽器上)
1. Session稱為“會話控制"
2. Session可以存儲用戶信息.
3. Session數據的生命周期是整個會話,如果會話關閉 則數據清空.
4. Session的數據結構 是key-value結構.
1. Cookie是一個小型文本文件,存儲到本地終端上.
2. Cookie可以存儲用戶信息.
3. Cookie的數據類型key-value
4. Cookie中的數據一般采用加密的方式保存
5. Cookie的數據可以"永久"保存.
Session和Cookie選擇
1.如果數據需要臨時保存,則選用Session, 如果數據需要長時間存儲選用Cookie.
2.如果對于安全性要求較高的數據,選用Session,如果安全性要求不高選用Cookie.
問題: 財務系統用戶信息選用什么技術保存信息? 選用session.
68. 說一下 session 的工作原理?
session是一個存在服務器上的類似于一個散列表格的文件。類似于一個大號的map,里面的鍵存儲的是用戶的sessionid,用戶向服務器發送請求的時候會帶上這個sessionid。這時就可以從中取出對應的值了。
69. 如果客戶端禁止 cookie 能實現 session 還能用嗎?不能
Cookie與 Session,一般認為是兩個獨立的東西,Session采用的是在服務器端保持狀態的方案,Cookie采用的是在客戶端保持狀態的方案。但因為Session是用Session ID來確定當前對話所對應的服務器Session,而Session ID是通過Cookie來傳遞的,禁用Cookie相當于失去了Session ID,也就得不到Session了。
71. 如何避免 sql 注入?
sql語句中出現了特殊符號#,改變了SQL語句
獲取新的傳輸器:Prepare Statement(簡單又有效的方法)
使用正則表達式過濾傳入的參數
字符串過濾
6、異常
74. throw 和 throws 的區別?
throw則是指拋出的一個具體的異常類型。
throws是用來聲明一個方法可能拋出的所有異常信息,不處理異常、異常往上傳,誰調誰處理
75. final、finally、finalize 有什么區別?
- final關鍵字用來修飾類、變量、方法--表示最終
- finally關鍵字一般作用在try-catch代碼塊中,將一定要執行的代碼方法finally代碼塊中,
- finalize是一個方法,屬于Object類的一個方法,一般由垃圾回收器來調用,當我們調用System的gc()方法的時候,由垃圾回收器調用finalize(),回收垃圾。
76. try-catch-finally 中哪個部分可以省略?
答:catch 可以省略
原因:
定義普通異常的時候需要捕獲catch,進行進一步處理。
運行時異常,catch就可以省略
至于加上finally,則是在不管有沒捕獲異常,都要進行的“掃尾”處理。
77、Error和Exception都是繼承于Throwable
error是系統錯誤
- Error類一般是指與虛擬機相關的問題,如系統崩潰,虛擬機錯誤,內存空間不足,方法調用棧溢出等。如java.lang.StackOverFlowError和Java.lang.OutOfMemoryError。對于這類錯誤,Java編譯器不去檢查他們。對于這類錯誤的導致的應用程序中斷,僅靠程序本身無法恢復和預防,遇到這樣的錯誤,建議讓程序終止
- Exception類表示程序可以處理的異常,可以捕獲且可能恢復。遇到這類異常,應該盡可能處理異常,使程序恢復運行,而不應該隨意終止異常。
- Exception又分為運行時異常(Runtime Exception)和受檢查的異常(Checked Exception )。
- RuntimeException:其特點是Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即使沒有用try……catch捕獲,也沒有用throws拋出,還是會編譯通過,如除數為零的ArithmeticException、錯誤的類型轉換、數組越界訪問和試圖訪問空指針等。處理RuntimeException的原則是:如果出現RuntimeException,那么一定是程序員的錯誤。
- 受檢查的異常(IOException等):這類異常如果沒有try……catch也沒有throws拋出,編譯是通不過的。這類異常一般是外部錯誤,例如文件找不到、試圖從文件尾后讀取數據等,這并不是程序本身的錯誤,而是在應用環境中出現的外部錯誤。
數據庫
78、數據庫中存儲的數據類型有哪些
varchar、int、date、text
Mysql中的
整數類型:int、big int、small int、Tiny int 、bit、bool、medium int
int(m)里的m是表示SELECT查詢結果集中的顯示寬度,并不影響實際的取值范圍
浮點數類型:float、double、decimal
浮點型在數據庫中存放的是近似值,而定點類型在數據庫中存放的是精確值。
字符串類型:char、varchar、text、tinytext、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB二進制數據(_Blob)TEXT以文本方式存儲,英文存儲區分大小寫,而_Blob是以二進制方式存儲,不分大小寫。
日期類型:Date、DateTime、TimeStamp、Time、Year
其他數據類型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等
redis數據庫中的數據類型
string字符串:統計網站訪問數量,當前在線人數,文章字數
散列hash:是一個鍵值(key=>value)對集合,存儲對象,讀取、修改用戶屬性(name,age,pwd等)
列表list:可重復、有序,簡單的字符串列表,實現熱銷榜,最新評論
集合set :不可重復、無序,利用交集求共同好友。利用唯一性,可以統計訪問網站的所有獨立IP
有序集合zset:不可重復,zset 和 set 一樣也是string類型元素的集合,大型在線游戲的積分排行榜
79.Redis的五種數據類型和適用的場景
1.String(數據緩存)
2.Hash(購物車 單點登錄 用戶信息 登錄信息)
3.List(評論 評分 有序可重)
4.Set(共同好友 無序不重)
5.Zset(最熱商品)
80.什么是緩存穿透?如何避免?什么是緩存雪崩?何如避免?
緩存穿透?一般的緩存系統,都是按照key去緩存查詢,如果不存在對應的value,就應該去后端系統查找(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對后端系統造成很大的壓力。這就叫做緩存穿透。
如何避免?對查詢結果為空的情況也進行緩存,緩存時間設置短一點,或者該key對應的數據insert了之后清理緩存。 2:對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。
緩存雪崩?當緩存服務器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給后端系統帶來很大壓力。導致系統崩潰。
如何避免?
1:在緩存失效后,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。
 2:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置為短期,A2設置為長期
 3:不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻。
81.Redis實現分布式鎖
Redis為單進程單線程模式,采用隊列模式將并發訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關系Redis中可以使用SETNX命令實現分布式鎖。
 將 key 的值設為 value ,當且僅當 key 不存在。 若給定的 key 已經存在,則 SETNX 不做任何動作
82.有沒有嘗試進行多機redis 的部署?如何保證數據一致的?
主從復制,讀寫分離
 一類是主數據庫(master)一類是從數據庫(slave),主數據庫可以進行讀寫操作,當發生寫操作的時候自動將數據同步到從數據庫,而從數據庫一般是只讀的,并接收主數據庫同步過來的數據,一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。
?
?
總結
 
                            
                        - 上一篇: 今天
- 下一篇: pthread 简要使用指南
