引言

前面两篇文章,我们介绍了四种内部类的定义和各自的特性,这篇文章,我们来看下这些内部类的使用场景。

内部类能做到什么

这里我们只是通过文字描述一下内部类的作用,不去罗列实际的例子了。
成员内部类使得java中的多继承的解决方案变得完整。《java编程思想》中这样描述:内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。也就是说,内部类允许继承多个非接口类型(类或抽象类)。这也很好理解,java中只能继承一个类,当外部类需要继承不止一个类的时候,就可以通过内部类来实现。每个内部类都可以单独继承一个类而不管外部类是否已经继承了这个类。
内部类还方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。也就是让代码更加简洁优雅。
内部类方便编写事件驱动程序。
内部类方便编写线程代码。

内部类与this逸出

成员内部类的this逸出

在《java并发编程实战》3.2章节发布与逸出中,有一个通过发布内部类实例而使this逸出的例子,书上描述的不是很清楚,我这里分析一下。
我们知道,成员内部类的创建依赖于外部类的对象,在外部类的非静态方法和构造方法中,我们都可以创建成员内部类的对象。当我们在外部类的构造方法中创建成员内部类的对象时,如果这个对象被其他代码获得,其他代码就能通过这个成员内部类的对象拿到外部类的this引用,进而导致没有初始化完整的外部对象被使用,看下面的例子:

  1. public class ThisEscape {
  2. private String name;
  3. public ThisEscape(String name) {
  4. ThisEscapeCaller.getEscapeName(new ThisEscapeInner());
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return name;
  9. }
  10. public class ThisEscapeInner{
  11. public String getOuterName(){
  12. return ThisEscape.this.getName();
  13. }
  14. }
  15. }
  16. public class ThisEscapeCaller {
  17. public static String getEscapeName(ThisEscape.ThisEscapeInner thisEscapeInner){
  18. System.out.println(thisEscapeInner.getOuterName());
  19. return thisEscapeInner.getOuterName();
  20. }
  21. public static void main(String[] args) {
  22. ThisEscape thisEscape = new ThisEscape("thisEscape");
  23. System.out.println(thisEscape.getName());
  24. }
  25. }

输出的结果:

  1. null
  2. thisEscape

ThisEscape构造方法中创建了一个成员内部类ThisEscapeInner的对象并将这个对象的引用传递给了外部代码ThisEscapeCaller的getEscapeName方法,这个方法获取了ThisEscape的name属性,但是这个属性还没有被初始化,这就导致了this的逸出。

匿名内部类的this逸出

除了成员内部类,匿名内部类同样能导致this逸出:

  1. public class ThisEscapeAnonymous {
  2. private String name;
  3. public String getName() {
  4. return name;
  5. }
  6. public ThisEscapeAnonymous(String name) throws InterruptedException {
  7. new Thread(){
  8. @Override
  9. public void run() {
  10. ThisEscapeAnonymousCaller.getOuterName(ThisEscapeAnonymous.this);
  11. super.run();
  12. }
  13. }.start();
  14. Thread.sleep(2000);
  15. this.name = name;
  16. }
  17. public static void main(String[] args) throws InterruptedException {
  18. ThisEscapeAnonymous thisEscape = new ThisEscapeAnonymous("thisEscape");
  19. System.out.println(thisEscape.getName());
  20. }
  21. }
  22. public class ThisEscapeAnonymousCaller {
  23. public static String getOuterName(ThisEscapeAnonymous thisEscapeAnonymous){
  24. System.out.println(thisEscapeAnonymous.getName());
  25. return thisEscapeAnonymous.getName();
  26. }
  27. }

构造方法中的sleep是为了保证外部方法先执行再进行变量赋值。输出的结果与上面是一样的:

  1. null
  2. thisEscape

所以,成员内部类和匿名内部类都会导致this逸出,我们在开发中应当注意这种情况,this逸出是不正确的对象构造方式,会造成数据的不一致。

小结

到此为止,我们应该理解了java中四种内部类的使用方式和各自的特性。在开发中,我们要逐渐学会在不同的场景下使用不同的内部类来满足我们的需求。