java类加载过程_java类的加载过程

在这本书里面,在讲到类初始化的五种情况时,提及了一个比较有趣的事情。先来看看下面的代码

public class SubClass {

static{

System.err.println("I m your son");

}

public static final int name = 111;

}

这个时候如果调用SubClass.name,是根本不会触发SubClass初始化的(这里是因为name是一个常量,和下面的例子不一样,如果这里把final去掉,是会触发Subclass的初始化的,因为对于静态字段而言,如果静态字段被引用,就会调用getstatic指令和putstatic指令,那么自然就会引发类的初始化,详情看下面关于触发类初始化的五种情况)。再来看看另一种情况;

public class SuperClass {

static{

System.err.println("I am your father");

}

public static int value = 123;

}

public class SubClass extends SuperClass{

static{

System.err.println("I m your son");

}

}

这个时候如果调用SubClass.value(静态字段和静态方法是可以继承但是无法被覆盖,所以这里调用value,只会导致直接定义这个静态变量的类被初始化),同样也是不会使得SubClass这个类进行初始化。那么问题来了,到底类在什么时候会进行初始化,类的初始化顺序到底是怎样的?让我们接着往下看。

一. 类加载的过程

虚拟机加载类主要有五个过程:加载、验证、准备、解析和初始化。

加载:加载是“类加载”的一个过程,希望读者没有混淆这两个概念。

在这个过程虚拟机主要完成三件事,

 通过一个类的全限定名___[解释全限定名]___来获取此类的二进制字节流,这点上,虚拟机并没有指明要从哪里获取类的二进制字节流,因此发展出了很多不一样的加载方式。比如jar,zip等压缩包中加载,从网络获取[如Applet],或者由其他文件生成[如从JSP生成]。

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

 在Java堆[这个没有强制规定,比如HotSpot则选择在方法区中生成这个对象]中生成一个代表这个类的java.lang.Class对象,作为程序访问方法区中的各种数据的外部入口[也就是说当常量池表中的数据被转换成运行时数据结构的时候,实际上[堆/方法区]有一个Class对象的实例可以访问到方法区的各类数据,包括常量池表,代码等]。

如果加载对象是普通的类或者接口(统称为C),则是通过类加载器(L)去加载C的二进制表示来创建。但是如果加载的是数组类,那情况就有所不同了,数组类本身不通过类加载器创建,它是由Java虚拟机直接创建的。但是数组类内部的元素类型最终还是要靠类加载器去加载。[后续可以添加类加载器的详细解释]

验证

验证是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机本身的安全。验证大致上有以下4个过程:

1) 文件格式验证:

a) 检查魔数,主、次版本号是否在当前虚拟机处理范围。

b) 常量池的常量是否不被支持[通过检查tag],指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量。

c) CONSTANT_Utf8_info类型的常量中是否有不符合UTF8编码的数据。

d) Class文件中各个部分及文件本身是否有被删除或者附加其他信息等等。

这个节点的主要目的是保证输入的字节流能被正确的解析并存储于方法区内,格式上符合描述一个java类型信息的要求。这个阶段是基于二进制流,只要通过了这个阶段的验证,字节流才会进入内存的方法区中存储。所以后续的三个阶段基于方法区的存储结构进行的,不会再直接操作字节流。

2) 元数据验证:这个阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。主要验证包括以下几点:

a) 这个类是否有父类(除了java.lang.Object外,所有的类都应该有父类)。

b) 这个类的父类是否继承了不允许被继承的类(被final修饰的类)。

c) 如果这个类不是抽象类,那么应该实现其父类或接口中要求实现的方法。

d) 类中的字段,方法是否与父类相矛盾(例如覆盖了父类的final字段,或者出现不符合规则的方法重载)。

这个阶段主要目的是对类的元数据信息进行语义校验,保证不存在不符合Java规范的元数据信息。

3) 字节码验证:

4) 符号引用验证:

准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,这些变量所使用的内存都将在方法区中分配。这里有几个值得注意的点:

1) 这里初始化的仅仅是类变量(被static修饰的变量)的初始化,并不包括实例变量。实例变量将会在对象实例化的时候随着对象一起分配在java堆中。

2) 这里所说的初始值,通常是数据类型的零值,举个例子:

public static int value = 123;

这句代码中,value在准备阶段的初始值为0,而不是123,因为这个时候还没开始执行任何的java方法。而把value的值置为123的putstatic指令是程序被编译后,存放在类构造器()方法中的。所以value置为123是在初始化[第五阶段]阶段才会执行。[还有一些其他类型的零值,可以参考虚拟机规范]

