Java设计模式-单例模式(全例记录)

1. 基础介绍

  1. 核心作用:保证一个类只有一个实例,并提供一个访问该实例的全局访问点;
  2. 优点:由于单例模式只生成一个实例,所以减少了系统的开销,当一个系统启动需要较多的资源时,可以直接在系统启动时产生一个单例对象,然后使其永久驻留内存;单例模式可以在系统设置全局访问点,优化共享资源的访问;
  3. 两种主要的单例模式实现方式:饿汉式,懒汉式;

2. 饿汉式介绍

  1. 线程安全,无法延时加载,也就是只要系统一运行,这个单例模式的类对象就会被加载到内存;
  2. 简单步骤:
    首先,将构造器私有化,这样就无法在类的外部直接创建对象;
    然后再类中创建一个本类对象(使用private static修饰,做到全局使用);
    再定义一个getInstance()方法,用来在其它的类中创建单例对象;
    public class Singleton {
        //通过这两个修饰的对象,是线程安全的;
        private static Singleton singleton = new Singleton();
    
        //创建一个私有的构造方法;
        private Singleton() {
        }
    
        //定义一个static方法用来在其它类中获得这个类的实例;
        public static Singleton getInstance() {
            return singleton;
        }
    }
    

3. 懒汉式

  1. 线程安全,延时加载,真正使用时创建;调用的效率比较低(因为每一次调用,都会重新创建一个对象);
  2. 简单步骤:
    首先先创建一个空的类实例,使用private static修饰;
    然后创建一个私有的构造方法;
    最后定义一个加锁的getInstance()方法,用于返回该类的对象;
    public class SingletonDemo {
    //创建一个该类的空对像;
    private static SingletonDemo sing;
    //创建该类的私有构造方法;
    private SingletonDemo(){}
    //创建一个getInstance()方法,用于创建该类的对象;这里用个锁,就可以防止创建多个对象同时被创建;当然,锁也可以放到里面去,放外面可能效率更加低;
    public synchronized static SingletonDemo getInstance(){
        //判断sing对象是否为空,为空则创建;反之,直接输出;
        if(sing==null){
                sing = new SingletonDemo();
            }
            return sing;
        }
    }
    

4. 双重检测锁实现

  1. 上面讲到,可以将synchronized放入到方法里面去,这里就是这样做的,这样可以提高它锁的效率;但是涉及很到jvm底层,目前还没学习,暂做了解;
    大致的步骤与上面一致,我就在懒汉式中直接修改了;
    public class SingletonDemo {
    	//创建一个该类的空对像;
    private static SingletonDemo sing=null;
     //创建该类的私有构造方法;
    private SingletonDemo(){}
     //创建一个getInstance()方法,用于创建该类的对象;
    public static SingletonDemo getInstance(){
       	 //判断sing对象是否为空,为空则创建;反之,直接输出;
       	 if(sing==null){
       	 //在里面进行加锁
            	synchronized (SingletonDemo.class) {
            	 if(sing==null)
            	    sing = new SingletonDemo();
           		 }
        	}
        	return sing;
    	}
    }
    

5. 登记式/静态内部类实现方式

  1. 外部没有static属性,不会像饿汉式一样直接加载对象;只有调用getInstance()方法时加载对象,加载类时,线程安全,因为创建的外部类的对象是static final修饰,导致只有一个实例,只能赋值一次;
  2. 有着饿汉式的高效调用,懒汉式的延时加载;
    基本结构介绍:
    首先先定义一个static的静态内部类,在该类中创建一个外部类的对象,并用private static final修饰该对象;
    创建一个私有化的外部类构造方法;
    最后在外部类中创建一个getInstance方法,用于返回该外部类的对象;
    public class SingleStatic {
    	//创建一个私有的属性;
        private String name = "神";
        //先创建一个该类的私有构造方法;
        private SingleStatic() {
        }
    
    //创建一个静态的内部类;
        private static class SingleInside {
            //定义一个外部类的实例;在内部类中是可以调用外部类的私有构造器的;
            private static final SingleStatic sig = new SingleStatic();
            //我想看看能不能调用外部类的私有属性,可以,很好,
            public static String string(){
                String name=sig.name;
                return name;
            }
        }
    
        //创建一个方法返回该类的对象;
        public static SingleStatic getInstance() {
            return SingleInside.sig;
        }
    
        public static String getname(){
            return SingleInside.string();
        }
    }
    

测试类

class test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SingleStatic aStatic = SingleStatic.getInstance();
                System.out.println(aStatic);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                SingleStatic instance = SingleStatic.getInstance();
                System.out.println(instance);
            }
        }).start();

        System.out.println(SingleStatic.getname());

    }
}

结果输出(符合单例模式):

Java设计模式-单例模式(全例记录)_第1张图片

6. 枚举

  1. 通过枚举实现单例模式,枚举本身就是一种单例模式,无延时加载,线程安全,调用效率高;
    public enum SingleEnum {
        //这就相当与创建好了;
        SINGLE_ENUM;
    }
    

7.总结

很明显,在单例模式中除了枚举的方式之外,其它的方式都可以通过反射的方式,强行获取对象的构造方法;当然也是可以避免的;只需要在创建前再次判断抛出一个异常结束就可以了;还有一些比如反序列化也可以进行破解,这里就不一一记录,感觉这种东西,只要了解,知道怎么规避就可以了,防止反序列化破解的话可以直接使用一个方法 readResolve() ,这个方法原理不是很懂,看别人的博客说是在使用对象流的时候会进行检查,如果有这个方法就会直接返回已有的对象,这个方法的出处没有,仿佛就是自己定义的一样;

Java杂记

a) 内部类可以直接调用外部类包括private的成员变量,构造方法;

b) 而外部类调用内部类需要建立内部类对象,当然,内部类也可以将方法设置为静态的;

你可能感兴趣的:(Java的设计模式)