日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
初級(jí)必備:?jiǎn)卫J降?個(gè)問題

故事

實(shí)話實(shí)說,關(guān)于單例模式,網(wǎng)上有N多個(gè)版本。你估計(jì)也看過很多版本。但看完了又能怎樣?我技術(shù)群里的一位小伙伴,上周面試,就因?yàn)橐粋€(gè)單例模式,然后叫他回去等通知了。

創(chuàng)新互聯(lián)建站網(wǎng)絡(luò)公司擁有十余年的成都網(wǎng)站開發(fā)建設(shè)經(jīng)驗(yàn),上千客戶的共同信賴。提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、網(wǎng)站開發(fā)、網(wǎng)站定制、買友情鏈接、建網(wǎng)站、網(wǎng)站搭建、響應(yīng)式網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)師打造企業(yè)風(fēng)格,提供周到的售前咨詢和貼心的售后服務(wù)

下面是這位同學(xué)被問到的問題:

1、說說單例模式的特點(diǎn)?

2、你知道單例模式的具體使用場(chǎng)景嗎?

3、單例模式常見寫法有幾種?

4、怎么樣保證線程安全?

5、怎么不會(huì)被反射攻擊?

6、怎樣保證不會(huì)被序列化和反序列化的攻擊?

7、枚舉為什么會(huì)不會(huì)被序列化?

.....

你也可以嘗試行的回答這幾個(gè)題,看看自己能回答上幾個(gè)。

定義

單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問其唯一的對(duì)象的方式,可以直接訪問,不需要實(shí)例化該類的對(duì)象。

特點(diǎn):

  • 1、單例類只能有一個(gè)實(shí)例。
  • 2、單例類必須自己創(chuàng)建自己的唯一實(shí)例。
  • 3、單例類必須給所有其他對(duì)象提供這一實(shí)例
  • 4、隱藏所有的構(gòu)造方法

**目的:**保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。

案例:一家企業(yè)只能有一個(gè)CEO,有多個(gè)了其實(shí)亂套了。

使用場(chǎng)景

需要確保任何情況下都絕對(duì)只有一個(gè)實(shí)例。

比如:ServletContext、ServletConfig、ApplicationContext、DBTool等,都使用到了單列模式。

單例模式的寫法

  • 餓漢式
  • 懶漢式(包含雙重檢查鎖、靜態(tài)內(nèi)部類)
  • 注冊(cè)式(以枚舉為例)

餓漢式

從名字上就能看出,餓漢:餓了就得先吃飽,所以,一開始就搞定了。

餓漢式主要是使用了static,餓漢式也有兩種寫法,但本質(zhì)可以理解為是一樣的。

 
 
 
 
  1. public class HungrySingleton{
  2.     private static final HungrySingleton INSTANCE;
  3.     static {
  4.         INSTANCE=new HungrySingleton();
  5.     }
  6. //    private static final HungrySingleton INSTANCE=new HungrySingleton();
  7.     private HungrySingleton(){
  8.     }
  9.     public static HungrySingleton getInstance(){
  10.         return INSTANCE;
  11.     }
  12. }

餓漢式有個(gè)致命的缺點(diǎn):浪費(fèi)空間,不需要也實(shí)例化。如果是成千上萬(wàn)個(gè),也這么玩,想想有多恐怖。

于是,就會(huì)想到,能不能在使用的時(shí)候在實(shí)例化,從而引出了懶漢式。

懶漢式

顧名思義,就是需要的時(shí)候再創(chuàng)建,因?yàn)閼?,你不調(diào)用我方法,我是不會(huì)干活的。

下面是懶漢式的Java代碼實(shí)現(xiàn):

 
 
 
 
  1. public class LazySingleton {
  2.     private static LazySingleton lazySingleton = null;
  3.     private LazySingleton() {
  4.     }
  5.     public static LazySingleton getInstance() {
  6.         if (lazySingleton == null) {//01
  7.             lazySingleton = new LazySingleton();//02
  8.         }
  9.         return lazySingleton;
  10.     } 
  11. }

進(jìn)入getInstance方法,先判斷l(xiāng)azySingleton是否為空,為空,則創(chuàng)建一個(gè)對(duì)象,然后返回此對(duì)象。

