java面試準備之JVM及設計模式

JVM

一、內存分區

1.1 程序計數器

內存空間小,線程私有。字節碼解釋器工作是就是通過改變這個計數器的值來選取下一條需要執行指令的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴計數器完成

如果線程正在執行一個 Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是 Native 方法,這個計數器的值則爲 (Undefined)。此內存區域是唯一一個在 Java 虛擬機規範中沒有規定任何 OutOfMemoryError 情況的區域。
1.2 Java 虛擬機棧

線程私有,生命週期和線程一致。描述的是 Java 方法執行的內存模型:每個方法在執行時都會牀創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行結束,就對應着一個棧幀從虛擬機棧中入棧到出棧的過程。

局部變量表:存放了編譯期可知的各種基本類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型)和 returnAddress 類型(指向了一條字節碼指令的地址)
StackOverflowError:線程請求的棧深度大於虛擬機所允許的深度。
OutOfMemoryError:如果虛擬機棧可以動態擴展,而擴展時無法申請到足夠的內存。
1.3 本地方法棧

區別於 Java 虛擬機棧的是,Java 虛擬機棧爲虛擬機執行 Java 方法(也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的 Native 方法服務。也會有 StackOverflowError 和 OutOfMemoryError 異常。

1.4 Java 堆

對於絕大多數應用來說,這塊區域是 JVM 所管理的內存中最大的一塊。線程共享,主要是存放對象實例和數組。內部會劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer, TLAB)。可以位於物理上不連續的空間,但是邏輯上要連續。

OutOfMemoryError:如果堆中沒有內存完成實例分配,並且堆也無法再擴展時,拋出該異常。
1.5方法區

屬於共享內存區域,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

1.6 運行時常量池

屬於方法區一部分,用於存放編譯期生成的各種字面量和符號引用。編譯器和運行期(String 的 intern() )都可以將常量放入池中。內存有限,無法申請時拋出 OutOfMemoryError。

1.7 直接內存

非虛擬機運行時數據區的部分
注:
JVM的方法區和永久帶是什麼關係?
永久帶又叫Perm區,只存在於hotspot jvm中,並且只存在於jdk7和之前的版本中,jdk8中已經徹底移除了永久帶,jdk8中引入了一個新的內存區域叫metaspace。
(1)並不是所有的jvm中都有永久帶,ibm的j9,oracle的JRocket都沒有永久帶。
(2)永久帶是實現層面的東西。
(3)永久帶裏面存的東西基本上就是方法區規定的那些東西。
我們可以說,永久帶是方法區的一種實現,當然,在hotspot jdk8中metaspace可以看成是方法區的一種實現。

二、 垃圾回收機制

主要負責兩件事情:
(1)發現無用的對象;
(2)回收被無用對象佔用的內存空間,使之再次被程序使用(一般是在CPU空閒或者內存不足時)。
注:事實上,除了釋放沒用對象佔用的內存空間外,垃圾回收也可以清除內存紀錄碎片(由於創建對象和垃圾回收器釋放丟棄對象所佔的內存空間)

三、 gc常見算法

引用計數、可達性分析算法判斷對象是否死亡
標記-清除算法、複製算法、標記-整理算法、分代收集算法 垃圾回收的四種算法**
3.1 引用計數法
簡單但是速度很慢,缺陷是不能處理循環引用的情況。
原理:此對象有一個引用,既增加一個計數器,刪除一個引用減少一個計數器,垃圾回收時,只回收計數器爲0的對象,此算法最致命的 是無法處理循環引用的情況。
3.2可達性分析算法(根搜索算法)
可達性分析算法是通過判斷對象的引用鏈是否可達來決定對象是否可以被回收。
從GC Roots(每種具體實現對GC Roots有不同的定義)作爲起點,向下搜索它們引用的對象,可以生成一棵引用樹,樹的節點視爲可達對象,反之視爲不可達。
在Java語言中,可以作爲GC Roots的對象包括下面幾種:
虛擬機棧(棧幀中的本地變量表)中的引用對象。
方法區中的類靜態屬性引用的對象。
方法區中的常量引用的對象。
本地方法棧中JNI(Native方法)的引用對象
真正標記以爲對象爲可回收狀態至少要標記兩次。
finalize()方法 最後的救贖
四種引用
強引用就是指在程序代碼之中普遍存在的,類似"Object obj = new Object()"這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
Object obj = new Object();
軟引用是用來描述一些還有用但並非必需的對象,對於軟引用關聯着的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進回收範圍進行第二次回收。如果這次回收還沒有足夠的內存,纔會拋出內存溢出異常。在JDK1.2之後,提供了SoftReference類來實現軟引用。
Object obj = new Object();
SoftReference sf = new SoftReference(obj);
弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象,只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK1.2之後,提供了WeakReference類來實現弱引用。
Object obj = new Object();
WeakReference wf = new WeakReference(obj);
虛引用也成爲幽靈引用或者幻影引用,它是最弱的一中引用關係。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。爲一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。在JDK1.2之後,提供給了PhantomReference類來實現虛引用。

