Java之类加载机制案例讲解

 更新时间:2021年07月31日 10:25:06   作者:Serendipity sn  
这篇文章主要介绍了Java之类加载机制案例讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

1.类加载

<1>.父子类执行的顺序

1.父类的静态变量和静态代码块(书写顺序)

2.子类的静态变量和静态代码块(书写顺序)

3.父类的实例代码块(书写顺序)

4.父类的成员变量和构造方法

5.子类的实例代码块

6.子类的成员变量和构造方法

<2>类加载的时机

如果类没有进行初始化,则需要先进行初始化,虚拟机规范则是严格规定有且只有5种情况必须先对类进行初始化(而加载,验证,准备要在这个之前开始)

1.创建类的实例(new的方式),访问某个类的静态变量,或者对该静态变量赋值,调用类的静态方法

2.反射的方式

3.初始化某个类的子类,则其父类也会被初始化

4.java虚拟机启动时被标记为启动类的类,直接使用java.exe来运行的某个主类(如main类)

5.使用jdk1.7的动态语言支持时

<3>类的生命周期

七个阶段:加载,验证,准备,解析,初始化,使用和卸载。其中验证,准备和解析三个部分被称为连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CicgRzZ9-162(file:///C:/Users/26905/Desktop/%E6%AF%94%E7%89%B9%E8%AF%BE%E4%BB%B6/%E7%B1%BB%E5%8A%A0%E8%BD%BD_files/Image.png)]

解析阶段在某些情况下可以在初始化阶段之后再进行,这是为了支持java语言的运行时绑定(动态绑定)

<4>类加载的过程

接下来我们详细讲解一下Java虚拟机中类加载的全过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。

1.加载

<1>通过一个类的全限定名来获取定义此类的二进制字节流。

<2>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

<3>在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2.验证

这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

3.准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

假设一个类变量的定义为:

public static int value=123;

那变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。

4.解析

虚拟机将常量池内的符号引用替换为直接引用的过程。

符号引用:符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。

直接引用:直接引用是和虚拟机实现的内存布局相关的。如果有了直接引用,那引用的目标必定已经在内存中存在。

5.初始化

在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器()方法的过程。

了解:

()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问:

public class Test{
    static{
        i=0; //给变量赋值可以正常编译通过
        System.out.print(i); //这句编译器会提示"非法向前引用"
    }
    static int i=1;
}

1.()方法(Class类的构造方法)与类的构造函数(或者说实例构造器()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的()方法执行之前,父类的()方法已经执行完毕。因此在虚拟机中第一个被执行的()方法的类肯定是java.lang.Object。

2.()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成()方法。

3.接口中定义的变量使用时,接口才会初始化:接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生()方法。但接口与类不同的是,执行接口的()方法不需要先执行父接口的()方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的()方法。

4.虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。

<5>类加载器

类加载器可以分为:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。他们的关系一般如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVRciBVN-1627452314592)(C:\Users\26905\AppData\Roaming\Typora\typora-user-images\image-20210728134416563.png)]

1.启动类加载器(BootstrapClassLoader)
这个类由C++语言实现,是虚拟机自身的一部分,并不继承ClassLoader,不能操作它。用来加载Java的核心类。

2.扩展类加载器(ExtClassLoader)
这个类加载器是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。

3.应用程序类加载器(AppClassLoader)
它负责在 JVM 启动时加载来自 Java 命令的 -classpath 或者 -cp 选项、java.class.path 系统属性指定的 jar 包和类路径。在应用程序代码里可以通过 ClassLoader 的静态方法 getSystemClassLoader() 来获取应用类加载器。如果没有特别指定,则在没有使用自定义类加载器情况下,用户自定义的类都由此加载器加载。

4.2 自定义加载器
用户自定义了类加载器,则自定义类加载器都以应用类加载器作为父加载器。应用类加载器的父类加载器为扩展类加载器。这些类加载器是有层次关系的,启动加载器又叫根加载器,是扩展加载器的父加载器

<6>类加载机制——双亲委派模型

双亲委派模型的过程:如果一个类加载器收到了类加载的请求,它首先不会自己尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,因此所有的加载请求信息最终都会传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(即它的搜索范围没有找到所需要的类)时,子加载器才会尝试自己去完成加载

先查找,再进行加载

(1)从下往上找

(2)从上往下加载

双亲委派模型的好处:双亲委派模型对于java程序的稳定运行极为重要

劣势:无法满足灵活的类加载方式。(解决方案:自己重写loadClass破坏双亲委派模型 例如SPI机制)

到此这篇关于Java之类加载机制案例讲解的文章就介绍到这了,更多相关Java之类加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot集成redis存对象乱码的问题及解决

    springboot集成redis存对象乱码的问题及解决

    这篇文章主要介绍了springboot集成redis存对象乱码的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java8新特性Lambda表达式的一些复杂用法总结

    Java8新特性Lambda表达式的一些复杂用法总结

    lambda表达式是JAVA8中提供的一种新的特性,它支持Java也能进行简单的“函数式编程”。 下面这篇文章主要给大家介绍了关于Java8新特性Lambda表达式的一些复杂用法的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07
  • 微服务和分布式的区别详解

    微服务和分布式的区别详解

    在本篇文章里小编给各位整理了关于微服务和分布式的区别以及相关知识点总结,有兴趣的朋友们学习下。
    2019-07-07
  • SpringBoot主键ID传到前端后精度丢失的问题解决

    SpringBoot主键ID传到前端后精度丢失的问题解决

    这篇文章主要通过示例为大家详细介绍一些SpringBoot如何解决雪花算法主键ID传到前端后精度丢失问题,文中的示例代码讲解详细,需要的可以参考一下
    2022-05-05
  • 浅析Java中SimpleDateFormat为什么是线程不安全的

    浅析Java中SimpleDateFormat为什么是线程不安全的

    SimpleDateFormat是Java中用于日期时间格式化的一个类,它提供了对日期的解析和格式化能力,本文主要来和大家一起探讨一下SimpleDateFormat为什么是线程不安全的,感兴趣的可以了解下
    2024-02-02
  • Java中的ConcurrentLinkedQueue松散队列解析

    Java中的ConcurrentLinkedQueue松散队列解析

    这篇文章主要介绍了Java中的ConcurrentLinkedQueue松散队列解析,链表是松散的,链表节点并不都是有效的,允许存在无效节点val=null,但是只有最后一个节点才能next=null,需要的朋友可以参考下
    2023-12-12
  • springboot相关面试题汇总详解

    springboot相关面试题汇总详解

    这篇文章主要介绍了springboot相关面试题汇总详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Spring Boot 多数据源处理事务的思路详解

    Spring Boot 多数据源处理事务的思路详解

    这篇文章主要介绍了Spring Boot 多数据源如何处理事务,本文单纯就是技术探讨,要从实际应用中来说的话,我并不建议这样去玩分布式事务、也不建议这样去玩多数据源,毕竟分布式事务主要还是用在微服务场景下,对Spring Boot 多数据源事务相关知识感兴趣的朋友参考下本文
    2022-06-06
  • 《阿里巴巴 Java开发手册》读后感小结

    《阿里巴巴 Java开发手册》读后感小结

    这篇文章主要介绍了《阿里巴巴 Java开发手册》读后感小结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 深入理解JVM自动内存管理

    深入理解JVM自动内存管理

    对于Java虚拟机在内存分配与回收的学习,本文主要介绍了JVM自动内存管理,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08

最新评论