我们都知道java的类可以由public、default(缺省、不写)来修饰,分别表示的含义是允许公开访问以及只允许包内其它类访问,而同一包内的类通常是为完成同一个功能而协作。
除此之外,我们还会遇到一些类,它们只是其它某个类的实现或组成的一部分,它们不应该被独立的创建出来,当它们创建的时候需要跟"宿体"连在一块,这就是内部类。就好像人类的心脏,你没法单独的new一个出来,它只能依赖于人体而存活(这本来就是它存在的目的),至少现代医学做不到离体存活,现代医学能做的只是把一个心脏取出来,立马换到另外一个人身上。
内部类
内部类通常是宿主类实现的一部分或者是宿主对外提供的一种工具。如果内部类只是为宿主的实现服务,可以将内部类修饰为private,这样也就限制了外部类的访问。而作为工具的内部类,一般访问修饰符为public或default。
为了使用方便,java允许在内部类中直接访问宿主类的成员(这也就决定你没法在宿主类外单独直接new一个内部类对象)。
下面是一个简易的用内部类实现容器迭代器的例子。
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; } } 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 Selector selector() { return new SequenceSelector(); } 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.println(selector.current()); selector.next(); } } }
.this和.new
由于内部类可以直接访问宿主类的成员,所以它自身就拥有对宿主类的引用。如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。
public class DotThis { void f() {System.out.println("DotThis.f()");} public class Inner{ public DotThis outer() { return DotThis.this; } } public Inner inner() {return new Inner(); }; public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dti = dt.inner(); dti.outer().f(); } }
有时你需要创建其某个内部类的对象,而非通过成员方法返回,在这种情况下必须直接使用某个外部类的对象来创建该内部类的对象,使用.new语法。
public class DotNew { public class Inner{}; public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); } }
匿名内部类
匿名内部类与内部类最大差异在于,由于它是匿名的,你没法在任意位置,随意的去new这样一个对象,所以它常用于只使用一次的类或者说只在某个地方(方法、对象成员)能new的类,从这个角度来看它是类访问最严格的控制:只在某个位置可以创建类。而内部类至少能在宿主类中随意创建。
匿名内部类支持直接访问所在方法的局部引用,由于引用和基本类型出了方法就无法访问到了,所以要求在匿名内部类中访问的在方法中的外部对象必须是final的。但是,匿名内部类访问的类成员对象不必是final的。
由于匿名类中不可能有命名构造器(因为它根本没有名字),但通过初始化代码段,就能够达到为匿名内部类创建一个构造器的效果。
abstract class Base{ public Base(int i) { System.out.println("Base constructor, i = " + i); } public abstract void f(); } public class AnonymousConstructor { private static int w = 7; public static Base getBase(int i,final int j) { return new Base(i) { { System.out.println("Inside instance initial"); } private int pj = j; private int pw = w; public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = AnonymousConstructor.getBase(47, 39); base.f(); } }
嵌套类
嵌套类也是定义在一个类中的类,不过它和宿主类除此之外并没有特别直接的关系。在使用形式上,一个内部类用static来修改就成为了一个嵌套类。在嵌套类中你不能直接访问宿主类中的非静态成员。嵌套类在实际工作中,使用很少,既然它跟一个普通类对宿主类的访问权限相似又何必放在一个宿主类中定义呢?而通常嵌套类又允许外部类直接定义该嵌套类的对象,这和普通类也就更相似了。