文档章节

类文件结构

曾劲松
 曾劲松
发布于 2017/07/18 20:36
字数 2757
阅读 34
收藏 1

Class文件:

1.常量池,主要有字面量(文本字面量和final常量值等)和符号引用(编译原理方面,类和接口全限定名,字段的名称和描述符,方法的名称和描述符)。

2.访问标志

 

类的加载过程

一个Java文件从被加载到被卸载这个生命过程,总共要经历5个阶段:

加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载

其中加载(除了自定义加载)+链接的过程是完全由jvm负责的,什么时候要对类进行初始化工作(加载+链接在此之前已经完成了),jvm有严格的规定(五种情况):

1.遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,假如类还没进行初始化,则马上对其进行初始化工作。其实就是3种情况:用new实例化一个类时、读取或者设置类的静态字段时(不包括被final修饰的静态字段,因为他们已经被塞进常量池了)、以及执行静态方法的时候。

2.使用java.lang.reflect.*的方法对类进行反射调用的时候,如果类还没有进行过初始化,马上对其进行。

3.初始化一个类的时候,如果他的父亲还没有被初始化,则先去初始化其父亲。

4.当jvm启动时,用户需要指定一个要执行的主类(包含static void main(String[] args)的那个类),则jvm会先去初始化这个类。

5.用Class.forName(String className);来加载类的时候,也会执行初始化动作。注意:ClassLoader的loadClass(String className);方法只会加载并编译某类,并不会对其执行初始化。

以上5种预处理称为对一个类进行主动的引用,其余的其他情况,称为被动引用,都不会触发类的初始化。

 

5.1 加载

“加载”(Loading)阶段是“类加载”(Class Loading)过程的第一个阶段,在此阶段,虚拟机需要完成以下三件事情: 
1、 通过一个类的全限定名来获取定义此类的二进制字节流。 
2、 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 
3、 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。 
加载阶段即可以使用系统提供的类加载器在完成,也可以由用户自定义的类加载器来完成。加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始。

5.2 验证

   验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
   Java语言本身是相对安全的语言,使用Java编码是无法做到如访问数组边界以外的数据、将一个对象转型为它并未实现的类型等,如果这样做了,编译器将拒绝编译。但是,Class文件并不一定是由Java源码编译而来,可以使用任何途径,包括用十六进制编辑器(如UltraEdit)直接编写。如果直接编写了有害的“代码”(字节流),而虚拟机在加载该Class时不进行检查的话,就有可能危害到虚拟机或程序的安全。
  不同的虚拟机,对类验证的实现可能有所不同,但大致都会完成下面四个阶段的验证:文件格式验证、元数据验证、字节码验证和符号引用验证。
   1、文件格式验证,是要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。如验证魔数是否0xCAFEBABE;主、次版本号是否正在当前虚拟机处理范围之内;常量池的常量中是否有不被支持的常量类型……该验证阶段的主要目的是保证输入的字节流能正确地解析并存储于方法区中,经过这个阶段的验证后,字节流才会进入内存的方法区中存储,所以后面的三个验证阶段都是基于方法区的存储结构进行的。
   2、元数据验证,是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。可能包括的验证如:这个类是否有父类;这个类的父类是否继承了不允许被继承的类;如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法……
   3、字节码验证,主要工作是进行数据流和控制流分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。如果一个类方法体的字节码没有通过字节码验证,那肯定是有问题的;但如果一个方法体通过了字节码验证,也不能说明其一定就是安全的。
   4、符号引用验证,发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在“解析阶段”中发生。验证符号引用中通过字符串描述的权限定名是否能找到对应的类;在指定类中是否存在符合方法字段的描述符及简单名称所描述的方法和字段;符号引用中的类、字段和方法的访问性(private、protected、public、default)是否可被当前类访问

验证阶段对于虚拟机的类加载机制来说,不一定是必要的阶段。如果所运行的全部代码确认是安全的,可以使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载时间。 
5.3 准备

   准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
    public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。

5.4 解析

   解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
   符号引用(Symbolic Reference):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
   直接引用(Direct Reference):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,如果有了直接引用,那么引用的目标必定已经在内存中存在。

5.5 初始化

   类初始化是类加载过程的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。
    初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。

 

有且仅有一下四种情况才对类进行“初始化”:

第二点:调用Class.forName()时会加载类同事会初始化改类。当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。使用Class.forName()静态方法才会导致强制初始化该类

对于静态字段,只有直接定义这个字段的类才会被初始化,因此,通过子类引用父类的静态字段只会初始化父类,子类是否初始化只与java虚拟机的实现以及设置有关。

Round 4:final 
/*一种特殊的情况: 
对于一个final类型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。Java编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此即使程序使用该静态类变量,也不会导致该类的初始化。
*/
class MyTest{
    static{
        System.out.println("static innit");
    }
    static final String constantTest="hello";
}

public class CompileConstantTest {
   public static void main(String[] args) {
    System.out.println(MyTest.constantTest);
   }
}