但是,問題來了:

兩個(gè)線程同時(shí)進(jìn)入getInstance方法,然后都去執(zhí)行01這行代碼,都是true,然后各自進(jìn)去創(chuàng)建一個(gè)對(duì)象,然后返回自己創(chuàng)建的對(duì)象。

這豈不是不滿足只有唯一 一個(gè)對(duì)象的了嗎?所以這類存在線程安全的問題,那怎么解決呢?

第一印象肯定都是想到加鎖。于是,就有了下面的線程安全的懶加載版本:

 
 
 
 
  1. public class LazySingleton {
  2.     private static LazySingleton lazySingleton = null;
  3.     private LazySingleton() {
  4.     }
  5.     //簡(jiǎn)單粗暴的線程安全問題解決方案
  6.     //依然存在性能問題
  7.   public synchronized static LazySingleton getInstance() {
  8.         if (lazySingleton == null) {
  9.             lazySingleton = new LazySingleton();
  10.         }
  11.         return lazySingleton;
  12.     }
  13. }

給getInstance方法加鎖同步鎖標(biāo)志synchronized,但是又涉及到鎖的問題了,同步鎖是對(duì)系統(tǒng)性能優(yōu)影響的,盡管JDK1.6后,對(duì)其做了優(yōu)化,但它畢竟還是涉及到鎖的開銷。

每個(gè)線程調(diào)用getInstance方法時(shí)候,都會(huì)涉及到鎖,所以又對(duì)此進(jìn)行了優(yōu)化成為了大家耳熟能詳?shù)碾p重檢查鎖。

雙重檢查鎖

代碼實(shí)現(xiàn)如下:

 
 
 
 
  1. public class LazyDoubleCheckSingleton { 
  2.     private static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
  3.     private LazyDoubleCheckSingleton() {
  4.     }
  5.     public static LazyDoubleCheckSingleton getInstance() {
  6.         if (lazyDoubleCheckSingleton == null) {//01
  7.             synchronized (LazyDoubleCheckSingleton.class) {
  8.                 if (lazyDoubleCheckSingleton == null) {//02
  9.                     lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
  10.                 }
  11.             }
  12.         }
  13.         return lazyDoubleCheckSingleton;
  14.     }
  15. }

這段代碼中,在01行,如果不為空,就直接返回,這是第一次檢查。如果為空,則進(jìn)入同步代碼塊,02行又進(jìn)行一次檢查。

雙重檢查就是現(xiàn)實(shí)if判斷、獲取類對(duì)象鎖、if判斷。

上面這段代碼,看似沒問題,其實(shí)還是有問題的,比如:指令重排序(需要有JVM知識(shí)墊底哈)

指令重排是什么意思呢?

比如java中簡(jiǎn)單的一句

 
 
 
 
  1. lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();

會(huì)被編譯器編譯成如下JVM指令:

memory =allocate(); //1:分配對(duì)象的內(nèi)存空間

ctorInstance(memory); //2:初始化對(duì)象

instance =memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址

但是這些指令順序并非一成不變,有可能會(huì)經(jīng)過JVM和CPU的優(yōu)化,指令重排成下面的順序:

memory =allocate(); //1:分配對(duì)象的內(nèi)存空間

instance =memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址

ctorInstance(memory); //2:初始化對(duì)象

為了防止指令重排序,所以,我們可以使用volatile來做文章(注意:volatile能防止指令重排序和線程可見性)。

于是,更好的版本就出來了。

 
 
 
 
  1. public class LazyDoubleCheckSingleton {
  2.     //使用volatile修飾
  3.     private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null; 
  4.     private LazyDoubleCheckSingleton() {
  5.     }
  6.     public static LazyDoubleCheckSingleton getInstance() {
  7.         if (lazyDoubleCheckSingleton == null) {
  8.             synchronized (LazyDoubleCheckSingleton.class) {
  9.                 if (lazyDoubleCheckSingleton == null) {
  10.                     lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
  11.                 }
  12.             }
  13.         }
  14.         return lazyDoubleCheckSingleton;
  15.     }
  16. }

