【JAVA笔记——道】Class初始化理解

静态域、非静态域、Final、父类以及抽象类正常实例化

设置Initalized用于初始化时对于结果进行输出

//被实例化类,设置初始方法用于显示初始化标记
public class Initalized {
    public Initalized(String s) {
        System.out.println(s);
    }
}

设置接口类用于测试类继承,设置内容如下

interface InitalizeaedInterface {
    //接口中变量默认都是static final
    Initalized s1 = new Initalized("Initalizeaed Interface");

    //usual method
    public void test1();

    //static method
    //public void test2();

    //final method
    public void test3();

    //static final method
    //public void test4();
}

设置抽象类作为实现类父类

package com.cunchen.practice.init;

//设置被实例化类的父类
public abstract class InitalizedAbstract {
    //父类中域中对于被实例化类进行实习化
    Initalized a = new Initalized("Initalizeaed Abstract");
    Initalized b;
    static Initalized c; 

    //被实例化类的父类的构造方法
    public InitalizedAbstract() {
        b = new Initalized("Abstract Constractor");
    }

    //父类的非静态方法
    public void test5() {
        b = new Initalized("Abstract method");
    }

    //父类的非静态方法,此方法在子类中被覆盖
    public void test6() {
        b = new Initalized("Abstract covered method");
    }

    //父类静态方法
    public static void test7() {
        c = new Initalized("Abstract static method");
    }
}

最终实现类如下

//实例化类
public class InitalizationTest extends InitalizedAbstract implements InitalizeaedInterface{
    //变量域中实例化
    Initalized s1 = new Initalized("Usual declared");
    //变量域中静态变量实例化
    static Initalized s2 = new Initalized("Static declared");
    //变量域中常量实例化
    final Initalized s5 = new Initalized("Final declared");
    //变量域中静态常量实例化
    static final Initalized s6 = new Initalized("Static final declared");

    Initalized s3;
    static Initalized s4;   

    //静态方法域中执行静态方法
    static {
        getReply();
    }

    //构造方法
    public InitalizationTest() {
        super();
        s3 = new Initalized("Constructor");
    }

    //普通方法
    public void test1() {
        s3 = new Initalized("Method");
    }

    //静态方法
    public static void test2() {
        s4 = new Initalized("Static Method");
    }

    //Final方法
    public final void test3() {
        s4 = new Initalized("Final Method");
    }

    //静态Final方法
    public static final void test4() {
        s4 = new Initalized("Static final Method");
    }

    //重写父类方法
    public void test6() {
        s3 = new Initalized("Test covered method");
    }

    //静态域执行方法
    public static void getReply() {
        s4 = new Initalized("Static block");
    }

    public static void main(String[] args) {
        //初始化过程
        InitalizationTest test = new InitalizationTest();
        System.out.println("-------------------------------");

        //方法执行过程
        test.test1();            //普通方法
        test.test2();            //静态方法
        test.test3();            //Final方法
        test.test4();            //Static Final 方法
        test.test5();            //父类方法
        test.test6();            //重写方法
        System.out.println("-------------------------------");

        //空对象访问
        test = null;
        test.test2();            //普通方法
        test.test1();            //静态方法
    }
}

实际运行结果如下

Static declared
Static final declared
Static block
Initalizeaed Abstract
Abstract Constractor
Usual declared
Final declared
Constructor
-------------------------------
Method
Static Method
Final Method
Static final Method
Abstract method
Test covered method
-------------------------------
Static Method
Exception in thread "main" java.lang.NullPointerException
    at com.cunchen.practice.init.InitalizationTest.main(InitalizationTest.java:74)

得出结论如下:
1.类内部初始化顺序为 静态域->非静态域->构造方法
2.类实例化过程中不会对于实现的接口的域进行初始化
3.类实例化过程中会在其构造方法之前父类初始化
4.无论是静态方法还是非静态方法,在实例化过程中不直接执行
5.子类重写父类方法之后调用重写方法将不会对父类方法调用
6.静态域静态方法会在类加载时就直接进行初始化
7.空对象可以访问静态化区域,但不可以访问非静态化区域,,这个问题还引出对象在JVM存储结构的问题,不同对象在虚拟机中不同的存储策略决定了其不同的实现方式

反射方式

新建RunTest类

    public class RunTest {

    public static void main(String[] args) throws ClassNotFoundException {          
        //反射看class初始化情况
        Class.forName("com.cunchen.practice.init.InitalizationTest");

        System.out.println("-------------------------------");
        InitalizationTest test = new InitalizationTest();
        System.out.println("-------------------------------");
        test.test1();
        test.test6();
        System.out.println("-------------------------------");


    }

}

执行之后得到结果如下

Static declared
Static final declared
Static block
-------------------------------
Initalizeaed Abstract
Abstract Constractor
Usual declared
Final declared
Constructor
-------------------------------
Method
Test covered method
-------------------------------

得出结论:

  1. 反射仅会目标类静态域进行初始化,而对非静态域不初始化
  2. 静态域只会在类初次加载初始化,再次加载不进行初始化
  3. 反射之后,类并未真正实例化,并未调用其构造方法

ClassLoader加载类

修改main方法如下:

public static void main(String[] args) throws ClassNotFoundException {  
        ClassLoader.getSystemClassLoader().loadClass("com.cunchen.practice.init.InitalizationTest");
        System.out.println("-------------------------------");

        //反射看class初始化情况
        Class.forName("com.cunchen.practice.init.InitalizationTest");

        System.out.println("-------------------------------");
        InitalizationTest test = new InitalizationTest();
        System.out.println("-------------------------------");
        test.test1();
        test.test6();
        System.out.println("-------------------------------");
    }

结果如下

-------------------------------
Static declared
Static final declared
Static block
-------------------------------
Initalizeaed Abstract
Abstract Constractor
Usual declared
Final declared
Constructor
-------------------------------
Method
Test covered method
-------------------------------

得到结论:ClassLoader在进行类加载之后并不对类进行任何初始化操作

深入理解

Class的装载包括3个步骤:加载(loading),连接(link),初始化(initialize)
Class.forName重载了两种实现方法,

     public static Class<?> forName(String className)
                throws ClassNotFoundException

    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
                                   throws ClassNotFoundException

第二个方法中的initialize方法其实就是指定Class被loading后是不是必须被初始化
实际上第一个方法执行结果等同于Class.forName(className, true, currentLoader)

ClassLoader.loadClass(String name)源码如下

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

实际执行情况为loadClass(name, false),装载的class将不会被link。

这两个方法都用于装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。相信到此大家也能明白在JDBC中经常用到的加载JDBC驱动程序是什么原理了把。

深入了解推荐
对象生命周期详解
Java ClassLoader详解

原文地址:https://www.cnblogs.com/cunchen/p/9464179.html