java class 属性 原理_Class文件结构、反射的实现原理与使用

参考:

https://blog.csdn.net/IT_GJW/article/details/80447947

https://zhuanlan.zhihu.com/p/24789506

Class文件在JVM底层的实现

索引:

9cabc9fef80b

Class文件.png

Class文件是一组以8位字节为基础单位的二进制流,

各个数据项目按严格的顺序紧凑的排列在Class文件中,

里面的信息主要描述以下信息:

1. 魔数和版本号

1.1. 魔数:0xCAFEBABE,确定这个文件是否为一个能被虚拟机接收的Class文件

1.2. Class文件的版本号:第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件。

2. 常量池

主要存放字面量(Literal)和符号引用(references)

字面量:文本字符串、final 类型的常量值 等

符号引用:

a. 类和接口的全限定名

b. 字段描述和描述符

c. 方法的名称和描述

Java代码在进行Javac编译时,并不像C和C++那样有“连接”的步骤,而是在虚拟机加载Class文件的时候进行动态连接。

也就是说,

在Class文件中不会保存各个方法、字段的最终内存布局信息,

当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。

JDK1.7中,总共有14种类型的常量,每种常量都是表类型的数据项。

这14种表都有一个共同的特点,就是表开始的第一位是一个u1类型的标志位,代表当前常量属于哪种常量类型。

14种常量类型代表的具体含义见下表

类型

标志

描述

Constant_Utf8_info

1

UTF-编码的字符串

Constant_Integer_info

3

整型字面量

Constant_Float_info

4

浮点型字面量

Constant_Long_info

5

长整型字面量

Constant_Double_info

6

双精度浮点型字面量

Constant_Class_info

7

类或接口的符号引用

Constant_String_info

8

字符串类型字面量

Constant_Fieldref_info

9

字段的符号引用

Constant_Methodref_info

10

类中方法的符号引用

Constant_InterfaceMethodref_info

11

接口中方法的符号引用

Constant_NameAndType_info

12

字段或方法的部分符号引用

Constant_MethodHandle_info

15

表示方法句柄

Constant_MethodType_info

16

标识方法类型

Constant_InvodeDynamic_info

18

表示一个动态方法调用点

可以通过用命令javap -verbose TestClass.class命令查看class文件的字节码内容,如

9cabc9fef80b

TestClass

例:Constant_Methodref_info的结构

tag

index

index

u1

u2

u2

标志位,值如上表,10

指向生命方法的类描述符Constant_Class_info的索引项

指向名称及类型描述符Constant_NameAndType_info的索引项

10

例:0x0004

例:0x000f

例:Constant_Class_info的结构

tag

index

u1

u2

标志位,值如上表,7

指向全限定名常量项的索引项

7

例:0x0011

例:Constant_Utf8_info的结构

tag

length

bytes

u1

u2

u1

标志位,值如上表,1

UTF-8编码的字符串占用的字节数

长度为length的UTF-8编码的字符串

1

例:java/lang/Object

3. 当前类的访问标志:

常量池结束之后,紧接着的两个字节代表访问标志,这个标志用于识别一些类或者接口层次的访问信息

a.这个Class是类还是接口

b.这个Class是否是public 等类型

c.这个Class是否是abstract ,是否被声明为final 等标志

4. 类索引、父类索引和接口索引集合

a.类索引:确定这个类的全限定名

b.父类索引:确定父类的全限定名

c.接口索引集合:描述这个类实现了哪些接口,它是一组u2类型的数据的集合,集合中的第一项是接口计数器,表示索引表的容量。如果一个类没有实现任何接口,则该计数器值为0。

5. 字段表集合(Fileds)

用于描述接口或者类中声明的变量。

包括信息有

类型

名称

数量

说明

u2

access_flags

1

字段作用域(public,private等修饰符)

是实例变量还是类变量(static)

可变性 (final)

并发可见性(volatile)

可否被序列化(transient)等信息

u2

name_index

1

对常量池的引用

代表字段的简单名称

u2

descriptor_index

1

对常量池的引用

代表字段的描述符

描述字段的数据类型

u2

attribute_count

1

计数器

如果其值为0:字段没有额外信息

如果其值不为0:则attribute_count后面会紧跟着attribute_count个attribute数据项。

attribute_info

attributes

attributes_count

6. 方法表集合:

包括

类型

名称

数量

说明

u2

access_flags

1

访问标志

u2

name_index

1

名称索引

u2

descriptor_index

1

描述符索引

u2

attributes_count

1

属性表集合

方法里的Java代码经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性表中。属性表作为class文件格式中最具有扩展性的一种数据项目,将在随后介绍。

attribute_info

attributes

attributes_count

7. 其他:包括属性表集合、Code 属性(指令) 等。

属性表(attribute_info)在前面的讲解中已经出现过多次了,字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。

与Class文件中其他的数据项目要求严格的顺序、长度和内容不同,

**属性表集合的限制稍微宽松了一些,不再要求各个属性表具有严格的顺序。