3.3 標記-清除算法
標記清除算法分爲“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。
之所以說他是最基礎的收集算法,是因爲後續的收集算法都是基於這種思路並且對其不足進行改進而得到的。
它的主要不足有兩個:

  1. 一個是效率問題,標記和清除兩個過程的效率都不高(效率不高
  2. 另一個是空間問題,標記清除後會產生大量不連續的內存碎片,空間碎片太多可能會導致以後再程序運行過程中需要分配較大對象時,無法找到足夠的連續的內存而不得不提前觸發另一次垃圾收集動作。(產生大量空間碎片

3.4 複製算法
把空間分成兩塊,每次只對其中一塊進行 GC。當這塊內存使用完時,就將還存活的對象複製到另一塊上面。(內存利用率只有一半)
解決前一種方法的不足,但是會造成空間利用率低下。因爲大多數新生代對象都不會熬過第一次 GC。所以沒必要 1 : 1 劃分空間。可以分一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 空間和其中一塊 Survivor。當回收時,將 Eden 和 Survivor 中還存活的對象一次性複製到另一塊 Survivor 上,最後清理 Eden 和 Survivor 空間。大小比例一般是 8 : 1 : 1,每次浪費 10% 的 Survivor 空間。但是這裏有一個問題就是如果存活的大於 10% 怎麼辦?這裏採用一種分配擔保策略:多出來的對象直接進入老年代。
3.5 標記-整理算法
不同於針對新生代的複製算法,針對老年代的特點,創建該算法。主要是把存活對象移到內存的一端
3.6 分代收集算法
根據存活對象劃分幾塊內存區,一般是分爲新生代和老年代。然後根據各個年代的特點制定相應的回收算法。
新生代
每次垃圾回收都有大量對象死去,只有少量存活,選用複製算法比較合理。
老年代
老年代中對象存活率較高、沒有額外的空間分配對它進行擔保。所以必須使用 標記 —— 清除 或者 標記 —— 整理 算法回收。
3.7 系統線程劃分
串行收集器
使用單線程處理所有垃圾回收工作,因爲無需多線程交互,所以效率比較高。但是,也無法使用多處理器的優勢。所以此收集器適合單處理器的機器。當然,此收集器也可以用在小數量(100M左右)情況下的多處理器機器上,可以使用-XX:+UseSerialGC打開。
並行收集器
對年輕代進行並行垃圾回收,因此可以減少垃圾回收的時間,一般在多線程多處理器上使用,使用-XX:+UseParallelGC打開。年老代進行並行收集。如果年老代不使用併發收集的話,是使用單線程進行垃圾回收,因此會制約擴展能力,使用-XX:+UseParallelOldGC打開。
使用-XX:ParallelGCThreads=設置並行垃圾回收的線程數,此值可以設置與機器處理數量相等。
此收集器可以進行如下配置:
最大垃圾回收暫停:指定垃圾回收時的最大暫停時間,通過-XX:MaxGCPauseMillis=指定。爲毫秒數,如果指定了這個值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。此值可能會減少應用的吞吐量。吞吐量:吞吐量爲垃圾回收時間與非垃圾回收時間的比值。通過-XX:GCTimeRatio=來設定,公式爲1/(1+N),例如,-XX:GCTimeRatio=19時,表示5%的時間用於垃圾回收,默認情況爲99,既1%的時間用於垃圾回收。
併發收集器
可以保證大部分工作都併發執行(應用不停止),垃圾回收只暫停很少的時間,此收集器適合對響應時間要求比較高的中,大型應用,使用-XX:+UseConcMarkSweepGC打開。
併發收集器主要減少年老代的暫停時間,他在應用不停止的情況下使用獨立的垃圾回收線程,跟蹤可達對象,在每個年老代垃圾回收週期中,在收集初期併發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次,第二次暫停會比第一次長,在此過程中多個線程同時進行垃圾回收工作。

併發收集器使用處理器換來短暫的停頓時間,在一個N個處理器的系統上,併發收集部分使用K/N個可用處理器進行回收,一般情況下1<=K<=N/4

在只有一個處理器的主機上使用併發收集器,設置爲incremental mode模式也可以獲得較短的停頓時間。

浮動垃圾:由於在應用運行的同時進行垃圾回收,所以有些垃圾回收可能在垃圾回收完成時產生,這樣就造成了“floating Garbage”,這些垃圾需要在下次垃圾回收週期時才能回收掉,所以併發收集器一般需要20%的預留空間用於這些浮動垃圾。

Concurrent Mode Failure:併發收集器在應用程序運行時進行收集,所以需要保證堆在垃圾回收的這段時間有足夠的空間供程序使用,否則,垃圾回收還未完成,堆空間先充滿了,這種情況下將會發生“併發模式失敗”,此時整個應用將會暫停。進行垃圾回收。

啓動併發收集器:因爲併發收集在應用運行時進行收集,睡衣必須保證收集完成之前有足夠的內存空間供程序使用,否則會出現“Concurrent Mode Failure”,通過設置-XX:CMSinitiatingOccupancyFraction=指定還有多少剩餘堆時開始執行併發收集。

並行:多個事件同一時間發生,同時做多件事
併發:多個事件在同一個時間間隔內發生。每年11.11日狂歡節,一天內接受的最大人數。

四、 垃圾回收器

收集算法是內存回收的理論,而垃圾回收器是內存回收的實踐。
4.1Serial 收集器

這是一個單線程收集器。意味着它只會使用一個 CPU 或一條收集線程去完成收集工作,並且在進行垃圾回收時必須暫停其它所有的工作線程直到收集結束。
分代收集算法
特點:Stop the world

4.2 ParNew 收集器

分代收集算法
可以認爲是 Serial 收集器的多線程版本

4.3 Parallel Scavenge 收集器

這是一個新生代收集器,也是使用複製算法實現,同時也是並行的多線程收集器。

CMS 等收集器的關注點是儘可能地縮短垃圾收集時用戶線程所停頓的時間,而 Parallel Scavenge 收集器的目的是達到一個可控制的吞吐量(Throughput = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間))。
作爲一個吞吐量優先的收集器,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整停頓時間。這就是 GC 的自適應調整策略(GC Ergonomics)。

4.4 Serial Old 收集器

收集器的老年代版本,單線程,使用 標記 —— 整理。

4.5 Parallel Old 收集器

Parallel Old 是 Parallel Scavenge 收集器的老年代版本。多線程,使用 標記 —— 整理

4.6 CMS 收集器

CMS (Concurrent Mark Sweep) 收集器是一種以獲取最短回收停頓時間爲目標的收集器。基於 標記 —— 清除 算法實現。

運作步驟:

初始標記(CMS initial mark):標記 GC Roots 能直接關聯到的對象
併發標記(CMS concurrent mark):進行 GC Roots Tracing
重新標記(CMS remark):修正併發標記期間的變動部分
併發清除(CMS concurrent sweep)

缺點:對 CPU 資源敏感、無法收集浮動垃圾、標記 —— 清除 算法帶來的空間碎片
浮動垃圾:由於CMS併發清理階段用戶線程還在運行着,伴隨程序運行自然就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在當此收集中處理掉他們,只好留待下一次GC時再清理。這一部分就叫做浮動垃圾。

4.7 G1 收集器

面向服務端的垃圾回收器。

優點:並行與併發、分代收集、空間整合、可預測停頓。
運作步驟:
初始標記(Initial Marking)
併發標記(Concurrent Marking)
最終標記(Final Marking)
篩選回收(Live Data Counting and Evacuation)

垃圾收集器沒有最好的,根據環境的不同搭配不同的垃圾收集器

注:什麼時候進行GC呢

由於對象進行了分代處理,因此垃圾回收區域、時間也不一樣。GC有兩種類型:Minor GC和Full GC。

Minor GC

一般情況下,當新對象生成,並且在Eden申請空間失敗時,就會觸發Minor GC,對Eden區域進行GC,清除非存活對象,並且把尚且存活的對象移動到Survivor區。然後整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。因爲大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,所以Eden區的GC會頻繁進行。因而,一般在這裏需要使用速度快、效率高的算法,使Eden去能儘快空閒出來。

Full GC
對整個堆進行整理,包括Young、Tenured和Perm。Full GC因爲需要對整個對進行回收,所以比Full GC要慢,因此應該儘可能減少Full GC的次數。在對JVM調優的過程中,很大一部分工作就是對於Full GC的調節。有如下原因可能導致Full GC:

老年代(Tenured)被寫滿
持久代(Perm)被寫滿
System.gc()被顯示調用
上一次GC之後Heap的各域分配策略動態變化

五、內存分配規則

5.1 對象優先在Eden分配
新生代 GC (Minor GC)

發生在新生代的垃圾回收動作,頻繁,速度快。

老年代 GC (Major GC / Full GC)

發生在老年代的垃圾回收動作,出現了 Major GC 經常會伴隨至少一次 Minor GC(非絕對)。Major GC 的速度一般會比 Minor GC 慢十倍以上。

5.2 大對象直接金進入老年代

虛擬機提供一個參數-XX:PretenureSize Threshold 設置大於此值的算是大對象

5.3 長期存活的對象將進入老年代

一個對象每經過一次Minor GC 年齡增加一歲,當年齡達到一定程度(默認15),就晉升到老年代

5.4 動態對象年齡判定

在Survivor空間中相同年齡所有對象的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無需等到最大值所需年齡

5.5 空間分配擔保

取之前每一次回收晉升到老年代對象容量的平均大小值,與老年代的剩餘空間進行比較,決定是否進行Full GC 來讓老年代騰出更多空間。

六、類與類加載器

6.1 類加載器分類
從虛擬機的角度來說,只存在兩種不同的類加載器:一種是啓動類加載器(Bootstrap ClassLoader),該類加載器使用C++語言實現,屬於虛擬機自身的一部分。另外一種就是所有其它的類加載器,這些類加載器是由Java語言實現,獨立於JVM外部,並且全部繼承自抽象類java.lang.ClassLoader。
JDK 默認提供瞭如下幾種ClassLoader:
1.bootstrap class loader(引導類加載器)
①用於加載 Java的核心類
②底層由C++編寫,並不繼承java.lang.ClassLoader
③負責加載 JAVA_HOME中jre/lib/rt.jar | resources.jar…裏所有的class
2.extensions class loader(擴展類加載器)
①負責加載Java的擴展類庫
②默認加載$JAVA_HOME中jre/lib/ext/目下的所有jar
③父類加載器爲null
3.system class loader(系統類加載器)(應用程序類加載器)
①根據Java應用的CLASSPATH來加載Java類
②Java中的應用類都是通過它來加載的
③父類加載器ExtClassLoader
4.custom class loader(自定義加載器)
6.2 雙親委託工作流程描述:

  1. 當前ClassLoader首先從自己已經加載的類中查詢是否此類已經加載,如果已經加載則直接返回原來已經加載的類。
    每個類加載器都有自己的加載緩存,當一個類被加載了以後就會放入緩存,等下次加載的時候就可以直接返回了。
  2. 當前classLoader的緩存中沒有找到被加載的類的時候,委託父類加載器去加載,父類加載器採用同樣的策略,首先查看自己的緩存,然後委託父類的父類去加載,一直到bootstrp ClassLoader.
  3. 當所有的父類加載器都沒有加載的時候,再由當前的類加載器加載,並將其放入它自己的緩存中,以便下次有加載請求的時候直接返回。
    ①CustomClassLoader接收到類加載請求
    查找緩存中是否有加載過該class實例:
    1>若緩存中已經存在,則返回該class實例
    2>若緩存中不存在,則將請求委派給父類加載器AppClassLoader
    ②AppClassLoader接收到類加載請求
    查找緩存中是否有加載過該class實例:
    1>若緩存中已經存在,則返回該class實例
    2>若緩存中不存在,則將請求委派給父類加載器ExtensionClassLoader
    ③ExtensionClassLoader接收到類加載請求
    查找緩存中是否有加載過該class實例:
    1>若緩存中已經存在,則返回該class實例
    2>若緩存中不存在,則將請求委派給父類加載器BootstrapClassLoader
    ⑤BootstrapClassLoader接收到類加載請求
    查找緩存中是否有加載過該class實例:
    1>若緩存中已經存在,則返回該class實例
    2>若緩存中不存在,BootstrapClassLoader在路徑sun.mic.boot.class下搜索
    3>若在搜索範圍內有搜索到,就將搜索到的class實例返回,否則交由子類加載器ExtensionClassLoader處理
    ⑥ExtensionClassLoader在路徑java.ext.dirs下搜索
    若在搜索範圍內有搜索到,就將搜索到的class實例返回,否則交由子類加載器AppClassLoader處理
    ⑦AppClassLoader在路徑java.class.path下搜索
    若在搜索範圍內有搜索到,就將搜索到的class實例返回,否則交由子類加載器CustomClassLoader處理
    ⑧CustomClassLoader在自定義時指定的路徑下搜索該class,若搜索到便返回class實例.
    否則將拋出ClassNotFountException異常

問題思考:

(1)爲什麼要是用這種雙親模式呢?
①其主要目的就是避免相同的類被重複加載進入內存中而造成不必要的空間浪費.父類加載器如果已經加載過該類,那麼子類就沒有必要在對其進行加載.
②安全因素方面的考量,雙親模式時從下至上依次去找尋該類的跡象,如果不使用雙親模式,那麼所有的類都會在子類加載器中被加載.這裏的子類加載器也包括自定義類加載器.試想一個場景,在未使用雙親模式的情況下,隨便一個人定義了一個包含病毒的自定義String類動態的替換掉了原有api中定義的String類型,那麼這個自定義的String類一旦被加載就會出現隱患問題.相反,如果使用雙親模式的話,即便你自定義了攜帶病毒的String類也不會被加載(因爲String類會在更高層級的父類加載器中被加載到,*具體就是因爲針對java.開頭的類,jvm的實現中已經保證了必須由bootstrp來加載
(2)JVM是如何判斷2個class文件是否相同的呢?
①判斷兩個class文件相同有兩個條件:
1>兩個類的類名相同
2>由同一個類加載器加載
②一個test.java文件經過javac編譯後生成test.class文件.有兩個類加載器ClassLoaderA&ClassLoaderB分別對其進行加載分別生成對應的java.lang.Class對象,這兩個對象站在JVM的角度會被認爲是兩個不同的對象,但它們卻是同一分字節碼文件.如果將這兩個Class實例生成的具體對象進行轉換時,就會拋出java.lang.ClassCaseException,提示這是兩個不同的類型
6.3不遵循“雙親委託機制”的場景
上面說了雙親委託機制主要是爲了實現不同的ClassLoader之間加載的類的交互問題,被大家公用的類就交由父加載器去加載,但是Java中確實也存在父類加載器加載的類需要用到子加載器加載的類的情況。下面我們就來說說這種情況的發生。
Java中有一個SPI(Service Provider Interface)標準,使用了SPI的庫,比如JDBC,JNDI等,我們都知道JDBC需要第三方提供的驅動纔可以,而驅動的jar包是放在我們應 用程序本身的classpath的,而jdbc 本身的api是jdk提供的一部分,它已經被bootstrp加載了,那第三方廠商提供的實現類怎麼加載呢?這裏面JAVA引入了線程上下文類加載的概念,線程類加載器默認會從父線程繼承,如果沒有指定的話,默認就是系統類加載器(AppClassLoader),這樣的話當加載第三方驅動的時候,就可 以通過線程的上下文類加載器來加載。另外爲了實現更靈活的類加載器OSGI以及一些Java appserver也打破了雙親委託機制。

設計模式

單例模式 確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例
多種單例模式實現
中介者模式:
通過讓對象彼此解耦,增加對象的複用性
通過將控制邏輯集中,可以簡化系統維護
通過中介者使一對所變成了一對一,便於理解
觀察者一般可以看做是第三者,比如在學校上自習的時候,大家肯定都有過交頭接耳、各種玩耍的經歷,這時總會有一個“放風”的小夥伴,當老師即將出現時及時“通知”大家老師來了。再比如,拍賣會的時候,大家相互叫價,拍賣師會觀察最高標價,然後通知給其它競價者競價,這就是一個觀察者模式。定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並自動更新。
1、常見的設計模式
單例模式、工廠模式、建造模式、觀察者模式、適配器模式、代理模式、裝飾模式.
參考:https://www.cnblogs.com/cr330326/p/5627658.html
2、設計模式的六大原則及其含義

  1. 單一職責原則:一個類只負責一個功能領域中的相應職責,或者可以定義爲:就一個類而言,應該只有一個引起它變化的原因。主要作用實現代碼高內聚,低耦合。
  2. 開閉原則:一個軟件實體應當對擴展開放,對修改關閉。即軟件實體應儘量在不修改原有代碼的情況下進行擴展。
  3. 里氏替換原則:所有引用基類(父類)的地方必須能透明地使用其子類的對象。里氏替換原則是實現開閉原則的方式之一
  4. 依賴倒置原則:抽象不應該依賴於細節,細節應當依賴於抽象。換言之,要針對接口編程,而不是針對實現編程。
  5. 接口隔離原則:使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴那些它不需要的接口。
  6. 迪米特法則:一個軟件實體應當儘可能少地與其他實體發生相互作用。
    參考:https://www.cnblogs.com/dolphin0520/p/3919839.html
    3、常見的單例模式以及各種實現方式的優缺點,哪一種最好,手寫常見的單利模式
    常見模式及優缺點:
    • 餓漢式:
    o 優點:不用加鎖可以確保對象的唯一性,線程安全。
    o 缺點:初始化對象會浪費不必要的資源,未實現延遲加載。
    • 懶漢式:
    o 優點:實現了延時加載。
    o 缺點:線程不安全,想實現線程安全,得加鎖(synchronized),這樣會浪費一些不必要的資源。
    • 雙重檢測鎖式(Double Check Lock–DCL):
    o 優點:資源利用率高,效率高。
    o 缺點:第一次加載稍慢,由於java處理器允許亂序執行,偶爾會失敗。
    • 靜態內部式:
    o 優點:第一次調用方法時才加載類,不僅保證線程安全還能保證對象的唯一,還延遲了單例的實例化
    o 缺點:
    4、設計模式在實際場景的應用
    單例:連接數據庫,記錄日誌
    5、Spring中用到了哪些設計模式
  7. 工廠模式:spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得bean對象,但是否是在傳入參數後創建還是傳入參數前創建這個要根據具體情況來定。
  8. 代理模式:Spring的AOP就是代理模式的體現。
  9. 觀察者模式:常用的地方是Listener的實現,spring中ApplicationListener就是觀察者的體現。
  10. 策略模式:spring在實例化對象的時候使用到了。
  11. 工廠方法:Spring中的FactoryBean就是典型的工廠方法模式。
    參考:https://www.cnblogs.com/hwaggLee/p/4510687.html
    6、MyBatis中用到了哪些設計模式
  12. Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
  13. 工廠模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
  14. 單例模式,例如ErrorContext和LogFactory;
  15. 代理模式,Mybatis實現的核心,比如MapperProxy、ConnectionLogger,用的jdk的動態代理;還有executor.loader包使用了cglib或者javassist達到延遲加載的效果;
  16. 組合模式,例如SqlNode和各個子類ChooseSqlNode等;
  17. 模板方法模式,例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler;
  18. 適配器模式,例如Log的Mybatis接口和它對jdbc、log4j等各種日誌框架的適配實現;
  19. 裝飾者模式,例如Cache包中的cache.decorators子包中等各個裝飾者的實現;
  20. 迭代器模式,例如迭代器模式PropertyTokenizer;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章