当然,上述情况也有例外的地方,如果类字段的字段属性表(参考class文件中的属性数据结构)中存在ConstatntValue[即同时被final和static修饰]属性,那么在准备阶段,变量value就会被初始化为ConstantValue属性所指定的值,例如上述变量中,编译时javac将会为value生成的ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue属性而将value赋值为123。

解析

解析阶段就是虚拟机将常量池内的符号引用[使用一组描述符来描述所引用的目标,符可以是任意形式的字面量,只要使用时能无歧义的定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中]替换为直接引用[直接引用可以是直接指向目标的指针,相对偏移量或者一个能间接定位到目标的句柄。直接引用与内存的布局有关,如果有了直接引用,则目标一定存在]的过程,符号引用在Class文件内的常量池中以CONSTANT_Fieldref_info,CONSTANT_Class_info,CONSTANT_Methodref_info等类型出现。那么,解析阶段中的直接引用于符号引用又有什么关联呢?

对同一个符号引用进行多次解析请求是很常见的,比如你在代码里面多次new同一个类。这里要分成两种情况:

1) invokeddynamic指令:这个指令的特殊之处在于,它是为了支持动态语言而存在的,也就是说,必须等到程序实际运行这条指令的时候,解析动作才能进行[目前仅使用java语言并不会生成这条指令]。相对的,其余触发的解析指定都是“静态”的,可以在刚刚完成加载阶段,还没开始执行代码时就进行解析。

2) 除了上述的指令外,虚拟机实现可以对第一次解析的结果进行缓存(在运行时常量池中记录直接引用,并把常量标识为已解析状态)。从而避免了多次解析。

解析动作主要针对“类或接口”,“字段”,“类方法”,“接口方法”,“方法类型”,“方法句柄”和“调用点限定符”7类符号引用进行[分别对应7种常量池表的CONSTATN_Class_info,CONSTATN_Fieldref_info,CONSTATN_Methodref_info,CONSTATN_InterfaceMethodref_info,CONSTATN_MethodType_info,CONSTATN_MethodHandle_info,CONSTATN_InvokeDynamic_info,后续三种和动态类型有关,目前java还是静态类型语言]。

初始化

在虚拟机中严格规定需要对类进行初始化的,有下面五种情况:

1) 遇到new,getstatic,putstatic或者invokestatic这4条字节码指令时。

2) 使用java.lang.reflect包的方法对类进行反射调用的时候。

3) 当初始化一个类,发现其父类并没有初始化时,需要先初始化父类。

4) 虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。

5) 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有初始化,则需要先触发其初始化。

对于以上五种初始化场景,虚拟机规范中使用了“只有”,除此之外,所有的引用类的方式都不会触发初始化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/533228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java mvc 导出excel_Java springMVC POI 导出 EXCEL

思路 :将需要导出的数据存放在一个List中创建一个EXCEL表 注意 XSSFWorkbook 只能操作2007以上的版本,XSSFWorkbook 只能操作2003一下的版本,所以需要的时候可以使用 Workbook创建对象处理兼容性遍历List 并将每条数据 写入 EXCEL表中具体代码…

java 排序原理_简单选择排序算法原理及java实现(超详细)

简单选择排序的原理简单选择排序的原理非常简单,即在待排序的数列中寻找最大(或者最小)的一个数,与第 1 个元素进行交换,接着在剩余的待排序的数列中继续找最大(最小)的一个数,与第 2 个元素交换。以此类推,一直到待排…

python对象点方法_python面向对象知识点疏理

面向对象技术简介类:用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。class类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。数据成员&#…

ckeditor java 上传_CKEditor粘贴图片自动上传到服务器(Java版)

环境:java,springmvc,ckeditor,tomcat,maven情况:在做项目的时候发现本地图片粘贴到ckeditor中,img标签的src中的值是“data:image/png;base64,”开头的,后面会跟一串字符串,图片越大字符串越长,这样的图片…

java 序列化声明_显式声明默认Java类序列化方法的原因是什么?