盡管相比前面的版本,確實(shí)改進(jìn)了很多,但依然有同步鎖,還是會(huì)影響性能問題。于是,又進(jìn)行優(yōu)化為靜態(tài)內(nèi)部類方式:

靜態(tài)內(nèi)部類

下面是靜態(tài)內(nèi)部類的代碼實(shí)現(xiàn):

利用了內(nèi)部類的特性,在JVM底層,能完美的規(guī)避了線程安全的問題,這種方式也是目前很多項(xiàng)目里喜歡使用的方式。

但是,還是會(huì)存在潛在的風(fēng)險(xiǎn),什么風(fēng)險(xiǎn)呢?

可以使用 反射 暴力的串改,同樣也會(huì)出現(xiàn)創(chuàng)建多個(gè)實(shí)例:

反射代碼實(shí)現(xiàn)如下:

 
 
 
 
  1. import java.lang.reflect.Constructor;
  2. public class LazyStaticSingletonTest {
  3.     public static void main(String[] args) {
  4.         try {
  5.             Class clazz = LazyStaticSingleton.class;
  6.             Constructor constructor = clazz.getDeclaredConstructor(null);
  7.             //強(qiáng)行訪問
  8.             constructor.setAccessible(true);
  9.             Object object = constructor.newInstance();
  10.             Object object1 = LazyStaticSingleton.getInstance();
  11.             System.out.println(object == object1);
  12.         } catch (Exception ex) {
  13.             ex.printStackTrace();
  14.         }
  15.     }
  16. }

這段代碼運(yùn)行結(jié)果為false。

所以,上面說的雙重檢查鎖的方式,通過反射,還是會(huì)存在潛在的風(fēng)險(xiǎn)。怎么辦呢?

在《Effect java 》這本書中,作者推薦使用枚舉來實(shí)現(xiàn)單例模式,因?yàn)槊杜e不能被反射。

枚舉

下面是枚舉式的單例模式的代碼實(shí)現(xiàn):

 
 
 
 
  1. public enum EnumSingleton {
  2.     INSTANCE;
  3.     private Object data;
  4.     public Object getData() {
  5.         return data;
  6.     }
  7.     public static EnumSingleton getInstance(){
  8.         return INSTANCE;
  9.     }
  10. }

我們把上面反射的那個(gè)代碼,來測(cè)試這個(gè)枚舉式單例模式。

 
 
 
 
  1. public class EnumTest {
  2.     public static void main(String[] args) {
  3.         try {
  4.             Class clazz = EnumSingleton.class;
  5.             Constructor constructor = clazz.getDeclaredConstructor(null);
  6.             //強(qiáng)行訪問
  7.             constructor.setAccessible(true);
  8.             Object object = constructor.newInstance();
  9.             Object object1 = EnumSingleton.getInstance();
  10.             System.out.println(object == object1);
  11.         } catch (Exception ex) {
  12.             ex.printStackTrace();
  13.         }
  14.     }
  15. }

運(yùn)行這段代碼:

 
 
 
 
  1. java.lang.NoSuchMethodException: com.tian.my_code.test.designpattern.singleton.EnumSingleton.()
  2.  at java.lang.Class.getConstructor0(Class.java:3082)
  3.  at java.lang.Class.getDeclaredConstructor(Class.java:2178)
  4.  at com.tian.my_code.test.designpattern.singleton.EnumTest.main(EnumTest.java:41)

還真的不能用反射來搞。如果此時(shí)面試官,為什么枚舉不能被反射呢?

為什么枚舉不能被反射呢?

我們?cè)诜瓷涞拇a中

 
 
 
 
  1. Constructor constructor = clazz.getDeclaredConstructor(null);

這行代碼是獲取他的無參構(gòu)造方法。并且,從錯(cuò)誤日志中,我們也可以看到,錯(cuò)誤出現(xiàn)就是在getConstructor0方法中,并且,提示的是沒有找到無參構(gòu)造方法。

很奇怪,枚舉也是類,不是說如果我們不給類顯示定義構(gòu)造方法時(shí)候,會(huì)默認(rèn)給我們創(chuàng)建一個(gè)無參構(gòu)造方法嗎?

于是,我想到了一個(gè)辦法,我們可以使用jad這個(gè)工具去反編譯的我們的枚舉式單例的.class文件。

