楼子湾

导航

统计
 

一.定义:singleton

一个类只有一个实例,而且这个实例自己创建。

二.单例模式的五种写法

核心代码,构造方法私有化,private,注意对外访问的是静态的,不然你们怎么访问的到

1.懒汉式:

用到的时候才创建,使用的时候检查有没有实例,如果没有就创建,有就发挥。有线程安全和不安全两种写法,区别在于synchronized

 1 public class Lazybones {
 2     private static Lazybones lazybones;
 3 
 4     private Lazybones(){}
 5 
 6     public static Lazybones getInstance(){
 7         if (null == lazybones){
 8             lazybones = new Lazybones();
 9         }
10         return lazybones;
11     }
12 }

 

2.饿汉式:

从名字上讲就是比较勤快的,实例在初始化的时候已经建好,不管你有没有使用到。好处是没有现成安全,坏处是浪费内存空间

1 public class HungryMan {
2     private static HungryMan hungryMan = new HungryMan();
3     private HungryMan(){}
4     public static HungryMan getInstance(){
5         return hungryMan;
6     }
7 }

 

3.双检锁:

又叫双重检验锁,综合懒汉模式和饿汉模式的优缺点整合而成。看下面代码,特点是增加了sychronized的关键字,而且sychronized(方法执行完会自动释放)关键字内外层都加了一层 if 条件判断,这样既保证了安全,又比直接上锁提高了执行效率,还节省了内存空间

synchronized本身monitorEnter,monitorExit已经具备volatile读写的内存语义,所以不用加volatile修饰

 1 public class DoubleCheck {
 2     private static DoubleCheck doubleCheck;
 3     private DoubleCheck(){}
 4     public static DoubleCheck getInstance(){
 5         if (null == doubleCheck){
 6             synchronized (DoubleCheck.class){
 7                 if (null == doubleCheck){
 8                     doubleCheck = new DoubleCheck();
 9                 }
10             }
11         }
12         return doubleCheck;
13     }
14 }

4.静态内部类:

内部类的加载时机如下,大家可以自己编写测试类,简单的打印就可以发现的,看下外部类和类不类加载是顺序

4.1外部类初次加载,会初始化静态变量、静态代码块、静态方法,但不会加载内部类和静态内部类。

4.2实例化外部类,调用外部类的静态方法、静态变量,则外部类必须先进行加载,但只加载一次。

4.2直接调用静态内部类时,外部类不会加载。

类加载时机:JAVA虚拟机在有且仅有的5种场景下会对类进行初始化。
1.遇到new、getstatic、setstatic或者invokestatic这4个字节码指令时,对应的java代码场景为:new一个关键字或者一个实例化对象时、读取或设置一个静态字段时(final修饰、已在编译期把结果放入常量池的除外)、调用一个类的静态方法时。
2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没进行初始化,需要先调用其初始化方法进行初始化。
3.当初始化一个类时,如果其父类还未进行初始化,会先触发其父类的初始化。
4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。
5.当使用JDK 1.7等动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
这5种情况被称为是类的主动引用,注意,这里《虚拟机规范》中使用的限定词是"有且仅有",那么,除此之外的所有引用类都不会对类进行初始化,称为被动引用。静态内部类就属于被动引用的行列。

静态内部类

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

测试静态内部类的加载时机:

 1 public class StaticTest {
 2     static {
 3         System.out.println("StaticTest 静态代码块加载-----1");
 4     }
 5 
 6     public StaticTest(){
 7         System.out.println("StaticTest 构造初始化----2");
 8     }
 9 
10     public static class InnerClass{
11         static {
12             System.out.println("InnerClass 静态代码块加载----3");
13         }
14         public InnerClass(){
15             System.out.println("InnerClass 构造初始化----4");
16         }
17     }
18 }
19 
20 public static void main(String[] args) {
21         StaticTest staticTest = new StaticTest();
22         System.out.println("===========");
23     }

结果:不调用内部类时不加载

 

第二次:调用内部类

StaticTest staticTest = new StaticTest();
System.out.println("====外部类初始化后睡眠2秒======");
Thread.sleep(2000);
StaticTest.InnerClass innerClass = new StaticTest.InnerClass();

结果:

 5.枚举:自动支持序列化机制,绝对防止多次实例化直接通过:EnumSingleton.ENUM_SINGLETON.anyMthod();调用,方便简洁

 1 public enum EnumSingleton {
 2   // 就是类的实例
 3     ENUM_SINGLETON;
 4 
 5     public void anyMthod() {
 6         System.out.println("单例模式的枚举写法");
 7     }
 8 }
 9 
10 public static void main(String[] args) throws InterruptedException {
11         EnumSingleton.ENUM_SINGLETON.anyMthod();
12     }

结果:

 

posted on 2020-03-11 16:00  楼子湾  阅读(...)  评论(...编辑  收藏