//只输出hello 

* 被动引用情景2 
* 通过数组引用来引用类,不会触发此类的初始化* 

public class Test {
    public static void main(String[] args) {
        SuperClass []s_list=new SuperClass[10];
    }
}

//没有输出

被动引用情景3 
* 静态常量在编译阶段会被存入调用类的常量池中,本质上并没有引用到定义常量类类,所以自然不会触发定义常量的类的初始化 。

class ConstClass {
    static {
        System.out.println("ConstClass init.");
    }

    public final static String value = "hello";
}
public class Test {
    public static void main(String[] args) {
        // System.out.println(SubClass.value);
        //SuperClass[] s_list = new SuperClass[10];
        System.out.println(ConstClass.value);
    }
}

一个例子引发的血案:

class SingleTon {
    /*位置一*/  //private static SingleTon singleTon = new SingleTon();
    public static int count1=11;
    public static int count2 =22;
	/*位置二*/ //private static SingleTon singleTon = new SingleTon();
    private SingleTon() {
		System.out.println("count1=" +count1+" count2=" +count2);
        count1++;
        count2++;
    }

    public static SingleTon getInstance() {
        return singleTon;
    }
}

public class Test {
    public static void main(String[] args) {
        SingleTon singleTon = SingleTon.getInstance();
        System.out.println("count1=" + singleTon.count1);
        System.out.println("count2=" + singleTon.count2);
    }
}

当语句在位置一时,输出为:

count1=0 count2=0
count1=11
count2=22

当语句在位置二时,输出为:

count1=11 count2=22
count1=12
count2=23

上面的结果可以用类的加载和初始化理论来解释:

1:SingleTon singleTon = SingleTon.getInstance();调用了类SingleTon的静态方法,触发类的初始化 
2:类加载的时候在准备阶段中为类的静态变量分配内存并初始化默认值 singleton=null count1=0,count2=0 
3:类初始化,为类的静态变量赋值和执行静态代码快。singleton赋值为new SingleTon()调用类的构造方法 
4:调用类的构造方法后count=1;count2=1 
5:继续为count1与count2赋值,此时count1没有赋值操作,所有count1为1,但是count2执行赋值操作就变为0

© 著作权归作者所有

曾劲松
粉丝 5
博文 200
码字总数 141434
作品 0
武汉
私信 提问
热修复与插件化基础——dex与class

一、dex/class浅析 1、class与dex对比 2、生成class与dex文件的指令 生成并运行class文件对于我们而言实在太熟悉了,这里只演示dex文件的生成与运行。 以 Hello World 为例: 1)生成dex文件...

CSDN_LQR
2018/05/13
0
0
Composite模式(组合设计模式)

Composite 设计模式? 在计算机的文件系统中,有“文件夹”的概念(在有些操作系统(Linux操作系统)中,也称为“目录”)。文件夹里面既可以放入文件,也可以放入其他文件夹(子文件夹)。在...

---dgw博客
2018/10/23
0
0
OC 知识:Foundation 框架及相关类详尽总结

本文用来介绍Foundation框架的相关知识,以及Foundation框架所提供类的相关知识总结。 1. 框架介绍 框架是由很多类、方法、函数和文档按照一定的逻辑组织起来的集合,以使开发程序变得更加容...

久依
02/21
0
0
简单易用的Tensorflow项目模版

为了有效的使用深度学习模型,一个结构化的和面向对象的文件结构能够有效的帮助我们增加代码的可用性,帮助我们更快地进入主项目并专注于模型的核心(模型,培训等)。 本文介绍的模版是一个...

洛荷
03/23
0
0
baksmali和smali源码分析(六)

smali框架源码主要是对于baksmali的一个逆向过程,也就是其编译过程。本身包的文件很少,也就是13个java文件 但是里面有几个有antlr3 和 jflex生成的词法分析器和解释器文件 smaliParser.ja...

sunzeduo
2014/08/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
39分钟前
2
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
今天
3
0
前端——使用base64编码在页面嵌入图片

因为页面中插入一个图片都要写明图片的路径——相对路径或者绝对路径。而除了具体的网站图片的图片地址,如果是在自己电脑文件夹里的图片,当我们的HTML文件在别人电脑上打开的时候图片则由于...

被毒打的程序猿
今天
2
0
Flutter 系列之Dart语言概述

Dart语言与其他语言究竟有什么不同呢?在已有的编程语言经验的基础上,我们该如何快速上手呢?本篇文章从编程语言中最重要的组成部分,也就是基础语法与类型变量出发,一起来学习Dart吧 一、...

過愙
今天
2
0
rime设置为默认简体

转载 https://github.com/ModerRAS/ModerRAS.github.io/blob/master/_posts/2018-11-07-rime%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%BB%98%E8%AE%A4%E7%AE%80%E4%BD%93.md 写在开始 我的Arch Linux上......

zhenruyan
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部