产生多于的class$1.class的原因

时间:2022-02-02 18:07:50

在java中,如果在一个类中定义了内部类,刚会生成:    super&this.class的文件,如果给某个控件添加了Listener事件,则会生成    super&i.class的文件,其中i为Listener的个数。   
例如:下面的程序:   
import    javax.swing.*;   
import    java.awt.*;   
import    java.awt.event.*;   


/**   
*    <p>Title:    </p>   
*    <p>Description:    </p>   
*    <p>Copyright:    Copyright    (c)    2001</p>   
*    <p>Company:    </p>   
*    @author    unascribed   
*    @version    1.0   
*/   

public    class    a    extends    JFrame    {   
JPanel    jPanel1    =    new    JPanel();   
JButton    jButton1    =    new    JButton();   
JButton    jButton2    =    new    JButton();   
JButton    jButton3    =    new    JButton();   

public    a()    {   
try    {   
jbInit();   
}   
catch(Exception    e)    {   
e.printStackTrace();   
}   
}   
private    void    jbInit()    throws    Exception    {   
jButton1.setText("jButton1");   
jButton1.addActionListener(new    java.awt.event.ActionListener()    {   
public    void    actionPerformed(ActionEvent    e)    {   
jButton1_actionPerformed(e);   
}   
});   
jButton2.setText("jButton2");   
jButton2.addActionListener(new    java.awt.event.ActionListener()    {   
public    void    actionPerformed(ActionEvent    e)    {   
jButton2_actionPerformed(e);   
}   
});   
jButton3.setText("jButton3");   
jButton3.addActionListener(new    java.awt.event.ActionListener()    {   
public    void    actionPerformed(ActionEvent    e)    {   
jButton3_actionPerformed(e);   
}   
});   
this.getContentPane().add(jPanel1,    BorderLayout.CENTER);   
jPanel1.add(jButton1,    null);   
jPanel1.add(jButton2,    null);   
jPanel1.add(jButton3,    null);   
}   

void    jButton1_actionPerformed(ActionEvent    e)    {   

}   

void    jButton2_actionPerformed(ActionEvent    e)    {   

}   

void    jButton3_actionPerformed(ActionEvent    e)    {   

}   
class    b{   
}   
class    c{   
class    c1{}   
}   
}   
编译后生成的class文件有:   
a.class   
a$1.class                      //jButton1.addActionListener   
a$2.class                      //jButton2.addActionListener   
a$3.class                      //jButton3.addActionListener   
a$b.class   
a$c.class   
a$c1.class   
但如果a.java这样定义:   
class    a{   
}   
class    b{   
}   
注意为同一个文件:   
刚编译后生成b.class,a.class   
而不是a$b.class   

2

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() {
}
}
}

3 对于引用中的interface--impl

在实现类中没有缺省的构造函数,在调用的同时,就会产生额外的文件