面试小米的人,都知道小米面试官最喜欢问内部类问题:
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
)
InnerDemo的main方法:
Main方法中总共就2行代码,分别对应图中的两个框框。重点看下创建InnerClass和StaticInnerClass的构造方法,会发现InnerClass的构造函数多了一个InnerDemo的参数(并且位置放在了第一个)。所以创建InnerClass的对象时,就已经将外部类的对象传入,因此非静态内部类能访问外部类的方法和属性。
接着再来看下InnerDemo$InnerClass.class的内容:
从这张图中也能看出编译器在编译内部类时,的确是修改了内部类的构造方法,在前面增加了外部类的参数。
那么有人问了,如果我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的flag下面写的是ACC_SYNTHETIC(这个修饰符的意思是,这个代码不是java里有的,而是编译器自己生成的)。
最后在看下StaticInnerClass内容:
可以看到静态内部类的构造方法还是没变的,只有java里定义的参数。
看到这里,你学废了嘛。