Java内部类持有外部类

面试小米的人,都知道小米面试官最喜欢问内部类问题:

1、静态内部类和非静态内部类的区别是什么?

答:非静态内部类可以访问外部类的方法和属性,但是静态内部类不行。

2、非静态内部类为啥能访问外部类的方法和属性?

答:因为非静态内部类持有了外部类对象的引用。

3、非静态内部类如何持有外部类对象的引用?

答:懵懵懵

大部分程序员都能回答出第二个问题,但是第三个问题可能就GG了。

先来看下Demo:

public class InnerDemo {

    public void main() {
        new InnerClass("lxx", 1);
        new StaticInnerClass("lxx");
    }

    private class InnerClass {
        String name;
        public InnerClass(String temp, int l) {
            super();
        }

        public void setName(String name) { }
    }

    public static class StaticInnerClass {
        private String name;
        public StaticInnerClass(String name) {
            super();
        }
    }
}
复制代码

将上述java文件变异成class文件,总共生成了3个class文件,分别是:

  • InnerDemo.class
  • InnerDemo$InnerClass.class
  • InnerDemo$StaticInnerClass.class

分别查看下这三个class内容。(javap -v InnerDemo.class)

扫描二维码关注公众号,回复: 13462315 查看本文章

InnerDemo的main方法:

inner.png Main方法中总共就2行代码,分别对应图中的两个框框。重点看下创建InnerClass和StaticInnerClass的构造方法,会发现InnerClass的构造函数多了一个InnerDemo的参数(并且位置放在了第一个)。所以创建InnerClass的对象时,就已经将外部类的对象传入,因此非静态内部类能访问外部类的方法和属性。

接着再来看下InnerDemo$InnerClass.class的内容:

inner2.png 从这张图中也能看出编译器在编译内部类时,的确是修改了内部类的构造方法,在前面增加了外部类的参数。

那么有人问了,如果我java代码改成这样呢?编译器还会增加一个InnerDemo参数吗,答案:是的。编译器不管你自身的构造函数是否有外部类的参数,编译器会直接给你加。

private class InnerClass {
    String name;

    public InnerClass(InnerDemo innerDemo,String temp, int l) {
        super();
    }

    public void setName(String name) { }
}
复制代码

在看下InnerClass这张图,里面有2个属性,一个是name(java代码里我们自己定义的),另一个是this 0 ,这个是编译器自己创建的,所以 t h i s 0,这个是编译器自己创建的,所以this 0的flag下面写的是ACC_SYNTHETIC(这个修饰符的意思是,这个代码不是java里有的,而是编译器自己生成的)。

最后在看下StaticInnerClass内容:

inner3.png 可以看到静态内部类的构造方法还是没变的,只有java里定义的参数。

看到这里,你学废了嘛。

猜你喜欢

转载自juejin.im/post/7035925191653851173