在此之前,我们需要了解,内部类与组合是完全不同的概念,虽然它看起来像是一种代码隐藏机制,但其实内部类是了解外围类,并且能与之通信。下面,我们深刻探讨内部类的用法与底层原理。
内部类的定义:将一个类的定义放在另一个类的定义内部,我们称之为内部类。
内部类可以分为四种:
- 
成员内部类:和成员变量一个级别 
- 
局部内部类:在方法里的内部类(方法域内生效 或者 方法内某一段代码块域内生效) 
- 
匿名内部类:基本上属于接口的实现类,一次性使用的场景。 
- 
静态内部类:static 修饰的成员内部类。 
举例子 1:创建一个内部类
//外围类public class CreateInnerClass {  //内部类1  class Contents{    private int i = 11;    public int value() {      return i;    }  }
  //内部类2  class Destination {    private String label;    public Destination(String whereTo) {      label = whereTo;    }    String readLabel() {      return label;    }  }
  //外围类方法  public void setDest(String dest) {    // 外围类的方法内部,完成内部类的构造    Contents c = new Contents();    Destination d = new Destination(dest);    System.out.println(d.readLabel());  }
  //外围类方法2  public Destination getAnotherDestination(){    return new Destination("anotherDestination");  }  //外围类方法3  public Contents getAnotherContents(){    return new Contents();  }
  public static void main(String[] args) {    //新建外围类    CreateInnerClass innerClass = new CreateInnerClass();    innerClass.setDest("Tasmania");    //外部类可以拥有返回指向内部类引用的方法。    Destination anotherDestination = innerClass.getAnotherDestination();    Contents anotherContents = innerClass.getAnotherContents();  }}
复制代码
代码分析:
如果从外部类的非静态方法之外的任意位置,创建某个内部类的对象,那么需要通过外围类引用,具体的指明这个对象的类型:OuterClassName.InnerClassName。
内部类拥有其外围类的所有元素的访问权。
当生成一个内部类的对象时,此对象与制造它的外围对象(enclosing object)之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。
举例子 2:内部类访问外围类的私有成员变量
首先,定义一个内部类行为的规范接口 Selector:
public interface Selector {  boolean end();  Object current();  void next();}
复制代码
然后,在外围类里定义这个内部类:
//外围类public class Sequence {    //数组,私有对象    private Object[] items;    private int next = 0;    //构造器    public Sequence(int size) {        items = new Object[size];    }    public void add(Object x) {        if (next < items.length) {            items[next++] = x;        }    }    //外围类构造内部类的方法    public Selector selector() {        return new SequenceSelector();    }
    //内部类,私有描述符    private class SequenceSelector implements Selector {        private int i = 0;        @Override        public boolean end() {            //这里直接访问的是外围类的私有成员变量哦!            return i == items.length;        }        @Override        public Object current() {            //这里直接访问的是外围类的私有成员变量哦!            return items[i];        }        @Override        public void next() {            //这里直接访问的是外围类的私有成员变量哦!            if (i < items.length) {                i++;            }        }    }}
复制代码
最后,我们运行测试用例:
public static void main(String[] args) {    Sequence sequence = new Sequence(10);    for (int i = 0; i < 10; i++) {        sequence.add(Integer.toString(i));    }    //获取内部类,通过内部类来访问外围类的私有成员属性    Selector selector = sequence.selector();    while (!selector.end()) {        System.out.print(selector.current() + " ");        selector.next();    }}
复制代码
内部类自动拥有对其外围类所有成员的访问权。
这是如何做到的呢?当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。 然后,在你访问此外围类的成员时,就是用那个引用来选择外围类的成员。这里面是编译器帮忙处理了所有的细节,因此得到结论:内部类的对象只能在与其外围类的对象相关联的情况下才能被创建(前提是:该内部类是非 static,静态内部类是其他情况,且看下文)。
.this:内部类链接到外围类引用的手段
举例子 3:.this 的用法
/** * <p> *    若要生成对外部对象的引用,使用类名+“.this”的方式。 * </p> */public class DotThis {    //外围类方法1    void f() {        System.out.println("DotThis.f()");    }    //外围类方法2:创建内部类    public Inner inner() {        return new Inner();    }    //内部类    public class Inner {        // 返回外部类的引用        public DotThis outerClass() {            //类名.this返回外围类引用            return DotThis.this;        }    }    public static void main(String[] args) {        DotThis dotThis = new DotThis();        //创建了一个内部类        DotThis.Inner dti = dotThis.inner();    }}
复制代码
.new:使用外围类引用创建内部类的手段
举例子 4:.new 的用法
/** * <p> *    希望其他对象创建其某个内部类的对象,使用.new获得对外部类对象的引用。 * </p> */public class DotNew {    //外围类仅仅提供了一个内部类,但是没有开放任何方法给客户端来调用,以达到从内部类的目的。    public class Inner{    }    public Inner getInner(){        return new Inner();    }    public static void main(String[] args) {        //这种情况,因为外围类没有提供获取内部类Inner的方法,但我们又需要创建这个内部类,那就使用.new        DotNew dn = new DotNew();        //通过.new关键字创建一个内部类        DotNew.Inner dni = dn.new Inner();        //普通方法创建一个内部类        DotNew.Inner dni2 = dn.getInner();    }}
复制代码
内部类的向上转型
这个更加容易理解,内部类可以继承抽象类,也可以实现接口。可以参考:《通过接口引用对象》与《设计模式的六大原则》。
举例子 5:内部类的向上转型
内部类需要实现的接口
public interface Conents {    int value();}public interface Destination {    String readLabel();}
复制代码
内部类的封装
public class TestInterfaceInnerClass {  private class PContents implements Conents {    private int i = 11;    @Override    public int value() {      return i;    }  }  protected class PDestination implements Destination {    private String label;    private PDestination(String whereTo) {      label = whereTo;    }    @Override    public String readLabel() {      return label;    }  }  public Destination destination(String s) {    return new PDestination(s);  }  public Conents contents() {    return new PContents();  }}
复制代码
测试 demo
public class TestDemo extends TestInterfaceInnerClass{  public static void main(String[] args) {    TestInterfaceInnerClass p = new TestInterfaceInnerClass();    Conents contents = p.contents();    Destination tasmania = p.destination("Tasmania");  }}
复制代码
代码分析:
内部类的用武之地:将内部类向上转型为基类时,尤其是转型为一个接口的时候,所得到的直接指向基类或接口的引用,能够很方便地隐藏实现细节;且这些接口的实现对于调用方,能够完全不可见的。
内部类还可以继续嵌套内部类。
举例子 6:多重嵌套的内部类
/** * <p> *     内部类嵌套 * </p> */class MNA{    private void f() {}    class A {        private void g() {}        public class B {            void h() {                g();                f();            }        }    }}public class MultiNestingAccess {    public static void main(String[] args) {        MNA mna = new MNA();        //通过外围类引用才能继续创建内部类A类        MNA.A mnaa = mna.new A();        //通过A类引用才能继续创建A类的内部类        MNA.A.B mnaab = mnaa.new B();        mnaab.h();    }}
复制代码
代码分析:
对于多重嵌套的非静态内部类,我们需要层次分明的构建每一次嵌套的对象,然后通过引用来创建对象。
本节是内部类学习的上部分,我们复习了:内部类的基础用法,外围类 &内部类的引用关系,以及多重嵌套内部类的定义使用。下部分我们详细讲解四种内部类的定义与使用,以及继承语法对内部类的影响。
《源码系列》
《经典书籍》
《Java并发编程实战:第2章 影响线程安全性的原子性和加锁机制》
《Java并发编程实战:第3章 助于线程安全的三剑客:final & volatile & 线程封闭》
《服务端技术栈》
《算法系列》
《设计模式》
