欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java設(shè)計(jì)模式之單例模式示例詳解

 更新時(shí)間:2021年12月27日 10:02:03   作者:LL.LEBRON  
單例模式是最簡(jiǎn)單也是最基礎(chǔ)的設(shè)計(jì)模式之一,單例模式確保某個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。本文將通過(guò)一些示例代碼為大家詳細(xì)介紹一下單例模式,感興趣的可以了解一下

0.概述

為什么要使用單例模式?

在我們的系統(tǒng)中,有一些對(duì)象其實(shí)我們只需要一個(gè),比如說(shuō):線程池、緩存、對(duì)話框、注冊(cè)表、日志對(duì)象、充當(dāng)打印機(jī)、顯卡等設(shè)備驅(qū)動(dòng)程序的對(duì)象。事實(shí)上,這一類對(duì)象只能有一個(gè)實(shí)例,如果制造出多個(gè)實(shí)例就可能會(huì)導(dǎo)致一些問(wèn)題的產(chǎn)生,比如:程序的行為異常、資源使用過(guò)量、或者不一致性的結(jié)果。因此這里需要用到單例模式

使用單例模式的好處?

對(duì)于頻繁使用的對(duì)象,可以省略創(chuàng)建對(duì)象所花費(fèi)的時(shí)間,這對(duì)于那些重量級(jí)對(duì)象而言,是非??捎^的一筆系統(tǒng)開(kāi)銷

由于new 操作的次數(shù)減少,因而對(duì)系統(tǒng)內(nèi)存的使用頻率也會(huì)降低,這將減輕 GC 壓力,縮短 GC 停頓時(shí)間

1.餓漢式

1.1 餓漢式單例實(shí)現(xiàn)

實(shí)例會(huì)提前創(chuàng)建:

/**
* 餓漢式
*
* @author xppll
* @date 2021/12/24 21:21
*/
public class Singleton1 implements Serializable {
    //構(gòu)造私有
    private Singleton1() {
        System.out.println("private Singleton1()");
    }

    //唯一實(shí)例
    private static final Singleton1 INSTANCE = new Singleton1();

    //獲得實(shí)例方法
    public static Singleton1 getINSTANCE() {
        return INSTANCE;
    }

    //其他方法
    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

測(cè)試:

/**
 * @author xppll
 * @date 2021/12/24 21:28
 */
public class TestSingleton {
    public static void main(String[] args) {
        //觸發(fā)Singleton1類的初始化,會(huì)為類的靜態(tài)變量賦予正確的初始值,單例對(duì)象就會(huì)被創(chuàng)建!
        Singleton1.otherMethod();
        System.out.println("-----------------------------------");
        System.out.println(Singleton1.getINSTANCE());
        System.out.println(Singleton1.getINSTANCE());
    }
}
//輸出:
private Singleton1()
otherMethod()
-----------------------------------
singleton.Singleton1@10bedb4
singleton.Singleton1@10bedb4

1.2 破壞單例的幾種情況

1.反射破壞單例

2.反序列化破壞單例

3.Unsafe破壞單例

演示:

/**
 * @author xppll
 * @date 2021/12/24 21:28
 */
public class TestSingleton {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //觸發(fā)Singleton1類的初始化,會(huì)為類的靜態(tài)變量賦予正確的初始值,單例對(duì)象就會(huì)被創(chuàng)建!
        Singleton1.otherMethod();
        System.out.println("-----------------------------------");
        System.out.println(Singleton1.getINSTANCE());
        System.out.println(Singleton1.getINSTANCE());

        //反射破壞單例
        reflection(Singleton1.class);

        //反序列化破壞單例
        serializable(Singleton1.getINSTANCE());

        //Unsafe破壞單例
        unsafe(Singleton1.class);

    }
	//反射破壞單例
    private static void reflection(Class<?> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //得到無(wú)參
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        //將此對(duì)象的 accessible 標(biāo)志設(shè)置為指示的布爾值,即設(shè)置其可訪問(wèn)性
        constructor.setAccessible(true);
        //創(chuàng)建實(shí)例
        System.out.println("反射創(chuàng)建實(shí)例:" + constructor.newInstance());
    }
	//反序列化破壞單例
    private static void serializable(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        //序列化
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        //反序列化
        System.out.println("反序列化創(chuàng)建示例:" + ois.readObject());
    }
	//Unsafe破壞單例
    private static void unsafe(Class<?> clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println("Unsafe 創(chuàng)建實(shí)例:" + o);
    }

}

