Java类加载顺序和静态内部类

在学习《effective Java》一书时接触到了构建器,也是第一次实际接触静态内部类这一概念,学习过程中产生了对静态内部类的加载和初始化产生了一些疑问,因此特地查阅了一点资料。此外还复习了一点类的加载顺序。

1.类加载顺序

1. 静态代码块

随着类的加载而运行,只执行一次
格式如下:

public class Order {
    static {
        System.out.println("静态代码块");
    }
}

当出现多个静态代码块时,按顺序执行,如下:

public class Order {
    static int i = 1;
    static {
        i = 2;
    }
    public static void main(String[] args) {
        System.out.println(Order.i);
    }
}
--------------------------------------------
输出为2
public class Order {
    static {
        i = 2;
    }
    static int i = 1;
    public static void main(String[] args) {
        System.out.println(Order.i);
    }
}
--------------------------------------------
输出为1

2. 构造代码块

随着对象的创建而运行,且先于构造函数的执行。有多个构造代码块时,也将按顺序执行。
格式如下:

public class Order {
     {
        System.out.println("构造代码块");
    }
}

3. 构造器

与类名同名的,通过new运算符来新建一个类的实例的函数。可以同时拥有多个不同参数类型、顺序的构造器。当未定义任何构造器时,编译器会默认提供一个无参的构造器。
格式如下:

public class Order {
    public Order(){
        System.out.println("构造器");
    }
}

4. 执行顺序

public class Order {
    static {
        System.out.println("静态代码块");
    }
    {
        System.out.println("构造代码块");
    }
    public Order(){
        System.out.println("构造器");
    }
    public static void main(String[] args) {
        new Order();
        System.out.println("===============");
        new Order();
    }
}
--------------------------------------------
输出结果:
静态代码块
构造代码块
构造器
===============
构造代码块
构造器

可以看出,静态代码块>>构造代码块>>构造器,而且不管new几个对象,静态代码块都只执行一次,构造代码块和构造器每次创建对象时都会执行。

5. 考虑继承的情况

public class Inheritance {
    public static void main(String[] args) {
        new Child();
        System.out.println("===============");
        new Child();
    }
}
class Parent{
    static {
        System.out.println("父类的静态代码块");
    }
    {
        System.out.println("父类的构造代码块");
    }
    Parent(){
        System.out.println("父类的构造器");
    }
}
class Child extends Parent{
    static {
        System.out.println("子类的静态代码块");
    }
    {
        System.out.println("子类的构造代码块");
    }
    Child(){
        System.out.println("子类的构造器");
    }
}
--------------------------------------------
结果如下:
父类的静态代码块
子类的静态代码块
父类的构造代码块
父类的构造器
子类的构造代码块
子类的构造器
===============
父类的构造代码块
父类的构造器
子类的构造代码块
子类的构造器
  • 首先加载父类和子类,与此同时,各自的静态代码块也被执行。
  • 接着开始创建父类对象,因为构造代码块是依托于构造器的,所以构造代码块和构造器是同时执行的。
  • 最后创建子类对象。
  • 因为类已经被加载了,所以之后再创建对象不会再执行静态代码块

注意的点:

  1. 当父类定义了有参构造器而没定义无参构造器,此时子类的构造器会报错,提示父类中没有可用的默认构造函数,这时候,相当于自动调用了super(),而父类中没有对应的构造器而报错,此时在子类构造器内用super语句调用对应参数的构造器,则可以解决报错。
    而如果父类中未定义任何构造器,也不会报错,这是因为未定义任何构造器时,编译器会提供默认的构造器。

6. 静态内部类

public class InnerClassTest {

    public InnerClassTest(){
        System.out.println("构造器");
    }
    static void testMethod(){
        System.out.println("静态方法");
    }
    static {
        System.out.println("静态代码块");
    }
    {
        System.out.println("构造代码块");
    }
    public static class StaticInnerClass{
        StaticInnerClass(){
            System.out.println("静态内部类的构造器");
        }
        static {
            System.out.println("静态内部类的静态代码块");
        }
        {
            System.out.println("静态内部类的构造代码块");
        }
        public static void testMethod(){
            System.out.println("静态内部类的静态方法");
        }
    }
    public  class InnerClass{
        InnerClass(){
            System.out.println("内部类的构造器");
        }
        {
            System.out.println("内部类的构造代码块");
        }
    }
}

测试结果:

    @Test public void test01(){
        new ClassTest();
    }
--------------------------------------------
输出:
静态代码块
构造代码块
构造器
    @Test public void test02(){
        ClassTest.testMethod();
    }
--------------------------------------------
输出:
静态代码块
静态方法
    @Test public void test03(){
        new ClassTest().new InnerClass();
    }
--------------------------------------------
输出:
静态代码块
构造代码块
构造器
内部类的构造代码块
内部类的构造器
    @Test public void test04(){
        new ClassTest.StaticInnerClass();
    }
--------------------------------------------
输出:
静态内部类的静态代码块
静态内部类的构造代码块
静态内部类的构造器
    @Test public void test05(){
        InnerClassTest.StaticInnerClass.testMethod();
    }
--------------------------------------------
输出:
静态内部类的静态代码块
静态内部类的静态方法

结论
当在顶层类中定义内部类时,静态内部类并不会随着顶层类的初始化而被初始化,而是当静态内部类的方法被调用时,静态内部类才被初始化。反之,静态内部类也可以独立于外部类被使用。(个人见解,如有不对欢迎大家指导)

你可能感兴趣的:(Java类加载顺序和静态内部类)