一 实战——实例化
1 代码
package chapter03.java1;
import org.junit.Test;
import java.io.*;
/**
* 测试类的主动使用:意味着会调用类的 <clinit>(),即执行了类的初始化阶段
*
* 当创建一个类的实例时,比如使用 new 关键字,或者通过反射、克隆、反序列化。
*/
public class ActiveUse1 {
public static void main(String[] args) {
// 使用 new 关键字
Order order = new Order();
}
// 序列化的过程:
@Test
public void test1() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("order.dat"));
oos.writeObject(new Order());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null)
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 反序列化的过程:(验证)
@Test
public void test2() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("order.dat"));
Order order = (Order) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Order implements Serializable {
static {
System.out.println("Order类的初始化过程");
}
}
2 测试
执行 main 方法时
Order类的初始化过程
执行 test1 时
Order类的初始化过程
执行 test2 时
Order类的初始化过程
二 实战——静态方法
1 代码
package chapter03.java1;
import org.junit.Test;
import java.io.*;
/**
* 测试类的主动使用:意味着会调用类的 <clinit>(),即执行了类的初始化阶段
*
* 当调用类的静态方法时,即当使用了字节码 invokestatic 指令。
*/
public class ActiveUse1 {
@Test
public void test3() {
Order.method();
}
}
class Order implements Serializable {
static {
System.out.println("Order类的初始化过程");
}
public static void method() {
System.out.println("Order method()....");
}
}
2 测试
Order类的初始化过程
Order method()....
三 实战——静态字段
1 代码
package chapter03.java1;
import org.junit.Test;
import java.util.Random;
/**
*
* 当使用类、接口的静态字段时(final修饰特殊考虑),比如,使用 getstatic 或者 putstatic 指令。(对应访问变量、赋值变量操作)
*
*/
public class ActiveUse2 {
@Test
public void test1(){
//System.out.println(User.num); // test1_1:类初始化了,因为 num 是变量
//System.out.println(User.num1); // test1_2:类没有初始化,因为 num1 是静态常量
System.out.println(User.num2); // test1_3:类初始化了,因为 num2 不是字面量方式声明的,需要方法调用,编译期间无法确定
}
@Test
public void test2(){
//System.out.println(CompareA.NUM1); // test2_1:接口不需要初始化,因为 NUM1 是静态常量
//System.out.println(CompareA.NUM2); // test1_2:接口需要初始化。因为 因为 NUM2 不是字面量方式声明的,而是方法调用,编译期间无法确定
}
}
class User{
static{
System.out.println("User类的初始化过程");
}
public static int num = 1;
public static final int num1 = 1;
public static final int num2 = new Random().nextInt(10);
}
interface CompareA{
public static final Thread t = new Thread(){
{
System.out.println("CompareA的初始化");
}
};
public static final int NUM1 = 1;
public static final int NUM2 = new Random().nextInt(10);
}
2 测试
当只运行test1_1
User类的初始化过程
1
当只运行test1_2
1
当只运行test1_3
User类的初始化过程
8
当只运行test2_1
1
当只运行test2_2
CompareA的初始化
2
四 实战——类的主动使用其它情况
1 代码
package chapter03.java1;
import org.junit.Test;
import java.util.Random;
/**
* 类的主动使用其他情况
* 8. 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。
* (涉及解析REF_getStatic、REF_putStatic、REF_invokeStatic方法句柄对应的类)
*/
public class ActiveUse3 {
static {
System.out.println("ActiveUse3的初始化过程");
}
// 4 当使用java.lang.reflect包中的方法反射类的方法时。比如:Class.forName("com.xxxx.xxx.Test")
@Test
public void test1() {
try {
Class clazz = Class.forName("chapter03.java1.Order");
// 输出语句
// ActiveUse3的初始化过程
// Order类的初始化过程
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 5 当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
// 注意:
// 当 Java 虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。
// >在初始化一个类时,并不会先初始化它所实现的接口
// >在初始化一个接口时,并不会先初始化它的父接口
// 因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态字段时,
// 才会导致该接口的初始化。
@Test
public void test2() {
System.out.println(Son.num);// 接口 CompareD 不会初始化
// 依次输出
// ActiveUse3的初始化过程
// Father类的初始化过程
// Son类的初始化过程
// 1
}
@Test
public void test3() {
System.out.println(CompareC.NUM1); // CompareC 父接口 CompareB 并不会初始化
// 依次输出
// ActiveUse3的初始化过程
// CompareC的初始化 --> 子接口初始化了,但是父接口并没有初始化
// 2094196067
}
// 6 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,该接口要在其之前被初始化。
// 如果 Son2 还有子类,根据5,那么其子类的父类,父类的父类也都要被初始化
@Test
public void test4() {
System.out.println(Son2.num);
// 依次输出
// ActiveUse3的初始化过程
// Father类的初始化过程
// CompareB的初始化 -> CompareB接口定义了default方法,所以接口要初始化,注意区分与CompareD的区别
// Son2类的初始化过程
// 1
}
// 7 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
public static void main(String[] args) {
System.out.println("hello");
// 依次输出
// ActiveUse3的初始化过程
// hello
}
}
class Father {
static {
System.out.println("Father类的初始化过程");
}
}
class Son extends Father implements CompareD {
static {
System.out.println("Son类的初始化过程");
}
public static int num = 1;
}
class Son2 extends Father implements CompareB {
static {
System.out.println("Son2类的初始化过程");
}
public static int num = 1;
}
interface CompareB {
public static final Thread t = new Thread() {
{
System.out.println("CompareB的初始化");
}
};
public default void method1() {
System.out.println("你好!");
}
}
interface CompareC extends CompareB {
public static final Thread t = new Thread() {
{
System.out.println("CompareC的初始化");
}
};
public static final int NUM1 = new Random().nextInt();
}
interface CompareD {
public static final Thread t = new Thread() {
{
System.out.println("CompareD的初始化");
}
};
}
2 测试
当执行 test1 时
ActiveUse3的初始化过程
Order类的初始化过程
当执行 test2 时
ActiveUse3的初始化过程
Father类的初始化过程
Son类的初始化过程
1
当执行 test3 时
ActiveUse3的初始化过程
CompareC的初始化
96077485
当执行 test4 时
ActiveUse3的初始化过程
Father类的初始化过程
CompareB的初始化
Son2类的初始化过程
1
当执行 main 时
ActiveUse3的初始化过程
hello