結(jié)果:

可以看出三種方式都會(huì)破壞單例!

1.3 預(yù)防單例的破壞

預(yù)防反射破壞單例

在構(gòu)造方法中加個(gè)判斷即可:

//構(gòu)造私有
private Singleton1() {
    //防止反射破壞單例
    if(INSTANCE!=null){
        throw new RuntimeException("單例對(duì)象不能重復(fù)創(chuàng)建");
    }
    System.out.println("private Singleton1()");
}

預(yù)防反序列化破壞單例

Singleton1()中重寫readResolve方法:

//重寫這個(gè)方法,如果序列化了,就會(huì)返回這個(gè),不會(huì)返回反序列化的對(duì)象
public Object readResolve(){
    return  INSTANCE;
}

Unsafe破壞單例無(wú)法預(yù)防

2.枚舉餓漢式

2.1 枚舉單例實(shí)現(xiàn)

枚舉實(shí)現(xiàn)單例:

/**
 * 枚舉實(shí)現(xiàn)單例
 *
 * @author xppll
 * @date 2021/12/24 22:23
 */
public enum Singleton2 {
    INSTANCE;

    //枚舉的構(gòu)造方法默認(rèn)是private的,可以不寫
    Singleton2() {
        System.out.println("private Singleton2()");
    }

    //重寫toString方法
    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    //獲得實(shí)例方法(這個(gè)可以不要,枚舉變量都是public的)
    public static Singleton2 getInstance() {
        return INSTANCE;
    }

    //其他方法
    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

測(cè)試:

/**
 * @author xppll
 * @date 2021/12/24 21:28
 */
public class TestSingleton {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //觸發(fā)Singleton2類的初始化,會(huì)為類的靜態(tài)變量賦予正確的初始值,單例對(duì)象就會(huì)被創(chuàng)建!
        Singleton2.otherMethod();
        System.out.println("-----------------------------------");
        System.out.println(Singleton2.getInstance());
        System.out.println(Singleton2.getInstance());
    }
}
//輸出:
private Singleton2()
otherMethod()
-----------------------------------
singleton.Singleton2@2de80c
singleton.Singleton2@2de80c

可以看出當(dāng)調(diào)用otherMethod()時(shí),就會(huì)觸發(fā)類的加載,枚舉對(duì)象就會(huì)創(chuàng)建,所以枚舉實(shí)現(xiàn)單例是餓漢式的

2.2 破壞單例

枚舉類實(shí)現(xiàn)單例的好處:

1.反序列化無(wú)法破壞枚舉單例

2.反射無(wú)法破壞枚舉單例

栗子:

需要先修改反射破壞代碼,枚舉需要有參構(gòu)造

public class TestSingleton {
    public static void main(String[] args) throws Exception {
        Singleton5.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(Singleton5.getInstance());
        System.out.println(Singleton5.getInstance());

        //反序列化破壞單例
        serializable(Singleton2.getInstance());

        //Unsafe破壞單例
        unsafe(Singleton2.class);

        //反射破壞單例
        reflection(Singleton2.class);
    }

    private static void unsafe(Class<?> clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println("Unsafe 創(chuàng)建實(shí)例:" + o);
    }

    private static void serializable(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        System.out.println("反序列化創(chuàng)建實(shí)例:" + ois.readObject());
    }

    private static void reflection(Class<?> clazz) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        System.out.println("反射創(chuàng)建實(shí)例:" + constructor.newInstance());
    }
}

結(jié)果:

可以看出

1.反射是無(wú)法創(chuàng)建枚舉對(duì)象!無(wú)法破壞枚舉單例

2.反序列化也不會(huì)破壞枚舉單例!

3.Unsafe依然會(huì)破壞!

3.懶漢式

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

/**
 * 懶漢式
 *
 * @author xppll
 * @date 2021/12/25 08:34
 */
public class Singleton3 implements Serializable {
    //構(gòu)造私有
    private Singleton3() {
        System.out.println("private Singleton3()");
    }

    //唯一實(shí)例
    private static Singleton3 INSTANCE = null;

