多出的类是怎么回事
最近我们做了一个项目,我们的任务是帮助分析几个奇怪Bug的产生原因,并修正。开发工具使用的是Eclipse3.0,需要提交给客户的成果物有源代码以及编译后打包的jar文件。
整个开发工作还算比较顺利,按期提交了成果物。但几天后,客户向我们提出了一个质疑,要求我们解释,我们提交的jar中包含有269个Class,而客户编译源代码的结果有271个Class,这相差的两个Class是怎么回事呢?
经过沟通,我们了解到客户是在命令行下用Javac命令进行编译的,为了查明问题的真相,我们也采用与客户同样的方式进行编译,发现编译结果确实是271个Class,那么多出的两个类是什么呢?
我们用beyond compare来比较两种方式的编译结果,发现多出的两个类是GameClient$1.Class和GameBoard$1.Class。反编译其中的GameClient$1.Class,得到如下结果(另一个除包名外与此相同):
// Decompiled by DJ v 2.8.8 .54 Copyright 2000 Atanas Neshkov Date: 2005-1-24 17:22:29
// Home Page : http://members.fortunecity.com/neshkov/dj.html - Check often for new version!
// Decompiler options: packimports(3)
// Source File Name: GameClient.java
package xxx.xxx.xxx.xxx; (此处隐去相关信息)
static class
{
}
从这个代码来看,程序中好像有匿名类的使用,但查看程序的源代码,却找不到。那么这两个类究竟是怎么产生的呢,javac编译时究竟发生了什么呢?
经过一番调查,终于定位了匿名类是如何产生的。让我们先来看看下面的代码:
public class OuterClass {
private InnerClass test = new InnerClass();
private class InnerClass {
}
}
你期望的编译结果可能是产生下面两个类文件:
OuterClass.class
OuterClass$InnerClass.class
而实际javac编译的结果类文件却是:
OuterClass.class
OuterClass$InnerClass.class
OuterClass$1.class
多出了OuterClass$1.class,这是因为:
当内部类是私有的且没有明确书写公有的构造函数时,那么缺省构造函数就是私有的,javac(SUN JDK 1.4)对这种情况的处理就是创建一个可以访问的带有一个参数的构造函数,而这个参数的类型是一个匿名的静态类,所以编译时就会多生成一个class文件。
你可能使用Eclipse进行编译,那么是的,结果跟你期望的一致,没有OuterClass$1.class。这是为什么?Eclipse JDT使用的编译器与JDK提供的javac不一样?是的,Eclipse JDT使用的是它自己内建的编译器,有一些增强的功能,包括对上述情况的完美处理。
终于明白了问题的根源在于,GameClient.java,GameBoard.java中分别有一个私有内部类没有定义构造函数。
那么至此,这个问题给我们带来的启示是:
1、 确认项目最终使用的Java类的编译器。如果可能,尽可能使用javac生成结果应用程序,或是与客户达成一致。
2、 尽量明确的书写缺省构造函数及其可见性,如将上面的代码改为:
public class OuterClass {
private InnerClass test = new InnerClass();
private class InnerClass {
public InnerClass() {
}
}
}
以上是我们通过这个项目,取得的一点经验,拿出来与大家分享,希望能够对大家有所帮助。