Java 类的加载和初始化

直接从书上抄来的,自己写一遍加深印象

类的加载

加载就是通过指定的类全限定名,获取此类的二进制字节流(可以是clazz文件或者直接内存读取或者远程网络,jar包等),然后将此二进制字节流转化为方法区的数据结构,在内存中生成一个代表这个类的java.lang.Class对象。

java.lang.Class这个类用于维护目标类,包括读取字段 读取构造函数,读取方法,创建实例(newInstance)等等。
class对象可以通过Class.forName("java.lang.String")获取,或者直接String.class获取,
或者"java.lang.String".getClass(),注意getClass方法是java.lang.Object中定义的,属于native方法

定义的类不一定是第一次使用的时候的才加载也可以预先加载,java虚拟机桓范允许系统预先加载某些类。

当类被加载后,系统会生成对应的class对象,接着进入连接阶段。连接阶段负责把类的二进制数据合并到jre中,具体分三阶段
1.验证文件是否合法,不会危害虚拟机的安全
2.为类的变量分配内存 设置默认值
3.将类的二进制数据中的符号引用替换成直接引用

类的初始化
在初始化阶段,主要对类变量执行初始化。将常量池(里面存储了我们编写的java类的类和接口的全限定名,字段的名称和描述符)中的符号引用转换为直接引用的过程。设置初始化一般有2种方法,1是声明的时候直接赋值,2是使用静态代码块
如果这个类还存在父类,那么先需要初始化直接父类,所以jvm最先初始化Object
如果类包含初始化语句则执行

什么时候会初始化?
1.创建实例 比如new 反射创建实例 反序列化等
2.调用某个类的方法(静态的)
3.访问某个类或接口的类变量
4.使用反射创建某个类或接口的class对象,例如class.forName("xx")
5.初始化某个类的子类 子类的所有父类都会初始化

注意一个final类型的类变量,如果编译时就确定了具体的指,那么这个类变量相当于宏变量,编译器会执行优化,调用这个变量的地方直接替换成该值,这个变量相当于不存在了。变量调用自然不会导致该类的初始化。

ClassLoader.loadClass()只会导致类被加载,Class.forName()才会执行类的初始化

类加载器
类加载器负责加载所有的类,系统为所有被载入内存的类生成对应的java.lang.Class实例。一个一旦被加载,则不会被再次载入了。每个被载入内存的类,都有一个唯一标识(类的全限定类名和其类加载器)

加载器规则
1.加载一个类时,该类所依赖和引用的其他类都由改类的加载器负责载入
2.如果一个类存在父类,则优先尝试使用父类的加载器加载该类
3.所有被载入的类都会被缓存,读取类的时候,会优先使用缓存