初始化阶段是执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的复制动作和静态语句块(static块)中的语句合并产生的
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化
public class Demo01 {
static{
System.out.println("静态初始化Demo01");
}
public static void main(String[] args) throws Exception {
System.out.println("Demo01的main方法!");
System.out.println(System.getProperty("java.class.path"));
//主动引用
new A();
// System.out.println(A.width);
// Class.forName("com.bjsxt.test.A");
//被动引用
// System.out.println(A.MAX);
// A[] as = new A[10];
// System.out.println(B.width);
}
}
class B extends A {
static {
System.out.println("静态初始化B");
}
}
class A extends A_Father {
public static int width=100; //静态变量,静态域 field
public static final int MAX=100;
static {
System.out.println("静态初始化类A");
width=300;
}
public A(){
System.out.println("创建A类的对象");
}
}
class A_Father extends Object {
static {
System.out.println("静态初始化A_Father");
}
}
- 类的主动引用(一定会发生类的初始化)
– new一个类的对象
– 调用类的静态成员(除了final常量)和静态方法
– 使用java.lang.reflect包的方法对类进行反射调用
– 当虚拟机启动,java Hello,则一定会初始化Hello类。说白了就是先启动main方法所在的类
– 当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类
- 类的被动引用(不会发生类的初始化)
– 当访问一个静态域时,只有真正声明这个域的类才会被初始化,即通过子类引用父类的静态变量,不会导致子类初始化
– 通过数组定义类引用,不会触发此类的初始化
– 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步
单例设计模式之饿汉设计模式:
1.私有构造方法(保证外界不能创建对象)
2.声明本类的引用类型变量,并且创建对象(保证对象自己创建自己)
3.提供一个静态方法,保证外界可以不同过对象来进行调用(必须是公共)
//饿汉设计模式
public class Single {
//私有化构造
private Single(){}
//创建本类对象
//当饿汉式类被主动引用时,初始化并构建对象,即使是多线程环境下<clinit>()方法也能保证只有一个对象被创建
private static Single single = new Single();
//提供一个公共的方法方式
public static Single getInstance() {
return single;
}
}
饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
单例设计模式之懒汉设计模式:
1.私有构造方法(保证外界不能创建对象)
2.声明本类的引用类型变量,但是先不创建对象
3.提供一个静态方法,保证外界可以不同过对象来进行调用,但是必须是调用方法的时候 才去创建对象
//懒汉单例设计模式
class Single02{
//私有化构造
private Single02(){}
//创建本类对象
private static Single02 single;
public static Single02 getInstance() {
if(single == null) {
single = new Single02();
}
return single;
}
}
饿汉式的类在加载的时候就实例化了,就算thread1和thread2同时获取它,取到的是类加载时实例化的那个变量的值,所以说是线程安全的;而懒汉式线程不安全,因为有可能thread1在if(instance==null)判断为真时进入了if体里但又没开始实例化,而这时thread2也进来了,最终就会有2个实例了。