一.类加载
虚拟机把class文件加载至内存之后,对字节码数据进行校验/解析/初始化等操作,最终形成可被VM直接使用的java类型,这就是虚拟机类加载机制.类的加载完全可以在运行时进行,这给VM提供了动态加载类提供了可行性.
类生命周期过程大概分为:加载-->校验-->解析-->初始化-->使用-->卸载;类在被"使用"之前必须进行前4个阶段,其中初始化工作可以被延迟进行,直到类需要创建实例/static方法或属性被调用(非编译常量)/反射机制中使用类的成员(Member)时被触发.
- 加载:通过类的全限定名的方式获取class文件的字节流.事实上,加载器可以从任何类型文件(zip,jar,txt等)或者网络中获取class字节流.参见ClassLoader类
- 校验:检查加载至内存的字节流是否符合当前JVM的规范要求,字节流信息需要被当前JVM(考虑到JVM的版本问题)正确的识别,包括字节流的格式/字节码的语义(语法)/类的继承关系/变量的声明合法性等进行全面校验,如果校验成功,则表明此class字节流是安全的/没有被修改的/可以被正常使用的.
- 准备:准备阶段是一个过渡阶段,在类文件校验合法之后,将会得出类的结构图,此时有必要对类的属性(static)进行默认值设定,比如static int默认为0,即使指定了值.不过对于final类型的,将会以"常量"特例被处理,直接持有原始值.
- 解析:解析阶段是JVM将常量池中的符号引用转换成直接引用的过程.
- 初始化:到此为止,一个类的"前戏"工作基本结束,接下来就可以"服务"了,此时类的属性(static)将会被设定为用户指定的值(区别于准备阶段),而且还会执行类的static区块.同时也会连带执行父类的static区块和静态属性的值初始化.对于类的static区块在多线程环境中被同步执行,如果有多个线程同事执行static区块,事实上最终只会在有一个线程执行.
二.类加载器
对于任意一个类,它的唯一性将有它的类加载器和其类型来决定;如果一个类被多个classLoader加载(最终执行load操作的classLoader必须不同),那么它们最终获得的class引用将不相等(即"!="),即使用==,equals(),instanceof,isInstance(),isAssignableFrom()都得不到预期的值,事实上可以简单的认为,他们是不同的"类"(类型).
- 引导类加载器:Bootstrap-classLoader,此类主要负责加载JAVA API,即处于$JAVA_HOME$\lib下的class文件,这些lib位置可以在外部通过-Xbootclasspath指定,但是此目录下的jar文件不能被重命名或者人为增加,否则将不能被正常加载;引导类加载器不能被java程序获得,尝试获得引导类加载器将返回null.
- 扩展类加载器:负责加载$JAVA_HOME$\lib\ext目录下的class,开发者可以人为的在此目录下添加jar文件,以便被此JVM之上的所有应用所收益.此加载器可以被程序获取,一般为classpath类加载器的父类.(sun.misc.Launcher$ExtClassLoader)
- 应用类加载器:Application ClassLoader,负责加载应用的classpatch下的所有class文件,其中ClassLoader.getSystemClassLoader()获得的就是此加载器.(sun.misc,Launcher$AppClassLoader);自定义类加载器的父加载器一般设定为它.
JVM并没有使用继承关系来组织这三种类加载器,而是采取了组合关系(即classLoader.setParent(...)).
JVM类加载过程和类关系维护采取了"双亲委派模型":对于任何类加载器(包括自定义类加载器)对于指定类的加载,首先交付给"父-类加载器"查找或加载,如果父-类加载器已经加载则直接获取class引用,否则继续传递当前类加载器的父-类加载器,直到引导类加载器,如果此时引导类加载器也没有持有class信息,则抛出异常(异常信息只作为中断信号),此时当前类加载器(直接接收加载请求的类加载器,比如自定义类加载器)将会尝试加载类信息...通过这种"委派关系",能够巧妙的(当然不是最佳的手段)规避类被重复加载的可能.
三.编译与泛型
java中的泛型,是伪泛型,只是简单的在API级别做了"模样",但是对于类的编译过程,则会导致泛型类型的擦除,即编译过程或者编译之后的文件中,最终泛型仍然以"原生类型"表示.即在运行时无法直接还原"泛型",java还支持了反射机制,为了让反射机制能够和"泛型"配合,那么java最终提供了Generic(例如GenericArrayType)和ParameterizedType来配合获得泛型(和参数化类--类型)的信息,这些信息虽然在编译时被擦除,但是元数据(metadata)仍然被有效的class文件中.
例如:List<Integer>在编译之后,API级别上就成了List,"丢失"了参数化的信息.那么方法 void invoke(List<Integer> list) 和void invoke(List<String> list),那么将不能共存在一个类中,因为编译之后它们是一样的.(识别方法的特性,有方法签名 + 参数列表,而定);但是还有一中特例,如果方法的参数列表中或者返回值中或者当前类是参数化的,那么对于方法的返回值类型也参与了"方法标识";例如上述两个方法,如果第二个方法改成Integer invoke(List<String> list)或者<T> T invoke(List<T> list)将被成功编译.
相关推荐
包括JVM执行过程、虚拟机类加载机制、运行时数据区、GC、类加载器、内存分配与回收策略等,全套视频加资料高清无密码 第1讲 说在前面的话 免费 00:05:07 第2讲 整个部分要讲的内容说明 免费 00:06:58 第3讲...
### 小结 本文详细介绍了JVM的基本运行机制、调试参数、垃圾收集算法以及监控和锁的应用等内容。深入理解这些知识点对于高效开发和维护Java应用程序至关重要。通过对JVM的工作原理有一个清晰的认识,开发者可以更好...
第86节类加载机制概述00:07:26分钟 | 第87节类加载时机00:13:15分钟 | 第88节类加载的过程-加载00:15:15分钟 | 第89节类加载的过程-验证00:10:24分钟 | 第90节类加载的过程-准备00:05:40分钟 | 第91节类加载的...
- **类数据共享**:`sharing`,表明开启了类数据共享(CDS),这是一种将JRE系统类存储在只读缓存中(如`.jsa`文件中的“Java Shared Archive”),供所有Java进程的类加载器作为共享资源使用的机制。CDS可以在频繁从...
1.9 本章小结 22 本章练习 22 第2章 理解面向对象 23 2.1 面向对象 24 2.1.1 结构化程序设计简介 24 2.1.2 程序的三种基本结构 25 2.1.3 面向对象程序设计简介 27 2.1.4 面向对象的基本特征 28 2.2 UML...
### JDBC个人小结 #### 一、JDBC简介与初始化 **JDBC**(Java Database Connectivity)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 编程语言编写的接口和类组成。...
【RMI(Remote Method Invocation)入门小结】 远程方法调用(RMI)是Java平台上的一个关键特性,它允许Java对象在不同的JVM(Java虚拟机)之间进行通信,从而实现分布式计算。RMI机制使得开发者可以像调用本地方法...
- 使用`Class.forName()`:通过字符串形式的类全名加载类,如`Class.forName("hibernate.ch03.obj.UserInfo")`,需要注意类必须已经被JVM加载。 2. 获取类名称: 要获取类的名称,可以通过Class对象调用`getName...
第三部分分析了虚拟机的执行子系统,包括类文件结构、虚拟机类加载机制、虚拟机字节码执行引擎。第四部分讲解了程序的编译与代码的优化,阐述了泛型、自动装箱拆箱、条件编译等语法糖的原理;讲解了虚拟机的热点探测...
自定义类加载器可以在类被加载到JVM之前对其进行修改,从而实现AOP。这种方法适用于需要对整个应用程序进行全局增强的情况。 **3.3.1 实现** 自定义类加载器可以通过覆盖`findClass`方法来实现。在这个方法中,...
理解类的加载、编译和运行过程有助于深入学习Java。 8. **工具实践**:理解并掌握如何使用JDK工具,如Javac编译器、Javadoc生成API文档、JAR打包工具等,能提高开发效率。 总之,Java基础涵盖了许多方面,从简单的...
- 方法区是JVM的一部分,它存储了类的元数据,如类名、超类、接口、访问修饰符等基本信息。 - 运行时常量池是方法区的一部分,它包含各种常量,如字符串、final变量、类名和方法名,这些常量通过索引进行访问。 -...
### Android高级工程师面试技术点小结 在当前竞争激烈的IT行业中,成为一名优秀的Android高级工程师不仅意味着需要具备深厚的技术功底,还需要不断跟进最新的技术趋势和发展方向。本文将根据标题、描述以及部分内容...
### Java基础知识小结 #### 1.1 `getPath()`、`getAbsolutePath()`、`getCanonicalPath()`的区别 在Java中,处理文件路径时经常会用到`getPath()`、`getAbsolutePath()`以及`getCanonicalPath()`这三个方法。它们...
- 较小的体积和更低的资源消耗使得它非常适合移动设备。 - 支持同时运行多个虚拟机实例,这使得每个Android应用都能拥有独立的虚拟机环境。 #### 二、Dalvik虚拟机架构特点 - **基于寄存器架构**:与传统的JVM...
titre3:小结 继承类成员初始化过程是一个复杂的过程,需要遵循特定的步骤。在这个过程中,JVM会加载类文件,对静态成员变量进行初始化,并创建对象。通过这个实例,我们可以更好地理解Java中的继承机制和成员初始...
#### 小结 《Java深度历险》第二章深入介绍了Java的类别加载器及其在实现动态性方面的重要作用。通过理解类别加载器的工作机制,开发者不仅能够更好地利用Java的这一特性,还可以开发出更加灵活、可扩展的应用程序...
#### 四、小结 通过以上介绍可以看出,`Class`对象在Java中扮演着极其重要的角色。理解其生成方式及其内部机制对于深入学习Java语言至关重要。无论是进行反射操作还是进行类的信息查询,`Class`对象都是不可或缺的...
- **类加载器(Class Loader)**:负责将.class文件加载到内存中,并对其进行校验、解析和初始化。 - **运行时数据区(Runtime Data Area)**:包括方法区、堆、栈等,用于存储运行时的数据。 - **执行引擎**:解释...