代码:
class Outer {
private int xx = 123;
public Inner getInner() {
return new Inner();
}
public class Inner {
public int getDate() {
return xx;
}
}
}反编译Outer类,
C:\>javap -private Outer
Compiled from "Test.java"
class Outer extends java.lang.Object{
    private int xx;
    Outer();
    public Outer$Inner getInner();
    static int access$000(Outer);
}编译器自动生成了一个方法,access$000(),包访问权限,于是我在另外个类中访问 access$000() 方法,结果说
找不到这个方法,这又是为什么呢,编译器做的限制?一定就访问不了吗?另外用 Outer$Inner 访问内部类也不可以的,也是类似的限制吗?

解决方案 »

  1.   

    说说你在别的类中是如何访问的?
    内部类和一般类的class文件还是有区别的,里面有标志位说明。
      

  2.   

    我又实验一下:
    class Outer {
    private int data = 9527;
    public Inner getInner() {
    return new Inner();
    }
    class Inner {
    public int getDate() {
    return data;
    }
    }
    static int access$9527(Outer outer) {    //手动编写的一个类似编译器自动生成的方法
    System.out.println("success!");
    return 1;
    }
    }反编译后为:
    Compiled from "Test.java"
    class Outer extends java.lang.Object{
        private int data;
        Outer();
        public Outer$Inner getInner();
        static int access$9527(Outer);
        static int access$000(Outer);
    }access$000()方法的字节码:
    static int access$000(Outer);
      Code:
       Stack=1, Locals=1, Args_size=1
       0:   aload_0
       1:   getfield        #1; //Field data:I
       4:   ireturn
      LineNumberTable:
       line 1: 0
    然后在另一个类中访问这个方法,主要代码:
    Outer o = new Outer();
    int x = Outer.access$9527(o);   上面那样写是可以的,但是改成这样就不行了
    int x = Outer.access$000(o);
     
    结果提示:
    Test2.java:5: 找不到符号
    符号: 方法 access$000(Outer)
    位置: 类 Outer
                    int x = Outer.access$000(o);
                                 ^
    1 错误   
    从 access$000()方法生成的字节码,以及常量池中,没看到有什么问题,但是就
    不能访问,我想是编译器做的手脚吧,但是不知道是在哪里做的手脚。另外access$000()需要传递一个Outer
    类型的参数,但是从字节码来看,也没用到这个参数,不知道是干什么用的。
      

  3.   

    static int access$000(Outer);
      Code:
      Stack=1, Locals=1, Args_size=1
      0:  aload_0
      1:  getfield        #1; //Field data:I
      4:  ireturn
      LineNumberTable:
      line 1: 0 这里aload_0不就是用了Outer类型的参数吗
      

  4.   

    没发现原因,不过用反射的方式可以调用。import java.lang.reflect.Method;public class Test {    public static void main(String[] args) {
            Outer outer = new Outer();        Class c = outer.getClass();
            try {
                Method method = c.getDeclaredMethod("access$000", new Class[]{Outer.class});
                Object obj = method.invoke(outer, new Object[]{outer});
                Integer i = (Integer) obj;
                System.out.println(i);
            } catch (Exception ex) {
            }
        }
    }
      

  5.   

    答:显然,楼主对JAVA编译器中对内部类的处理是不大清楚的.1)static Type access$iii(Outer); 是JAVA编译器自动生成的十分重要的方法(该方法的个数由你的内部类要访问的外部类的变量个数相关),目的是:用于内部类访问外部类的数据成员时使用.2)因此:JAVA编译器在生成内部类的访问外部类的数据成员时,会自动生成代码来调用这个方法.
    以你的代码为例:内部类Inner中的方法
    public int getDate() { 
    return xx; 

    生成的代码如下:(经javap 处理后)public int getDate();
      LineNumberTable:
       line 12: 0  Code:
       Stack=1, Locals=1, Args_size=1
       0:   aload_0
       1:   getfield        #1; //Field this$0:LOuter;
       4:   invokestatic    #3; //Method Outer.access$000:(LOuter;)I
       7:   ireturn看代码第4行:通过Outer.access$000(Outer o)来获取外部类对象o中的private型的数据成员(请注意:这可是从另外一个类中访问其它类的private型数据成员--不是通过反射技术)3)进一步:
    若外部类定义了两个private 数据成员如下:
    private int xx=12;
    private float yy=12.4f;
    这两个数据成员在内部类中都要访问,则编译器会自动生成两个access方法:
    static int access$000(Outer);  用于内部类访问private int xx;
    static float access$100(Outer);  用于内部类访问private float yy;4)这种内部类访问外部类中private数据成员的技术(不是通过反射!) 给安全留下了可能的小隐患(因为有些private数据成员是不提供外界访问它的所谓的getter()的).为此,编译器对自己自动生成的这些access$000()方法,在编译时进行检查,是不允许程序员直接来调用的.但是:我们可以利用JAVA编译器对类的编译特性来绕过这个检查:目的是,达到在自己的其它类中直接来调用这些access$000()方法.
    这样,我们可采用这个技术(即:在自己的类中--注意不是内部类,而是外部类中直接来调用这个access$000(Outer);)来访问其它类的private的数据成员了.具体技术演示如下:
    第一步:定义如下的类:
    class Outer { 
    private final int xx = 123;  
    //由于是final,故不再自动生成access$000(Outer);public Inner getInner() { 
    return new Inner(); 

    public class Inner { 
    public int getDate() { 
    return xx; 

    } //class Inner static int access$000(Outer)//这个是自已定义的!
     {
      return  1;
     } 
    第二步:定义你的其它类,来直接调用这个access$000()方法
    public class Test1
    {
     public static void main(String[] args)
     {  System.out.println(Outer.access$000(new Outer()));  //这个调用是没有问题的,因为是自己定义的!
     }}将上述两个JAVA文件编译成class,成其是第二步的 Test1.class第三步:这是变戏法的一步:
    将第一步的类Outer改为如下:
    class Outer { 
    private  int xx = 123;  
    //由于不是final,故自动生成access$000(Outer);public Inner getInner() { 
    return new Inner(); 

    public class Inner { 
    public int getDate() { 
    return xx; 

    } //class Inner
    /*将这个第一步中自己定义的access$000去掉,因为编译器会自动生成它!
     static int access$000(Outer {
      return  1;
     } */重新编译第三步中的这个类,而第二步中的类Test.class不动它. 此时,我们达到了这样一个目的:在类Test1中调用了Outer类中编译器自动生成的这个access$000(...)了.
      

  6.   

    只是想说明:我们可以不通过反射来在其它类中直接调用access$000方法,即:如何绕过这个检查 而已.
      

  7.   

    我比较好奇的对于已经编译好的class 里面的access$0 access$1 access$2 运行时JVM如何判断 这些0 1 2分别对应的那个函数或者方法