Java初始化

类初始化

涉及子类初始化时的执行顺序

顺序以下java

①父类静态变量和静态代码块(按照声明顺序);shell

②子类静态变量和静态代码块(按照声明顺序);数组

③父类成员变量和代码块(按照声明顺序);code

④父类构造器;对象

⑤子类成员变量和代码块(按照声明顺序);源码

⑥子类构造器。虚拟机

如下源码来自牛客中的一道题it

class A {
    public A() {
        System.out.println("class A");
    }

    {
        System.out.println("I'm A class"); 
    }

    static {
        System.out.println("class A static"); 
    }
}

public class B extends A {
    public B() {
        System.out.println("class B");
    }

    {
        System.out.println("I'm B class"); 
    }

    static {
        System.out.println("class B static"); 
    }

    public static void main(String[] args) {
        new B();
    }
}

运行结果io

class A static
class B static
I'm A class
class A
I'm B class
class B

在子类初始化时子类涉及重写父类

在分析上,若是父类即将调用的方法被子类重写了,那么父类就会调用子类重写后的方法,而不会调用父类的方法。编译

所以牢记,子类重写父类方法后,调用时会调用子类重写后的方法

如下源码来自牛客中的一道题

public class Base 
{
    private String baseName = "base";

    public Base()
    {
        callName();
    }

    public void callName()
    {
        System.out.println(baseName);
    }
    
    static class Sub extends Base 
    {
        private String baseName = "sub";
        public void callName()
        {
            System.out.println(baseName);
        }
    }  
    
    public static void main(String[] args) 
    {  
        Base b = new Sub();
    }
}

运行结果

null

子类实现的方法中调用的baseName为子类中的私有属性。

如下源码来自牛客中的一道题

public class Demo {
    

    class Super 
    {  
        int flag = 1;

        Super() {
            test();
        }  
       
        void test() {
            System.out.println("Super.test() flag=" + flag);
        }
    } 
    

    class Sub extends Super 
    {
        Sub(int i) {  
            flag = i;
            System.out.println("Sub.Sub()flag=" + flag);
        }  
        
        void test() {
            System.out.println("Sub.test()flag=" + flag);
        }
    }  
    

    public static void main(String[] args) 
    {  
        new Demo().new Sub(5);
    }
}

运行结果

Sub.test()flag=1
Sub.Sub()flag=5

主动引用和被动引用

主动引用

JVM规范严格规定了有且只有5种状况必须对类进行“初始化”

  1. 当读或写入一个类的静态变量的时候;当用new实例化对象的时候;当调用一个类的静态方法的时候
  2. 当使用java.lang.reflect对类进行反射调用的时候,若类未初始化,则须要先触发其初始化
  3. 初始化一个类的时候,若发现其直接父类没有被初始化,则去初始化其直接父类。若直接父类向上还有父类未初始化,则继续向上初始化...
  4. 当JVM启动时,用户须要指定一个要执行的主类(就是包含main()方法的那个类),虚拟机会先初始化这个类
  5. 使用JDK1.7动态语言支持的时候的一些状况。

被动引用

被动引用:除了以上五种以外,其余的全部引用类的方式都不会触发初始化。

如下为被动引用的状况

经过子类引用父类的的静态变量,不会致使子类初始化

/**
 * @author mmengyiyu
 */
public class NoInitialization
{
    public static void main(String[] args)
    {
        System.out.println(SubClass.baseStaticIntVar);
    }
}

class Base
{
    static final int baseStaticIntVar = 1;
    static
    {
        System.out.println("Base类的静态初始化块...");
    }
}

class SubClass extends Base
{
    static
    {
        System.out.println("SubClass类的静态初始化块...");
    }
}

运行结果

1

经过数组定义来引用类,不会触发此类的初始化

/**
 * @author mmengyiyu
 */
public class NoInitialization
{
    public static void main(String[] args)
    {
        Base[] bases = new Base[5];
        SubClass[] subClasses = new SubClass[5];
    }
}

class Base
{
    static
    {
        System.out.println("Base类的静态初始化块...");
    }
}

class SubClass extends Base
{
    static
    {
        System.out.println("SubClass类的静态初始化块...");
    }
}

无运行结果

不会触发类的初始化,天然不可能调用static代码块。

常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,所以不会触发定义常量的类的初始化

/**
 * @author mmengyiyu
 */
public class NoInitialization
{
    public static void main(String[] args)
    {
        System.out.println(A.constantIntVar);
    }
}

class A
{
    static
    {
        System.out.println("静态初始化块...");
    }
    static final int constantIntVar = 100;
}

运行结果

100
相关文章
相关标签/搜索