    public static Singleton3 getInstance() {
        //第一次調(diào)用的時(shí)候才創(chuàng)建
        if (INSTANCE == null) {
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }

    //其他方法
    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

測(cè)試:

/**
 * @author xppll
 * @date 2021/12/24 21:28
 */
public class TestSingleton {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        Singleton3.otherMethod();
        System.out.println("-----------------------------------");
        System.out.println(Singleton3.getInstance());
        System.out.println(Singleton3.getInstance());
    }
}

結(jié)果:

可以看出只有在第一次調(diào)用getInstance()時(shí)才會(huì)創(chuàng)建唯一的單例對(duì)象,因此是懶漢式的。

但是這種方式在多線程環(huán)境下是會(huì)有問(wèn)題的,可能多個(gè)線程會(huì)同時(shí)執(zhí)行INSTANCE = new Singleton3();。因此這里需要在getInstance()方法上加上synchronized關(guān)鍵字保證多線程下的正確性:

public static synchronized Singleton3 getInstance() {
    //第一次調(diào)用的時(shí)候才創(chuàng)建
    if (INSTANCE == null) {
        INSTANCE = new Singleton3();
    }
    return INSTANCE;
}

但是這種方法是有問(wèn)題的,第一次創(chuàng)建完對(duì)象后,以后的操作是不需要在加鎖的,所以這種方式會(huì)影響性能!

我們的目標(biāo)應(yīng)該是第一次創(chuàng)建單例的時(shí)候給予保護(hù),后續(xù)操作則不需要加鎖保護(hù)!

4.雙檢鎖懶漢式

針對(duì)上面的問(wèn)題,這里給出第四種方法雙檢鎖懶漢式進(jìn)行優(yōu)化:

/**
 * 雙檢鎖懶漢式
 *
 * @author xppll
 * @date 2021/12/25 08:53
 */
public class Singleton4 {
    //構(gòu)造私有
    private Singleton4() {
        System.out.println("private Singleton4()");
    }

    //唯一實(shí)例
    //這里volatile的作用是保證共享變量有序性!
    private static volatile Singleton4 INSTANCE = null;

    //雙檢鎖優(yōu)化
    public static synchronized Singleton4 getInstance() {
        //實(shí)例沒(méi)創(chuàng)建,才會(huì)進(jìn)入內(nèi)部的 synchronized 代碼塊,提高性能,防止每次都加鎖
        if (INSTANCE == null) {
            //可能第一個(gè)線程在synchronized 代碼塊還沒(méi)創(chuàng)建完對(duì)象時(shí),第二個(gè)線程已經(jīng)到了這一步,所以里面還需要加上判斷
            synchronized (Singleton4.class) {
                //也許有其他線程已經(jīng)創(chuàng)建實(shí)例,所以再判斷一次
                if (INSTANCE == null) {
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }

    //其他方法
    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

5.內(nèi)部類懶漢式

內(nèi)部類懶漢式單例實(shí)現(xiàn):

/**
 * 內(nèi)部類懶漢式
 *
 * @author xppll
 * @date 2021/12/25 09:24
 */
public class Singleton5 {

    //構(gòu)造私有
    private Singleton5() {
        System.out.println("private Singleton5()");
    }

    //靜態(tài)內(nèi)部類實(shí)現(xiàn)懶漢式單例,靜態(tài)變量的創(chuàng)建會(huì)放在靜態(tài)代碼塊里執(zhí)行,jvm會(huì)保證其線程安全
    //只有第一次用到內(nèi)部類時(shí),才會(huì)初始化創(chuàng)建單例
    private static class Holder {
        static Singleton5 INSTANCE = new Singleton5();
    }

    //獲得實(shí)例方法
    public static Singleton5 getInstance() {
        return Holder.INSTANCE;
    }

    //其他方法
    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

測(cè)試:

/**
 * @author xppll
 * @date 2021/12/24 21:28
 */
public class TestSingleton {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        Singleton5.otherMethod();
        System.out.println("-----------------------------------");
        System.out.println(Singleton5.getInstance());
        System.out.println(Singleton5.getInstance());
    }
}

結(jié)果:

可以看出內(nèi)部類實(shí)現(xiàn)單例也是懶漢式的!

6.JDK中單例的體現(xiàn)

Runtime 體現(xiàn)了餓漢式單例

System類下的Console 體現(xiàn)了雙檢鎖懶漢式單例

Collections 中的 EmptyNavigableSet內(nèi)部類懶漢式單例

Collections 中的ReverseComparator.REVERSE_ORDER 內(nèi)部類懶漢式單例

Comparators.NaturalOrderComparator.INSTANCE 枚舉餓漢式單例

到此這篇關(guān)于Java設(shè)計(jì)模式之單例模式示例詳解的文章就介紹到這了,更多相關(guān)Java單例模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論