类的初始化时机
1.创建类的实例
2.访问类或接口的静态变量(static final常量除外,static final变量可以)
3.调用类的静态方法
4.反射(Class.forName(packageName.className))
5.初始化类的子类(子类初始化问题:满足主动调用,即访问子类中的静态变量、方法,否则仅父类初始化)
6.java虚拟机启动时被标明为启动类的类
注:加载顺序:启动类的static block最先加载(父类静态成员、静态代码块—>子类静态成员、静态代码块—>父类实例成员、代码块——>父类构造函数—>子类实例成员、代码块—>子类构造函数)
Java代码
- package test01;
- class Singleton {
- public static Singleton singleton = new Singleton();
- public static int a;
- public static int b = 0;
- private Singleton() {
- super();
- a++;
- b++;
- }
- public static Singleton GetInstence() {
- return singleton;
- }
- }
- public class MyTest {
- public static void main(String[] args) {
- Singleton mysingleton = Singleton.GetInstence();
- System.out.println(mysingleton.a);
- System.out.println(mysingleton.b);
- }
- }
从入口开始看
Singleton mysingleton = Singleton.GetInstence();
是根据内部类的静态方法要一个Singleton实例。
这个时候就属于主动调用Singleton类了。
之后内存开始加载Singleton类
1):对Singleton的所有的静态变量分配空间,赋默认的值,所以在这个时候,singleton=null、a=0、b=0。注意b的0是默认值,并不是咱们手工为其赋予的的那个0值。
2):之后对静态变量赋值,这个时候的赋值就是我们在程序里手工初始化的那个值了。此时singleton = new Singleton();调用了构造方法。构造方法里面a=1、b=1。之后接着顺序往下执行。
3):
- public static int a;
- public static int b = 0;
a没有赋值,保持原状a=1。b被赋值了,b原先的1值被覆盖了,b=0。所以结果就是这么来的。类中的静态块static块也是顺序地从上到下执行的。
编译时常量、非编译时常量的静态变量
Java代码
- package test01;
- class FinalStatic {
- public static final int A = 4 + 4;
- static {
- System.out.println("如果执行了,证明类初始化了……");
- }
- }
- public class MyTest03 {
- public static void main(String[] args) {
- System.out.println(FinalStatic.A);
- }
- }
结果是只打印出了8,证明类并没有初始化。反编译源码发现class里面的内容是
public static final int A = 8;
也就是说编译器很智能的、在编译的时候自己就能算出4+4是8,是一个固定的数字。没有什么未知的因素在里面。
将代码稍微改一下
public static final int A = 4 + new Random().nextInt(10);
这个时候静态块就执行了,证明类初始化了。在静态final变量在编译时不定的情况下。如果客户程序这个时候访问了该类的静态变量,那就会对类进行初始化,所以尽量使用静态final变量,尽量没什么可变因素在里面,否则性能会有所下降。
1. ClassLoader的剖析
ClassLoader的loadClass方法加载一个类不属于主动调用,不会导致类的初始化。如下代码块
Java代码
- ClassLoader classLoader = ClassLoader.getSystemClassLoader();
- Class clazz = classLoader.loadClass("test01.ClassDemo");
并不会让类加载器初始化test01.ClassDemo,因为这不属于主动调用此类
jvm初始化类时,要求其父类都已经被初始化,但此规则不适用于接口
1.在初始化一个类时,并不会先初始化它所实现的接口
2.在初始化一个接口时,并不会先初始化它的父类接口
因此,一个父接口并不会因为它的子接口或实现类的初始化而初始化。当且仅当程序首次使用接口的静态变量(注意是静态变量,使用静态常量不会初始化)时,才回导致该接口的初始化
public class Test02 {
public static void main(String[] args) {
System.out.println(J.i);
System.out.println(K.j);
}
static int out(String s, int i) {
System.out.println(s + "=" + i);
return i;
}
}
interface I {
int i = 1;
int ii = Test02.out("ii", 2);
}
interface J extends I {
int j = Test02.out("j", 3);
int jj = Test02.out("jj", 4);
}
interface K extends J {
int k = Test02.out("k", 5);
}
调用:System.out.println(J.i);
打印:1
解释:J.i 对于J而言没有主动调用,对I 有调用但是i 为static final常量,因此J、I类都没有初始化
调用:System.out.println(K.j);
打印:j=3
jj=4
3
解释:K.j 对于K而言没有主动调用,对于J 调用了该接口的j为static final 变量,满足主动调用,因此初始该接口
转自:
http://aixiangct.blog.163.com/blog/static/91522461201131872019995?suggestedreading