找到我們的class文件所在目錄,然后我們可以執(zhí)行下面這個(gè)命令:

 
 
 
 
  1. C:\Users\Administrator>jad D:\workspace\my_code\other-local-demo\target\classes
  2. com\tian\my_code\test\designpattern\singleton\EnumSingleton.class
  3. Parsing D:\workspace\my_code\other-local-demo\target\classes\com\tian\my_code\t
  4. st\designpattern\singleton\EnumSingleton.class... Generating EnumSingleton.jad

注意:class文件目錄以及生成的jad文件所在的目錄。

然后打開EnumSingleton.jad 文件:

于是,我就想到了,那我們使用有參構(gòu)造方法來創(chuàng)建:

 
 
 
 
  1. public class EnumTest {
  2.     public static void main(String[] args) {
  3.         try {
  4.             Class clazz = EnumSingleton.class; 
  5.             Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
  6.             //強(qiáng)行訪問
  7.             constructor.setAccessible(true);
  8.             Object object = constructor.newInstance("田維常",996);
  9.             Object object1 = EnumSingleton.getInstance();
  10.             System.out.println(object == object1);
  11.         } catch (Exception ex) {
  12.             ex.printStackTrace();
  13.         }
  14.     }
  15. }

再次運(yùn)行這段代碼,結(jié)果:

 
 
 
 
  1. java.lang.IllegalArgumentException: Cannot reflectively create enum objects
  2.  at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
  3.  at com.tian.my_code.test.designpattern.singleton.EnumTest.main(EnumTest.java:45)

提示很明顯了,就是不讓我們使用反射的方式創(chuàng)建枚舉對(duì)象。

 
 
 
 
  1. public T newInstance(Object ... initargs)
  2.      throws InstantiationException, IllegalAccessException,
  3.             IllegalArgumentException, InvocationTargetException
  4.  {
  5.      if (!override) {
  6.          if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
  7.              Class caller = Reflection.getCallerClass();
  8.              checkAccess(caller, clazz, null, modifiers);
  9.          }
  10.      }
  11.      //Modifier.ENUM就是用來判斷是否為枚舉的
  12.      if ((clazz.getModifiers() & Modifier.ENUM) != 0)
  13.          throw new IllegalArgumentException("Cannot reflectively create enum objects");
  14.      ConstructorAccessor ca = constructorAccessor;   // read volatile
  15.      if (ca == null) {
  16.          ca = acquireConstructorAccessor();
  17.      }
  18.      @SuppressWarnings("unchecked")
  19.      T inst = (T) ca.newInstance(initargs);
  20.      return inst;
  21.  }

所以,到此,我們才算真正的理清楚了,為什么枚舉不讓反射的原因。

序列化破壞

我們以非線程安全的餓漢式來演示一下,看看序列化是如何破壞到了模式的。

 
 
 
 
  1. public class ReflectTest {
  2.     public static void main(String[] args) {
  3.         // 準(zhǔn)備兩個(gè)對(duì)象,singleton1接收從輸入流中反序列化的實(shí)例
  4.         HungrySingleton singleton1 = null;
  5.         HungrySingleton singleton2 = HungrySingleton.getInstance();
  6.         try {
  7.             // 序列化
  8.             FileOutputStream fos = new FileOutputStream("HungrySingleton.txt");
  9.             ObjectOutputStream oos = new ObjectOutputStream(fos);
  10.             oos.writeObject(singleton2);
  11.             oos.flush();
  12.             oos.close();
  13.             // 反序列化
  14.             FileInputStream fis = new FileInputStream("HungrySingleton.txt");
  15.             ObjectInputStream ois = new ObjectInputStream(fis);
  16.             singleton1 = (HungrySingleton) ois.readObject();
  17.             ois.close();
  18.             System.out.println(singleton1);
  19.             System.out.println(singleton2);
  20.             
  21.             System.out.println(singleton1 == singleton2);
  22.         } catch (Exception e) {
  23.             e.printStackTrace();
  24.         }
  25.     }
  26. }

