第158天学习打卡(单例模式)
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点:
-
可以提高程序员的思维能力、编程能力和设计能力
-
使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
-
使设计的代码可重用性高,可读性强,可靠性高,灵活性好,可维护性强
设计模式的基本要素
-
模式名称
-
问题
-
解决方案
-
效果
GoF 23
-
一种思维,一种态度,一种进步
创建型模式:
-
单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式
结构型模式:
-
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型模式:
-
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。
OOP(面向对象)七大原则
-
开闭原则:对扩展开放,对修改关闭
-
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
-
依赖倒置原则:要面向接口编程,不要面向实现编程
-
单一职责原则:控制类的粒度大小,将对象解耦,提高其内聚性
-
接口隔离原则: 要为各个类建立它们需要的专用接口
-
迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
-
合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
单例模式(构造器一定要私有)
饿汉式
package com.doudou.note.single;
//饿汉式单例
public class Hungry {
//可能会浪费空间
private byte[] date1 = new byte[1024*1024];
private byte[] date2 = new byte[1024*1024];
private byte[] date3 = new byte[1024*1024];
private byte[] date4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
//单例模式下是ok的
//多线程并发 会出现问题
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
输出结果:
Thread-0ok
Thread-3ok
Thread-1ok
Thread-2ok
Process finished with exit code 0
DCL懒汉式
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//单例模式下是ok的
//多线程并发 会出现问题
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
输出结果:
Thread-0ok
Process finished with exit code 0
加入volatile实现双重指令重排和原子性操作
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
}
静态内部类
package com.doudou.note.single;
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,存在反射
反射可以破坏单例
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果:
mainok
mainok
com.doudou.note.single.LazyMan
破坏反射
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
Exception in thread "main" 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.doudou.note.single.LazyMan.main(LazyMan.java:46)
Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常
at com.doudou.note.single.LazyMan.<init>(LazyMan.java:11)
... 5 more
Process finished with exit code 1
破坏单例模式
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
com.doudou.note.single.LazyMan
破坏反射2
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private static boolean qinjiang = false;
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (qinjiang == false){
qinjiang = true;
}else{
throw new RuntimeException("不要试图使用反射破话异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
Exception in thread "main" 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.doudou.note.single.LazyMan.main(LazyMan.java:51)
Caused by: java.lang.RuntimeException: 不要试图使用反射破话异常
at com.doudou.note.single.LazyMan.<init>(LazyMan.java:15)
... 5 more
Process finished with exit code 1
破坏单例
package com.doudou.note.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
//懒汉式单例
public class LazyMan {
private static boolean qinjiang = false;
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (qinjiang == false){
qinjiang = true;
}else{
throw new RuntimeException("不要试图使用反射破话异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
qinjiang.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
qinjiang.set(instance, false);//第一个对象的值再改为false
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
结果
com.doudou.note.single.LazyMan
枚举(反射不能破坏枚举)
package com.doudou.note.single;
//enum 是一个什么? 枚举本身也是一个Class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
输出结果:
INSTANCE
INSTANCE
Process finished with exit code 0
反射不能破坏枚举证明
package com.doudou.note.single;
//enum 是一个什么? 枚举本身也是一个Class类
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//私有权限进行破除
EnumSingle instance2 = declaredConstructor.newInstance();
//NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
System.out.println(instance1);
System.out.println(instance2);
}
}
输出结果(发现证明失败):
Exception in thread "main" java.lang.NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at com.doudou.note.single.Test.main(EnumSingle.java:20)
对枚举的class文件进行编译

把class文件反编译为java文件,首先把jad.exe复制到这个目录下

查看反编译的java文件可以发现枚举确实不能破坏单例
package com.doudou.note.single;
//enum 是一个什么? 枚举本身也是一个Class类
import java.lang.reflect.Constructor;
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);//私有权限进行破除
EnumSingle instance2 = declaredConstructor.newInstance();
//NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
System.out.println(instance1);
System.out.println(instance2);
}
}
结果(这个说明反射不能破坏枚举):
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.doudou.note.single.Test.main(EnumSingle.java:20)
Process finished with exit code 1
枚举的最终反编译源码(发现枚举没有无参构造 只有有参构造 而且有两个参数)
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.doudou.note.single;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/doudou/note/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
B站学习网址:

浙公网安备 33010602011771号