Artikel ini membawakan anda pengetahuan yang berkaitan tentang java Ia terutamanya memperkenalkan kandungan yang berkaitan tentang corak tunggal dalam corak reka bentuk adalah kelas objek tunggal dimulakan sekali. Saya harap ia akan membantu semua orang.
Pembelajaran yang disyorkan: "tutorial video java" Kaedah terbaik.
. Di Jawa, kita boleh mengatakan bahawa hanya satu contoh objek kelas wujud dalam JVM. Dalam Android, kita boleh mengatakan bahawa terdapat hanya satu contoh objek kelas ini semasa menjalankan program.Apa itu singleton? Sebagai prinsip asas,
kelas objek tunggal hanya akan dimulakan sekali
Langkah pelaksanaan mudah mod tunggal:
Ikuti langkah di atas untuk menulis corak tunggal yang anda rasa lebih ketat, dan kemudian lihat jika singleton yang anda tulis boleh memenuhi syarat berikut:
Adakah singleton anda dimuatkan atas permintaan?
涉及到并发三要素:原子性、可见性、有序性
//JAVA实现public class SingleTon { //第三步创建唯一实例 private static SingleTon instance = new SingleTon(); //第一步构造方法私有 private SingleTon() { } //第二步暴露静态方法返回唯一实例 public static SingleTon getInstance() { return instance; } }//Kotlin实现object SingleTonReka bentuk yang ringkas, menyelesaikan masalah instantiasi berbilang benang.
Kelemahan:
Apabila mesin maya memuatkan kelas SingleTon, pembolehubah statik kelas akan diberikan nilai semasa fasa permulaan, iaitu apabila mesin maya memuatkan kelas (Kaedah getInstance mungkin tidak dipanggil pada masa ini) telah dipanggil untuk mencipta contoh objek Selepas itu, sama ada objek contoh digunakan atau tidak, ia akan menduduki ruang memori. 2. Lazy Man Style new SingleTon();
//JAVA实现public class SingleTon { //创建唯一实例 private static SingleTon instance = null; private SingleTon() { } public static SingleTon getInstance() { //延迟初始化 在第一次调用 getInstance 的时候创建对象 if (instance == null) { instance = new SingleTon(); } return instance; } }//Kotlin实现class SingleTon private constructor() { companion object { private var instance: SingleTon? = null get() { if (field == null) { field = SingleTon() } return field } fun get(): SingleTon{ return instance!! } } }Reka bentuknya juga agak mudah, tidak seperti Hungry Man Style Apabila Singleton ini dimuatkan, ia diubah suai oleh statik Pembolehubah statik akan dimulakan kepada null Ia tidak akan menduduki memori pada masa ini Sebaliknya, objek contoh akan dimulakan dan dibuat atas permintaan apabila kaedah getInstance dipanggil buat kali pertama.
Kelemahan:
Tiada masalah dalam persekitaran satu benang Dalam persekitaran berbilang benang, isu keselamatan benang akan berlaku. Apabila dua utas menjalankan pernyataaninstane == null pada masa yang sama dan kedua-duanya lulus, mereka masing-masing akan membuat instantiat objek, jadi ia bukan lagi satu.
如何解决懒汉式在多线程环境下的多实例问题?
//JAVA实现public class SingleTon { private static class InnerSingleton{ private static SingleTon singleTon = new SingleTon(); } public SingleTon getInstance(){ return InnerSingleton.singleTon; } private SingleTon() { } }//kotlin实现class SingleTon private constructor() { companion object { val instance = InnerSingleton.instance } private object InnerSingleton { val instance = SingleTon() } }Kaedah penyegerakan langsung
//JAVA实现public class SingleTon { //创建唯一实例 private static SingleTon instance = null; private SingleTon() { } public static synchronized SingleTon getInstance() { if (instance == null) { instance = new SingleTon(); } return instance; } }//Kotlin实现class SingleTon private constructor() { companion object { private var instance: SingleTon? = null get() { if (field == null) { field = SingleTon() } return field } @Synchronized fun get(): SingleTon{ return instance!! } } }Hanya satu benang boleh membuat instantiate objek melalui penguncian, yang menyelesaikan masalah keselamatan benang.
Keburukan:
Untuk kaedah statik, kata kunci yang disegerakkan akan mengunci keseluruhan Kelas Setiap kali kaedah getInstance dipanggil, urutan akan disegerakkan, yang sangat tidak cekap objek instance dicipta, Tidak perlu terus menyegerak.Catatan:
此处的synchronized保证了操作的原子性和内存可见性。
//JAVA实现 public class SingleTon { //创建唯一实例 private static volatile SingleTon instance = null; private SingleTon() { } public static SingleTon getInstance() { if (instance == null) { synchronized (SingleTon.class) { if (instance == null) { instance = new SingleTon(); } } } return instance; } }//kotlin实现class SingleTon private constructor() { companion object { val instance: SingleTon by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SingleTon() } } } 或者class SingleTon private constructor() { companion object { @Volatile private var instance: SingleTon? = null fun getInstance() = instance ?: synchronized(this) { instance ?: SingleTon().also { instance = it } } } }Menambah blok kod penyegerakan untuk menentukan sama ada objek contoh wujud Jika ia tidak wujud, buatnya Pada masa ini, masalah itu boleh diselesaikan sepenuhnya, kerana walaupun berbilang rangkaian diperlukan untuk mendapatkannya, Instance objek, tetapi hanya satu utas akan memasuki blok kod penyegerakan pada masa yang sama Selepas objek dibuat pada masa ini, walaupun utas lain memasuki blok kod penyegerakan sekali lagi, kerana objek contoh telah dibuat, mereka akan kembali secara langsung. boleh. Tetapi mengapa kita perlu menilai contoh itu kosong lagi dalam langkah sebelumnya blok kod penyegerakan? Ini kerana selepas kami mencipta objek instance, kami secara langsung menentukan sama ada objek instance itu kosong Jika ia tidak kosong, kembalikan terus, yang mengelakkan daripada memasuki blok kod penyegerakan semula dan meningkatkan prestasi.
Kelemahan:
Tidak dapat mengelakkan pantulan ganas untuk mencipta objek.Catatan:
此处的volatile发挥了内存可见性及防止指令重排序作用。
public enum SingletonEnum { INSTANCE; public static void main(String[] args) { System.out.println(SingletonEnum.INSTANCE == SingletonEnum.INSTANCE); } }
以最初的DCL为测试案例,看看如何进行反射攻击及又如何在一定程度上避免反射攻击。
反射攻击代码如下:
public static void main(String[] args) { SingleTon singleton1 = SingleTon.getInstance(); SingleTon singleton2 = null; try { Class<SingleTon> clazz = SingleTon.class; Constructor<SingleTon> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); singleton2 = constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("singleton1.hashCode():" + singleton1.hashCode()); System.out.println("singleton2.hashCode():" + singleton2.hashCode()); }
执行结果:
singleton1.hashCode():1296064247 singleton2.hashCode():1637070917
通过执行结果发现通过反射破坏了单例。 如何保证反射安全呢?只能以暴制暴,当已经存在实例的时候再去调用构造函数直接抛出异常,对构造函数做如下修改:
public class SingleTon { //创建唯一实例 private static volatile SingleTon instance = null; private SingleTon() { if (instance != null) { throw new RuntimeException("单例构造器禁止反射调用"); } } public static SingleTon getInstance() { if (instance == null) { synchronized (SingleTon.class) { if (instance == null) { instance = new SingleTon(); } } } return instance; } }
此时可防御反射攻击,抛出异常如下:
java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.imock.demo.TestUtil.testSingleInstance(TestUtil.java:45) at com.imock.demo.TestUtil.main(TestUtil.java:33) Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用 at com.imock.demo.SingleTon.<init>(SingleTon.java:16) ... 6 more Exception in thread "main" java.lang.NullPointerException at com.imock.demo.TestUtil.testSingleInstance(TestUtil.java:49) at com.imock.demo.TestUtil.main(TestUtil.java:33) Process finished with exit code 1
然后我们把上述测试代码修改如下(调换了singleton1的初始化顺序)
:
public static void main(String[] args) { SingleTon singleton2 = null; try { Class<SingleTon> clazz = SingleTon.class; Constructor<SingleTon> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); singleton2 = constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("singleton2.hashCode():" + singleton2.hashCode()); SingleTon singleton1 = SingleTon.getInstance(); //调换了位置,在反射之后执行 System.out.println("singleton1.hashCode():" + singleton1.hashCode()); }
执行结果:
singleton2.hashCode():1296064247 singleton1.hashCode():1637070917
发现此防御未起到作用。
缺点:
如何避免序列化攻击?只需要修改反序列化的逻辑就可以了,即重写 readResolve()
方法,使其返回统一实例。
protected Object readResolve() { return getInstance(); }
脆弱不堪的单例模式经过重重考验,进化成了完全体,延迟加载,线程安全,反射及序列化安全。简易代码如下:
饿汉模式
public class SingleTon { private static SingleTon instance = new SingleTon(); private SingleTon() { if (instance != null) { throw new RuntimeException("单例构造器禁止反射调用"); } } public static SingleTon getInstance() { return instance; } }
静态内部类
public class SingleTon { private static class InnerStaticClass{ private static SingleTon singleTon = new SingleTon(); } public SingleTon getInstance(){ return InnerStaticClass.singleTon; } private SingleTon() { if (InnerStaticClass.singleTon != null) { throw new RuntimeException("单例构造器禁止反射调用"); } } }
懒汉模式
public class SingleTon { //创建唯一实例 private static SingleTon instance = null; private SingleTon() { if (instance != null) { throw new RuntimeException("单例构造器禁止反射调用"); } } public static SingleTon getInstance() { //延迟初始化 在第一次调用 getInstance 的时候创建对象 if (instance == null) { instance = new SingleTon(); } return instance; } }
缺点:
(枚举实现单例是最为推荐的一种方法,因为就算通过序列化,反射等也没办法破坏单例性,底层实现比如newInstance方法内部判断枚举抛异常)
推荐学习:《java视频教程》
Atas ialah kandungan terperinci Mari analisa singleton corak reka bentuk java bersama-sama. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!