为了能够正确解析Class文件,《Java虚拟机规范(Java SE 7)》中,预定义了21项属性表。下文将对一些常用的属性表进行讲解。

属性名称

使用位置

含义

Code

方法表

Java代码编译成的字节码指令

ConstantValue

字段表

final关键字定义的常量值

Deprecated

类、方法表、字段表

被声明为deprecated的方法和字段

Exceptions

方法表

方法抛出的异常

EnclosingMethod

类文件

仅当一个类为局部类或者匿名类时才能拥有这个属性

这个属性用于标识这个类所在的外围方法

InnerClasses

类文件

内部类列表

LineNumberTable

Code属性

Java源码的行号与字节码指令的对应关系

LineVariableTable

Code属性

方法的局部变量描述

StackMapTable

Code属性

Signature

类、方法表、字段表

SourceFile

类文件

记录源文件名称

SourceDebugExtension

类文件

Synthetic

类、方法表、字段表

LocalVariableTypeTable

RuntimeVisibleAnnotations

类、方法表、字段表

RuntimeInvisibleAnnotations

类、方法表、字段表

RuntimeVisibleParameter

方法表

RuntimeInvisibleParameter

方法表

AnnotationDefault

方法表

BootstrapMethod

类文件

Code属性

Code属性存储编译后的字节码指令,它出现在方法表的属性集合中,

但并非所有方法都必须存在这个属性,譬如抽象方法就不存在Code属性。

Code属性的结构如下表所示

类型

名称

数量

u2

attribute_name_index

1

指向CONSTANT_Utf8_info型常量的索引

值固定为“Code”

代表该属性的属性名称

attribute_length指示了属性值的长度

u4

attribute_length

1

u2

max_stack

1

操作数栈的最大值

在方法执行的任意时刻

操作数栈都不会超过这个深度

虚拟机运行时需要根据这个值来分配栈帧中操作栈深度

u2

max_local

1

局部变量表所需的存储空间

它的单位是Slot

u4

code_length

1

字节码长度

u1

code

code_length

存储字节码指令的一系列字节流

每个指令就是一个u1类型的单字节

u1类型的取值范围是0x00~0xFF

一共可以表达256条指令

u2

exception_table_length

1

exception_info

exception_table

exception_table_length

u2

attributes_count

1

attribute_info

attributes

attributes_count

可以通过用命令javap -verbose TestClass.class命令查看一个class文件中方法的Code属性,如

9cabc9fef80b

TestClass

反射

Java反射机制就是在运行状态中,

对于任意一个类,都能够知道这个类的属性和方法。

对于任意一个对象能够调用它的任意一个属性和方法。

这种动态获取的信息和动态调用对象的方法的功能称为Java语言的反射机制

反射机制就是通过Class类实现的。

在Java中,Object 类是所有类的根类,而Class类就是描述Java类的类。

在Java中,每一个class都有一个相应的Class对象,

在将Java源码编译成.class文件中就会生成一个Class对象,

Class对象表示这个类的类型信息,你也可以理解成Class是类的类型

注意:因为Class类也是类,所以Object也包括Class类

我们创建对象一般是通过new关键字创建,

但是new是静态加载类,一旦找不到类就会编译不通过。

但是通过反射机制创建对象一旦找不到类则抛出java.lang.ClassNotFoundException异常。

Class对象的常用方法:

Constructor[] getConstructors():返回此Class对象所表示的类的所有public构造方法

Method[] getMethods():返回此Class对象所表示的类的所有public方法

Method[] getDeclaredMethods():返回此Class对象所表示的类的所有方法,与方法的访问级别无关

Field[] getFields():返回此Class对象所表示的类的所有public属性

Field[] getDecalaredDields():返回此Class对象所表示的类的所有属性,与属性访问级别无关

Object get(Object obj):得到引用类型属性值

void set(Object obj,Object val):将obj对象的该属性设置成val值。针对引用类型赋值

Object invoke(Object obj,Object args):调用类的方法,obj是执行该方法的对象,args是执行该方法时传入该方法的参数

通过Class类得到指定的对象:

public interface Office {

/**

* 描述

*/

public void describe();

}

public class Word implements Office {

@Override

public void describe() {

System.out.println("大家好,我是Word");

}

}

public class Test {

public static void main(String[] string) throws ClassNotFoundException {

try {

@SuppressWarnings("rawtypes")

//传入接口实现类的路径

Class office = Class.forName("com.Word");

try {

//创建该类的对象

Office word = (Office)office.newInstance();

//调用方法

word.describe();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

}

通过对象得到Class类

public class Test {

@SuppressWarnings("rawtypes")

public static void main(String[] string) throws ClassNotFoundException {

Word word = new Word();

Class word1 = word.getClass(); // 通过对象的getClass()方法获取Class

Class word2 = Word.class; // 通过类.class获取Class

Class word3 = Class.forName("com.Word"); // 通过路径获取Class

System.out.println(word1 == word2);

System.out.println(word2 == word3);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值