3、类的加载、连接与初始化过程详解
3.1、类的加载
类的加载指的是将类的class文件中的二进制数据读入到内存中,将其放置在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象(规范并未说明Class对象放在哪里,HotSpot虚拟机将其放在了方法区中),用来封装类在方法区内的数据结构。
注意:一个类不管创建了多少实例,这些实例对应的Class对象只有一个
加载.class文件的几种方式
从本地系统中直接加载(最常使用,.class文件位于类路径下面)
通过网络下载.class文件
从zip,jar等归档文件中加载.class文件
从专有数据库中提取.class文件
将java源文件动态编译为.class文件(动态代理,web开发中jsp转换为servlet)
可以通过配置jvm参数(-XX:+TraceClassLoading)打印出类的加载信息
jvm参数简介:
都以“-XX:”打头,共有3大类:
-XX:+<option>:表示开启option选项
-XX:-<option>:表示关闭option选项
-XX:<option>=<value>:表示将option选项的值设置为value
举例:
-XX:+TraceClassLoading 开启追踪类的加载信息并打印出来
-XX:-TraceClassLoading 关闭追踪类的加载信息并打印出来
3.2、类的连接
参考2.2章节
3.3、类何时被初始化
所有的java虚拟机实现必须在每个类或接口被java程序【首次主动使用】时才初始化他们
java程序对类的使用方式分为2种,主动使用和被动使用。
-主动使用(7种)
1)创建类的实例,如创建一个学生对象new Student(),注意,创建一个学生的数组对象new Student[1]并不属于主动使用范畴,不会引起类的初始化
2)访问某个类或接口的静态变量,或者对该静态变量进行赋值
3)调用类的静态方法
4)反射(Class.forName("com.shtec.Test"))
5)初始化一个类的子类
6)java虚拟机启动时被标记为启动类的类(包含main方法的那个类)
7)JDK1.7开始提供的动态语言支持(了解)
java.lang.invoke.MethodHandle实例的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化
2)、3)对应的 助记符
访问静态变量 getstatic
对静态变量赋值 putstatic
调用静态方法 invokestatic
案例:
package com.shtec.init; /** * 对于静态字段来说,只有直接定义了该字段的类才会被初始化。子类调用父类的静态字段并不会引起子类的初始化 * 当一个类在初始化时,会先初始化它的父类。 * * 注意:类是否被初始化并不影响类的【加载】过程 * 虚拟机参数: * -XX:+TraceClassLoading 用于追踪类的加载信息并打印出来 * @author sunhao * */ public class Demo { public static void main(String[] args) { //对于静态字段来说,只有直接定义了该字段的类才会被初始化。子类调用父类的静态字段并不会引起子类的初始化 System.out.println(Child.a); //输出如下: //Parent static block //parent //当一个类在初始化时,会先初始化它的父类。 System.out.println(Child.b); //输出如下: //Parent static block //Child static block //child } } class Parent{ public static String a = "parent"; static{ System.out.println("Parent static block"); } } class Child extends Parent{ public static String b = "child"; static{ System.out.println("Child static block"); } }
-被动使用
除了以上7种情况,其他使用java类的方式都被看作是对类的【被动使用】,都不会导致类的初始化