将一个类定义放在另外一个类、方法、做用域、匿名类等地方,就是内部类;内部类只能由外部类对象建立(经过外部方法或者.new方法),内部类对象建立时必须已经有一个外部类对象,而且与之链接(在内部类中会建立一个指向其外部类对象的引用),内部类能够访问到外部类对象的全部成员(包括private);java
如:示例代码中,在Parcel类的内部定义了内部类:Contents和Destination,在外部类方法ship中,能够直接建立内部类对象;可是在类外部,只能想建立外部类对象q,而后再经过该对象的contents()和to()方法来建立内部类对象;或者能够经过q2.new来调用内部类的构造方法;注意内部类的引用类型格式是OutClass.InnerClass;git
在内部类中,可使用OutClass.this来调用外部类对象(必然存在该对象,且和内部类对象链接);github
当内部类指定是private时,只能在类的内部调用该内部类,外部类对象没法访问而且建立该内部类对象;protected内部类则只有外部类的子类能够访问;经过建立private内部类能够经过类来完成一些工做而且隐藏全部类定义的细节;框架
在做用域中使用类时,超过做用域该类就没法被访问;如示例代码展现了几种内部类的定义方式;单元测试
注意在Parcel七、Parcel8中使用了匿名类,经过基类和接口建立匿名类对象的同时建立类的定义,匿名类的意义为:建立继承自一个基类或者接口的匿名类对象,默认添加继承关系;测试
使用匿名类时,不要忘记类定义后的分号;优化
若是如Parcel9中匿名内部类使用外部类中定义的对象,那么编译器会要求其参数引用是final的(如外部类方法destination中),不然将会出错;this
如AnonymousConstructor中,能够经过对实例的初始化实现一个匿名构造器功能;若是匿名类中没有直接使用i,则外部方法的参数不须要指定是final的;又如Parcel10中,因为if语句并不能在初始化时被执行,全部匿名类中对实例化的初始化实际效果就至关因而构造器;spa
匿名内部类只能实现接口和扩展类的其中一个;对象
在上一节最后使用到了工厂方法,将多个子类的构造器使用通用接口包装,而后对该接口进行调用便可实现多种子类的构造活动;而因为子类的构造器是不可见的,而且没有必要建立做为工厂的具名类,而只须要一个单一的工厂对象便可,使用匿名内部类改进后的代码为:示例代码;改进后的代码更加具备实际意义(消除了每一个子类单独的工厂类);
当不须要内部类和外部类的对象之间有联系时,能够将内部类声明为static,这种方式称为嵌套类;
嵌套类因为申明了static,并不像内部类同样,具备一个外部类的对象引用;一旦建立了嵌套类,就说明了嵌套类中的对象并不须要外部类对象的引用,而且不能从嵌套类的对象中访问非静态的外部类对象,没有new方法和this;
正常状况下,接口中是不能放置任何代码的,可是嵌套类能够做为接口的一部分;因为接口中全部的成员都将变为static和public,类也同样,一旦放入接口中,该类就是一个嵌套类;如:示例代码;
经过在每一个类中添加main()方法能够完成单元测试的工做,可是这样作会将带上已经编译后的额外代码,经过将main方法放置到嵌套类中,能够优化单元测试,如: 示例代码;在编译以后,每一个嵌套类(以及内部类)都会产生一个OutClass$InnerClass.class相似的文件,单元测试的类也同样,只要运行java TestBed\$Tester便可进行测试工做;只要在发布时,简单的将该文件删除就能够删除单元测试的内容;
1. 实现多重继承
经过使用内部类独立继承自一个接口的实现完成多重继承的工做;同时因为外部类不能同时继承自两个基类,只能经过内部类完成多重继承;
如:示例代码中,A、B接口能够经过X、Y两种方式实现多重继承;C、D抽象类则只能经过Z来实现多重继承;
2. 在同一个外围类中,可使用多个内部类以不一样的方式实现同一个接口,这样能够在同一个类中完成一个接口的多种实现而不用另外建立一个新类,经过这种方式能够造成更加清晰的代码结构;如Sequence中实现ForwardSelector和ReverseSelector两种Selector接口实现:
interface Selector{
char getCharAt(int i);
}
class Sequence {
private String s;
Sequence(String s){
this.s = s;
}
private Selector fs(){
return new Selector(){
public char getCharAt(int i){
return s.charAt(i);
}
};
}
private Selector rs(){
return new Selector(){
public char getCharAt(int i){
int index = s.length() - i;
return s.charAt(index);
}
};
}
public char forwardSelector(int i){return fs().getCharAt(i);}
public char reverseSelector(int i){return rs().getCharAt(i);}
}
3. 实现回调
当一个外部类继承自一个接口的实现时,能够经过内部类完成接口的另外一个实现;并在适当的适合进行选择;如:示例代码 中,Callee2继承自MyIncrement,该类是接口Incrementable的一个实现,Callee2在某些时候须要使用继承来的方法,可是同时若是又有对该接口的另外实现,则能够经过新建内部类:Closure来完成,在Closure中返回一个Callee2的钩子,而且只能调用increment方法;而且经过设置内部类为private以及getCallbackReference()方法来隐藏实现过程;
4. 控制框架的编写;
控制框架每每由单个类来建立,用内部类来表示解决问题所必须的各类不一样的action(),从而使得实现的细节被封装起来;
1. 内部类的继承
因为内部类必需要和一个外部类的对象链接才能够被实例化,因此在继承一个内部类时,在构造方法中应该引用一个其外部类的对象,而且必须使用:
outclassReference.super()
的方式进行引用;如:
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner {
InheritInner(WithInner wi){
wi.super();
}
}
2. 内部类的覆盖
内部类和外部类是彻底独立的个体,各自在本身的命名空间内,如:示例代码中,在new BigEgg()时,先调用基类构造方法,打印New Egg()以及调用基类的内部类Yolk构造方法;此时,BigEgg()中的Yolk类没有起到做用,可是,若是添加BigEgg的构造方法,而且在其中建立Yolk内部类,则是能够完成覆盖的(实际上是生成了一个同名方法);真正完成内部类的覆盖,则须要使内部类也继承基类中的内部类,并完成改写操做,以下面的示例;
在示例代码中还能够看到内部类的初始化过程:
基类的内部类 -> 基类 -> 基类内部类 -> 子类内部类 -> 子类;
3. 局部内部类
当一个内部类在做用域内建立时,则会局部内部类,局部内部类不能访问说明符,不是外部类的一部分,而是做用域的一部分,因此能够访问做用域内的常量以及外部类的成员;
局部做用域常在方法体内定义,而且被用来重载构造器,或者生成多个内部类对象等;