在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的。
提供了更大的灵活性,增加了更多的可能性。
类实例化:
使用。
除了主动使用的七种情况,其它使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化。
import java.util.UUID;
/**
* -XX:+TraceClassLoading,用于追踪类的加载信息并打印出来
* -XX:+<option>,表示开启option选项
* -XX:-<option>,表示关闭option选项
* -XX:<option>=<value>,表示将option选项的值设置为value
*/
public class Main {
public static void main(String[] args) {
//Example 1
System.out.println(Child.stringParent);
/*
输出结果
Parent static block
hello world
对于静态字段来说,只有直接定义了该字段的类才会被初始化。
*/
//Example 2
System.out.println(Child.stringChild);
/*
输出结果
Parent static block
Child static block
welcome
初始化一个类的子类时,要求它的父类都已经被初始化完毕。
*/
//Example 3
System.out.println(Parent1.stringParent1);
/*
输出结果
hello parent1
在编译阶段,常量就会被存入到调用这个常量的方法所在的类的常量池当中,
本质上,调用类并没有直接引用到定义常量的类,因此并不会触发定义常量类
的初始化。
注意:这里指的是将常量存放到了Main的常量池中,
之后Parent1与Main就没有任何关系了,
甚至可以将Parent1的class文件删除。
*/
//Example 4
System.out.println(Parent2.stringParent2);
/*
输出结果
Parent2 static block
71bd2365-e20c-4d3e-b071-2b56287afadf
当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,
这时在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类被初始化。
*/
//Example 5
Parent3[] parent3s=new Parent3[1];
System.out.println(parent3s.getClass());
System.out.println(parent3s.getClass().getSuperclass());
/*
输出结果
class [LParent3;
class java.lang.Object
对于数组实例来说,其类型时有JVM在运行期动态生成的,表示为[LParents3这种形式。
动态生成的类型,其父类型就是Object
对于数组来说,JavaDoc经常将构成数组的元素称为Component,实际上就是将数组降低一个维度后的类型。
ints.getClass() Class [i
chars.getClass() Class [C
booleans.getClass() Class [Z
shorts.getClass() Class [S
bytes.getClass() Class [B
*/
//Example 6
Singleton singleton = Singleton.getInstance();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2: " + Singleton.counter2);
System.out.println("counter3: " + Singleton.counter3);
/*
输出结果
Singleon() counter1: 1
Singleon() counter2: 2
Singleon() counter3: 1
counter1: 1
counter2: 2
counter3: 0
准备阶段赋默认值,初始化是从上到下顺序执行。
*/
}
}
//Example 1
class Parent {
public static String stringParent = "hello world";
static {
System.out.println("Parent static block");
}
}
//Example 2
class Child extends Parent {
public static String stringChild = "welcome";
static {
System.out.println("Child static block");
}
}
//Example 3
class Parent1 {
public static final String stringParent1 = "hello parent1";
static {
System.out.println("Parent1 static block");
}
}
//Example 4
class Parent2 {
public static final String stringParent2 = UUID.randomUUID().toString();
static {
System.out.println("Parent2 static block");
}
}
//Example 5
class Parent3 {
static {
System.out.println("Parent3 static block");
}
}
//Example 6
class Singleton {
public static int counter1;
public static int counter2 = 1;
private static Singleton singleton = new Singleton();
private Singleton() {
counter1++;
counter2++;
counter3++;
System.out.println("Singleon() counter1: " + counter1);
System.out.println("Singleon() counter2: " + counter2);
System.out.println("Singleon() counter3: " + counter3);
}
public static int counter3 = 0;
public static Singleton getInstance() {
return singleton;
}
}
助记符:
类加载器用来把类加载到Java虚拟机中。从JDK1.2版本开始,类的加载过程采用父亲委托机制,这种机制能更好的保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父类加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载Sample类。
在准备阶段,Java虚拟机为类的静态变量分配内存,并设置默认的初始值。例如对于以下Sample类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间,并赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并且赋予默认值0。
在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。
在程序中,静态变量的初始化有两种途径:
import java.util.UUID;
public class Main {
public static void main(String[] args) {
System.out.println(Child5.c);
System.out.println(Child4.b);
System.out.println(Parent4.a);
/*
输出结果
5
随机0 ~ 3;
0
classLoader.Parent4 invoked
当一个接口在初始化时,并不要求其父接口都完成了初始化,
只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化
*/
}
}
import java.util.Random;
public interface Parent4 {
public static final int a = new Random().nextInt(4);
public static final Thread thread = new Thread() {
{
System.out.println("classLoader.Parent4 invoked");
}
};
}
interface Child4 extends Parent4 {
public static final int b = new Random().nextInt(4);
}
class Child5 implements Child4 {
public static int c = 5;
}
package classLoader;
public class Test {
public static void main(String[] args) {
System.out.println(Child6.a);
System.out.println("-----------");
Child6.doSomething();
/*
输出结果:
Parent6 static block
3
-----------
do something
如果用子类类名访问父类的静态变量或静态方法,
都表示对父类的主动使用,而不是对子类的主动使用。
*/
}
}
class Parent6{
static int a = 3;
static {
System.out.println("Parent6 static block");
}
static void doSomething(){
System.out.println("do something");
}
}
class Child6 extends Parent6{
static {
System.out.println("Child6 static block");
}
}
package classLoader;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clazz = classLoader.loadClass("classLoader.CL");
System.out.println(clazz);
System.out.println("---------");
clazz = Class.forName("classLoader.CL");
System.out.println(clazz);
/*
class classLoader.CL
---------
Class CL
class classLoader.CL
调用ClassLoader类的loadClass方法加载一个类,
并不是对类的主动使用,不会导致类的初始化。
*/
}
}
class CL{
static {
System.out.println("Class CL");
}
}