Java:内部类
看Thinking in Java中内部类的一章,有内部类和静态内部类,书中把后者叫做嵌套类。常见用法实例:
一般内部类(非静态)
1 public class InnerClassTest { 2 public static void main(final String args[]) { 3 SomeOuter outer = new SomeOuter(); 4 SomeOuter.SomeInner x = outer.new SomeInner(); 5 x.test(); 6 } 7 } 8 9 class SomeOuter { 10 public class SomeInner { 11 public static final int x = 2; 12 13 // public static int y = 2; public static final Integer Z = 2; 14 15 public SomeInner() { 16 System.out.println("inner constructor"); 17 } 18 19 public void show() { 20 System.out.println("Inner show"); 21 } 22 23 public void test() { 24 // method in outter class can be invoked directly 25 info(); 26 27 // inner this & outer this 28 this.show(); 29 SomeOuter.this.show(); 30 } 31 } 32 33 public void show() { 34 System.out.println("Outer show"); 35 } 36 37 public void info() { 38 System.out.println("outer info"); 39 } 40 } 41 42 class ExtendedInner extends SomeOuter.SomeInner { 43 public ExtendedInner(final int x, final SomeOuter outer) { 44 outer.super(); 45 } 46 47 }
其中最后的继承内部类用法有点特殊,因为非静态的内部类含有一个外部类的引用(如SomeOuter.this),因而在内部类的派生类中的构造方法也需要提供一个外部类的实例并做如上调用。
非静态内部类中不能含有静态成员(字段和方法),但是final修饰的基本类型除外(也许是因为final修饰的基本类型可以作为编译期常量看待的原因,如果用new Random().nextInt()进行初始化的话也不行,必须用常量值初始化)。
内部类继承
下面的用法估计更加少见,在派生类中对基类的内部类进行继承。由于派生类含有指向基类的引用所以构造内部类时就不用向在外部那样传入一个内部类的外部类实例引用了。
public class InnerClassTest { public static void main(final String args[]) { SomeOuterComplex xComplex = new SomeOuterComplex(); xComplex.info(); } } class SomeOuterBasic { protected SomeInner inner; public SomeOuterBasic() { System.out.println("some out basic constructor"); inner = new SomeInner(); } class SomeInner { public SomeInner() { System.out.println("some inner constructor in basic"); } public void say() { System.out.println("some inner basic say"); } } public void info() { inner.say(); } } class SomeOuterComplex extends SomeOuterBasic { class SomeInner extends SomeOuterBasic.SomeInner { public SomeInner() { System.out.println("some inner constructor in complex"); } @Override public void say() { System.out.println("some inner complex say"); } } public SomeOuterComplex() { System.out.println("some out complex constructor"); inner = new SomeInner(); } }
静态内部类与单例模式/延迟初始化
静态内部类没有指向其外部类的引用,因而不能访问其外部类的非静态字段(即实例字段),和一般的类用法最像。当在接口中使用静态内部类时,则可以实现在接口中“放置”一定的代码。
public class InnerClassTest { public static void main(final String args[]) { DummyInterface.DoSome.say(); Outer.Inner x = new Outer.Inner(); x.inc(); x.inc(); Singleton.otherThings(); Singleton xSingleton = Singleton.getInstance(); xSingleton.importantThings(); } } class Outer { public Outer() { System.out.println("constructor of Outer"); } public static class Inner { private static String name = "Inner Static Class"; private Integer instanceInteger; public Inner() { instanceInteger = 0; System.out.println("constructor inner: " + name); } public void inc() { instanceInteger++; System.out.println(instanceInteger); } } } interface DummyInterface { class DoSome { public static void say() { System.out.println("say from DoSome"); } static { System.out.println("code running in an interface"); } } } class Singleton { private static class Holder { static { System.out.println("static construct in Singleton.Holder"); } public static Singleton instance = new Singleton(); } private Singleton() { System.out.println("private singleton constructor"); } public static Singleton getInstance() { return Holder.instance; } public static void otherThings() { System.out.println("other things"); } public void importantThings() { System.out.println("instance important things"); } }
可以用来作为延迟初始化和单例模式,静态内部类在没有使用时静态初始化本分不会进行,但使用到时才会初始化,而且这个机制是JVM保证的,即使在并发情况下依然能够正确运行(因为JVM必须考虑并发时多线程同时对一个类的初次使用的发生)。
匿名内部类
匿名内部类只能实现一个接口,或者继承一个类。因为是匿名的自然就没自己定义的构造函数名称了。
public class InnerClassTest { public static void main(final String args[]) { Keyboard enKeyboard = new Keyboard("english keyboard") { @Override public void language() { System.out.println("english"); } @Override public void keys() { System.out.println("104 keys"); } }; Keyboard cnKeyboard = new Keyboard("chinese keyboard", "pinyin") { @Override public void language() { System.out.println("chinese"); } @Override public void keys() { System.out.println("pinyin 101"); } }; enKeyboard.info(); cnKeyboard.info(); Say enSay = new Say() { @Override public void hello() { System.out.println("hello"); } @Override public void bye() { System.out.println("good bye"); } }; Say cnSay = new Say() { @Override public void hello() { System.out.println("你好"); } @Override public void bye() { System.out.println("再见"); } }; enSay.hello(); cnSay.hello(); } } interface Say { void hello(); void bye(); } abstract class Keyboard { private final String name; public Keyboard(final String name) { this.name = name; } public Keyboard(final String name, final String extra) { this(name + "-" + extra); } public void info() { System.out.println(name); language(); keys(); } public abstract void language(); public abstract void keys(); }
当继承类是调用父类构造函数和一般的继承不一样(不是用super)。
局部内部类
仅在相应的作用域内有效,如果创建了一个实现了接口的实例,则再外部也可以通过接口约定使用,但是除此之外其他成员和方法无法访问了。和一般内部类一样不能在里面定义静态字段和方法,final修饰的基本类型除外。
1 public class InnerClassTest { 2 public static void main(final String args[]) { 3 Say impl = buildSayObj(); 4 5 impl.hello(); 6 7 } 8 9 private static Say buildSayObj() { 10 class Temp implements Say { 11 // public static final Integer x = 1; 12 @Override 13 public void hello() { 14 System.out.println("glad to see you!"); 15 } 16 17 // public static void other() { 18 // } 19 } 20 return new Temp(); 21 } 22 } 23 24 interface Say { 25 void hello(); 26 }