类的主动使用:
Java程序对类的主动使用,意味着会调用类的<clinit>(),即执行类的初始化阶段。情况如下:
1. 创建类的实例时,比如使用new关键字,或通过反射、克隆、反序列化;
2. 调用某个类的静态方法时,即使用了字节码invokestatic指令;
3. 调用类或接口的静态字段时(final修饰符特殊考虑);
4. 使用java.lang.reflec包中的方法反射类的方法:比如:Class.forName("com.hahaha.Test")
5. 初始化一个类的子类;
6.如果一个接口定义了default方法,直接或间接实现该接口的类进行初始化前,先对该接口进行初始化
7. java虚拟机启动时被指定为主类(包含main()方法的类)的初始化;
8. 初次调用java.lang.invoke.MethodHandle的实例,初始化MethodHandle指向的方法所在的类
代码测试:
一. 创建类的实例
1. new关键字
public class Active1 {
public static void main(String[] args) {
User user = new User();
}
}
class User{
static {
System.out.println("调用User的初始化");
}
}
2. 反序列化
public class Active1 {
//序列化过程
@Test
public void oos(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
oos.writeObject(new User());
} catch (IOException e) {
e.printStackTrace();
}finally {
if(oos == null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//反序列化过程(验证)
@Test
public void ois(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("test.txt"));
User user = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(ois == null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class User implements Serializable{
static {
System.out.println("调用User的初始化");
}
}
二. 调用某个类的静态方法
public class Active1 {
@Test
public void test(){
User.method();
}
}
class User implements Serializable{
static {
System.out.println("调用User的初始化");
}
public static void method(){
System.out.println("调用类的静态方法");
}
}
三. 调用类或接口的静态字段时(final修饰符特殊考虑)
针对类只包含static,是在类的初始化阶段进行。
针对类、接口:static+final,且显示赋值中不涉及到方法或构造器调用的基本数据类型或String类型的显式赋值,是在链接阶段的准备环节进行。
public class Active2 {
@Test
public void test1(){
//只包含static,是在类的初始化阶段进行
System.out.println(Man.num);
}
@Test
public void test2(){
//static+final,且显示赋值中不涉及到方法或构造器调用的基本数据类型或String类型的显式赋值,是在链接阶段的准备环节进行
System.out.println(Man.num2);
}
@Test
public void test3(){
//static+final,且显示赋值中涉及到String类型的显式赋值,是在类的初始化阶段进行
System.out.println(Man.num3);
}
@Test
public void test4(){
//static+final,且显示赋值中涉及到方法或构造器调用的基本数据类型,是在类的初始化阶段进行
System.out.println(Man.num4);
}
}
class Man {
static {
System.out.println("调用Man的初始化");
}
public static int num = 1;
public static final int num2 = 1;
public static final String num3 = new String("123");
public static final int num4 = new Random().nextInt(10);
}
针对接口只包含static,是在链接阶段的准备环节进行。
public class Active2 {
@Test
public void test1(){
//只包含static,是在链接阶段的准备环节进行
System.out.println(ComA.num);
}
@Test
public void test2(){
//static+final,且显示赋值中不涉及到方法或构造器调用的基本数据类型或String类型的显式赋值,是在链接阶段的准备环节进行
System.out.println(ComA.num2);
}
@Test
public void test3(){
//static+final,且显示赋值中涉及到方法或构造器调用的基本数据类型或String类型的显式赋值,是在类的初始化阶段进行
System.out.println(ComA.num3);
}
}
interface ComA{
//当调用此方法时说明接口被初始化
public static final Thread t = new Thread(){
{
System.out.println("ComA的初始化");
}
};
public static int num = 1;
public static final int num2 = 1;
public static final int num3 = new Random().nextInt(10);
}
四. 使用java.lang.reflec包中的方法反射类的方法
public class Active1 {
@Test
public void test() throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.xxx.active.User");
}
}
class User {
static {
System.out.println("调用User的初始化");
}
}
五. 初始化一个类的子类
public class Active1 {
@Test
public void test() {
new Student();
}
}
class User {
static {
System.out.println("调用User的初始化");
}
}
class Student extends User{
}
补充说明: 当Java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。 在初始化一个类时,并不会先初始化它所实现的接口 在初始化一个接口时,并不会先初始化它的父接口 因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态字段时,才会导致该接口的初始化
六. 接口定义了default方法
定义了default方法,实现接口被初始化
public class Active2 {
@Test
public void test1(){
new Use();
}
}
class Use implements ComA{
}
interface ComA {
//当调用此方法时说明接口被初始化
public static final Thread t = new Thread(){
{
System.out.println("ComA的初始化");
}
};
public default void method(){
System.out.println("调用了默认方法");
}
}
未定义 default方法,实现接口未能被初始化
public class Active2 {
@Test
public void test1(){
new Use();
}
}
class Use implements ComA{
}
interface ComA {
//当调用此方法时说明接口被初始化
public static final Thread t = new Thread(){
{
System.out.println("ComA的初始化");
}
};
}
七. 指定为主类(包含main()方法的类)的初始化
class Use {
static {
System.out.println("主类被初始化");
}
public static void main(String[] args) {
}
}
八. 初始化MethodHandle指向的方法所在的类