JVM-类加载

类加载系统

什么是类加载?

字节码存储在硬盘上,需要运行时,类加载系统负责将类的信息加载到内存中(方法区)为每个类创建一个class类的对象,使用的是ClassLoader进行加载,充当一个快递员的角色。ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。

类加载的角色 

 1.class file 存在于硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载JVM当中来,根据这个模板实例化出n个一模一样的实例。

2.class file加载到JVM中,被称为DNA元数据模板,放在方法区中。

3.在.class-->JVM-->最终称为元数据模板,此过程就要有一个运输工具(类加载器 Class Loader) ,扮演一个快递员的角色。

类加载过程

加载(Loading)-->验证(Verification)-->准备(Preparation)-->解析(Resolution)-->初始化(Initialization)

加载

将硬盘字节读入到内存中,生成此类的Class对象,把硬盘上的结构转为内存结构。

链接

验证:

      1.验证字节格式,是否被修改(污染)。

       2.验证语法,例如类是否继承final的类

准备:

       在准备阶段负责为类的静态属性分配内存,为类中静态的变量赋予初始值;

       不包含用final修饰的static常量,在编译时进行初始化。

        例如:static int num  = 123; -->在准备阶段static int num 的初始值时0,而不是123。

        在准备阶段不为静态的常量进行赋值。

解析:

         将符号引用(文件中的逻辑引用)转为直接引用(内存中的实际地址)。

初始化

  对类中静态成员进行赋值

类什么时候初始化?

  (1)创建类的实例,也就是new一个对象;

  (2)访问某个类或接口的静态变量,或者对该静态变量赋值;

  (3)调用类的静态方法;

  (4)反射(Class.forName("")):

  (5)初始化一个类的子类(会首先初始化子类的父类)。

类的初始化顺序

对static修饰的变量或语句块进行赋值。

如果同时包含多个静态变量和静态代码块,按照自上而下的顺序依次执行

类初始化顺序:父类static-->子类static-->父类构造方法-->子类构造方法

public class ClassInit {
    static int num = 10;  //准备阶段为静态的变量赋默认值 0  初始化阶段赋值10
    static final int sum = 100;//static final 在编译期间赋值
    static {
        num = 20;
    }

    public static void main(String[] args) {
        System.out.println(num);
    }
}

 类加载器分类

类加载器就是通过一个类的全限定名称来获取其二进制文件(即.class文件)并加载到JVM中的工具。

站在JVM的角度看,类加载器可以分为两种:

   1.启动类加载器(引导类加载器),在部分不是用Java语言写的。

   2.其他类加载器(这部分指的是用Java语言写的类加载器)。

站在Java开发人员的角度看:

   1.启动类加载器,负责加载Java核心类;

   2.扩展类加载器  负责加载\jre\lib\ext目录下的类,包含应用程序类加载器;

   3.应用程序类加载器,负责加载自己写的程序中的类。

双亲委派机制

      为了确保类加载的正确性、安全性,在加载类时,采用双亲委派机制,当需要加载程序中一个类时,会先让加载器的父级去加载,直到最顶级的启动类加载器,如果父级找到了返回使用,如果依然没有找到,那么就委派给子级去加载,找到了就返回,如果所有的类加载器都没有找到,报类找不到异常。

优点

     安全,避免了自己写的类替换了系统中的类;

     避免类重复加载。

类的主动使用/被动使用

      在JVM中规定,每个类或者接口被首次主动使用时才对其进行初始化,有主动使用,自然就有被动使用。

主动使用:

     -通过new关键字被导致类的初始化,这是大家经常使用的初始化一个类的方式;

     -访问类的静态变量,包括读取和更新;

     -访问类的静态方法;

     -对某个类进行反射操作,会导致类的初始化;

     -初始化子类会导致父类的初始化;

     -执行该类的main函数。

被动使用:

     -引用该类的静态常量(注意是常量,不会导致初始化,但这里的常量是指已经指定字面量的常量,但是对于需要计算得出结果的常量会导致初始化)。

public final static int NUM = 5 ; //不会导致类初始化,被动使用
public final static int RAD = new Random().nextInt() ; //会导致类的初始化,主动使用

     -创建数组,用类作为类型使用,不会导致该类的初始化。

User[] users= new User[10] ;

 主动使用和被动使用的区别在于类是否会被初始化。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值