转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73472553
Java编译后的class文件格式如下定义:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
关于class文件中的各项说明,可以参考虚拟机规范或者底下参考文献的两篇文章,已经总结的非常的详细,本文对其中重要的几项作一个总结。
根据虚拟机规范,常量池项目类型有以下分类
规范规定的总是很抽象的,下面举几个例子就好懂。
类型 | 常量池索引 | 值 | 备注 |
---|---|---|---|
CONSTANT_Integer | 1 | 10 | 字面量:基础数据类型 |
类型 | 常量池索引 | 值 | 备注 |
---|---|---|---|
CONSTANT_String | 1 | #2 | 符号引用 |
CONSTANT_Utf8 | 2 | test | 字面量:文字字符串 |
类型 | 常量池索引 | 值 | 备注 |
---|---|---|---|
CONSTANT_Class | 1 | #1 | 符号引用:类全限定名 |
CONSTANT_Utf8 | 2 | Ljava/util/Date; | 字面量:文字字符串 |
在类中定义了field 字段,并且在类的其他地方(如方法中)使用到它,这是会用到Field常量,如以下代码
Class Test {
String name;
public void setName(String a) {
// 这里会使用Field常量
name = a; // 翻译成指令为: getfield #5
}
}
类型 | 常量池索引 | 值 | 备注 |
---|---|---|---|
CONSTANT_Class | 1 | #2 | 符号引用 |
CONSTANT_Utf8 | 2 | package/path/Test | 字面量:文字字符串 |
CONSTANT_Utf8 | 3 | name | 字面量:文字字符串 |
CONSTANT_Utf8 | 4 | Ljava/lang/String; | 字面量:文字字符串 |
CONSTANT_Fieldref | 5 | #1.#6 | 符号引用 |
CONSTANT_NameAndType | 6 | #3.#4 | 符号引用 |
假如我们只定义了方法,但是这些方法没有在类总的其他地方被用到(如2.3中的代码),则这些方法引用信息并不会放到常量中。下面代码则可以使用到Method常量。
Class Test {
String name;
public String getName() {
return name;
}
public do() {
// 这里会使用Method常量
setName("test"); // 翻译成指令为: invokevirtual #5
}
}
类型 | 常量池索引 | 值 | 备注 |
---|---|---|---|
CONSTANT_Class | 1 | #2 | 符号引用 |
CONSTANT_Utf8 | 2 | package/path/Test | 字面量:文字字符串 |
CONSTANT_Utf8 | 3 | getName | 字面量:文字字符串 |
CONSTANT_Utf8 | 4 | ()java/lang/String; | 字面量:文字字符串 |
CONSTANT_Methodref | 5 | #1.#6 | 符号引用 |
CONSTANT_NameAndType | 6 | #3.#4 | 符号引用 |
首先需要说明的是,属性不仅在class文件结构中使用,在field_info和method_info中也使用。
在字段域出现的属性有ConstantValue(final常量)、Deprecated(被禁用的指示符)、Synthetic(编译器产生的指示符)。
方法域出现的属性有Code、Deprecated、Exceptions、Synthetic 。
(1)Code
Code类型的属性表(attribute_info)可以说是class文件中最为重要的部分,因为它包含的是JVM可以运行的机器码指令,JVM能够运行这个类,就是从这个属性中取出机器码的。
Code属性表包含:
(1)常量池的CONSTANT_Fieldref
和跟字段域field_info
的区别?
CONSTANT_Fieldref,这只是一个字段的符号引用,通常作为字节码指令(opcode)getfield/getstatic/putfield/putstatic的操作数使用。
field_info是类中定义字段本身信息的描述,关于类加载过程中field部分的解析,以及其作为InstanceKlass类中成员属性的layout。
对于getfield/getstatic/putfield/putstatic四种与Field Reference相关的指令,在字段决议——resolve_field时,会有两者的协同使用——根据符号引用去_field中查找真正的字段。符号引用只是一个对应字段的一个字符串,符号引用经第一次解析(解析结果存入ConstantPoolCache),就会变成直接引用——field_offset。
对于CONSTANT_Methodref
和方法域method_info
也类似。
(2)如何防止class文件被劫持?
主要通过类加载双亲委派模型实现。
jvm首先会检查该类是否已经被加载,若没有被加载,则会委托父加载器进行装载,只有当父加载器无法加载时,才会调用自身的findClass()方法进行加载。这样避免了子加载器加载一些试图冒名顶替可信任类的不可靠类,也不会让子加载器去实现父加载器实现的加载工作。
并且jvm规定只有运行时包(同一个类加载器加载的、属于同一个包的多个类型集合),才能访问同一包内的类(和其子类)的protected成员。
(3)如何防止class文件反编译?
可以参考下这篇博客:http://blog.csdn.net/yuxiaohui78/article/details/8247096
http://blog.csdn.net/luanlouis/article/details/39892027
http://www.cnblogs.com/iceAeterNa/p/4874197.html
转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73472553