反编译后内部类的问题

michael9527 2009-02-09 01:54:38
代码:
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 访问内部类也不可以的,也是类似的限制吗?
...全文
3376 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
new_stu 2012-08-30
  • 打赏
  • 举报
回复
我比较好奇的对于已经编译好的class 里面的access$0 access$1 access$2 运行时JVM如何判断 这些0 1 2分别对应的那个函数或者方法
fash_frank 2011-06-23
  • 打赏
  • 举报
回复
我这边也有一个这样的
但是我不知道怎么拿出来说
纠结
muyu114 2011-05-17
  • 打赏
  • 举报
回复
看看怎么解释的
xingzibird 2010-03-19
  • 打赏
  • 举报
回复
正遇到此问题,反编译如何处理内部类?
haixin611 2009-12-16
  • 打赏
  • 举报
回复
云上飞翔 2009-02-10
  • 打赏
  • 举报
回复
只是想说明:我们可以不通过反射来在其它类中直接调用access$000方法,即:如何绕过这个检查 而已.
云上飞翔 2009-02-10
  • 打赏
  • 举报
回复
[Quote=引用楼主 michael9527 的帖子:]
代码:
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…
[/Quote]
答:显然,楼主对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(...)了.
ZangXT 2009-02-10
  • 打赏
  • 举报
回复
mark,学习。
ZangXT 2009-02-09
  • 打赏
  • 举报
回复
没发现原因,不过用反射的方式可以调用。

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) {
}
}
}
ZangXT 2009-02-09
  • 打赏
  • 举报
回复
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类型的参数吗
michael9527 2009-02-09
  • 打赏
  • 举报
回复
我又实验一下:
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
类型的参数,但是从字节码来看,也没用到这个参数,不知道是干什么用的。
cocy_chan 2009-02-09
  • 打赏
  • 举报
回复
不懂
学习
ZangXT 2009-02-09
  • 打赏
  • 举报
回复
说说你在别的类中是如何访问的?
内部类和一般类的class文件还是有区别的,里面有标志位说明。
gulang76 2009-02-09
  • 打赏
  • 举报
回复
O,不懂,帮不了你.
反编译可以上网找个反编译软件嘛,很方便的.

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