将一个类的定义放在另一个类的定义内部,这就是内部类,外部类没有访问内部类成员的权限,它只能通过创建内部类的对象来调用内部类的成员。
创建内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Essay { class Content{ private String content = "ABC"; public String getContent(){ return content; } } public Content createContent(){ return new Content(); } public static void main(String[] args) { Essay essay = new Essay(); Content content = essay.createContent(); System.out.println(content.getContent()); } }
|
链接到外部类
当创建一个非静态内部类的对象时,它不能像正常创建对象那样去创建,它必须由一个外部类的对象去创建。所以,一个内部类会自动地保存创建它的外部类的引用,即它会自动拥有外部类的所有访问权(包括private成员)。如下,这是一个迭代器设计模式的例子,SequenceSelector可以访问Sequence中的items成员,通过items来实现对序列的end(判断是否结束)、current(取出当前元素)、next(下移)操作,这里的SequenceSelector对于Sequence来说是一个辅助类,它为Sequence提供了一个迭代的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| interface Selector{ void next(); Object current(); boolean end(); public Sequence getSequence(); } public class Sequence { private Object[] items; private int count = 0;
public Sequence(int size) { items = new Object[size]; } public void add(Object x){ if (count<items.length) items[count++] = x; } protected class SequenceSelector implements Selector{ private int next = 0; @Override public void next() { if (next<items.length) next++; } public Sequence getSequence(){ return Sequence.this; }
@Override public Object current() { return items[next]; }
@Override public boolean end() { return next == items.length; } }
public Selector getSelector(){ return new SequenceSelector();
}
public static void main(String[] args) { Sequence s = new Sequence(10); for (int i = 0; i < s.items.length; i++) { s.add("ABC"); } Selector selector = s.getSelector(); while (!selector.end()){ System.out.print(selector.current()+" "); selector.next(); } } }
|
内部类的访问修饰符
跟普通类一样,内部类有本类、子类、包的这几个范围,根据修饰符的不同而有所变化。若把内部类声明为private,该内部类就只能在外部类的范围内进行访问。
访问修饰符 |
本类 |
同包(无论是否继承) |
不同包的子类 |
不同包的非子类 |
public |
true |
true |
true |
true |
protected |
true |
true |
true |
|
默认 |
true |
true |
|
|
private |
true |
|
|
|
使用.this与.new
在内部类中可以使用外部类的类名.this
来表示外部类的引用,在外部类中,如果想要在创建某一个外部类对象关联的内部类,则可以使用外部类的对象名.new
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class DotThis { void f(){ System.out.println("DotThis.f()"); } public class Inner{ public DotThis outer(){ return DotThis.this; } } public void a(){ Inner inner = new Inner(); } public static void main(String[] args) { DotThis dotThis = new DotThis(); Inner inner = dotThis.new Inner();
DotThis outer = inner.outer(); System.out.println(dotThis==outer); } }
|
在不同的位置嵌入内部类
- 定义在方法中的内部类
- 定义在作用域内的类,比如if作用域
- 实现了接口的匿名类
- 扩展了非默认构造器的匿名类
- 执行字段初始化的匿名类
- 通过实例初始化实现构造的匿名类
匿名内部类
没有类名的类,即匿名类,一般这种类只能定义在别的类内部,所以,也叫匿名内部类,它是用来创建一个继承自某个类的匿名类的对象,通过new表达式会自动向上转型为对父类的引用。如下content方法中创建了一个继承自Contents的匿名类对象,该对象在返回时,自动向上转型为Contents类型。即匿名内部类是继承机制的一个简写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| interface Contents{ int value(); } public class Parcel { public Contents content(){ return new Contents() { int value = 10; @Override public int value() { return value; } }; } public static void main(String[] args) { Parcel parcel = new Parcel(); Contents content = parcel.content(); System.out.println(content.value()); } }
|
通过有参构造来创建匿名内部类
由于Wrapping类中只有一个带参的构造器,在创建继承自Wrapping的匿名内部类对象时,如果不传参数,程序会因找不到构造器而报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Wrappinng{ private int i; public Wrappinng(int x){ i = x; } public int value(){ return i; }; } public class Parcel2 { public Wrappinng wrappinng(int x){ return new Wrappinng(x){ @Override public int value() { return super.value() * 10; } }; }
public static void main(String[] args) { Parcel2 parcel2 = new Parcel2(); Wrappinng wrappinng = parcel2.wrappinng(10); System.out.println(wrappinng.value()); } }
|
通过实例初始化来实现匿名内部类的构造效果
由于匿名内部类没有类名,所以不能为该类去定义一个构造器,但可以使用初始化实例来实现构造器的效果,如下{…}就是一个初始化代码,可以起到构造的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Destination{ public String readLabel(){return "";} } public class Parcel3 { public Destination destination(final String dest, final float price){ return new Destination(){ private int cost; { cost = Math.round(price); if (cost>100) System.out.println("Over budget"); } private String label = dest; @Override public String readLabel(){return label;} }; } public static void main(String[] args) { Parcel3 parcel3 = new Parcel3(); Destination destination = parcel3.destination("ABC", 110F); System.out.println(destination.readLabel()); } }
|
使用匿名内部类来简化工厂方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| interface Game{ boolean move(); } interface GameFactory{ Game getGame(); } class Checkers implements Game{ private int move = 0; private static final int MOVE = 3; public static GameFactory gameFactory = new GameFactory(){ @Override public Game getGame() { return new Checkers(); } }; @Override public boolean move() { System.out.println("Checkers Move "+move); return ++move != MOVE; } } class Chess implements Game{ private int move = 0; private static final int MOVE = 4; public static GameFactory gameFactory = new GameFactory() { @Override public Game getGame() { return new Chess(); } }; @Override public boolean move() { System.out.println("Chess Move "+move); return ++move != MOVE; } } public class Games { public static void playGame(GameFactory gameFactory){ Game game = gameFactory.getGame(); while (game.move()); } public static void main(String[] args) { playGame(Checkers.gameFactory); playGame(Chess.gameFactory); } }
|
嵌套类
如果不需要内部类对象与其外部类的对象之间有联系,那可以将内部类声明为static,这通常称为嵌套类。嵌套类一般表示着三种含义:1、不需要外部类的对象;2、不能从嵌套类的对象中访问非静态的外部类成员;3、普通内部类不能有static数据和static方法,但嵌套类可以包含这些东西。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Parcel4 { private static int i; private static class ParcelContent implements Contents{ private int i = 10; @Override public int value() { return i; } } private static class ParcelDestination extends Destination { private String label;
private ParcelDestination(String label) { this.label = label; i = 20; }
@Override public String readLabel() { return label+i; } static class AnotherLevel{ static int x = 10; public static void f(){} } } public static Contents contents(){ return new ParcelContent(); } public static Destination destination(){ return new ParcelDestination("ParcelDestination1"); } public static void main(String[] args) { Contents c = contents(); Destination d = destination(); System.out.println(c.value()); System.out.println(d.readLabel()); ParcelDestination parcelDestination = new ParcelDestination("ParcelDestination2"); System.out.println(parcelDestination.label); System.out.println(ParcelDestination.AnotherLevel.x); } }
|
闭包与回调
关于闭包,有下面几种解释:
- 闭包是一个可调用的对象,它包含了一些信息,这些信息来自于创建它的作用域。
- 闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
- 是引用了自由变量的函数。这个函数通常被定义在另一个外部函数中,并且引用了外部函数中的变量。
在Java中,闭包是通过“接口和内部类来实现的”,可以看出,内部类是面对对象的闭包,它不仅包含了外部类对象的信息,还自动拥有了一个指向此外部类对象的引用,即内部类可以操作所有成员。因此可以把非静态内部类当成面向对象领域的闭包。那么,通过这种仿闭包的非静态内部类可以很方便地实现回调,这是一种非常灵活的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| interface Incrementable{ void increment(); }
class Callee1 implements Incrementable{ private int i = 0; @Override public void increment() { i++; System.out.println("Callee1:"+i); } }
class MyIncrement{ private int i = 0; public void increment() { i++; System.out.println("MyIncrement:"+i); } } class Callee2 extends MyIncrement{ private class Closure implements Incrementable{ private int i = 0; @Override public void increment() { Callee2.this.increment(); i++; System.out.println("Closure:"+i); } } public Incrementable getIncrementable(){ return new Closure(); } } class Caller{ private Incrementable incrementable;
public Caller(Incrementable incrementable) { this.incrementable = incrementable; } public void go(){ incrementable.increment(); } } public class Callbacks { public static void main(String[] args) { Caller caller1 = new Caller(new Callee1()); Caller caller2 = new Caller(new Callee2().getIncrementable()); caller1.go(); caller2.go(); } }
|
内部类的继承
当一个类是继承自内部类时,它的一部分成员是依赖于其继承的内部类的外部类的对象引用,所以,该类在构造时,必须传入一个指向外部类对象的引用,指定由此外部类去创建内部类,具体做法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class WithInner{ class Inner{ public void call(){ WithInner.this.print(); } } public void print(){ System.out.println("Print in withinner."); } } public class InheritInner extends WithInner.Inner { InheritInner(WithInner withInner) { withInner.super(); } public void call(){ super.call(); } public static void main(String[] args) { WithInner withInner = new WithInner(); InheritInner inheritInner = new InheritInner(withInner); inheritInner.call(); } }
|
内部类标识符
由于在编译后,每个类都会产生一个.class文件,为了区别内部类和外部类,一般都在编译的时候在外部类的后面加一个”$”,比如,上面的例子生成的class文件是:WithInner$Inner.class,若是匿名内部类,系统会生成一个数字作为匿名类的名字。