类加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象。
类加载的过程
java类的加载,连接,初始化都是在程序运行期间完成的。
- 加载:查找加载class二进制数据。
- 连接
- 验证:确保被加载类的正确性。
- 准备:为类的静态变量分配内存,并将其初始化为默认值。
- 解析:把类中的符号引用转换为直接引用。
- 初始化:为类的静态变量赋予正确的初值。
类加载中的初始化
所有的java虚拟机实现必须在每个类或者接口被java程序首次主动使用时才初始化他们。
主动使用分为以下6种:
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(Class.forname(“Test”))
- 初始化一个类的子类
- java虚拟机启动时被标记为启动类的类
例子
例子1
public class Main {
public static void main(String[] args) {
System.out.println(MyChild.str2);
}
}
class MyParent{
public static String str="hello";
static {
System.out.println("Parent block ok");
}
}
class MyChild extends MyParent{
public static String str2="hello";
static {
System.out.println("Child block ok");
}
}
这里的MyChild.str2属于主动调用中的第三种所以会打印出"Child block ok",因为初始化了子类MyChild所以会父类也会初始化"Parent block ok"也会打印。
例子2
public class Main {
public static void main(String[] args) {
System.out.println(MyChild.str2);
}
}
class MyParent{
public final static String str="hello";
static {
System.out.println("Parent block ok");
}
}
class MyChild extends MyParent{
public final static String str2="hello";
static {
System.out.println("Child block ok");
}
}
这里加了个final字段,最终结果是不会初始化2个类,因为在编译期间,final字段已经放入Main的常量池中了。
本质:编译时可以初始化的,就不需要在运行时初始化。
例子3
public class Main {
public static void main(String[] args) {
System.out.println(MyChild.str2);
}
}
interface MyParent{
String str="hello";
}
interface MyChild extends MyParent {
String str2="hello";
}
此时运行不会对2个接口初始化,因为接口成员属性默认是public final static。
例子4
public class Main {
public static void main(String[] args) {
System.out.println(Singleton.counter1);
System.out.println(Singleton.counter2);
}
}
class Singleton{
public static int counter1;
private static Singleton singleton=new Singleton();
private Singleton(){
counter1++;
counter2++;
}
public static int counter2=0;
public static Singleton getInstance(){
return singleton;
}
}
此时打印出来的值是什么?
分2部:
- 在连接的准备阶段,所有静态变量分配内存,并初始化默认值,counter1=0,counter2=0,singleton=null。
- 在初始化阶段,按顺序初始化。
- counter1=0
- singleton=new Singleton()(counter1=1,counter2=1)
- counter2=0
所以最终输出时counter1=1,counter2=0。