单个类加载顺序
InitOrderClass:
public class InitOrderClass {
static {
System.out.println("初始化静态代码块");
}
static int staticNum = initStaticNum();
{
System.out.println("初始化非静态代码块");
}
public int num = initNum();
public static int initStaticNum(){
System.out.println("初始化静态字段");
return 1;
}
public int initNum(){
System.out.println("初始化非静态变量");
return 2;
}
public InitOrderClass(){
System.out.println("初始化无参构造函数");
}
}
InitOrderClassEx:
public class InitOrderClassEx {
static int staticNum = initStaticNum();
static {
System.out.println("初始化静态代码块");
}
public int num = initNum();
{
System.out.println("初始化非静态代码块");
}
public static int initStaticNum(){
System.out.println("初始化静态字段");
return 1;
}
public int initNum(){
System.out.println("初始化非静态变量");
return 2;
}
public InitOrderClassEx(){
System.out.println("初始化无参构造函数");
}
}
InitOrderClass与InitOrderClassEx的区别是:
- InitOrderClass中静态代码块在静态字段前,非静态变量在非静态代码块前
- InitOrderClassEx中静态代码块在静态字段后,非静态变量在非静态代码块后
客户端代码:
public class ClientClass {
public static void main(String[] args) {
InitOrderClass initOrderClass = new InitOrderClass();
System.out.println("---------------");
InitOrderClassEx initOrderClassEx = new InitOrderClassEx();
System.out.println("---------------");
initOrderClass = new InitOrderClass();
}
}
运行结果:
初始化静态代码块
初始化静态字段
初始化非静态代码块
初始化非静态变量
初始化无参构造函数
---------------
初始化静态字段
初始化静态代码块
初始化非静态变量
初始化非静态代码块
初始化无参构造函数
---------------
初始化非静态代码块
初始化非静态变量
初始化无参构造函数
从运行结果可以看到:
- 类初始化顺序为:静态代码块/静态字段–>非静态代码块/非静态变量–>构造函数;
- 静态代码块与静态字段的初始化顺序由声明顺序决定,非静态代码块与非静态变量的初始化顺序也是由声明顺序决定;
- 静态代码块与静态字段的初始化,只在Class对象首次加载的时候进行一次
父子类加载顺序
类加载顺序,主要考察静态与非静态代码,父类与子类的加载顺序。
父类代码:
public class SuperClass {
static int staticNum = initSuperStaticNum();
static {
System.out.println("父类静态代码块");
}
int num = initSuperNum();
{
System.out.println("父类非静态代码块");
}
public static int initSuperStaticNum(){
System.out.println("父类静态字段");
return 1;
}
public int initSuperNum(){
System.out.println("父类非静态变量");
return 2;
}
public SuperClass(){
System.out.println("父类无参构造函数");
}
}
子类代码:
public class ExtendClass extends SuperClass{
static int staticNum = initExtendStaticNum();
static {
System.out.println("子类静态代码块");
}
int num = initExtendNum();
{
System.out.println("子类非静态代码块");
}
public static int initExtendStaticNum(){
System.out.println("子类静态字段");
return 3;
}
public int initExtendNum(){
System.out.println("子类非静态字段");
return 4;
}
public ExtendClass(){
System.out.println("子类无参构造函数");
}
}
客户端代码:
public class ClientClass {
public static void main(String[] args) {
new ExtendClass();
}
}
运行结果:
父类静态字段
父类静态代码块
子类静态字段
子类静态代码块
父类非静态变量
父类非静态代码块
父类无参构造函数
子类非静态字段
子类非静态代码块
子类无参构造函数
整体的加载顺序为:先父类后子类,先静态后非静态。
具体加载顺序如下:
- 父类静态字段/静态代码块–>子类静态字段/静态代码块–>父类非静态变量/非静态代码块–>父类构造函数–>子类非静态变量/非静态代码块–>子类构造函数
- 静态字段与静态代码块、非静态变量与非静态代码块之间的顺序同样由声明顺序决定
几种不会触发初始化实例的情况
情况一
通过子类引用父类的静态字段,不会导致子类初始化。
class SuperClass01 {
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass01 {
static {
System.out.println("SubClass init!");
}
}
public class TestClass {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
结果如下:
SuperClass init!
123
可以看到,父类被初始化,并取到相应的值,但是子类并没有被初始化。
情况二
通过数组定义来引用类,不会触发此类的初始化
class SuperClass01 {
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
public class TestClass {
public static void main(String[] args) {
SuperClass01[] subClasses = new SuperClass01[10];
}
}
运行后,控制台没有输出,SuperClass01并没有被初始化。
情况三
常量**(static final修饰)**在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
class ConstClass {
static {
System.out.println("ConstClass init");
}
static final String HELLOWORLD = "hello world";
}
public class TestClass {
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORLD);
}
}
结果如下:
hello world
通过输出结果可以看到,ConstClass类并没有被初始化。因为在编译的时候,常量**(static final修饰)**会存入调用类的常量池中(此处是指main函数所在的类的常量池),在调用时本质上并没有引用到定义此常量的类,而是直接访问了自己的常量池。
static注意事项
-
静态变量在静态代码块之前声明,静态代码块中对此静态变量既可以赋值,也可以访问;
-
静态变量在静态代码块之后声明,静态代码块中对此静态变量只能赋值,不可以访问;
public class StaticTestClass {
static int value01 = 1;
static {
value01 = 2;
value02 = 3;
System.out.println(value01);
System.out.println(value02); // 报错:Illegal forward reference,非法前向引用
}
static int value02 = 4;
}
静态变量与静态方法调用
当调用目标类的静态变量或静态方法时,不会触发该类的代码块或构造方法的执行。
Handler类:
class Handler {
static int value = 1;
static {
System.err.println("静态代码块");
}
static void print(){
System.err.println("静态方法");
}
{
System.err.println("非静态代码块");
}
public Handler(){
System.err.println("构造函数");
}
}
测试类1:
public class TestClass {
public static void main(String[] args) {
System.out.println(Handler.value);
}
}
测试类1结果如下:
静态代码块
1
测试类2:
public class TestClass {
public static void main(String[] args) {
Handler.print();
}
}
测试类2结果如下:
静态代码块
静态方法