使用工具:
Java 8
IDEA 2018
1. 内部类的设计原因
①内部类方法可以访问外部类的属性,包括私有属性(将内部类定义成单独的外部类,则需要提供访问域的public方法)
②内部类可以对同一个包中的其他类隐藏起来(内部类可以是外部类私有的,而外部类的权限只可以是包、public)
③当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
注意:内部类可以访问外部类的属性,而外部类不能访问内部类的属性。
举个例子:
public class OuterAndInnerClass { private int number; public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); } class InnerClass{ private String name; public void getOuterField(){ System.out.println("inner class: " + number); // inner class: 0 } } }
内部类调用外部类的private属性,是可以的。这个内部类inner对象是属于外部类outer对象的内部类对象。
再来看看外部类调用内部类的情况:
可以看到,无法调用到内部类的属性,因为没有内部类的对象,我们构建一个内部类参数传入方法中,试试
public class OuterAndInnerClass { private int number; public void getInnerClassField(InnerClass inner){ System.out.println(inner.name); } class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); // inner.getOuterField(); outer.getInnerClassField(inner); // name } }
虽然可以调用到内部类的属性,但是这个作为方法参数传入的,并不是直接调的。若不采用传内部类到方法中,也可以为外部类定义个内部类的属性,通过该属性调用
但这样的调用与内部类直接访问外部类的属性是不同意义的。外部类和内部类的关系是:has a。那么,我们就要问问,内部类是怎么调用到外部类的属性的?
2. 内部类是如何访问外部类的属性?
我们知道类的方法隐含了两个参数:this和super。this指代的是当前对象,super指代的是父类对象,我们打印内部类中的this和super看是否指向InnerClass和Object
package onehundred; public class OuterAndInnerClass { private int number; class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("this " + this.getClass().getName()); // this onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + super.getClass().getName()); // super onehundred.OuterAndInnerClass$InnerClass System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); // outer.getInnerClassField(inner); // name } }
打印发现,this和super竟然都指向内部类,内部类没有写extends不应该默认继承Object类吗?为什么super是内部类本身?
还记得getClass()在哪里定义吗?Object类中该方法被定义为final,所以this.getClass()和super.getClass()调用的是同一个方法,由于getOuterField()是InnerClass类调用,所以打印出来都是InnerClass类。正确的调用应该使用getSuperClass()
package onehundred; public class OuterAndInnerClass { private int number; class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("this " + this.getClass().getName()); // this onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + super.getClass().getName()); // super onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + this.getClass().getSuperclass().getName()); // super java.lang.Object System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); // outer.getInnerClassField(inner); // name } }
既然this指向当前对象,super指向父类对象,那内部类是如何调用的外部类属性呢?
我们使用javap将代码反编译。
class onehundred.OuterAndInnerClass$InnerClass { final onehundred.OuterAndInnerClass this$0; onehundred.OuterAndInnerClass$InnerClass(onehundred.OuterAndInnerClass); public void getOuterField(); }
我们发现内部类多了一个外部类的final字段和一个带参构造器,外部类的引用有了,但是是如何访问到外部类的private字段的?
我们反编译外部类:
public class onehundred.OuterAndInnerClass { public onehundred.OuterAndInnerClass(); public static void main(java.lang.String[]); static int access$000(onehundred.OuterAndInnerClass); }
外部类多个一个static方法,并返回一个int型的值。内部类就是通过调用这个static方法得到了外部类的private字段。如果我们在内部类中访问外部类的boolean型字段,static方法就会返回一个boolean型的值。
总结:
可以在内部类中访问外部类的域,因为一个方法可以引用调用这个方法的对象数据域。内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。
外围类的引用在内部类的构造器中设置,编译器修改了所有内部类的构造器,添加一个外部类引用的参数。