運(yùn)行結(jié)果:

 
 
 
 
  1. com.tian.my_code.test.designpattern.singleton.HungrySingleton@7e6cbb7a
  2. com.tian.my_code.test.designpattern.singleton.HungrySingleton@452b3a41
  3. false

看到了嗎?

使用序列化是可以破壞到了模式的,這種方式,可能很多人不是很清楚。

如何防止呢?

我們對(duì)非線程安全的餓漢式代碼進(jìn)行稍微修改:

 
 
 
 
  1. public class HungrySingleton implements Serializable{
  2.     private static final HungrySingleton INSTANCE;
  3.     static {
  4.         INSTANCE=new HungrySingleton();
  5.     } 
  6.     private HungrySingleton(){
  7.     }
  8.     public static HungrySingleton getInstance(){
  9.         return INSTANCE;
  10.     }
  11.     //添加了readResolve方法,并返回INSTANCE
  12.     private Object readResolve方法,并返回(){
  13.         return INSTANCE;
  14.     }
  15. }

再次運(yùn)行上那段序列化測(cè)試的代碼,其結(jié)果如下:

 
 
 
 
  1. com.tian.my_code.test.designpattern.singleton.HungrySingleton@452b3a41
  2. com.tian.my_code.test.designpattern.singleton.HungrySingleton@452b3a41
  3. true

嘿嘿,這樣我們是不是就避免了只創(chuàng)建了一個(gè)實(shí)例?

答案:否

在類ObjectInputStream的readObject()方法中調(diào)用了另外一個(gè)方法readObject0(false)方法。在readObject0(false)方法中調(diào)用了checkResolve(readOrdinaryObject(unshared))方法。

在readOrdinaryObject方法中有這么一段代碼:

 
 
 
 
  1. Object obj;
  2. try { 
  3.      //是否有構(gòu)造方法,有構(gòu)造放就創(chuàng)建實(shí)例
  4.       obj = desc.isInstantiable() ? desc.newInstance() : null;
  5.  } catch (Exception ex) {
  6.  ... 
  7.  }
  8. //判斷單例類是否有readResolve方法
  9. if (desc.hasReadResolveMethod()) {
  10.     Object rep = desc.invokeReadResolve(obj); 
  11. }
  12. //invokeReadResolve方法中
  13. if (readResolveMethod != null) { 
  14.     //調(diào)用了我們單例類中的readResolve,并返回該方法返回的對(duì)象
  15.     //注意:是無參方法
  16.      return readResolveMethod.invoke(obj, (Object[]) null);
  17. }

繞了半天,原來他是這么玩的,上來就先創(chuàng)建一個(gè)實(shí)例,然后再去檢查我們的單例類是否有readResolve無參方法,我們單例類中的readResolve方法

 
 
 
 
  1. private Object readResolve(){
  2.         return INSTANCE;
  3. }

結(jié)論

我們重寫了readResolve()無參方法,表面上看是只創(chuàng)建了一個(gè)實(shí)例,其實(shí)只創(chuàng)建了兩個(gè)實(shí)例。

緊接著,面試官繼續(xù)問:枚舉式單例能不能被序列化破壞呢?

枚舉式單例能不能被序列化破壞呢?

答案:不能被破壞,請(qǐng)看我慢慢給你道來。

don't talk ,show me the code。

我們先來驗(yàn)證一下是否真的不能被破壞,請(qǐng)看代碼:

 
 
 
 
  1. public class EnumTest {
  2.     public static void main(String[] args) {
  3.         // 準(zhǔn)備兩個(gè)對(duì)象,singleton1接收從輸入流中反序列化的實(shí)例
  4.         EnumSingleton singleton1 = null;
  5.         EnumSingleton singleton2 = EnumSingleton.getInstance();
  6.         try {
  7.             // 序列化
  8.             FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
  9.             ObjectOutputStream oos = new ObjectOutputStream(fos);
  10.             oos.writeObject(singleton2);
  11.             oos.flush();
  12.             oos.close();
  13.             // 反序列化
  14.             FileInputStream fis = new FileInputStream("EnumSingleton.obj");
  15.             ObjectInputStream ois = new ObjectInputStream(fis);
  16.             singleton1 = (EnumSingleton) ois.readObject();
  17.             ois.close();
  18.             System.out.println(singleton1);
  19.             System.out.println(singleton2);
  20.             System.out.println(singleton1 == singleton2);
  21.         } catch (Exception e) {
  22.             e.printStackTrace();
  23.         }
  24.     }
  25. }

