静态内部类实现的单例模式是线程安全的

一、静态内部类(静态嵌套类)vs非静态内部类(内部类)

静态内部类

*静态内部类只能访问外部类的静态方法和静态属性,如果是private也能访问,其他则不能访问,创建对象不依赖外部类
*静态内部类可以定义静态的属性和方法

非静态内部类

  • 内部类可以访问其所在类的属性(包括所在类的私有属性),内部类创建自身对象需要先创建其所在类的对象
  • 可以定义内部接口,且可以定义另外一个内部类实现这个内部接口
  • 内部类不能定义static元素
  • 内部类可以多嵌套

总结

静态内部类和外部类是相互独立的,创建实例或者创建什么类型的对象都不受限制
非静态内部类是外部类的一部分,创建实例必须先创建外部类的实例,可以访问外部类的所有属性

二、类的初始化及对象创建

类的加载、链接、初始化

1.class文件(二进制)通过类的加载器(引导类加载器、自定义加载器)加载(双亲委派机制)到内存中,并创建java.lang.Class对象(类是一类事物的抽象,Class是类的抽象,抽象的抽象)
2.链接:
①验证:验证字节码文件格式等是否正确
②准备:类的静态变量分配内存,并设置默认值
③解析:符号引用转换为直接引用
3.初始化
初始化阶段是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块static{}中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问

类实例化的时机

1.使用new关键字创建对象
2.使用Class类的newInstance方法(反射机制)
3.使用Constructor类的newInstance方法(反射机制)
4.使用Clone方法创建对象
5.使用(反)序列化机制创建对象

类实例化的过程

static final 变量编译阶段分配内存空间并赋值→父类的类构造器() -> 子类的类构造器() -> 父类的成员变量和实例代码块 -> 父类的构造函数 -> 子类的成员变量和实例代码块 -> 子类的构造函数。

三、静态内部类创建单例对象怎么保证线程安全

package com.bo.singleton;
/*静态内部类创建单例对象*/
public class StaticInnerTest {
    public StaticInnerTest() {
        System.out.println("静态内部类无参构造函数");
    }

    public static StaticInnerTest getInstance() {
        return Inner.sit;
    }
    //方法不需要设置同步
    public static class Inner{
        private static final StaticInnerTest sit = new StaticInnerTest();
    }

    public static void main(String[] args) {
        StaticInnerTest s1 = StaticInnerTest.getInstance();
        StaticInnerTest s2 = StaticInnerTest.getInstance();
        System.out.println(s1==s2);
    }
}

静态内部类的特点:外部类加载时不需要加载静态内部类,不被加载则不占用内存,(延迟加载)当外部类调用getInstance方法时,才加载静态内部类,静态属性保证了全局唯一,静态变量初始化保证了线程安全,所以这里的方法没有加synchronized关键字(JVM保证了一个类的 初始化在多线程下被同步加锁)