阅读到thining in java第八章《接口与内部类》的时候,在研究一个例子程序的时候遇到了一个小问题,请大家帮忙解答一下。
该例子程序涉及到了三个class文件,分别是Contents.java,Destination.java,Parcel10.java。其中的Contents.java和Destination.java是两个interface,而Parcel10.java是含有静态内部类的一个类文件。具体代码如下:
Contents.javapublic interface Contents {
int value();
}
Destination.javapublic interface Destination {
String readLabel();
}
Parcel10.javapublic class Parcel10 {
private static class PContents implements Contents {
private int i = 11;
public int value() {
return i;
}
}
protected static class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
public static void f() {}
static int x = 10;
static class AnotherLevel {
public static void f() {}
static int x = 10;
}
}
public static Destination dest(String s) {
return new PDestination(s);
}
public static Contents cont() {
return new PContents();
}
/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
Contents c = cont();
Destination d = dest("Tanzania");
}
}
编译Parcel10.java得到了如下的class文件:Contents.class
Destination.class
Parcel10$1.class
Parcel10$PContents.class
Parcel10$PDestination$AnotherLevel.class
Parcel10$PDestination.class
Parcel10.class
我的疑惑就是:编译出来的大部分class文件我都能理解,但是关于这个Parcel10$1.class文件我不知道是如何产生的,我能理解Parcel10$1.class是一个Parcel10的匿名内部类,但是我的代码中并没有创建一个匿名类,不知道这个Parcel10$1.class是如何产生的,请赐教。

