类加载,类初始化及对象实例化

类的加载分为三个阶段,加载--->链接--->初始化

类加载的过程

将class表示的二进制文件加载到内存,放在方法区中,并在堆中创建一个java.lang.Class对象(封装的是class的数据结构)

类的主动使用,会加载类

 1 new Test()

 2 对类中的静态变量进行读写,对接口中的静态变量进行读取

 3 反射某个类 , Class.forName()

 4 调用静态方法

 5 初始化子类时,父类将被初始化

 6 启动类  ,采用java命令执行某个类

 类的被动使用,不会加载类

1 子类引用父类的静态变量 ,父类会被初始化,子类不会被初始化

public class Parent {
    public static int a = 123;

    static {
        System.out.println("Parent init");
    }
}

public class Child extends Parent {

    static {
        System.out.println("Child init");
    }
}

public class Test {
    public static void main(String[] args) {
        // 这里是访问的父类的静态变量
        System.out.println(Child.a);
    }
}

只初始化了父类

2 通过引用类型定义数组,不会触发此类的初始化

3  final修饰的常量,不会触发此类的初始化,因为在编译期间就放入了常量池

4  final修饰的复杂类型,会触发此类的初始化,因为在编译期间不能计算得出其值

4 JVM最先初始化的总是java.lang.Object类

类初始化要注意的地方

/**
 * 
    *   在static块里,不能对在它后面的静态变量进行读取的操作,但可以写入
     *   在对它前面定义的静态变量,可以读取和写入  
 */
public class MyObject {

    private static  String  x = "abc"; 
    
    static {
        
        x= "edf";
        b =12;
        //这里会提示错误,can not reference a field before define it 
        //System.out.println(b);
    }
    
    private static int b = 10;
}
/**
 * 
    *  在初始化类Foo时候,会执行类的static块。这时候开启两个线程进行new对象,JVM保证了只能有一个线程执行类的初始化
 *
 */
public class ClinitTest {

    
    public static void main(String[] args) {
        new Thread(()-> {new Foo();}).start();
        new Thread(()-> {new Foo();}).start();
    }
    
}

class Foo{
    
    private static AtomicBoolean init = new AtomicBoolean(true);
    static {
        System.out.println(Thread.currentThread().getName() + " will be inint");
        while (init.get()) {

        }
        System.out.println(Thread.currentThread().getName() + "  have done inint");
    }
}

对象实例化

类的初始化和对象实例化有时是同时进行的

class Price {

    static Price P = new Price(2.7);
    static double apple = 20;
    double price;

    public Price(double orange) {
        price = apple - orange;
    }
}

public class PriceTest {
    public static void main(String[] args) {
        //Price.P访问了类的静态变量,会触发类的初始化,即(加载,连接,初始化),当执行构造函数时
        //apple还没有初始化完成,处于连接阶段的准备阶段,其值为默认值0,这时构造函数计算的price为-2.7
        System.out.println(Price.P.price);// 结果为-2.7
    }
}
class Price {
    
    
    static Price P = new Price(2.7);
    final static double apple = 20;
    double price;
    
    public Price(double orange) {
        price = apple - orange;
    }
}

public class PriceTest {
    public static void main(String[] args) {
        //apple在编译阶段就完成赋值了,其值为20,这时构造函数计算的price为17.3
        System.out.println(Price.P.price);// 结果为17.3
    }
}
class Price {
    
    static double apple = 20;
    static Price P = new Price(2.7);
    double price;
    
    public Price(double orange) {
        price = apple - orange;
    }
}

public class PriceTest {
    public static void main(String[] args) {
        //Price.P访问了类的静态变量,会触发类的初始化,即(加载,连接,初始化),当执行构造函数时
        //apple已经完成了初始化,其值为20了,这时构造函数计算的price为17.3
        System.out.println(Price.P.price);// 结果为17.3
    }
}
原文地址:https://www.cnblogs.com/moris5013/p/10557842.html