2.1 ASM-类-结构

ASM-类-结构

本章介绍了使用ASM core的API,生成编译后的class和转换编译后的class。 首先展示了编译后的class文件,然后介绍相应的ASM接口、组件和工具类来生成和转换这些class,并且附带了很多说明性的事例。 方法、注释、和Generics(TODO 需要找一个合理的翻译)会在接下来的章节介绍。

2.1 结构

2.1.1 概览

编译类的整体结构是十分简单的。事实上,不同于本地编译的应用程序,一个编译后的class保留了结构化的信息和源码中几乎所有的符号(symbol)引用。

事实上,一个编译的class包含以下部分:

  1. 描述修饰符的部分(例如public、private),类名、父类、实现的接口集合和类的注解(annotation)。
  2. class声明的属性部分。每一个部分都包括修饰符、属性名、属性类型和属性的注解。
  3. class声明的构造函数和方法部分。每一个部分都包括修饰符、方法名、返回类型、参数类型和方法的注解。它还包含了方法编译后的字节码信息,由一系列的字节码指令集组成。

源码和编译后的class之间有一些不同之处:

  1. 一个编译后的class仅仅描述一个类,然后一个源码文件可以包含几个类的声明。例如一个源码文件可以声明一个主类和一个该类的内部类,在编译后会成为两个class文件:一个表示该主类,另一个表示内部类。 然而该主类文件中包含了对内部类的引用,并且内部类定义的内部方法包含了对他们封闭方法的引用。(TODO inner classes defined inside methods contain a reference to their enclosing method.)
  2. 一个编译后的class不包含注释,但是包含了类、属性、方法和代码这些元素所关联的附加属性。自从Java 5中引入了注解,起到了相同的作用后,附加属性就几乎不再被使用了。
  3. 一个编译后的class不包含package声明和import声明部分,因此所有的类型名称必须是全路径的。

另一个非常重要的结构不同是一个编译后的class包含了常量池部分。常量池是一个数组,包括该类中所有出现的数字型、字符串和类型的常量。 这些常量在常量池中只会被定义一次,在class文件的其他部分使用数组的索引来关联该常量值。 幸运的是,ASM隐藏了所有常量池相关的细节,因此你就不需要关心常量池了。表格2.1总结了编译后class的整体结构,确切的结构可以在Java虚拟机规范第四章中找到。

表格2.1 :编译后的class结构(*表示0个或者多个)

类结构

修饰符,类名,父类,接口

常量池:数值、字符串、类型常量

源文件名称(可选)

封闭的方法引用

注解*

Attribute*

内部类*

名称

类属性*

修饰符、名称、类型

注解

Attribute

方法*

修饰符、名称、返回值类型和参数类型

注解

Attribute

编译后的code

另一个重要的区别就是Java类型的表示方式在源码和编译后的class中不同。下一个部分将介绍它们如何在编译后的class中表示。

2.1.2 内部名

在许多情况下,类型被约束成一个类或者接口。 例如一个类的父类、一个类所实现的接口、被方法抛出的异常都不能是基础类型或者数组,一定是类或者接口。 这些类型在编译后class中使用内部名(internal name)表示。 这些内部名称是一个全路径类型,只不过分隔符由英文逗点换成了反斜线。 例如String类型的内部名是java/lang/String

2.1.3 类型描述符

内部名仅仅用于被约束成类或者接口的类型。在所有其他情况下,比如字段类型,Java中的类型在编译后的class中使用类型描述符表示。 类型描述符表格(表格 2.2)如下.

表格2.2 :Java类型描述符

Java类型

类型描述符

boolean

Z

char

C

byte

B

short

S

int

I

float

F

long

J

double

D

void

V

Object

Ljava/lang/Object;

int[]

[I

Object[][]

[[Ljava/lang/Object;

基础类型的描述符使用单个字符:Z表示boolean,C表示char,B表示byte,S表示short,I表示int,F表示float,J表示long,D表示double。 类的类型描述符就是内部名加上前缀L和后缀分号(;)。例如String的类型描述符是Ljava/lang/String;。 数组类型的描述符就是前缀[加上数组元素的类型描述符。

2.1.4 方法描述符

一个方法描述符是由一系列类型描述符组成的一个字符串,包括方法的参数类型和返回值类型。 方法描述符使用小括号开始,小括号内部是方法入参的类型描述符按照顺序拼接的字符串,在加上返回值的类型描述符组成,返回值是void的时候,使用V。 方法的描述符不包含方法名和参数名。

表格2.3 :一些方法描述符示例

源码中的方法

方法描述符

void m(int i, float f)

(IF)V

int m(Object o)

(Ljava/lang/Object;)I

int[] m(int i, String s)

(ILjava/lang/String;)[I

Object m(int[] i)

([I)Ljava/lang/Object;

只要你理解了类型描述符的转换方法,也很容易理解方法描述符。例如(I)I描述的是一个入参是int类型,返回值是int的方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

1643 线段覆盖 3

1643 线段覆盖 3 时间限制: 2 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个...

28580
来自专栏PHP在线

PHP数组操作汇总

对于Web编程来说,最重要的就是存取和读写数据了。存储方式可能有很多种,可以是字符串、数组、文件的形式等。数组,可以说是PHP的数据应用中较重要的一种方式。PH...

33340
来自专栏逆向技术

C语言第四讲,typedef 关键字,以及作用域

        C语言第四讲,typedef 关键字,以及作用域 一丶typedef关键字   在C语言中,有typedef 关键字,这个关键字的作用就是允许你...

29850
来自专栏日常分享

Spring 学习笔记(七)—— 切入点表达式

  语法结构:   execution(   方法修饰符  方法返回值  方法所属类 匹配方法名 (  方法中的形参表 )  方法申明抛出的异常  )

6410
来自专栏吾爱乐享

java学习之数组元素排序,冒泡排序和选择排序

12840
来自专栏HTML5学堂

break以及continue语句

HTML5学堂:ECMAScript当中存在着break以及continue两种语句,这两种语句通常用于循环语句以及分支语句当中。那么,break以及conti...

32040
来自专栏蜕变

Python 数据类型

Python主要数据类型包括list(列表)、tuple(元组)、dict(字典)和set(集合)等对象,下面逐一介绍这些Python数据类型。

10100
来自专栏noteless

[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式

前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明

19920
来自专栏转载gongluck的CSDN博客

C++拷贝构造函数(深拷贝,浅拷贝)

对于普通类型的对象来说,它们之间的复制是很简单的,例如:   int a=88;   int b=a;   而类对象与普通对象不同,类对象...

31070
来自专栏程序员互动联盟

【编程基础】C语言指针、引用和取值

什么是指针?什么是内存地址?什么叫做指针的取值?指针是一个存储计算机内存地址的变量。“引用”表示计算机内存地址。从指针指向的内存读取数据称作指针的取值。指针可以...

39470

扫码关注云+社区

领取腾讯云代金券