解决方案 »

  1.   

    我编译出来没有那个Parcel10$1.class
    jdk6
    myeclipse6.5
    你再试试吧 
    反编译一下看看
      

  2.   

    编译出来的确有Parcel10$1.class这个文件,
    同时,我反编译这个class文件,内容却是如下:
    static class Parcel10$1
    {
    }
    期待楼下高手解答
      

  3.   


    我机器没反编译工具,我的环境是jdk1.5,我自己javadoc编译的,没使用IDE工具。
      

  4.   

    我的也没有,JDK6
      

  5.   

    我用的也是JDK6编译出来有Parcel10$1.class这个文件。具体怎么产生的还真的有点能不明白,等待高手来解答。
      

  6.   

     public static Destination dest(String s) {
            return new PDestination(s);
        }
        public static Contents cont() {
            return new PContents();
        }把这两个方法注释掉就没有了,继续研究.......
      

  7.   

    经典的一个问题阿~~~这个匿名类是编译器用来限制权限用的,构建了一个假的构造函数来达到你上面private静态内部类。你可以用javap -c来看看就能看到那个奇怪的构造函数了
      

  8.   

    在解析下为什么eclipse下面没有,因为你用:javap -c Parcel10$PDestination来看eclipse生成的class的时候,构造函数变成了:Parcel10$PDestination(java.lang.String, Parcel10$PDestination);到底用Parcel10$PDestination还是Parcel10$1,和编译器相关。
      

  9.   


    你的解释我没太看明白,我直行了一下javap -c Parcel10.class,打印出来的东西看得不是很明白,没找到那个假的构造函数。另外按#8的方法我测试了一下,去掉 public static Destination dest(String s) {
    return new PDestination(s);
    }
    public static Contents cont() {
    return new PContents();
    }这两个函数之后,再从新编译确实就没有了Parcel10$1.class这个文件,貌似按你的说法无法解释这个现象。
      

  10.   


    回你和13#:去掉这两个方法的确是没有了,理由在于
     
    new PContents();和new PDestination(s);你可以试着把这两个方法去掉,加上下面两个成员变量:PContents pContents = new PContents();
    PDestination pDestination = new PDestination(s);又会有了。必须要去访问构造函数,编译器才会去生成那个虚拟类的。怎么看javap的问题。我现在在公司的笔记本上,没装Java的环境,晚上回去后帖出来,分析给你看吧。但是不是javap -c Parcel10而是,javap -c Parcel10$PDestination
    我记得这问题是有好几次都被误提为java:compiler的一个bug,但都被invalid掉了。
      

  11.   

    按照#18的"调用构造函数"说法,我把代码简化了一下,又做了一个测试:package com.lxxzhy.test;public interface Contents {
    int value();
    }package com.lxxzhy.test;public class CommonTesting {

    private Contents c = new PContents(); protected static class PContents implements Contents {
            private int i = 11;
            public int value() {
                return i;
            }
        }
    }
    那个XXXXX$1.class的文件就没有了.
      

  12.   

    续#20我再做了一个测试:package com.lxxzhy.test;public interface Destination {
    String readLabel();}package com.lxxzhy.test;public class CommonTesting { private Destination d = new PDestination(""); protected static class PDestination implements Destination {
    private String label; private PDestination(String whereTo) {
    label = whereTo;
    } public String readLabel() {
    return label;
    } public static void f() {
    } static int x = 10; static class AnotherLevel {
    public static void f() {
    } static int x = 10;
    }
    }
    }那个XXXX$1.class的文件又出现了,由此可以证明,并非什么有没有调用构造函数的问题,而可能在于,静态内部类中又有静态内部类的问题(猜想,待证).
      

  13.   

    我再次把我#20的代码修改了一下:package com.lxxzhy.test;public interface Contents {
    int value();
    }package com.lxxzhy.test;public class CommonTesting { private Contents c = new PContents(""); protected static class PContents implements Contents {
    private int i = 11; private PContents(String whereTo) {
    }
    public int value() {
    return i;
    }
    }
    }就是对静态内部类定义了构造方法,这时编译,那个XXXXXX$1.class再次出现,看来我#21的推断是不正确的.对比#20和这里的代码,可以看出:没有自定义静态内部类的构造方法,那个神气的类文件不会出现,定义了,就神奇般出现了.
      

  14.   

    我不明白你们怎么做那么多测试来猜测不按照我的方法求证一下呢?你看看我本机运行的全过程吧?D:\workspace\Test\src>javac Parcel10.javaD:\workspace\Test\src>javap -c Parcel10$PDestination
    Compiled from "Parcel10.java"
    public class Parcel10$PDestination extends java.lang.Object implements Destinati
    on{
    static int x;public java.lang.String readLabel();
      Code:
       0:   aload_0
       1:   getfield        #3; //Field label:Ljava/lang/String;
       4:   areturnpublic static void f();
      Code:
       0:   returnParcel10$PDestination(java.lang.String, Parcel10$1);
      Code:
       0:   aload_0
       1:   aload_1
       2:   invokespecial   #1; //Method "<init>":(Ljava/lang/String;)V
       5:   returnstatic {};
      Code:
       0:   bipush  10
       2:   putstatic       #4; //Field x:I
       5:   return}
    D:\workspace\Test\src>javap -c Parcel10$PContents
    Compiled from "Parcel10.java"
    class Parcel10$PContents extends java.lang.Object implements Contents{
    public int value();
      Code:
       0:   aload_0
       1:   getfield        #3; //Field i:I
       4:   ireturnParcel10$PContents(Parcel10$1);
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method "<init>":()V
       4:   return}请注意这两句:Parcel10$PDestination(java.lang.String, Parcel10$1);和Parcel10$PContents(Parcel10$1);对比下12#我给出的Eclipse的结果和说的话。后来你做的实验,请也用这个方法看看到底。
      

  15.   

    如果你不明白我说的那个构造函数需要被访问,看下面这段:
    D:\workspace\Test\src>javap -c Parcel10
    Compiled from "Parcel10.java"
    public class Parcel10 extends java.lang.Object{
    public Parcel10();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   returnpublic static Destination dest(java.lang.String);
      Code:
       0:   new     #2; //class Parcel10$PDestination
       3:   dup
       4:   aload_0
       5:   aconst_null
       6:   invokespecial   #3; //Method Parcel10$PDestination."<init>":(Ljava/lang/
    String;LParcel10$1;)V
       9:   areturnpublic static Contents cont();
      Code:
       0:   new     #4; //class Parcel10$PContents
       3:   dup
       4:   aconst_null
       5:   invokespecial   #5; //Method Parcel10$PContents."<init>":(LParcel10$1;)V   8:   areturnpublic static void main(java.lang.String[]);
      Code:
       0:   invokestatic    #6; //Method cont:()LContents;
       3:   astore_1
       4:   ldc     #7; //String Tanzania
       6:   invokestatic    #8; //Method dest:(Ljava/lang/String;)LDestination;
       9:   astore_2
       10:  return}
    注意这两句,6:   invokespecial   #3; //Method Parcel10$PDestination."<init>":(Ljava/lang/
    String;LParcel10$1;)V和5:   invokespecial   #5; //Method Parcel10$PContents."<init>":(LParcel10$1;)V如果编译器不构造Parcel10$1就会报没有类对吧?所以必须构造一个空的。为什么需要修改默认的构造函数,参造我11#说的。如果你不信,去翻翻Java编译器的bug报告,我记得有这么一条的。Okay?不要乱猜了~20#说的,自己用这个求证下,说不定什么原因没生成,但不要去追究了。知道是这样一回事就可以了
      

  16.   

    之所以做那么多测试,只是试图从源代码级别上分析,什么情况下能产生那个$1.class的类.原来你所说的是字节码访问了构造方法,我开始以为是说源代码里面调用了构造方法,所以做了#20的代码测试.反正从源代码上来说,如果静态内部类不定义构造方法,则不会出现$1.class文件.
      

  17.   


    请看我第一个回复,11#。那个特殊的构造参数是为了让你的内部类的构造函数私有化。你20#的例子,没必要改成22#那样,你改成这样,也可以出现了:public class CommonTesting {    private Contents c = new PContents();    protected static class PContents implements Contents {
            private int i = 11;
            
            private PContents() { 
            }         public int value() {
                return i;
            }
        } 
    }你22#改成这样,就不出现了:public class CommonTesting {    private Contents c = new PContents("");    protected static class PContents implements Contents {
            private int i = 11;        public PContents(String whereTo) {
            }        public int value() {
                return i;
            }
        }
    }
    这次彻底明白了吧?
      

  18.   

    #20-#22的测试是看了你#18的而做的,你说:
    ======================
    去掉这两个方法的确是没有了,理由在于new PContents();和new PDestination(s);你可以试着把这两个方法去掉,加上下面两个成员变量:PContents pContents = new PContents();
    PDestination pDestination = new PDestination(s);又会有了。必须要去访问构造函数,编译器才会去生成那个虚拟类的.
    ========================
    所以我试试是否有new new PContents();或者new PDestination(s);就必然出现$1.class.结果不是这样的.现在看来,即使定义了静态内部类的构造方法,如果用非private修饰的,也不会出现.
      

  19.   

    呃,我看了下,我表述太简单了可能。我的话要全部连在一起看的,也就是说这两句话要在原来的程序里面改的。理由很简单,原来private static class PContents implements Contents 也就是说PContents本身的构造函数就是私有的~也就是说,你这样子,public class Parcel10 {
        static PContents pContents = new PContents();    private static class PContents implements Contents {
            private int i = 11;
            public int value() {
                return i;
            }
        }
    //    protected static class PDestination implements Destination {
    //        private String label;
    //        private PDestination(String whereTo) {
    //            label = whereTo;
    //        }
    //        public String readLabel() {
    //            return label;
    //        }
    //        public static void f() {}
    //        static int x = 10;
    //        static class AnotherLevel {
    //            public static void f() {}
    //            static int x = 10;
    //        }
     //   }
        /**
         * @param args
         */
        public static void main(String[] args) {
            Contents c = Parcel10.pContents;
        }
    }也会有Parcel10$1.class~这又是另外一个问题了:私有化的内部类的构造函数问题~:p