如何查看java java 执行class文件件

在MyEclipse中如何用查看引用的jar包中的class文件的java源代码?
在MyEclipse中如何用查看引用的jar包中的class文件的java源代码?
09-09-02 &
看jar包里的信息 myeclipse如果是最新的话,spring和hibernate各种版本都有,spring1.2和2.0 hibernate2.0,3.1的都有0 回答者: zidane_xue - 经理 四级   9-5 16:51
请登录后再发表评论!
如果你知道jar包中的类名,可以按ctrl键不放 +鼠标左键点击该类名,就会 跟踪到你想要看的类名。
请登录后再发表评论!
要看jar包里的java源文件,只能去网上下载源文件,在jar包里是看不到源代码的。
请登录后再发表评论!如何通过反编译工具与插件 查看java *.class 文件源码 - CSDN博客
如何通过反编译工具与插件 查看java *.class 文件源码
【java 反编译】:开发了反编译工具,可以方便查看*.class 文件源码。下面介绍几种查看源码的方式:工具&插件
JD-GUI &是显示java 源代码 *.class文件的&图形界面工具。可以在这里下载:
下载下来后,打开该工具,直接将jar包 拖拽到 显示窗口即可查看源代码。
举例:通过 jd-gui.exe 工具 查看&xUtils-2.6.14.jar 包 源码:
但是,一般开发用的是IDE(Integrated Development Environment&集成开发环境) 比如 Eclipse,android studio。IDE一般插件都很丰富,方便提高开发效率。如果有IDE还是用图形界面工具查看jar包而不用插件的话,这是比较醉的,由于之前不知道用插件,醉了很久才用!!没文化真可怕啊!插指算了算:以后要“多探讨,多接触,增加知识的广度!多研究,多细学,增加知识深度!”
。下面概要介绍 Eclipse,android studio 下 安装插件,查看 jar包 *.class 文件。
2、JD-Eclipse&
(1)& 插件。下载后,打开Eclipse,选择:Help--&Install New Software...--&add --archive... &按步骤操作即可:
安装后,重启Eclipse,应该可以查看 *.class文件的代码。
ADD and Archive...
选择要安装的插件:
&选中后,一步一步next,之后重启Eclipse。
效果如下:
3、JD-IntelliJ
JD-Intellij 是&IntelliJ IDEA 的插件,安装了这个插件,可以方便查看 jar包源码。
以android studio为例: 可以离线安装也可以在线安装。
(1)、离线安装步骤:
& &a、下载jd-intellij-0.6.zip ;
& &b、Android studio &File-&Settings..-&Plugins --&install plugin from disk..导入下载的插件&
& &c、重启android studio&
(2)、在线安装
Android studio &File-&Settings..-&Plugins--&Browse repositores..搜索JD-Intellij
重启studio后,查看 jar包的class文件 源码:
参考资料:
有所不足、多多指正!
本文已收录于以下专栏:
相关文章推荐
查看java字节码 
1 javac –verbose查看运行类是加载了那些jar文件
HelloWorld演示:
javac –verbose HelloWorld.java
可以看到虚拟...
查看class文件的源码! 
我的开发环境:eclipse3.4,MyEclipse6.5; 
某些时候,你使用了第三方的jar 包的函数,发现存在某方面问题,想查看函数实现是怎样 
在开发过程中经常会遇到这样的情况,自己写个类继承某个jar包中的类,想在用eclipse开发程序的过程中,看看jar包中对应的类中的方法,但jar包中的都是class文件,往往按下ctrl+鼠标,想看...
他的最新文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)实例分析Java Class的文件结构
字体:[ ] 类型:转载 时间:
今天把之前在Evernote中的笔记重新整理了一下,发上来供对java class 文件结构的有兴趣的同学参考一下
学习Java的朋友应该都知道Java从刚开始的时候就打着平台无关性的旗号,说“一次编写,到处运行”,其实说到无关性,Java平台还有另外一个无关 性那就是语言无关性,要实现语言无关性,那么Java体系中的class的文件结构或者说是字节码就显得相当重要了,其实Java从刚开始的时候就有两套 规范,一个是Java语言规范,另外一个是Java虚拟机规范,Java语言规范只是规定了Java语言相关的约束以及规则,而虚拟机规范则才是真正从跨 平台的角度去设计的。今天我们就以一个实际的例子来看看,到底Java中一个Class文件对应的字节码应该是什么样子。 这篇文章将首先总体上阐述一下Class到底由哪些内容构成,然后再用一个实际的Java类入手去分析class的文件结构。
在继续之前,我们首先需要明确如下几点:
1)Class文件是有8个字节为基础的字节流构成的,这些字节流之间都严格按照规定的顺序排列,并且字节之间不存在任何空隙,对于超过8个字节的数据,将按 照Big-Endian的顺序存储的,也就是说高位字节存储在低的地址上面,而低位字节存储到高地址上面,其实这也是class文件要跨平台的关键,因为 PowerPC架构的处理采用Big-Endian的存储顺序,而x86系列的处理器则采用Little-Endian的存储顺序,因此为了Class文 件在各中处理器架构下保持统一的存储顺序,虚拟机规范必须对起进行统一。
2) Class文件结构采用类似C语言的结构体来存储数据的,主要有两类数据项,无符号数和表,无符号数用来表述数字,索引引用以及字符串等,比如 u1,u2,u4,u8分别代表1个字节,2个字节,4个字节,8个字节的无符号数,而表是有多个无符号数以及其它的表组成的复合结构。可能大家看到这里 对无符号数和表到底是上面也不是很清楚,不过不要紧,等下面实例的时候,我会再以实例来解释。
明确了上面的两点以后,我们接下来后来看看Class文件中按照严格的顺序排列的字节流都具体包含些什么数据:
(上图来自The Java Virtual Machine Specification Java SE 7 Edition)
在看上图的时候,有一点我们需要注意,比如cp_info,cp_info表示常量池,上图中用 constant_pool[constant_pool_count-1]的方式来表示常量池有constant_pool_count-1个常量,它 这里是采用数组的表现形式,但是大家不要误以为所有的常量池的常量长度都是一样的,其实这个地方只是为了方便描述采用了数组的方式,但是这里并不像编程语 言那里,一个int型的数组,每个int长度都一样。明确了这一点以后,我们在回过头来看看上图中每一项都具体代表了什么含义。
1)u4 magic 表示魔数,并且魔数占用了4个字节,魔数到底是做什么的呢?它其实就是表示一下这个文件的类型是一个Class文件,而不是一张JPG图片,或者AVI的电影。而Class文件对应的魔数是0xCAFEBABE.
2)u2 minor_version 表示Class文件的次版本号,并且此版本号是u2类型的无符号数表示。
3) u2 major_version 表示Class文件的主版本号,并且主版本号是u2类型的无符号数表示。major_version和minor_version主要用来表示当前的虚拟 机是否接受当前这种版本的Class文件。不同版本的Java编译器编译的Class文件对应的版本是不一样的。高版本的虚拟机支持低版本的编译器编译的 Class文件结构。比如Java SE 6.0对应的虚拟机支持Java SE 5.0的编译器编译的Class文件结构,反之则不行。
4) u2 constant_pool_count 表示常量池的数量。这里我们需要重点来说一下常量池是什么东西,请大家不要与Jvm内存模型中的运行时常量池混淆了,Class文件中常量池主要存储了字 面量以及符号引用,其中字面量主要包括字符串,final常量的值或者某个属性的初始值等等,而符号引用主要存储类和接口的全限定名称,字段的名称以及描 述符,方法的名称以及描述符,这里名称可能大家都容易理解,至于描述符的概念,放到下面说字段表以及方法表的时候再说。另外大家都知道Jvm的内存模型中 有堆,栈,方法区,程序计数器构成,而方法区中又存在一块区域叫运行时常量池,运行时常量池中存放的东西其实也就是编译器长生的各种字面量以及符号引用, 只不过运行时常量池具有动态性,它可以在运行的时候向其中增加其它的常量进去,最具代表性的就是String的intern方法。
5)cp_info 表示常量池,这里面就存在了上面说的各种各样的字面量和符号引用。放到常量池的中数据项在The Java Virtual Machine Specification Java SE 7 Edition 中一共有14个常量,每一种常量都是一个表,并且每种常量都用一个公共的部分tag来表示是哪种类型的常量。
下面分别简单描述一下具体细节等到后面的实例 中我们再细化。CONSTANT_Utf8_info tag标志位为1, UTF-8编码的字符串CONSTANT_Integer_info tag标志位为3, 整形字面量CONSTANT_Float_info tag标志位为4, 浮点型字面量CONSTANT_Long_info tag标志位为5, 长整形字面量CONSTANT_Double_info tag标志位为6, 双精度字面量CONSTANT_Class_info tag标志位为7, 类或接口的符号引用CONSTANT_String_info tag标志位为8,字符串类型的字面量CONSTANT_Fieldref_info tag标志位为9, 字段的符号引用CONSTANT_Methodref_info tag标志位为10,类中方法的符号引用CONSTANT_InterfaceMethodref_info tag标志位为11, 接口中方法的符号引用CONSTANT_NameAndType_info tag 标志位为12,字段和方法的名称以及类型的符号引用
6) u2 access_flags 表示类或者接口的访问信息,具体如下图所示:
7)u2 this_class 表示类的常量池索引,指向常量池中CONSTANT_Class_info的常量
8)u2 super_class 表示超类的索引,指向常量池中CONSTANT_Class_info的常量
9)u2 interface_counts 表示接口的数量
10)u2 interface[interface_counts]表示接口表,它里面每一项都指向常量池中CONSTANT_Class_info常量
11)u2 fields_count 表示类的实例变量和类变量的数量
12) field_info fields[fields_count]表示字段表的信息,其中字段表的结构如下图所示:
上图中access_flags表示字段的访问表示,比如字段是public,private,protect 等,name_index表示字段名 称,指向常量池中类型是CONSTANT_UTF8_info的常量,descriptor_index表示字段的描述符,它也指向常量池中类型为 CONSTANT_UTF8_info的常量,attributes_count表示字段表中的属性表的数量,而属性表是则是一种用与描述字段,方法以及 类的属性的可扩展的结构,不同版本的Java虚拟机所支持的属性表的数量是不同的。
13) u2 methods_count表示方法表的数量
14)method_info 表示方法表,方法表的具体结构如下图所示:
其中access_flags表示方法的访问表示,name_index表示名称的索引,descriptor_index表示方法的描述 符,attributes_count以及attribute_info类似字段表中的属性表,只不过字段表和方法表中属性表中的属性是不同的,比如方法 表中就Code属性,表示方法的代码,而字段表中就没有Code属性。其中具体Class中到底有多少种属性,等到Class文件结构中的属性表的时候再 说说。
15) attribute_count表示属性表的数量,说到属性表,我们需要明确以下几点:属性表存在于Class文件结构的最后,字段表,方法表以及Code属性中,也就是说属性表中也可以存在属性表属性表的长度是不固定的,不同的属性,属性表的长度是不同的
上面说完了Class文件结构中每一项的构成以后,我们以一个实际的例子来解释以下上面所说的内容。 代码如下:package com.ejushang.TestC public class TestClass implements Super{ private static final int staticVar = 0; private int instanceVar=0; public int instanceMethod(int param){ return param+1; } } interface Super{ }通过jdk1.6.0_37的javac 编译后的TestClass.java对应的TestClass.class的二进制结构如下图所示:
下面我们就根据前面所说的Class的文件结构来解析以下上图中字节流。
1)魔数从Class的文件结构我们知道,刚开始的4个字节是魔数,上图中从地址000003h的内容就是魔数,从上图可知Class的文件的魔数是0xCAFEBABE。
2)主次版本号接下来的4个字节是主次版本号,有上图可知从000005h对应的是0×0000,因此Class的minor_version 为0×0000,从000007h对应的内容为0×0032,因此Class文件的major_version版本为 0×0032,这正好就是jdk1.6.0不带target参数编译后的Class对应的主次版本。
3)常量池的数量接下来的2个字节从000009h表示常量池的数量,由上图可以知道其值为0×0018,十进制为24个,但是对于常量池的数量 需要明确一点,常量池的数量是constant_pool_count-1,为什么减一,是因为索引0表示class中的数据项不引用任何常量池中的常 量。
4)常量池我们上面说了常量池中有不同类型的常量,下面就来看看TestClass.class的第一个常量,我们知道每个常量都有一个u1类型的tag标识来表示 常量的类型,上图中0000000ah处的内容为0x0A,转换成二级制是10,有上面的关于常量类型的描述可知tag为10的常量是Constant_Methodref_info,而Constant_Methodref_info的结够如下图所示:
其中class_index指向常量池中类型为CONSTANT_Class_info的常量,从TestClass的二进制文件结构中可以看出 class_index的值为0×0004(地址为0000000bh-0000000ch),也就是说指向第四个常量。
name_and_type_index指向常量池中类型为CONSTANT_NameAndType_info常量。从上图可以看出name_and_type_index的值为0×0013,表示指向常量池中的第19个常量。
接下来又可以通过同样的方法来找到常量池中的所有常量。不过JDK提供了一个方便的工具可以让我们查看常量池中所包含的常量。通过javap -verbose TestClass 即可得到所有常量池中的常量,截图如下:
从上图我们可以清楚的看到,TestClass中常量池有24个常量,不要忘记了第0个常量,因为第0个常量被用来表示 Class中的数据项不引用任何常量池中的常量。从上面的分析中我们得知TestClass的第一个常量表示方法,其中class_index指向的第四 个常量为java/lang/Object,name_and_type_index指向的第19个常量值为&init&:()V,从这里可 以看出第一个表示方法的常量表示的是java编译器生成的实例构造器方法。通过同样的方法可以分析常量池的其它常量。OK,分析完常量池,我们接下来再分 析下access_flags。5)u2 access_flags 表示类或者接口方面的访问信息,比如Class表示的是类还是接口,是否为public,static,final等。具体访问标示的含义之前已经说过 了,下面我们就来看看TestClass的访问标示。Class的访问标示是从0000010dh-0000010e,期值为0×0021,根据前面说的 各种访问标示的标志位,我们可以知道:0×01|0×0020 也即ACC_PUBLIC 和 ACC_SUPER为真,其中ACC_PUBLIC大家好理解,ACC_SUPER是jdk1.2之后编译的类都会带有的标志。
6)u2 this_class 表示类的索引值,用来表示类的全限定名称,类的索引值如下图所示:
从上图可以清楚到看到,类索引值为0×0003,对应常量池的第三个常量,通过javap的结果,我们知道第三个常量为 CONSTANT_Class_info类型的常量,通过它可以知道类的全限定名称为:com/ejushang/TestClass /TestClass
7)u2 super_class 表示当前类的父类的索引值,索引值所指向的常量池中类型为CONSTANT_Class_info的常量,父类的索引值如下图所示,其值为0×0004, 查看常量池的第四个常量,可知TestClass的父类的全限定名称为:java/lang/Object
8)interfaces_count和 interfaces[interfaces_count]表示接口数量以及具体的每一个接口,TestClass的接口数量以及接口如下图所示,其中 0×0001表示接口数量为1,而0×0005表示接口在常量池的索引值,找到常量池的第五个常量,其类型为CONSTANT_Class_info,其 值为:com/ejushang/TestClass/Super
9)fields_count 和 field_info, fields_count表示类中field_info表的数量,而field_info表示类的实例变量和类变量,这里需要注意的是 field_info不包含从父类继承过来的字段,field_info的结构如下图所示:
其中access_flags表示字段的访问标示,比如public,private,protected,static,final等,access_flags的取值如下图所示:
其中name_index 和 descriptor_index都是常量池的索引值,分别表示字段的名称和字段的描述符,字段的名称容易理解,但是字段的描述符如何理解呢?其实在JVM 规范中,对于字段的描述符规定如下图所示:
其中大家需要关注一下上图最后一行,它表示的是对一维数组的描述符,对于String[][]的描述符将是[[ Ljava/lang/String,而对于int[][]的描述符为[[I。接下来的attributes_count以及 attribute_info分别表示属性表的数量以及属性表。下面我们还是以上面的TestClass为例,来看看TestClass的字段表吧。
首先我们来看一下字段的数量,TestClass的字段的数量如下图所示:
从上图中可以看出TestClass有两个字段,查看TestClass的源代码可知,确实也只有两个字段,接下来我们看看第一个字段,我们知道第一个字段应该为private int staticVar,它在Class文件中的二进制表示如下图所示:
其中0x001A表示访问标示,通过查看access_flags表可知,其为ACC_PRIVATE,ACC_STATIC,ACC_FINAL,接下 来0×07分别表示常量池中第6和第7个常量,通过查看常量池可知,其值分别为:staticVar和I,其中staticVar为字 段名称,而I为字段的描述符,通过上面对描述符的解释,I所描述的是int类型的变量,接下来0×0001表示staticVar这个字段表中的属性表的 数量,从上图可以staticVar字段对应的属性表有1个,0×0008表示常量池中的第8个常量,查看常量池可以得知此属性为 ConstantValue属性,而ConstantValue属性的格式如下图所示:
其中attribute_name_index表述属性名的常量池索引,本例中为ConstantValue,而ConstantValue的 attribute_length固定长度为2,而constantValue_index表示常量池中的引用,本例中,其中为0×0009,查看第9个 常量可以知道,它表示一个类型为CONSTANT_Integer_info的常量,其值为0。
上面说完了private static final int staticVar=0,下面我们接着说一下TestClass的private int instanceVar=0,在本例中对instanceVar的二进制表示如下图所示:
其中0×0002表示访问标示为ACC_PRIVATE,0x000A表示字段的名称,它指向常量池中的第10个常量,查看常量池可以知道字段名称为 instanceVar,而0×0007表示字段的描述符,它指向常量池中的第7个常量,查看常量池可以知道第7个常量为I,表示类型为 instanceVar的类型为I,最后0×0000表示属性表的数量为0.
10)methods_count 和 method_info ,其中methods_count表示方法的数量,而method_info表示的方法表,其中方法表的结构如下图所示:
从上图可以看出method_info和field_info的结构是很类似的,方法表的access_flag的所有标志位以及取值如下图所示:
其中name_index和descriptor_index表示的是方法的名称和描述符,他们分别是指向常量池的索引。这里需要结解释一下方法的描述 符,方法的描述符的结构为:(参数列表)返回值,比如public int instanceMethod(int param)的描述符为:(I)I,表示带有一个int类型参数且返回值也为int类型的方法,接下来就是属性数量以及属性表了,方法表和字段表虽然都有 属性数量和属性表,但是他们里面所包含的属性是不同。接下来我们就以TestClass来看一下方法表的二进制表示。首先来看一下方法表数量,截图如下:
从上图可以看出方法表的数量为0×0002表示有两个方法,接下来我们来分析第一个方法,我们首先来看一下TestClass的第一个方法的access_flag,name_index,descriptor_index,截图如下:
从上图可以知道access_flags为0×0001,从上面对access_flags标志位的描述,可知方法的access_flags的取值为 ACC_PUBLIC,name_index为0x000B,查看常量池中的第11个常量,知道方法的名称为&init&,0x000C表示 descriptor_index表示常量池中的第12常量,其值为()V,表示&init&方法没有参数和返回值,其实这是编译器自动生成 的实例构造器方法。接下来的0×0001表示&init&方法的方法表有1个属性,属性截图如下:从上图可以看出0x000D对应的常量池中的常量为Code,表示的方法的Code属性,所以到这里大家应该明白方法的那些代码是存储在Class文件方法表中的属性表中的Code属性中。接下来我们在分析一下Code属性,Code属性的结构如下图所示:
其中attribute_name_index指向常量池中值为Code的常量,attribute_length的长度表示Code属性表的长度(这里 需要注意的时候长度不包括attribute_name_index和attribute_length的6个字节的长度)。
max_stack表示最大栈深度,虚拟机在运行时根据这个值来分配栈帧中操作数的深度,而max_locals代表了局部变量表的存储空间。
max_locals的单位为slot,slot是虚拟机为局部变量分配内存的最小单元,在运行时,对于不超过32位类型的数据类型,比如 byte,char,int等占用1个slot,而double和Long这种64位的数据类型则需要分配2个slot,另外max_locals的值并 不是所有局部变量所需要的内存数量之和,因为slot是可以重用的,当局部变量超过了它的作用域以后,局部变量所占用的slot就会被重用。
code_length代表了字节码指令的数量,而code表示的时候字节码指令,从上图可以知道code的类型为u1,一个u1类型的取值为0×00-0xFF,对应的十进制为0-255,目前虚拟机规范已经定义了200多条指令。
exception_table_length以及exception_table分别代表方法对应的异常信息。
attributes_count和attribute_info分别表示了Code属性中的属性数量和属性表,从这里可以看出Class的文件结构中,属性表是很灵活的,它可以存在于Class文件,方法表,字段表以及Code属性中。
接下来我们继续以上面的例子来分析一下,从上面init方法的Code属性的截图中可以看出,属性表的长度为0×,max_stack的 值为0×0002,max_locals的取值为0×0001,code_length的长度为0x0000000A,那么h- h为字节码,接下来exception_table_length的长度为0×0000,而attribute_count的值为 0×57h-h的值为0x000E,它表示常量池中属性的名称,查看常量池得知第14个常量的值为 LineNumberTable,LineNumberTable用于描述java源代码的行号和字节码行号的对应关系,它不是运行时必需的属性,如果通 过-g:none的编译器参数来取消生成这项信息的话,最大的影响就是异常发生的时候,堆栈中不能显示出出错的行号,调试的时候也不能按照源代码来设置断 点,接下来我们再看一下LineNumberTable的结构如下图所示:
其中attribute_name_index上面已经提到过,表示常量池的索引,attribute_length表示属性长度,而start_pc和 line_number分表表示字节码的行号和源代码的行号。本例中LineNumberTable属性的字节流如下图所示:
上面分析完了TestClass的第一个方法,通过同样的方式我们可以分析出TestClass的第二个方法,截图如下:
其中access_flags为0×0001,name_index为0x000F,descriptor_index为0×0010,通过查看常量池可 以知道此方法为public int instanceMethod(int param)方法。通过和上面类似的方法我们可以知道instanceMethod的Code属性为下图所示:
最后我们来分析一下,Class文件的属性,从000199h为Class文件中的属性表,其中0×0011表示属性的名称,查看常量池可以知道属性名称为SourceFile,我们再来看看SourceFile的结构如下图所示:
其中attribute_length为属性的长度,sourcefile_index指向常量池中值为源代码文件名称的常量,在本例中SourceFile属性截图如下:
其中attribute_length为0×表示长度为2个字节,而soucefile_index的值为0×0012,查看常量池的第18个常量可以知道源代码文件的名称为TestClass.java
最后,希望对技术感兴趣的朋友多交流。个人微博:()
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具博客分类:
一般在查找资源时,可根据本地路径和网络路径来定位具体的资源文件。其中本地路径又可分为绝对路径和相对路径。Java在查找资源时的最佳实践是通过相对路径来确定资源,java的类装载器ClassLoader内部包装了相对路径到绝对路径的转化实现。对于任意一个自定义类,可通过其Class类的getResource()方法获取到URL形式的资源绝对路径。
&&&&&&& 在具体的实践过程中,碰到一个小问题。测试用例如下:
工程结构:
Java-learning
&&&&&& |---- src/main/java
&&&&&& |---- src/test/java
&&&&&& |---- src/main/resources/META-INF/spring
&&&&&& |---- src/main/ resources
&&&&&& |---- 引用的jar包和maven的pom.xml等
测试代码包路径:
src/main/java
&&&&&& |---- com.lee.java.learning
&&&&&&&&&&&&& |---- compiler
&&&&&&&&&&&&&&&&&&&& |---- Test.java
&&&&&&&&&&&&& |---- AnClass.java
测试代码:
AnClass.java
view plain
package com.lee.java.&
public class AnClass {&
&&& public static void main(String[] args) {&
view plain
package com.lee.&
import java.net.URL;&
public class Test {&
&&& public static void main(String[] args) {&
&&&&&&& Test test = new Test();&
&&&&&&& test.test();&
&&& public void test() {&
&&&&&&& URL root = this.getClass().getResource("/");&
&&&&&&& URL current1 = this.getClass().getResource("");&
&&&&&&& URL current2 = this.getClass().getResource(".");&
&&&&&&& URL parent = this.getClass().getResource("..");&
&&&&&&& URL self1 = this.getClass().getResource("Test.class");&
&&&&&&& URL self2 = this.getClass().getResource("./Test.class");&
&&&&&&& URL brother = this.getClass().getResource("../AnClass.class");&
&&&&&&& System.out.println("root = "+root);&
&&&&&&& System.out.println("current1 = "+current1);&
&&&&&&& System.out.println("current2 = "+current2);&
&&&&&&& System.out.println("parent = "+parent);&
&&&&&&& System.out.println("self1 = "+self1);&
&&&&&&& System.out.println("self2 = "+self2);&
&&&&&&& System.out.println("brother = "+brother);&
测试输出结果:
root = file:/D:/Java/workspace_1/java-learning/target/test-classes/
current1 = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/compiler/
current2 = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/compiler/
parent = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/
self1 = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/compiler/Test.class
self2 = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/compiler/Test.class
brother = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/AnClass.class
分析应该是classpath的问题。eclipse下调试程序,观察命令行参数:
view plain
E:\java6\bin\javaw.exe -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:4584 -Dfile.encoding=UTF-8 -classpath D:\java\eclipse\my_workspace\java-learning\target\test-D:\java\eclipse\my_workspace\java-learning\target\E:\working\maven_lib\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.E:\working\maven_lib\junit\junit\4.7\junit-4.7.E:\working\maven_lib\org\springframework\spring\2.5.6\spring-2.5.6.jar com.sdo.lee.piler.Test&
再查看了一下工程的.classpath文件:
view plain
&?xml version="1.0" encoding="UTF-8"?&&
&classpath&&
&&& &classpathentry kind="output" path="target/classes"/&&
&&& &classpathentry including="**/*.java" kind="src" path="src/main/java"/&&
&&& &classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/java"/&&
&&& &classpathentry excluding="**/*.java" kind="src" path="src/main/resources/META-INF/spring"/&&
&&& &classpathentry excluding="META-INF/spring/|**/*.java" kind="src" path="src/main/resources"/&&
&&& &classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar"/&&
&&& &classpathentry kind="var" path="M2_REPO/junit/junit/4.7/junit-4.7.jar"/&&
&&& &classpathentry kind="var" path="M2_REPO/org/springframework/spring/2.5.6/spring-2.5.6.jar"/&&
&&& &classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/&&
&/classpath&&
发现更改上面的其中一条&classpathentry&如下,即可得到预期结果。
&classpathentry including="**/*.java" kind="src" output="target/classes" path="src/main/java"/&
同时,更改后的eclipse命令行参数如下:
view plain
E:\java6\bin\javaw.exe -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:4785 -Dfile.encoding=UTF-8 -classpath D:\java\eclipse\my_workspace\java-learning\target\D:\java\eclipse\my_workspace\java-learning\target\test-E:\working\maven_lib\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.E:\working\maven_lib\junit\junit\4.7\junit-4.7.E:\working\maven_lib\org\springframework\spring\2.5.6\spring-2.5.6.jar com.sdo.lee.piler.Test&
eclipse在运行时是如何根据.classpath文件来添加命令行参数的-classpath属性不得而知。
(初步实践发现:eclipse可能是逐条读取classpathentry,一旦发现指定output属性,即将其path属性指定的路径添加到命令行参数的-classpath属性,并在最后将kind="output"指定的的默认path属性路径也添加到-classpath属性中。至于是否正确,有待验证。)
对于相对路径的解析('/','.','..',''等),java的源代码如下:
Class.class
view plain
public java.net.URL getResource(String name) {&
&&&&&&& name = resolveName(name);&
&&&&&&& ClassLoader cl = getClassLoader0();&
&&&&&&& if (cl==null) {&
&&&&&&&&&&& // A system class.&
&&&&&&&&&&& return ClassLoader.getSystemResource(name);&
&&&&&&& }&
&&&&&&& return cl.getResource(name);&
view plain
private String resolveName(String name) {&
&&&&&&& if (name == null) {&
&&&&&&&&&&&&
&&&&&&& }&
&&&&&&& if (!name.startsWith("/")) {&
&&&&&&&&&&& Class c =&
&&&&&&&&&&& while (c.isArray()) {&
&&&&&&&&&&&&&&& c = c.getComponentType();&
&&&&&&&&&&& }&
&&&&&&&&&&& String baseName = c.getName();&
&&&&&&&&&&& int index = baseName.lastIndexOf('.');&
&&&&&&&&&&& if (index != -1) {&
&&&&&&&&&&&&&&& name = baseName.substring(0, index).replace('.', '/')&
&&&&&&&&&&&&&&&&&&& +"/"+&
&&&&&&&&&&& }&
&&&&&&& } else {&
&&&&&&&&&&& name = name.substring(1);&
&&&&&&& }&
ClassLoader.class
view plain
public URL getResource(String name) {&
&&& if (parent != null) {&
&&&&&&& url = parent.getResource(name);&
&&& } else {&
&&&&&&& url = getBootstrapResource(name);&
&&& if (url == null) {&
&&&&&&& url = findResource(name);&
view plain
public static URL getSystemResource(String name) {&
&&& ClassLoader system = getSystemClassLoader();&
&&& if (system == null) {&
&&&&&&& return getBootstrapResource(name);&
&&& return system.getResource(name);&
view plain
protected URL findResource(String name) {&
view plain
private static URL getBootstrapResource(String name) {&
&&&&&&& try {&
&&&&&&&&&&& // If this is a known JRE resource, ensure that its bundle is&&
&&&&&&&&&&& // downloaded.& If it isn't known, we just ignore the download&
&&&&&&&&&&& // failure and check to see if we can find the resource anyway&
&&&&&&&&&&& // (which is possible if the boot class path has been modified).&
&&&&&&&&&&& sun.jkernel.DownloadManager.getBootClassPathEntryForResource(name);&
&&&&&&& } catch (NoClassDefFoundError e) {&
&&&&&&&&&&& // This happens while Java itse DownloadManager&
&&&&&&&&&&& // isn't accessible when this code is first invoked.& It isn't an&
&&&&&&&&&&& // issue, as if we can't find DownloadManager, we can safely assume&
&&&&&&&&&&& // that additional code is not available for download.&
&&&&&&& }&
&&& URLClassPath ucp = getBootstrapClassPath();&
&&& Resource res = ucp.getResource(name);&
&&& return res != null ? res.getURL() :&
从上面的源代码可以看到,Class类的getResource方法先是进行一个简单的路径解析,然后将其委托给其装载器去处理,装载器则是委托其父装载器处理,一直到bootstrap装载器。bootstrap装载器是非java实现的,sun没有开源,具体如何实现不得可知,但有一点可以肯定的是根据classpath来进行装载。java的官方文档说明中讲到:classloader装载类时的优先级(-jar命令参数 & -classpath命令参数 & CLASSPATH环境变量 & 默认的'.'指定的当前路径)。详细可参考官方文档How Classes are Found一文。
在相对路径解析时,我们发现所有的相对路径是以'/'指定的.class文件根目录来进行解析。观察上面的eclipse命令行参数猜想:类装载器默认将-classpath参数指定的第一个目录作为'/'指定的根目录,即getResource('/')返回的结果,而一旦/后面跟有具体的文件时(如'./Test.class'),则会顺序遍历classpath参数指定的所有路径,找到相应的Test.class文件,返回其绝对路径的URL,如有多个,则返回第一个找到的Test.class文件的路径。
为求证,在纯命令行环境下进行实践,实践结果与猜想的一致。
总结:对于这种采用构建工具进行构建的项目,往往指定有多个.class文件输出目录(对于只有一个.class文件输出目录时,不存在本文开始处描述的问题),因此在采用getResource(String name)之类的涉及到相对路径的方法调用,要稍加注意,有时候结果可能与预期出现偏差。
浏览: 10559 次
来自: 上海
写的这么好咋没人呢?!顶一个!
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 java class文件查看器 的文章

 

随机推荐