運(yùn)行結(jié)果:

 
 
 
 
  1. INSTANCE
  2. INSTANCE
  3. true

確實(shí),枚舉式單例是不會(huì)被序列化所破壞,那為什么呢?總得有個(gè)證件理由吧。

在類ObjectInputStream的readObject()方法中調(diào)用了另外一個(gè)方法readObject0(false)方法。在readObject0(false)方法中調(diào)用了checkResolve(readOrdinaryObject(unshared))方法。

 
 
 
 
  1. case TC_ENUM:
  2.    return checkResolve(readEnum(unshared));

在readEnum方法中

 
 
 
 
  1. private Enum readEnum(boolean unshared) throws IOException {
  2.         if (bin.readByte() != TC_ENUM) {
  3.             throw new InternalError();
  4.         }
  5.         Class cl = desc.forClass();
  6.         if (cl != null) {
  7.             try {
  8.                 @SuppressWarnings("unchecked")
  9.                 //重點(diǎn)
  10.                 Enum en = Enum.valueOf((Class)cl, name);
  11.                 result = en;
  12.                 //...其他代碼省略
  13.             }
  14.         }
  15. }
  16. public static > T valueOf(Class enumType,
  17.                                                 String name) {
  18.        //enumType.enumConstantDirectory()返回的是一個(gè)HashMap
  19.        //通過HashMap的get方法獲取
  20.         T result = enumType.enumConstantDirectory().get(name);
  21.         if (result != null)
  22.             return result;
  23.         if (name == null)
  24.             throw new NullPointerException("Name is null");
  25.         throw new IllegalArgumentException(
  26.             "No enum constant " + enumType.getCanonicalName() + "." + name);
  27. }
  28. //返回一個(gè)HashMap
  29.  Map enumConstantDirectory() {
  30.         if (enumConstantDirectory == null) {
  31.             T[] universe = getEnumConstantsShared();
  32.             if (universe == null)
  33.                 throw new IllegalArgumentException(
  34.                     getName() + " is not an enum type");
  35.             //使用的是HashMap
  36.             Map m = new HashMap<>(2 * universe.length);
  37.             for (T constant : universe)
  38.                 m.put(((Enum)constant).name(), constant);
  39.             enumConstantDirectory = m;
  40.         }
  41.         return enumConstantDirectory;
  42. }

所以,枚舉式單例模式是使用了Map

在Spring中也是有大量使用這種注冊(cè)式單例模式,IOC容器就是典型的代表。

總結(jié)

本文講述了單例模式的定義、單例模式常規(guī)寫法。單例模式線程安全問題的解決,反射破壞、反序列化破壞等。

注意:不要為了套用設(shè)計(jì)模式,而使用設(shè)計(jì)模式。而是要,在業(yè)務(wù)上遇到問題時(shí),很自然地聯(lián)想單設(shè)計(jì)模式作為一種捷徑方法。

單例模式的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

在內(nèi)存中只有一個(gè)實(shí)例,減少內(nèi)存開銷??梢员苊鈱?duì)資源的多重占用。設(shè)置全局訪問點(diǎn),嚴(yán)格控制訪問。

缺點(diǎn)

沒有借口,擴(kuò)展性很差。如果要擴(kuò)展單例對(duì)象,只有修改代碼,沒有其他途徑。

單例模式是 不符合開閉原則的。

知識(shí)點(diǎn)

單例模式的重點(diǎn)知識(shí)總結(jié):

  • 私有化構(gòu)造器
  • 保證線程安全
  • 延遲加載
  • 防止反射攻擊
  • 防止序列化和反序列化的破壞

本文轉(zhuǎn)載自微信公眾號(hào)「Java后端技術(shù)全?!梗梢酝ㄟ^以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java后端技術(shù)全棧公眾號(hào)。


本文標(biāo)題:初級(jí)必備:?jiǎn)卫J降?個(gè)問題
當(dāng)前路徑:http://www.5511xx.com/article/ccioiej.html