javap反编译探寻内部类为何能访问外部私有成员

Java语言理论告诉我们内部类对象持有外部类对象的一个引用,这说明内部类与外部类还是独立的两个类,只不过内部类对象通过持有外部类的对象的引用来维持这个关系。

通常任何一个类都不可能访问另一个类的私有成员,那么内部类是如何做到访问外部类的私有成员的呢?

其实“持有外部类对象的一个引用”这句话已经给了我们提示,Java代码中并不需要自己去声明这么一个引用,因此是编译器背着我们创建了这个引用。既然编译器有这个习性,它很可能也给外部类的私有成员创建了getter方法使得内部类可以访问。

下面我们用javap命令反编译class文件来探寻这个猜想是否正确。

javap 用法:javap class文件路径 获得类摘要信息,直接输出到标准输出。

第一步,验证内部类对象持有外部类对象的引用。

先定义一个外部类和一个内部类:

class Outer {
    class Inner {
    }
}

使用 javac 编译出class文件,会生成多个class文件:Outer.class Outer$Inner.class
javap Outer$Inner.class 输出:

class Outer$Inner {
  final Outer this$0; // 外部类对象的引用
  Outer$Inner(Outer); // 还生成了一个构造函数,传入了外部类的引用。
}

一个有趣的问题来了:如果Inner显式定义了构造函数会怎么样?

class Outer {
    class Inner {
        private final String name;
        public Inner(String name) {
            this.name = name;
        }
    }
}

javap Outer$Inner.class 输出:

class Outer$Inner {
  final Outer this$0;
  public Outer$Inner(Outer, java.lang.String); // 改造了显式定义的构造函数

第二步,验证外部类生成了私有成员变量的访问器

先添加一个私有成员变量:

class Outer {
    private final int code;
    public Outer(int code) {
        this.code = code;
    }
    class Inner {
        private final String name;
        public Inner(String name) {
            this.name = name;
        }
    }
}

javap输出:

class Outer {
  public Outer(int);
}

可见,内部类不访问外部类私有成员变量时,并没有隐藏的方法声明。

然后,添加内部类对外部类私有成员变量的访问:

class Outer {
    private final int code;
    public Outer(int code) {
        this.code = code;
    }
    class Inner {
        private final String name;
        public Inner(String name) {
            this.name = name;
        }
        public void hello() {
            System.out.println("Inner: hello: " + code);
        }
    }
}

javap Outer.class 输出:

class Outer {
  public Outer(int);
  static int access$000(Outer); // 多了一个静态方法,返回私有成员。
}

再添加一个私有方法:

class Outer {
    private final int code;
    public Outer(int code) {
        this.code = code;
    }
    private void sayhi(String message) {
        System.out.println("Outer: hi, " + message);
    }
    class Inner {
        private final String name;
        public Inner(String name) {
            this.name = name;
        }
        public void hello() {
            System.out.println("Inner: hello: " + code);
            sayhi("this is from inner");
        }
    }
}

javap Outer.class 输出:

class Outer {
  public Outer(int);
  static int access$000(Outer);
  static void access$100(Outer, java.lang.String);
}

结论:

  1. 内部类创建了一个外部类对象的引用,并通过改造构造函数将其传入内部类。
  2. 内部类如果不访问外部类的私有成员,并不会生成访问方法,而是需要的时候才生成。
  3. 外部类生成的访问方法,是static类型的,并传入外部类对象引用,返回值与参数根据需要访问的变量和函数相对应。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 1、成员内部类: 即作为外部类的一个成...
    ikaroskun阅读 5,060评论 0 13
  • 内部类简介 虽然Java是一门相对比较简单的编程语言,但是对于初学者, 还是有很多东西感觉云里雾里, 理解的不是很...
    Winterfell_Z阅读 5,048评论 1 3
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 33,586评论 18 399
  • 夜深人静,孩子也进入甜蜜的梦乡。喧闹的一天终于结束了。可我时常是舍不得睡的,毕竟这是一天当中唯一属于自己的时光,真...
    舒瀚阅读 1,450评论 0 0
  • 我佩服的人,他的名字叫陈俊浩,他的一副脸帅气,他的学习不一般他孝敬他帅气,他聪明,她活泼可爱。她大方宽容。他帮喜欢...
    石豪阅读 2,649评论 0 0

友情链接更多精彩内容