我定期看到具有以下结构的Java类:class MyClass implements Serializable {private static final long serialVersionUID 1L;// ...private void writeObject(final java.io.ObjectOutputStream s) throws IOException {s.defaultWriteObject();}private void read…

java 百度poi_Android应用中使用百度地图API之POI(三)

先看执行后的图吧:POI(Point of Interest)。中文能够翻译为“兴趣点”。在地理信息系统中。一个POI能够是一栋房子、一个商铺、一个邮筒、一个公交站等 具体:http://developer.baidu.com/map/sdkandev-4.htm主要应用 MKSearch 类:com.baidu.…

java 顺序存储键值对_java://Comparator、Comparable的用法(按照要求将map集合的键值对进行顺序输出)...

import java.util.*;public class Person implements Comparable//使Person的属性具有比较性{private String name;private int age;public Person(String name,int age)//初始化构造函数{this.name name;this.age age;}public void set(String name,int age)//重新设置姓名和…

java日历事件处理_日历表的事件处理和管理(刘静)

1 //添加gridview,显示具体的日期2 SuppressLint("ResourceAsColor")3 private voidaddGridView() {4 LinearLayout.LayoutParams params newLinearLayout.LayoutParams(5 LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);6 //取得屏幕的宽度和高度7 WindowMa…

java第四次上机作业_第十java上机作业

第十java上机作业 第七章上级作业 7.1 public class Rectangle { double width1; double height1; String color“white“; public Rectangle(){} public Rectangle(double width,double height,String color) { this.widthwidth; this.heightheight; this.colorcolor; } publi…

java解析带斜杠的参数_Java Spring MVC应用程序仅接受带有斜杠的POST请求

war文件名是forms.war。 web.xml中的url模式为"/"控制器操作的RequestMapping为"/"如果遇到localhost:8080 /表单,则RequestMethod.GET操作正常工作如果针对localhost:8080/forms点击发布数据,则不会触发RequestMethod.P…

java文件编码格式环境变量_Jenkins maven 构建乱码,修改file.encoding系统变量编码为UTF-8...

一切都是windows的控制台默认编码GBK问题情景:使用jenkins构建,console 输出的中文乱码。代码编码格式是utf-8,因为Jenkins会默认读取当前系统的编码格式,导致构建日志乱码和selenium自动化测试输入的中文乱码。控制台输出乱码摸索…

http multipart java_Http MultiPart请求

我正在尝试使用httpClient库上传图像(多部分/表单数据) . 我可以使用httpPost方法和byteArrayRequestEntity上传图像 . 以下是我使用的代码:File file new File(imageFilePath);HttpClient client new HttpClient();PostMethod method new PostMethod("http…

java分布式会话redis_详解springboot中redis的使用和分布式session共享问题

对于分布式使用NginxTomcat实现负载均衡,最常用的均衡算法有IP_Hash、轮训、根据权重、随机等。不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不…

python3.8使用方法_python3.8新特性

python3.8新特性Python3.8稳定版已发布,官网发布了一篇介绍新特性的文章,在此记录一下。新增赋值操作符:作用:把较大表达式中的某部分值赋给变量,因为看起来像海象的眼睛和牙齿,: 操作符有个特别的名字:海象操作符范例…

java责任链设计模式 订单_Java责任链设计模式实例分析

本文实例讲述了Java责任链设计模式。分享给大家供大家参考,具体如下:一 代码abstract class AbstractHandler {private Handler Handler null;// 持有责任链中下一个责任处理者的引用public void setHandler( Handler handler ) {this.Handler handler…

java map去重复的数据_使用HashMap去重复数据.

因为HashMap是键值对形式,所以一个键只对应一个Value,利用这个原理,我们就可以根据某列重复数据做键对重复数据进行处理啦~首先先看看我要处理 的数据然后我最后想要的结果:需求:根据groupName每个分组信息只显示一条数…

centos6.5 MySQL 服务器_启用CentOS6.5 64位安装时自带的MySQL数据库服务器

本人在虚拟机上又安装了一台linux机器,作为MySQL数据库服务器用,在安装时选择了系统自带的MySQL服务器端,以下是启用步骤。首先开启mysqld服务#service mysqld start进入/usr/bin目录#cd /usr/bin设定mysql数据库root用户的密码#mysqladmin -…

java xml date_W3C XML 模式时间数据类型与java Date进行转换

W3C XML 模式时间数据格式:"2014-11-17T 09:40:25" 或者 “2014-11-17T 09:40:2508:00”要想把以上格式的时间转换为能被java所使用个格式有以下3种办法:(1)、jdk中有这么一个类XMLGregorianCalendar,使用XMLGregorianCalendar类中的toGreGreg…

PHP stomp 连接判断,php实现通过stomp协议连接ActiveMQ操作示例

本文实例讲述了php实现通过stomp协议连接ActiveMQ操作。分享给大家供大家参考,具体如下:前面介绍了php ActiveMQ的安装与使用,这里再来讲述一下php通过stomp协议连接ActiveMQ。一、安装php的stomp扩展http://pecl.php.net/package/stomp如&am…

authinfo.php,【nginxphp】后台权限认证方式

一、最常用的方法(代码中限制)1、如何限制IPfunction get_new_ip(){if(getenv(HTTP_CLIENT_IP)) {$onlineip getenv(HTTP_CLIENT_IP);} elseif(getenv(HTTP_X_FORWARDED_FOR)) {$onlineip getenv(HTTP_X_FORWARDED_FOR);} elseif(getenv(REMOTE_ADDR)) {$onlineip getenv(RE…