享元模式在JDK源码中的应用——Java设计模式系列学习笔记
1. String中的享元模式
Java中將String類定義為final(不可改變的),JVM中字符串一般保存在字符串常量池中,java會(huì)確保一個(gè)字符串在常量池中只有一個(gè)拷貝,這個(gè)字符串常量池在JDK6.0以前是位于常量池中,位于永久代,而在JDK7.0中,JVM將其從永久代拿出來(lái)放置于堆中。
我們做一個(gè)測(cè)試:
public class Main {public static void main(String[] args) {String s1 = "hello";String s2 = "hello";String s3 = "he" + "llo";String s4 = "hel" + new String("lo");String s5 = new String("hello");String s6 = s5.intern();String s7 = "h";String s8 = "ello";String s9 = s7 + s8;System.out.println(s1==s2);//trueSystem.out.println(s1==s3);//trueSystem.out.println(s1==s4);//falseSystem.out.println(s1==s9);//falseSystem.out.println(s4==s5);//falseSystem.out.println(s1==s6);//true} }String類的final修飾的,以字面量的形式創(chuàng)建String變量時(shí),jvm會(huì)在編譯期間就把該字面量hello放到字符串常量池中,由Java程序啟動(dòng)的時(shí)候就已經(jīng)加載到內(nèi)存中了。這個(gè)字符串常量池的特點(diǎn)就是有且只有一份相同的字面量,如果有其它相同的字面量,jvm則返回這個(gè)字面量的引用,如果沒有相同的字面量,則在字符串常量池創(chuàng)建這個(gè)字面量并返回它的引用。
由于s2指向的字面量hello在常量池中已經(jīng)存在了(s1先于s2),于是jvm就返回這個(gè)字面量綁定的引用,所以s1==s2。
s3中字面量的拼接其實(shí)就是hello,jvm在編譯期間就已經(jīng)對(duì)它進(jìn)行優(yōu)化,所以s1和s3也是相等的。
s4中的new String("lo")生成了兩個(gè)對(duì)象,hel,new String("lo"),hel存在字符串常量池,new String("lo")存在堆中,String s4 = "hel" + new String("lo")實(shí)質(zhì)上是兩個(gè)對(duì)象的相加,編譯器不會(huì)進(jìn)行優(yōu)化,相加的結(jié)果存在堆中,而s1存在字符串常量池中,當(dāng)然不相等。s1==s9的原理一樣。
s4==s5兩個(gè)相加的結(jié)果都在堆中,不用說,肯定不相等。
s1==s6中,s5.intern()方法能使一個(gè)位于堆中的字符串在運(yùn)行期間動(dòng)態(tài)地加入到字符串常量池中(字符串常量池的內(nèi)容是程序啟動(dòng)的時(shí)候就已經(jīng)加載好了),如果字符串常量池中有該對(duì)象對(duì)應(yīng)的字面量,則返回該字面量在字符串常量池中的引用,否則,創(chuàng)建復(fù)制一份該字面量到字符串常量池并返回它的引用。因此s1==s6輸出true。
2. Integer 中的享元模式
使用例子如下:
public static void main(String[] args) {Integer i1 = 12 ;Integer i2 = 12 ;System.out.println(i1 == i2);Integer b1 = 128 ;Integer b2 = 128 ;System.out.println(b1 == b2); }輸出:
true false為什么第一個(gè)是true,第二個(gè)是false?
反編譯后可以發(fā)現(xiàn) Integer b1 = 128; 實(shí)際變成了 Integer b1 = Integer.valueOf(128);,所以我們來(lái)看 Integer 中的 valueOf 方法的實(shí)現(xiàn)
IntegerCache 緩存類
//是Integer內(nèi)部的私有靜態(tài)類,里面的cache[]就是jdk事先緩存的Integer。 private static class IntegerCache {static final int low = -128;//區(qū)間的最低值static final int high;//區(qū)間的最高值,后面默認(rèn)賦值為127,也可以用戶手動(dòng)設(shè)置虛擬機(jī)參數(shù)static final Integer cache[]; //緩存數(shù)組static {// high value may be configured by propertyint h = 127;//這里可以在運(yùn)行時(shí)設(shè)置虛擬機(jī)參數(shù)來(lái)確定h :-Djava.lang.Integer.IntegerCache.high=250String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {//用戶設(shè)置了int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);//雖然設(shè)置了但是還是不能小于127// 也不能超過最大值h = Math.min(i, Integer.MAX_VALUE - (-low) -1);}high = h;cache = new Integer[(high - low) + 1];int j = low;//循環(huán)將區(qū)間的數(shù)賦值給cache[]數(shù)組for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);}private IntegerCache() {} }可以看到 Integer 默認(rèn)先創(chuàng)建并緩存 -128 ~ 127 之間數(shù)的 Integer 對(duì)象,當(dāng)調(diào)用 valueOf 時(shí)如果參數(shù)在 -128 ~ 127 之間則計(jì)算下標(biāo)并從緩存中返回,否則創(chuàng)建一個(gè)新的 Integer 對(duì)象
public final class Long extends Number implements Comparable<Long> {public static Long valueOf(long var0) {return var0 >= -128L && var0 <= 127L ? Long.LongCache.cache[(int)var0 + 128] : new Long(var0);} private static class LongCache {private LongCache(){}static final Long cache[] = new Long[-(-128) + 127 + 1];static {for(int i = 0; i < cache.length; i++)cache[i] = new Long(i - 128);}}//... }同理,Long 中也有緩存,不過不能指定緩存最大值
3. Apache Commons Pool2中的享元模式
對(duì)象池化的基本思路是:將用過的對(duì)象保存起來(lái),等下一次需要這種對(duì)象的時(shí)候,再拿出來(lái)重復(fù)使用,從而在一定程度上減少頻繁創(chuàng)建對(duì)象所造成的開銷。用于充當(dāng)保存對(duì)象的“容器”的對(duì)象,被稱為“對(duì)象池”(Object Pool,或簡(jiǎn)稱Pool)
Apache Commons Pool實(shí)現(xiàn)了對(duì)象池的功能。定義了對(duì)象的生成、銷毀、激活、鈍化等操作及其狀態(tài)轉(zhuǎn)換,并提供幾個(gè)默認(rèn)的對(duì)象池實(shí)現(xiàn)。
有幾個(gè)重要的對(duì)象:
- PooledObject(池對(duì)象):用于封裝對(duì)象(如:線程、數(shù)據(jù)庫(kù)連接、TCP連接),將其包裹成可被池管理的對(duì)象。
- PooledObjectFactory(池對(duì)象工廠):定義了操作PooledObject實(shí)例生命周期的一些方法,PooledObjectFactory必須實(shí)現(xiàn)線程安全。
- Object Pool (對(duì)象池):Object Pool負(fù)責(zé)管理PooledObject,如:借出對(duì)象,返回對(duì)象,校驗(yàn)對(duì)象,有多少激活對(duì)象,有多少空閑對(duì)象。
重要方法:
borrowObject:從池中借出一個(gè)對(duì)象。
returnObject:將一個(gè)對(duì)象返還給池。
總結(jié)
以上是生活随笔為你收集整理的享元模式在JDK源码中的应用——Java设计模式系列学习笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 题意解读+详细题解-Leecode 31
- 下一篇: 深入浅出,对于代理模式的理解(代理模式与