Java面试题总结一
java 中操作字符串都有哪些類?它們之間有什么區別?
操作字符串的類有:String、StringBuffer、StringBuilder。
區別:
String 和 StringBuffer、StringBuilder 的區別在于 String 聲明的是不可變的對象,每次操作都會生成新的 String 對象,然后將指針指向新的 String 對象,而 StringBuffer、StringBuilder 可以在原有對象的基礎上進行操作,所以在經常改變字符串內容的情況下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的區別在于,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高于 StringBuffer,所以在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。
原文鏈接:https://blog.csdn.net/xuewei_ing/article/details/123381796
Array和List轉換
1.數組轉換成List集合
方法一
笨方法就是通過add把數組中的數據循環添加到List集合中
方法二
采用java中集合自帶的asList()方法就可以完成轉換了
2.List集合轉換成數組
方法一
笨方法是把List中的數據循環添加到數組中
方法二
采用集合的toArray()方法直接把List集合轉換成數組,這里需要注意,不能這樣寫:
String[] array = (String[]) mlist.toArray();
這樣寫的話,編譯運行時會報類型無法轉換java.lang.ClassCastException的錯誤,這是為何呢,這樣寫看起來沒有問題啊
因為java中的強制類型轉換是針對單個對象才有效果的,而List是多對象的集合,所以將整個List強制轉換是不行的
正確的寫法應該是這樣的
不管是數組轉換成集合,還是集合轉換成數組,都要注意轉換類型的一致性,String[]數組轉String類型的集合,當需要使用int,double等集合的時候,需要使用對應的對象
如:數組int[]用Integer[],double[]用Double[] ,因為List集合是對象的集合,而int、double等不是對象,所以需要用字段的對應對象類
原文鏈接:https://blog.csdn.net/simon_it/article/details/80184834
https://blog.csdn.net/zhuwentao2150/article/details/51713565
參考:https://www.cnblogs.com/goloving/p/7693388.html
List、Set、Map 之間的區別是什么?
原文鏈接:https://blog.csdn.net/fangchao2011/article/details/89184615
ArrayList和linkedList的區別
ArrayList
優點:ArrayList 是實現了基于動態數組的數據結構,因為地址連續,一旦數據存儲好了,查詢操作效率會比較高(在內存里是連著放的)。
缺點:因為地址連續,ArrayList 要移動數據,所以插入和刪除操作效率比較低。
LinkedList
優點:LinkedList 基于鏈表的數據結構,地址是任意的,所以在開辟內存空間的時候不需要等一個連續的地址。對于新增和刪除操作,LinkedList 比較占優勢。LinkedList 適用于要頭尾操作或插入指定位置的場景。
缺點:因為 LinkedList 要移動指針,所以查詢操作性能比較低。
適用場景分析
當需要對數據進行對隨機訪問的時候,選用 ArrayList。
當需要對數據進行多次增加刪除修改時,采用 LinkedList。
如果容量固定,并且只會添加到尾部,不會引起擴容,優先采用 ArrayList。
當然,絕大數業務的場景下,使用 ArrayList 就夠了,但需要注意避免 ArrayList 的擴容,以及非順序的插入。
HashMap和HashTable的相同點與區別
相同點:
1、都實現了 Map、Cloneable、Serializable(當前 JDK 版本 1.8)。
2、Hashtable、HashMap 都使用了 Iterator。而由于歷史原因,Hashtable 還使用了Enumeration 的方式。
區別:
1、兩者父類不同
HashMap是繼承自AbstractMap類,而Hashtable是繼承自Dictionary類。不過它們都實現了同時實現了map、Cloneable(可復制)、Serializable(可序列化)這三個接口。
2、對外提供的接口不同
Hashtable比HashMap多提供了elments() 和contains() 兩個方法。 elments() 方法繼承自Hashtable的父類Dictionnary。elements() 方法用于返回此Hashtable中的value的枚舉。contains()方法判斷該Hashtable是否包含傳入的value。它的作用與containsValue()一致。事實上,contansValue() 就只是調用了一下contains() 方法。
3、對null的支持不同
Hashtable:key和value都不能為null。這個可以從 Hashtable 源碼中的 put 方法看到,判斷如果 value 為 null 就直接拋出空指針異常,在 put 方法中計算 key 的 hash 值之前并沒有判斷 key 為 null 的情況,那說明,這時候如果 key 為空,照樣會拋出空指針異常。
HashMap:key可以為null,但是這樣的key只能有一個,因為必須保證key的唯一性;可以有多個key值對應的value為null。在計算 hash 值的時候,有判斷,如果key==null ,則其 hash=0 ;至于 value 是否為 null,根本沒有判斷過。
4、安全性不同
HashMap是線程不安全的,在多線程并發的環境下,可能會產生死鎖等問題,因此需要開發人員自己處理多線程的安全問題。
Hashtable是線程安全的,它的每個方法上都有synchronized 關鍵字,因此可直接用于多線程中。
雖然HashMap是線程不安全的,但是它的效率遠遠高于Hashtable,這樣設計是合理的,因為大部分的使用場景都是單線程。當需要多線程操作的時候可以使用線程安全的ConcurrentHashMap。ConcurrentHashMap雖然也是線程安全的,但是它的效率比Hashtable要高好多倍。因為ConcurrentHashMap使用了分段鎖,并不對整個數據進行鎖定。
5、計算hash值的方法不同
Hashtable 直接使用對象的 hash 值。hash 值是 JDK 根據對象的地址或者字符串或者數字算出來的 int 類型的數值。然后再使用除留余數法來獲得最終的位置。然而除法運算是非常耗費時間的,效率很低。HashMap 為了提高計算效率,將哈希表的大小固定為了 2 的冪,這樣在取模預算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多。
6、出生的版本不一樣
Hashtable 出生于 Java 發布的第一版本 JDK 1.0,HashMap 出生于 JDK1.2。
7、初始容量大小和每次擴充容量大小不同
默認情況下,初始容量不同,Hashtable 的初始長度是 11,之后每次擴充容量變為之前的2n+1(n 為上一次的長度)而 HashMap 的初始長度為 16,之后每次擴充變為原來的兩倍。另外在 Hashtable 源碼注釋中有這么一句話:
阿里內部資料
Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable . If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.
大致意思:Hashtable 是線程安全,推薦使用 HashMap 代替 Hashtable;如果需要線程安全高并發的話,推薦使用 ConcurrentHashMap 代替 Hashtable。PS:這個回答完了,面試官可能會繼續問:HashMap 是線程不安全的,那么在需要線程安全的情況下還要考慮性能,有什么解決方式?這里最好的選擇就是 ConcurrentHashMap 了,但面試官肯定會叫你繼續說一下ConcurrentHashMap 數據結構以及底層原理等。
HashMap 與 ConcurrentHashMap 的異同
1、 都是 key-value 形式的存儲數據;
2、HashMap 是線程不安全的,ConcurrentHashMap 是 JUC 下的線程安全的;
3、 HashMap 底層數據結構是數組 + 鏈表(JDK 1.8 之前)。JDK 1.8 之后是數組 + 鏈表 + 紅黑樹。當鏈表中元素個數達到 8 的時候,鏈表的查詢速度不如紅黑樹快,鏈表會轉為紅黑樹,紅黑樹查詢速度快;
4、 HashMap 初始數組大小為 16(默認),當出現擴容的時候,以 0.75 * 數組大小的方式進行擴容;
5、 ConcurrentHashMap 在 JDK 1.8 之前是采用分段鎖來現實的 Segment + HashEntry,Segment 數組大小默認是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized來保證并發安全進行實現。
JDK1.7和1.8中HashMap的區別小結
1、 存儲的數據結構不同
JDK1.7及之前以數組+鏈表的形式存儲元素。
JDK1.8及之后以數組+鏈表+紅黑樹的形式存儲元素。(默認長度為16)
為什么這樣這樣做?
因為當鏈表的長度過長的時候,查詢的效率就直線下降,所以在JDK1.8之后將其設計為當鏈表的長度達到一定的閾值的時候,就會將鏈表結構轉換為紅黑樹結構,紅黑樹是一種自平衡的二叉搜索樹,提高查詢效率。
2、插入數據的方式不同
JDK1.7及之前采用的是鏈表頭部插入數據。
JDK1.8及之后采用的是鏈表尾部插入數據。
擴容后轉換數據不需要遍歷到鏈表的尾部再插入。
最近添加的元素可能馬上就要被獲取,頭插的方式只需要遍歷到鏈表的頭部就能匹配到了。
擴容后鏈表可能會倒序,并發擴容可能會產生循環鏈
3、 hash運算不同
JDK1.7及之前計算hash運算多
JDK1.8及之后計算hash運算少
4、 擴容時調整的數據方式不同
JDK1.7及之前擴容后數據會根據hash值重新計算索引的位置,然后將數據存放到對應的位置上。
JDK1.8及之后擴容后數據要么等于原來的位置,要么等于原來的位置+舊容量。
5、 擴容方式不同
JDK1.7及之前首先檢查是否需要進行擴容,再插入數據(擴容為原來的兩倍)
存放新的元素時,已經存在的元素的個數必須大于等于閾值。
存放新的元素時,與已經存在的元素發生hash碰撞(新元素key計算hash值換算出來的數組下標的位置上已存在元素),滿足這兩個條件就會進行擴容。
JDK1.8及之后首先插入數據,再檢查是否需要擴容
當數組的容量未達到64的時候,則以兩倍進行擴容。
當數組的容量達到64之后,若鏈表的元素大于等于8個時,則將鏈表結構轉換為紅黑樹結構。
刪除元素后,當紅黑樹中的節點小于等于6個時,則將紅黑樹結構轉換回鏈表結構。
當紅黑樹的節點不少于32個的時候,才會繼續進行擴容,擴容機制更加優化。
原文鏈接:https://blog.csdn.net/qq_40980826/article/details/120397873
HashMap遍歷的五種方法
使用 Iterator 遍歷 HashMap EntrySet
使用 Iterator 遍歷 HashMap KeySet
使用 For-each 循環迭代 HashMap
使用 Lambda 表達式遍歷 HashMap
使用 Stream API 遍歷 HashMap
原文鏈接:https://blog.csdn.net/weixin_60589106/article/details/122482640
java創建線程的四種方式
1.直接初始化Thead類,實現Runnable接口
2.繼承Thread類
3.實現callable接口、通過Callable和Future創建線程
4.使用線程池創建線程
(線程池解決兩個不同的問題:它們通常在執行大量異步任務時提高性能,因為減少了每個任務的調用開銷,并且它們提供了一種方法來限制和管理執行任務集合時消耗的資源(包括線程)。每個ThreadPoolExecutor還維護一些基本的統計信息,比如完成任務的數量。)
a.使用ThreadPoolExecutor創建線程.這種方式比較靈活
b.使用Executors創建線程池
什么是守護線程?
守護線程(即daemon thread),是個服務線程,準確地來說就是服務其他的線程。
守護線程是一種支持性線程,主要用于后臺調度以及支持性的工作。守護線程具備自動結束生命周期的特性,而非守護線程則不具備。
為什么JAVA對象需要實現序列化?
序列化是一種用來處理對象流的機制
所謂對象流:
就是將對象的內容進行流化,可以對流化后的對象進行讀寫操作,也可以將流化后的對象傳輸于網絡之間。
序列化是為了解決在對對象流進行讀寫操作時所引發的問題
序列化的實現:
將需要被序列化的類實現serializable接口(標機接口),該接口沒有需要實現的方法,implements serializable只是為了標注該對象是可被序列化的,然后使用一個輸出流(如:fileOutputStream)來構造一個objectOutoutStream(對象流)對象;接著使用objectOutputStream對象的writeObject(object obj)方法就可以將參數為obj的對象寫出(即保存其狀態),要恢復的話則用輸出流。
什么時候使用序列化:
1、對象序列化可以實現分布式對象
主要應用例如RMI(即遠程調用Remote Method Invocation)要利用對象序列化運行遠程主機上的服務,就像在本地機上運行對象時一樣
2、java對象序列化不僅保留一個對象的數據,而且遞歸保存對象引用的每個對象的數據
可以將整個對象層次寫入字節流中,可以保存在文件中或在網絡連接上傳遞,利用對象序列化可以進行對象的“深復制”,即復制對象本身及引用的對象本身,序列化一個對象可能得到整個對象序列
3、序列化可以將內存中的類寫入文件或數據庫中
比如:將某個類序列化后存為問價,下次讀取時只需將文件中的數據反序列化就可以將原先的類還原到內存中,也可以將類序列化為流數據進行傳輸,總的來說就是將一個已經實例化的類轉成文件存儲,下次需要實例化的時候只要反序列化即可將類實例化到內存中,并保留序列化時類中的所有變量和狀態
4、對象、文件、數據有許多不同的格式,很難統一傳輸和保存
序列化以后就都是字節流了,無論是什么東西,都能變成一樣的東西,就可以進行通用的格式傳輸或保存,傳輸結束以后,要再次使用,就進行反序列化還原,這樣對象還是對象,文件還是文件
因為java中要將對象序列化為流的形式進行傳輸
對象的序列化就是為了數據傳輸,在你的代碼的里是對象格式,而在傳輸的時候不可能還保持這對象的樣子。
當兩個進程再進行遠程通信時,彼此可以發送各種類型的數據,無論是何種類型的數據都會以二進制序列的形式在網絡上傳輸,發送方需要將這個java對象轉換為字節序列才能在網絡上傳送,接收方則需要把字節序列再恢復為java對象
1、概念
序列化:把java對象轉換為字節序列的過程
反序列化:把字節序列恢復為java對象的過程
2、用途:
a、把對象的字節序列永久的保存到硬盤上,通常放在一個文件中
b、在網絡上傳輸對象的字節序列
所謂的serializable,就是java提供的通用數據保存和讀取的接口,至于從什么地方讀出來和保存到那里去都被隱藏在函數參數的背后了,這樣子,任何類型只要實現了serializable接口,就可以被保存到文件中,或者作為數據流通過網絡發送到別的地方,也可以用管道來傳輸系統的其他程序中,這樣子極大地簡化了類的設計,只要設計一個保存一個讀取功能就能解決上面說的所有問題
java的對象序列化能讓你將一個實現了serializable接口的對象轉換成一組byte,這樣日后要用這個對象的時候,你就能把這些byte數據恢復出來,并據此從新構建那個對象了
工作流當中流程變量的幾種數據類型:string、integer、short、long、double、boolean、date、binary、serializable,這就是為什么要將javabean實現序列化的原因,因為你將對象設置到流程變量中必須要實現序列化,否則會在設置流程變量的時候報錯找不到該類型
java對象序列化機制就是把內存中的java對象轉成二進制流,java對象序列化后很方便的存儲或者在網絡中傳輸
java的序列化機制是通過運行時判斷類的序列化ID(serialVersionUID)來判斷版本的一致性
在反序列時,java虛擬機會通過二進制流中的serialVarsionUID與本地的相對應的實體類進行比較,如果相同就認為是一致的,可以反序列化,正確獲得信息,否則拋出序列化版本不一致的異常
所以涉及到數據傳輸或者存儲的類,嚴格意義上來說都要加上序列化ID,這也是一種良好的編程習慣
原文鏈接:https://blog.csdn.net/qq_52052392/article/details/118388052
java鎖的種類
Java鎖的種類:自旋鎖、可重入鎖、悲觀鎖、樂觀鎖、同步鎖、互斥鎖
宏觀上:樂觀鎖、悲觀鎖
樂觀鎖是一種樂觀思想,即認為讀多寫少,遇到并發寫的可能性低,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,采取在寫時先讀出當前版本號,然后加鎖操作(比較跟上一次的版本號,如果一樣則更新),如果失敗則要重復讀-比較-寫的操作。
java中的樂觀鎖基本都是通過CAS操作實現的,CAS是一種更新的原子操作,比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。
悲觀鎖是就是悲觀思想,即認為寫多,遇到并發寫的可能性高,每次去拿數據的時候都認為別人會修改,所以每次在讀寫數據的時候都會上鎖,這樣別人想讀寫這個數據就會block直到拿到鎖。java中的悲觀鎖就是Synchronized,AQS框架下的鎖則是先嘗試cas樂觀鎖去獲取鎖,獲取不到,才會轉換為悲觀鎖,如RetreenLock。
自旋鎖:自旋鎖原理非常簡單,如果持有鎖的線程能在很短時間內釋放鎖資源,那么那些等待競爭鎖的線程就不需要做內核態和用戶態之間的切換進入阻塞掛起狀態,它們只需要等一等(自旋),等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。
自適應自旋鎖:根據上一次自旋情況動態調整自旋時間
偏向鎖、輕量級鎖、重量級鎖
原文鏈接:https://blog.csdn.net/SwjtuPC/article/details/123234460
Java 中線程同步鎖和互斥鎖
一 概述
1.1 互斥
所謂互斥,就是不同線程,通過競爭進入臨界區(共享的數據和硬件資源),為了防止訪問沖突,在有限的時間內只允許其中之一獨占性的使用共享資源。如不允許同時寫。
1.2 同步
同步關系則是多個線程彼此合作,通過一定的邏輯關系來共同完成一個任務。一般來說,同步關系中往往包含互斥,同時,對臨界區的資源會按照某種邏輯順序進行訪問。如先生產后使用。
1.3 兩者區別
總的來說,兩者的區別就是:互斥是通過競爭對資源的獨占使用,彼此之間不需要知道對方的存在,執行順序是一個亂序。同步是協調多個相互關聯線程合作完成任務,彼此之間知道對方存在,執行順序往往是有序的。
二 總結
糾結到底是同步鎖還是互斥鎖其實是沒有什么意義的,你可以認為它們就屬于一個東西,如果你只是摳這些概念的話,很容易陷入在一個"活鎖"中,出也出不來。
在 Java 中,互斥鎖就是兩種,synchronized 和 Lock 接口的 xxxLock 實現類。但是道理都是一樣的。無非就是哪種寫起來更方便。
鎖的目的就是避免多個線程對同一個共享的數據并發修改帶來的數據混亂。如果存在線程安全性問題,一個非常有效的方式就是加鎖,這里的同步鎖和互斥鎖其實就是一個意思。
鎖在操作系統層面的意思就是 Mutex,互斥,意思就是說我(某個線程)獲取鎖(進入臨界區)之后,其他線程不能再進入臨界區,這樣就達到了互斥的目的,如下圖所示。
鎖的實現要處理的大概就只有以下4類問題:
“誰拿到了鎖“,這個信息存哪里(可以是當前 class,當前 instance 的 markword,也可以是某個具體的 Lock 的實例)
誰能搶到鎖的規則(只能一個人搶到 - Mutex;能搶有限多個數量 - Semaphore;自己可以反復搶 - 重入鎖;讀可以反復搶到但是寫獨占 - 讀寫鎖……)
搶不到時怎么辦(搶不到玩命搶;搶不到暫時睡著,等一段時間再試/等通知再試;或者二者的結合,先玩命搶幾次,還沒搶到就睡著)
如果鎖被釋放了還有其他等待鎖的怎么辦(不管,讓等的線程通過超時機制自己搶;按照一定規則通知某一個等待的線程;通知所有線程喚醒他們,讓他們一起搶……)
有了這些選擇,你就可以按照業務需求組裝出你需要鎖。
互斥就是線程 A 訪問了一組數據,線程 BCD 就不能同時訪問這些數據,直到 A 停止訪問了
同步就是 ABCD 這些線程要約定一個執行的協調順序。比如 D 要執行,B 和 C 必須都得做完,而 B 和 C 要開始,A 必須先得做完
這是兩種典型的并發問題。恰當的使用鎖,可以解決同步或者互斥的問題。
你可以說 Mutex 是專門被設計來解決互斥的;Barrier,Semaphore 是專門來解決同步的。但是這些都離不開上述對上述4個問題的處理。同時,如果遇到了其他的具體的并發問題,你也可以定制一個鎖來滿足需要。
原文鏈接:https://blog.csdn.net/liuwg1226/article/details/119900991
靜態方法和非靜態方法上加鎖的區別
1.靜態方法加鎖:類鎖
Synchronized修飾靜態方法,實際上是對該類對象加鎖,俗稱“類鎖”
2.非非靜態方法加鎖:對象鎖
Synchronized修飾非靜態方法,實際上是對調用該方法的對象加鎖,俗稱“對象鎖”
1.對象鎖鑰匙只能有一把才能互斥,才能保證共享變量的唯一性
2.在靜態方法上的鎖,和 實例方法上的鎖,默認不是同樣的,如果同步需要制定兩把鎖一樣。
3.關于同一個類的方法上的鎖,來自于調用該方法的對象,如果調用該方法的對象是相同的,那么鎖必然相同,否則就不相同。
比如 new A().x() 和 new A().x(),對象不同,鎖不同,如果A的單例的,就能互斥。
4.靜態方法加鎖,能和所有其他靜態方法加鎖的 進行互斥
5.靜態方法加鎖,和xx.class 鎖效果一樣,直接屬于類的
原文鏈接:https://blog.csdn.net/zzprongyi/article/details/91046236
Java死鎖如何避免?
什么是死鎖?
死鎖就是當兩個或兩個以上的線程因競爭相同資源而處于無限期的等待,這樣就導致了多個線程的阻塞,出現程序無法正常運行和終止的情況。
死鎖產生的4個必要條件:
(1)互斥條件:系統要求對所分配的資源進行排他性控制,即在一段時間內某個資源僅為一個進程所占有(比如:打印機,同一時間只能一個人打印)。此時若有其他進程請求該資源,則請求只能等待,直到有資源釋放了位置;
(2)請求和保持條件:進程已經持有了一個資源,但是又要訪問一個新的被其他進程占用的資源那么就會阻塞,并且對自己占用的一個資源保持不放;
(3)不剝奪條件:進程對已經獲取的資源未使用完之前不能被剝奪,只能使用完之后自己釋放。
(4)環路等待條件:存在一種進程資源的循環等待鏈,鏈中每一個進程已獲得的資源同時被鏈中下一個進程所請求。
死鎖的常見代碼情況
情況一:
運行結果:
分析:這里產生死鎖的原因是因線程小明先持有了鎖smallGate,然后進行了sleep睡眠之后想要持有鎖largeGate;而此時線程小張已經持有了鎖largeGate,同樣想要持有smallGate,于是出現了資源競爭導致阻塞等待的情況;
情況二:
如何避免死鎖?
(1)保持加鎖順序:當多個線程都需要加相同的幾個鎖的時候(例如上述情況一的死鎖),按照不同的順序枷鎖那么就可能導致死鎖產生,所以我們如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發生。
(2)獲取鎖添加時限:上述死鎖代碼情況二就是因為出現了獲取鎖失敗無限等待的情況,如果我們在獲取鎖的時候進行限時等待,例如wait(1000)或者使用ReentrantLock的tryLock(1,TimeUntil.SECONDS)這樣在指定時間內獲取鎖失敗就不等待;
(3)進行死鎖檢測:我們可以通過一些手段檢查代碼并預防其出現死鎖。
死鎖檢測
Java中死鎖檢測手段最多的就是使用JDK帶有的jstack和JConsole工具了。下面我們以jstack為例來進行死鎖的檢測;
(1)先運行我們的代碼程序
(2)使用JDK的工具JPS查看運行的進程信息,如下:
(3)使用jps查看到的進程ID對其進行jstack 進程分析
原文出處:https://baijiahao.baidu.com/s?id=1721832902655524388&wfr=spider&for=pc
jdk1.8 的新特性
在看接口Collection的底層代碼時,看見接口中有方法的實現,在查閱資料后發現在jdk1.8版本增加了很多新特性,其中就有接口中發法的實現。廢話不多說關于jdk1.8的新特性如下:
1、Lambda表達式:Lambda允許把函數作為一個方法的參數(函數作為參數傳遞到方法中)。
2、方法引用:方法引用提供了非常有用的語法,可以直接引用已有Java類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。
3、默認方法:默認方法就是一個在接口里面有了一個實現的方法。
4、新工具:新的編譯工具,如:Nashorn引擎 jjs、 類依賴分析器jdeps。
5、Stream API:新添加的Stream API(java.util.stream) 把真正的函數式編程風格引入到Java中。
6、Date Time API:加強對日期與時間的處理。
7、Optional類:Optional 類已經成為 Java 8 類庫的一部分,用來解決空指針異常。
8、Nashorn,JavaScript引擎:JDK1.8提供了一個新的Nashorn javascript引擎,它允許我們在JVM上運行特定的javascript應用。
原文鏈接:https://blog.csdn.net/weixin_51746436/article/details/121295051
Stream 流
待補充
深拷貝和淺拷貝區別是什么?
淺拷貝只是復制了對象的引用地址,兩個對象指向同一個內存地址,所以修改其中任意的值,另一個值都會隨之變化,這就是淺拷貝(例:assign())
深拷貝是將對象及值復制過來,兩個對象修改其中任意的值另一個值不會改變,這就是深拷貝(例:JSON.parse()和JSON.stringify(),但是此方法無法復制函數類型)
原文轉載:https://blog.csdn.net/fangchao2011/article/details/89186117
sql性能優化
1、為搜索字段創建索引。
2、避免使用 select *,列出需要查詢的字段。
3、垂直分割分表。
4、選擇正確的存儲引擎。
什么是聚集索引和非聚集索引及其兩者區別
一、聚集索引
定義:數據行的物理順序與列值(一般是主鍵的那一列)的 邏輯順序相同,一個表中只能擁有一個聚集索引。
二、非聚集索引
定義:該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同,一個表中可以擁有多個非聚集索引。
根本區別:數據行的物理順序與表的某個列值的邏輯順序是否一致。
區別:
1、聚集索引在葉子節點存儲的是表中的數據,而非聚集索引在葉子節點存儲的是主鍵和索引列;
2、聚集索引中表記錄的排列順序和索引的排列順序一致,而非聚集索引的排列順序不一致;
3、聚集索引每張表只能有一個,而非聚集索引可以有多個。
主鍵索引和聚集索引的區別
java 索引有哪些_數據庫應用中常見的索引類型都有哪些
一、索引的介紹
索引就相當書的目錄,比如一本500頁的書,如果你想快速找到其中的某一個知識點,在不借助目錄的情況下,你得一點點慢慢的找,要找好一會兒。同樣,對于數據庫的表,而言,索引就是它的“目錄”,提高了數據查詢的效率。
二、數據庫常用索引類型
1.哈希索引
哈希索引是一種以鍵-值(key-value)存儲數據的結構,只有精確匹配索引所有列的查詢才有效。對于每一行數據,存儲引擎都會對所有的索引列計算一個哈希碼,哈希碼是一個較小的值,并且不同鍵值的行計算出來的哈希碼也不一樣。哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。即我們只要輸入待查找的值即key,就可以找到其對應的值即value。
2.空間數據索引
MyISAM表支持空間索引,可以用作地理數據存儲,該索引無須前綴查詢??臻g索引會從所有維度來索引數據。查詢時,可以有效地使用任意維度來組合查詢。必須使用MySQL的GIS相關函數如MBRCONTAINS()等來維護數據。
3.全文索引
全文索引是一種特殊類型的索引,它查找的是文本中的關鍵詞,而不是直接比較索引中的值。全文索引適用于MATCH
AGAINST操作,而不是普通的WHERE條件操作。
全文索引支持各種字符內容的搜索(包括char、varchar和text類型),也支持自然語言搜索和布爾搜索。
原文鏈接:https://blog.csdn.net/weixin_34170737/article/details/114932754
三、數據庫常見的四種索引
1.普通索引:主要以B+樹和哈希索引為主,任務是加快對數據的訪問速度,常用于查詢和排序的條件,值可以為空并沒有唯一性的限制
2.唯一性索引:與普通索引類似,不同的是唯一性索引,索引列的值必須是唯一的,但可以為空
3.主鍵索引:主鍵索引是一種特殊的唯一性索引,在定義主鍵是自動創建,是創建在主鍵上的索引,所有屬性列唯一而且不能為空
4.全文索引:MySQL全文檢索是利用查詢關鍵字和查詢列內容之間的相關度進行檢索,可以利用全文索引來提高匹配的速度
5.主鍵索引與唯一性索引的區別:
1)主鍵約束比唯一性索引的約束嚴格,在沒有設定主鍵時,非空唯一性索引自動成為主鍵;
2)主鍵索引所對應的屬性列的值不能為空,唯一性索引對應的屬性列的值可以為空
3)主鍵索引只有一個,唯一性索引可以有多個
原文鏈接:https://blog.csdn.net/weixin_45217447/article/details/108569966
索引失效
1.查詢條件中有or,即使有部分條件帶索引也會失效
總結:查詢條件中帶有or,除非所有的查詢條件都建有索引,否則索引失效
2.like查詢是以%開頭
3.如果列類型是字符串,那在查詢條件中需要將數據用引號引用起來,否則不走索引
4.索引列上參與計算會導致索引失效
總結:將參與計算的數值先算好,再查詢
5.違背最左匹配原則
6.如果mysql估計全表掃描要比使用索引要快,會不適用索引
7.other
10)隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤. 由于表的字段tu_mdn定義為varchar2(20),但在查詢時把該字段作為number類型以where條件傳給Oracle,這樣會導致索引失效. 錯誤的例子:select * from test where tu_mdn=13333333333; 正確的例子:select * from test where tu_mdn=‘13333333333’;
13)like “%_” 百分號在前.
14)表沒分析.
15)單獨引用復合索引里非第一位置的索引列.
16)字符型字段為數字時在where條件里不添加引號.
17)對索引列進行運算.需要建立函數索引.
18)not in ,not exist.
19)當變量采用的是times變量,而表的字段采用的是date變量時.或相反情況。
20)B-tree索引 is null不會走,is not null會走,位圖索引 is null,is not null 都會走
21)聯合索引 is not null 只要在建立的索引列(不分先后)都會走, in null時 必須要和建立索引第一列一起使用,當建立索引第一位置條件是is null 時,其他建立索引的列可以是is null(但必須在所有列 都滿足is null的時候),或者=一個值; 當建立索引的第一位置是=一個值時,其他索引列可以是任何情況(包括is null =一個值),以上兩種情況索引都會走。其他情況不會走。
原文鏈接:https://blog.csdn.net/sy_white/article/details/122112440
mybatis 中 #{}和 ${}的區別是什么?
#{}是預編譯處理,KaTeX parse error: Expected ‘EOF’, got ‘#’ at position 21: …串替換; Mybatis在處理#?{}時,會將sql中的#{}替…{}時,就是把${}替換成變量的值;
使用#{}可以有效的防止SQL注入,提高系統安全性。
什么是spring?
Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器框架。
AOP思想
面向切面編程利用了一種稱為【橫切】的技術,剖解開封裝的對象內部,并將那些影響了多個類的公共行為封裝到一個可重用的模塊,并將其命名為【切面(Aspect)】。簡單的說,就是那些與業務無關,卻為業務模塊鎖共同調用的邏輯或責任封裝起來,便于減少系統的重復代碼,降低模塊之間的耦合,并有利于未來的可操作性和可維護性。
利用【橫切】技術,AOP把軟件系統分為了兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與業務處理關系不大的部分是橫切關注點。橫切關注點的一個特點是,它們經常發生在核心關注點的多處,而各處基本相似,比如說權限認證、日志和事務等。AOP的作用在于分離系統中的各個關注點,將核心關注點和橫切關注點分離開來。
原文鏈接:https://blog.csdn.net/u010008759/article/details/123226527
談談你對Spring的AOP理解
AOP(Aspect-Oriented Programming,面向切面編程)能夠將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任(例如事務處理、日志管理、權限控制等)封裝起來,便于減少系統的重復代碼,降低模塊間的耦合度,并有利于未來的可擴展性和可維護性。
控制反轉(IOC)、依賴注入(DI)、AOP思想
Spring有三大核心思想,分別是控制反轉(IOC,Inversion Of Controller),依賴注入(DI,Dependency Injection)和面向切面編程(AOP,Aspect Oriented Programming)。
控制反轉(IOC,Inversion Of Controller)
控制反轉不是什么技術,而是一種設計思想。在Java開發中,IOC意味著將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。
傳統正轉
控制有反轉,當然也就有原來的正轉。正轉就是說當你需要用到某一個對象的時候,就需要主動去new一個對象實例,才能夠使用該對象。
控制反轉
反轉就是當你需要用到某一個對象的時候,就向Spring IOC容器發請求,由Spring IOC容器返回一個對象實例給你,即將組件之間的關系從程序內部提到外部容器來管理。
依賴注入(DI,Dependency Injection)
依賴注入是控制反轉的具體實現。
因為對象資源的獲取全部要依賴于Spring IOC容器,組件之間的依賴關系由容器在應用系統運行期來決定,在需要的時候由Spring IOC容器動態地往組件中注入需要的實例對象就叫做依賴注入。
面向切面編程(AOP,Aspect Oriented Programming)
面向切面編程利用了一種稱為【橫切】的技術,剖解開封裝的對象內部,并將那些影響了多個類的公共行為封裝到一個可重用的模塊,并將其命名為【切面(Aspect)】。簡單的說,就是那些與業務無關,卻為業務模塊鎖共同調用的邏輯或責任封裝起來,便于減少系統的重復代碼,降低模塊之間的耦合,并有利于未來的可操作性和可維護性。
利用【橫切】技術,AOP把軟件系統分為了兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與業務處理關系不大的部分是橫切關注點。橫切關注點的一個特點是,它們經常發生在核心關注點的多處,而各處基本相似,比如說權限認證、日志和事務等。AOP的作用在于分離系統中的各個關注點,將核心關注點和橫切關注點分離開來。
原文鏈接:https://www.cnblogs.com/yanggb/p/10337600.html
spring事務
Spring事務五個特性:
事務傳播機制
事務隔離機制
只讀
事務超時
回滾規則
詳細介紹鏈接:https://blog.csdn.net/sssspider/article/details/101310312
原文鏈接:https://blog.csdn.net/sssspider/article/details/101310312
說說事務的隔離級別
未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據
提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀)
可重復讀(Repeated Read):在同一個事務內的查詢都是事務開始時刻一致的,Mysql的InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重復讀,但是還存在幻讀(多個事務同時修改同一條記錄,事務之間不知道彼此存在,當事務提交之后,后面的事務修改的數據將會覆蓋前事務,前一個事務就像發生幻覺一樣)
可串行化(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞。
不可重復讀和幻讀的區別主要是:解決不可重復讀需要鎖定了當前滿足條件的記錄,而解決幻讀需要鎖定當前滿足條件的記錄及相近的記錄。比如查詢某個商品的信息,可重復讀事務隔離級別可以保證當前商品信息被鎖定,解決不可重復讀;但是如果統計商品個數,中途有記錄插入,可重復讀事務隔離級別就不能保證兩個事務統計的個數相同。
說說事務的傳播級別
Spring事務定義了7種傳播機制:
1、PROPAGATION_REQUIRED:默認的Spring事物傳播級別,若當前存在事務,則加入該事務,若
不存在事務,則新建一個事務。
2、PAOPAGATION_REQUIRE_NEW:若當前沒有事務,則新建一個事務。若當前存在事務,則新建
一個事務,新老事務相互獨立。外部事務拋出異?;貪L不會影響內部事務的正常提交。
3、 PROPAGATION_NESTED:如果當前存在事務,則嵌套在當前事務中執行。如果當前沒有事務,
則新建一個事務,類似于REQUIRE_NEW。
4、PROPAGATION_SUPPORTS:支持當前事務,若當前不存在事務,以非事務的方式執行。
5、PROPAGATION_NOT_SUPPORTED:以非事務的方式執行,若當前存在事務,則把當前事務掛
起。
6、PROPAGATION_MANDATORY:強制事務執行,若當前不存在事務,則拋出異常.
7、PROPAGATION_NEVER:以非事務的方式執行,如果當前存在事務,則拋出異常。
Spring事務傳播級別一般不需要定義,默認就是PROPAGATION_REQUIRED,除非在嵌套事務的情
況下需要重點了解。
Spring 事務實現方式
編程式事務管理:這意味著你可以通過編程的方式管理事務,這種方式帶來了很大的靈活性,但很難維護。
聲明式事務管理:這種方式意味著你可以將事務管理和業務代碼分離。你只需要通過注解或者XML配置管理事務。
事務三要素是什么?
數據源:表示具體的事務性資源,是事務的真正處理者,如MySQL等。
事務管理器:像一個大管家,從整體上管理事務的處理過程,如打開、提交、回滾等。
事務應用和屬性配置:像一個標識符,表明哪些方法要參與事務,如何參與事務,以及一些相關屬
性如隔離級別、超時時間等。
spring 自動裝配 bean 有哪些方式?
spring中bean裝配有兩種方式:
隱式的bean發現機制和自動裝配
在java代碼或者XML中進行顯示配置
spring 事務實現方式有哪些?
編程式事務管理對基于 POJO 的應用來說是唯一選擇。我們需要在代碼中調用beginTransaction()、commit()、rollback()等事務管理相關的方法,這就是編程式事務管理。
基于 TransactionProxyFactoryBean 的聲明式事務管理
基于 @Transactional 的聲明式事務管理
基于 Aspectj AOP 配置事務
BeanFactory與FactoryBean的區別
一、這兩個概念,命名比較相似,剛接觸源碼的時候,困惑了一票程序猿:
1)BeanFactory 是接口,提供了IOC容器最基本的形式,給具體的IOC容器的實現提供了規范,頂層接口。
2)FactoryBean 也是接口,為IOC容器中Bean的實現提供了更加靈活的方式,FactoryBean在IOC容器的基礎上給Bean的實現加上了一個簡單的工廠模式和裝飾模式 ,我們可以在getObject()方法中靈活配置.
區別:FactoryBean是個Bean.在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似 .
二、深入分析:
BeanFactory:
1)負責生產和管理Spring中bean的一個工廠;
2)IOC容器的核心接口,它的職責包括:實例化、定位、配置應用程序中的對象及建立這些對象的依賴。
3)多種實現:如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一個,該實現將以XML方式描述組成應用的對象及對象間的依賴關系。
FactoryBean
FactoryBean是一個接口,當在IOC容器中的Bean實現了FactoryBean后,通過getBean(String BeanName)獲取到的Bean對象并不是FactoryBean的實現類對象,而是這個實現類中的getObject()方法返回的對象。要想獲取FactoryBean的實現類,就getBean(&BeanName),在BeanName之前加上&。
原文鏈接:https://blog.csdn.net/jason_jiahongfei/article/details/106164376
說說你對Spring MVC的理解
1、 用戶發送請求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到請求調用HandlerMapping處理器映射器。
3、 處理器映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet。
4、 DispatcherServlet調用HandlerAdapter處理器適配器。
5、 HandlerAdapter經過適配調用具體的處理器(Controller,也叫后端控制器)。
6、 Controller執行完成返回ModelAndView。
7、 HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。
8、 DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。
9、 ViewReslover解析后返回具體View。
10、DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。
11、 DispatcherServlet響應用戶。
前端控制器(DispatcherServlet):接收請求,響應結果,相當于電腦的CPU。
處理器映射器(HandlerMapping):根據URL去查找處理器。
處理器(Handler):需要程序員去寫代碼處理邏輯的。
處理器適配器(HandlerAdapter):會把處理器包裝成適配器,這樣就可以支持多種類型的處理器,類比筆記本的適配器(適配器模式的應用)。
視圖解析器(ViewResovler):進行視圖解析,多返回的字符串,進行處理,可以解析成對應的頁面。
SpringCloud是什么?
1.Spring Cloud就是微服務系統架構的一站式解決方案,是各個微服務架構落地技術的集合體,俗稱微服務全家桶
2.在平時我們構建微服務的過程中需要做如服務發現注冊、配置中心、負載均衡、斷路器、數據監控等操作,而Spring Cloud 為我們提供了一套簡易的編程模型,使我們能在 Spring Boot 的基礎上輕松地實現微服務項目的構建
SpringBoot和SpringCloud有啥關系?
SpringBoot專注于快速、方便的開發單個微服務個體,SpringCloud關注全局的服務治理框架。
//區別:SpringBoot可以離開SpringCloud獨立使用開發項目,但是SpringCloud離不開SpringBoot,屬于依賴的關系。
SpringBoot和SpringCloud的區別?
SpringBoot專注于快速方便的開發單個個體微服務。
SpringCloud是關注全局的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務整合并管理起來,為各個微服務之間提供,配置管理、服務發現、斷路器、路由、微代理、事件總線、全局鎖、決策競選、分布式會話等等集成服務。
SpringBoot可以離開SpringCloud獨立使用開發項目, 但是SpringCloud離不開SpringBoot ,屬于依賴的關系。
SpringBoot專注于快速、方便的開發單個微服務個體,SpringCloud關注全局的服務治理框架。
SpringBoot 實現熱部署有哪幾種方式?
主要有兩種方式:
Spring Loaded
Spring-boot-devtools
如何理解 Spring Boot 配置加載順序?
在 Spring Boot 里面,可以使用以下幾種方式來加載配置。
1)properties文件;
2)YAML文件;
3)系統環境變量;
4)命令行參數;
等等……
Erueka如何實現高可用
高可用:可以通過運行多個Eureka server實例并相互注冊的方式實現,Server節點之間會彼此增量地同步信息,從而確保節點中數據一致。
Erueka的自我保護機制
當Eureka Server 節點在短時間內丟失了過多實例的連接時(比如網絡故障或頻繁啟動關閉客戶端)節點會進入自我保護模式,保護注冊信息,不再刪除注冊數據,故障恢復時,自動退出自我保護模式。
項目性能優化
01概述
性能優化根據優化的類別,分為業務優化和技術優化。業務優化產生的效果也是非常大的,但它屬于產品和管理的范疇。同作為程序員,在平常工作中,我們面對的優化方式,主要是通過一系列的技術手段,來完成對既定的優化目標。這一系列的技術手段,我大體歸納為如圖以下 7 類:
可以看到,優化方式集中在對計算資源和存儲資源的規劃上。優化方法中有多種用空間換時間的方式,但只照顧計算速度,而不考慮復雜性和空間問題,也是不可取的。我們要做的,就是在照顧性能的前提下,達到資源利用的最優狀態。
接下來,我簡要介紹一下這7個優化方向。如果你感覺比較枯燥,那也沒關系,我們本文的目的,就是讓你的腦海里有一個總分的概念,以及對理論基礎有一個整體的認識。
02復用優化
在寫代碼的時候,你會發現有很多重復的代碼可以提取出來,做成公共的方法。這樣,在下次用的時候,就不用再費勁寫一遍了。
這種思想就是復用。上面的描述是編碼邏輯上的優化,對于數據存取來說,有同樣的復用情況。無論是在生活中還是編碼中,重復的事情一直在發生,如果沒有復用,工作和生活就會比較累。
在軟件系統中,談到數據復用,我們首先想到的就是緩沖和緩存。注意這兩個詞的區別,它們的意義是完全不同的,很多同學很容易搞混,在這里簡單地介紹一下。
緩沖(Buffer),常見于對數據的暫存,然后批量傳輸或者寫入。多使用順序方式,用來緩解不同設備之間頻繁地、緩慢地隨機寫,緩沖主要針對的是寫操作。
緩存(Cache),常見于對已讀取數據的復用,通過將它們緩存在相對高速的區域,緩存主要針對的是讀操作。
與之類似的,是對于對象的池化操作,比如數據庫連接池、線程池等,在 Java 中使用得非常頻繁。由于這些對象的創建和銷毀成本都比較大,我們在使用之后,也會將這部分對象暫時存儲,下次用的時候,就不用再走一遍耗時的初始化操作了。
03計算優化
并行執行
現在的 CPU 發展速度很快,絕大多數硬件,都是多核。要想加快某個任務的執行,最快最優的解決方式,就是讓它并行執行。并行執行有以下三種模式。
第一種模式是多機,采用負載均衡的方式,將流量或者大的計算拆分成多個部分,同時進行處理。比如,Hadoop 通過 MapReduce 的方式,把任務打散,多機同時進行計算。
第二種模式是采用多進程。比如 Nginx,采用 NIO 編程模型,Master 統一管理 Worker 進程,然后由 Worker 進程進行真正的請求代理,這也能很好地利用硬件的多個 CPU。
第三種模式是使用多線程,這也是 Java 程序員接觸最多的。比如 Netty,采用 Reactor 編程模型,同樣使用 NIO,但它是基于線程的。Boss 線程用來接收請求,然后調度給相應的 Worker 線程進行真正的業務計算。
像 Golang 這樣的語言,有更加輕量級的協程(Coroutine),協程是一種比線程更加輕量級的存在,但目前在 Java 中還不太成熟,就不做過多介紹了,但本質上,它也是對于多核的應用,使得任務并行執行。
變同步為異步
再一種對于計算的優化,就是變同步為異步,這通常涉及編程模型的改變。同步方式,請求會一直阻塞,直到有成功,或者失敗結果的返回。雖然它的編程模型簡單,但應對突發的、時間段傾斜的流量,問題就特別大,請求很容易失敗。
異步操作可以方便地支持橫向擴容,也可以緩解瞬時壓力,使請求變得平滑。同步請求,就像拳頭打在鋼板上;異步請求,就像拳頭打在海綿上。你可以想象一下這個過程,后者肯定是富有彈性的,體驗更加友好。
惰性加載
最后一種,就是使用一些常見的設計模式來優化業務,提高體驗,比如單例模式、代理模式等。舉個例子,在繪制 Swing 窗口的時候,如果要顯示比較多的圖片,就可以先加載一個占位符,然后通過后臺線程慢慢加載所需要的資源,這就可以避免窗口的僵死。
04結果集優化
接下來介紹一下對結果集的優化。舉個比較直觀的例子,我們都知道 XML 的表現形式是非常好的,那為什么還有 JSON 呢?除了書寫要簡單一些,一個重要的原因就是它的體積變小了,傳輸效率和解析效率變高了,像 Google 的 Protobuf,體積就更小了一些。雖然可讀性降低,但在一些高并發場景下(如 RPC),能夠顯著提高效率,這是典型的對結果集的優化。
這是由于我們目前的 Web 服務,都是 C/S 模式。數據從服務器傳輸到客戶端,需要分發多份,這個數據量是急劇膨脹的,每減少一小部分存儲,都會有比較大的傳輸性能和成本提升。
像 Nginx,一般都會開啟 GZIP 壓縮,使得傳輸的內容保持緊湊??蛻舳酥恍枰恍〔糠钟嬎隳芰?#xff0c;就可以方便解壓。由于這個操作是分散的,所以性能損失是固定的。
了解了這個道理,我們就能看到對于結果集優化的一般思路,你要盡量保持返回數據的精簡。一些客戶端不需要的字段,那就在代碼中,或者直接在 SQL 查詢中,就把它去掉。
對于一些對時效性要求不高,但對處理能力有高要求的業務。我們要吸取緩沖區的經驗,盡量減少網絡連接的交互,采用批量處理的方式,增加處理速度。
結果集合很可能會有二次使用,你可能會把它加入緩存中,但依然在速度上有所欠缺。這個時候,就需要對數據集合進行處理優化,采用索引或者 Bitmap 位圖等方式,加快數據訪問速度。
05資源沖突優化
我們在平常的開發中,會涉及很多共享資源。這些共享資源,有的是單機的,比如一個 HashMap;有的是外部存儲,比如一個數據庫行;有的是單個資源,比如 Redis 某個 key 的Setnx;有的是多個資源的協調,比如事務、分布式事務等。
現實中的性能問題,和鎖相關的問題是非常多的。大多數我們會想到數據庫的行鎖、表鎖、Java 中的各種鎖等。在更底層,比如 CPU 命令級別的鎖、JVM 指令級別的鎖、操作系統內部鎖等,可以說無處不在。
只有并發,才能產生資源沖突。也就是在同一時刻,只能有一個處理請求能夠獲取到共享資源。解決資源沖突的方式,就是加鎖。再比如事務,在本質上也是一種鎖。
按照鎖級別,鎖可分為樂觀鎖和悲觀鎖,樂觀鎖在效率上肯定是更高一些;按照鎖類型,鎖又分為公平鎖和非公平鎖,在對任務的調度上,有一些細微的差別。
對資源的爭用,會造成嚴重的性能問題,所以會有一些針對無鎖隊列之類的研究,對性能的提升也是巨大的。
06算法優化
算法能夠顯著提高復雜業務的性能,但在實際的業務中,往往都是變種。由于存儲越來越便宜,在一些 CPU 非常緊張的業務中,往往采用空間換取時間的方式,來加快處理速度。
算法屬于代碼調優,代碼調優涉及很多編碼技巧,需要使用者對所使用語言的 API 也非常熟悉。有時候,對算法、數據結構的靈活使用,也是代碼優化的一個重要內容。比如,常用的降低時間復雜度的方式,就有遞歸、二分、排序、動態規劃等。
一個優秀的實現,比一個拙劣的實現,對系統的影響是非常大的。比如,作為 List 的實現,LinkedList 和 ArrayList 在隨機訪問的性能上,差了好幾個數量級;又比如,CopyOnWriteList 采用寫時復制的方式,可以顯著降低讀多寫少場景下的鎖沖突。而什么時候使用同步,什么時候是線程安全的,也對我們的編碼能力有較高的要求。
這部分的知識,就需要我們在平常的工作中注意積累,后面的課時中,也會挑比較重要的知識點穿插講解。
07高效實現
在平時的編程中,盡量使用一些設計理念良好、性能優越的組件。比如,有了 Netty,就不用再選擇比較老的 Mina 組件。而在設計系統時,從性能因素考慮,就不要選 SOAP 這樣比較耗時的協議。再比如,一個好的語法分析器(比如使用 JavaCC),其效率會比正則表達式高很多。
總之,如果通過測試分析,找到了系統的瓶頸點,就要把關鍵的組件,使用更加高效的組件進行替換。在這種情況下,適配器模式是非常重要的。這也是為什么很多公司喜歡在現有的組件之上,再抽象一層自己的;而當在底層組件進行切換的時候,上層的應用并無感知。
08JVM 優化
因為 Java 是運行在 JVM 虛擬機之上,它的諸多特性,就要受到 JVM 的制約。對 JVM 虛擬機進行優化,也能在一定程度上能夠提升 JAVA 程序的性能。如果參數配置不當,甚至會造成 OOM 等比較嚴重的后果。
目前被廣泛使用的垃圾回收器是 G1,通過很少的參數配置,內存即可高效回收。CMS 垃圾回收器已經在 Java 14 中被移除,由于它的 GC 時間不可控,有條件應該盡量避免使用。
JVM 性能調優涉及方方面面的取舍,往往是牽一發而動全身,需要全盤考慮各方面的影響。所以了解 JVM 內部的一些運行原理,還是特別重要的,它有益于我們加深對代碼更深層次的理解,幫助我們書寫出更高效的代碼。
09小結
以上就是代碼優化的 7 個大方向,我們通過簡要的介紹,讓大家對性能優化的內容有了大體的了解。這7大方向是代碼優化的最主要方向,當然,性能優化還包含數據庫優化、操作系統優化、架構優化等其他一些內容,這些不是我們的重點,在后面的文章中,我們也只做簡要的介紹。
接下來,我們會了解一些性能評估工具,了解操作系統的一些資源限制,然后針對這7個優化點,進行展開討論。本文時適合案例分析后回讀,更加能夠加深你對 Java 性能優化的理解。
原文鏈接:https://blog.csdn.net/HongYu012/article/details/123423207
總結
以上是生活随笔為你收集整理的Java面试题总结一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Elasticsearch 英文分词
- 下一篇: 微信小程序“虚拟支付”问题汇总