5.1、接口初始化规则
在了解接口的初始化规则前,先搞清类的初始化规则。
当java虚拟机初始化一个类时,会先初始化它的所有父类。但是这条规则并不适用于接口。
在初始化一个类时,并不会先初始化它所实现的接口;
在初始化一个接口时,并不会先初始化它的父接口;
使用一句话总结:实现类或者子接口的初始化并不会导致父接口的初始化。
只有当程序首次主动使用特定接口的静态变量(运行期常量)时,才会导致接口的初始化。这里需要注意的是接口中定义的变量都是常量,而常量又分为编译期常量和运行期常量,编译期常量的值在编译期间就可以确定,直接存储在了调用类的常量池中,所以访问接口中的编译期常量并不会导致接口的初始化,只有访问接口中的运行期常量才会引起接口的初始化。
下面通过代码验证上述结论:
package com.shtec.init;
import java.util.UUID;
/**
* 在初始化一个类时,并不会先初始化它所实现的接口;
* 在初始化一个接口时,并不会先初始化它的父接口;
* @author sunhao
*
*/
public class Demo3 {
public static void main(String[] args) {
//在初始化一个类时,并不会先初始化它所实现的接口;
System.out.println(child3.a);
//输出: 1
//没有输出“Parent3 invoked”,说明父接口Parent3并没有被初始化。
//在初始化一个接口时,并不会先初始化它的父接口;
System.out.println(Parent3.str);
//输出:
//Parent3 invoked
//a58ee35a-6590-416d-bf6b-ba6976f0d268
}
}
interface GrandPa{
//下面这段代码相当于普通类中的“静态代码块”,打印出来就相当于该接口被初始化了;
public static Thread t = new Thread(){
{
System.out.println("GrandPa invoked");
}
};
}
interface Parent3 extends GrandPa{
//运行期常量
String str = UUID.randomUUID().toString();
//下面这段代码相当于普通类中的“静态代码块”,打印出来就相当于该接口被初始化了;
public static Thread t = new Thread(){
{
System.out.println("Parent3 invoked");
}
};
}
class child3 implements Parent3{
public static int a = 1;
}
5.2、类加载、连接、初始化案例剖析
下面使用两个案例复习一下类型的加载、连接和初始化过程;
package com.shtec.init;
public class Demo4 {
public static void main(String[] args) {
System.out.println("a=" + Singleton.a);
System.out.println("b=" + Singleton.b);
//输出:
//a=1
//b=1
}
}
//定义一个单例类
class Singleton{
public static int a;
public static int b = 0;
private static Singleton singleton = new Singleton();
private Singleton(){
a++;
b++;
}
public static Singleton getInstance(){
return singleton;
}
}
如果对上面Demo4的代码输出没有疑问,请继续下面的代码示例:
package com.shtec.init;
/**
* 分析:
* 将代码【public static int b = 0;】放在构造方法下面,输出结果为什么变了?
* 请注意使用“类的加载、连接(验证、准备和解析)和初始化过程分析该案例”;
* Singleton在初始化之前,会先经历连接阶段中的准备阶段,准备阶段会为类的【静态变量】分配内存,并将其初始化为【默认值】,
* 此时,a=0;singleton=null,b=0;
* 然后Singleton经历初始化阶段,代码自上而下执行,为类的静态变量赋予正确的初始值
* 此时,a=0;
* singleton会创建一个对象,调用Singleton类的构造方法,此时a=1;b=1;
* 接着初始化静态变量b,b的值又被更改为0;
* 所以最终输出:
* //a=1
* //b=0
* @author sunhao
*
*/
public class Demo4 {
public static void main(String[] args) {
System.out.println("a=" + Singleton.a);
System.out.println("b=" + Singleton.b);
//输出:
//a=1
//b=0
}
}
//定义一个单例类
class Singleton{
public static int a;
private static Singleton singleton = new Singleton();
private Singleton(){
a++;
b++;
}
public static int b = 0;//注意,本段代码调动了位置
public static Singleton getInstance(){
return singleton;
}
}