可以将一个类的定义放在另一个类的定义内部,这就是内部类。
- 内部类拥有其外围类的所有元素的访问权(包括private等)
- 在内部类中,如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。
- 想要创建内部类的对象,必须要先有其外部类的对象,通过外部类对象的.new方法创建内部类对象。(内部类中可以有内部类进行多层嵌套,创建对象的方法以此类推)例:
public Class A{ public Class B{...} publci static void main(String[] arg){ A a = new A(); A.B b = a.new b(); } }
- 内部类可以实现某个接口的对象,使其外部类拥有此接口的引用,这与向上转型为这个对象的基类实质上是一样的。
匿名内部类
public Class A{
public B getB(){
return new B(){
private int i=0;
}
}
publci static void main(String[] arg){
A a = new A();
B b = a.getB();
}
}
- 要先有个B类,其中B类可以是一个接口,也可以是个抽象类,也能是个普通类
- getB()方法将返回值的生成与这个返回值的类的定义结合在一起,另外这个类是匿名的,它没有名字。他看起来像是你正要创建一个B对象,然后你却说:“等等,我想在这插入一个类的定义”。上面的代码相当于:
public Class A{ public Class C implements B{ private i = 0; } public B getB(){ return new C(); } publci static void main(String[] arg){ A a = new A(); B b = a.getB(); } }
当你要在匿名内部类中使用外部类的对象时,编译器会要求其参数的引用是final的。如:
public Class A{
public B getB(final int a){
return new B(){
private int i = a;
}
}
public static void main(String[] arg){
A a = new A();
B b = a.getB();
}
}
- 使用匿名内部类优化工厂方法
interface Service{
void method1();
void method2();
}
inteface ServiceFactory{
Service getService();
}
Class AService implements Service{
public void method1(){print("A1");}
public void method2(){print("A2");}
public static ServiceFactory factory =
new ServiceFactory(){
public Service getService(){
return new AService();
}
}
}
Class BService implements Service{
public void method1(){print("B1");}
public void method2(){print("B2");}
public static ServiceFactory factory =
new ServiceFactory(){
public Service getService(){
return new BService();
}
}
}
嵌套类
如果将内部类声明为static,这样的内部类我们称为嵌套类。普通内部类对象隐式地保留了一个指向创建它的外围对象的引用,而嵌套类不需要内部类对象与其外围类对象之间有联系。因此嵌套类意味着:
- 要创建嵌套类对象,并不需要其外围类的对象。
- 不能从嵌套类的对象中访问非静态的外围类对象。
- 普通的内部类的字段与方法只能放在类的外部层次上,因此普通的内部类不能有static数据与字段,也不能包含嵌套类,但是嵌套类中可以有static字段与数据,也可以进行进一步的嵌套类。
- 普通的内部类可以通过一个特殊的this引用链接到其外围类对象,嵌套类则不行,这使得嵌套类更类似于一个static方法。
接口内部的类
一般来说我们不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分。放置在接口中的任何类都自动地是public和static的,因为类是static的,你只是将嵌套类置于接口的命名空间,因此这不违反借口规则。你甚至可以在内部类中实现其外围接口。
public interface A{
void printA();
Class Test implements A{
public void printA(){print("AAA");}
publci static void main(String[] arg){
new Test().printA();
}
}
}
如果你想要创建某些公共代码,使他能被某个借口的所有不同实现所共用,那么嵌套类显然很方便。
为什么需要内部类
内部类最吸引人的原因是:
- 每个内部类都能对立地继承一个(接口的)实现,所以无论外围类是否已经继承过来某个(接口的)实现,对于内部类都没影响
- 内部类允许继承多个非接口类型(类或抽象类)
这使得我们可以灵活地实现“多重继承”。
使用内部类,还可以获得其他一些特性:
1)内部类可以有多个实例,每个实例都有自己的状态信息,并与外围对象信息相互独立
2)在单个外围类中可以让多个内部类以不同的方式实现同一个接口或继承同一个类
3)内部类没有令人迷惑的“is-a”关系,它就是一个独立的实体
内部类的继承
因为内部类的构造器连接指向外围类对象的引用,所以在继承内部类的时候事情会有点复杂。重点在于指向外围类对象的引用必须被初始化,而导出类中不再存在可连接的默认对象。因此要使用特殊语法说明它们之前的关联:
Class Aa{
Class Bb{ }
}
public Class Cc extends Aa.Bb{
Cc(Aa aa){
aa.super();
}
public static void main(String[] args){
Aa a = new Aa();
Cc c = new Cc(a);
}
}
Bb是Aa的内部类,当Cc想要继承Bb时必须在构造器中使用如下语法:
enclosingClassReference.super(); 即例子中的aa.super();
内部类的覆盖
一个类继承外围类并重新定义外围类中的内部类,内部类不会被覆盖。例:
class Egg{
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk()");
}
}
public Egg(){
System.out.println("New Egg");
y = new Yolk();
}
}
public class BigEgg extends Egg{
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk");
}
}
public static void main(String[] args){
new BigEgg();
}
}
输出如下:
New Egg
Egg.Yolk()
我们重写的类并没有起作用,两个内部类是完全独立的两个实体,要想覆盖内部类,则要通过继承extends来进行覆盖
class Egg{
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk()");
}
}
public Egg(){
System.out.println("New Egg");
y = new Yolk();
}
}
public class BigEgg extends Egg{
public class Yolk extends Egg.Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk");
}
}
public static void main(String[] args){
new BigEgg();
}}
局部内部类
可以在代码块里创建内部类,典型的方法就是在一个方法体中创建。
- 局部内部类不能有访问说明符,因为它不是外围类的一部分;但它可以访问当前代码块内的常量以及此外围类的所有成员。
局部内部类与匿名内部类都能在方法体中创建,那我们为什么要使用局部内部类而不是匿名内部类呢?
- 局部内部类有自己的构造器,匿名内部类只能用于实例的初始化
- 因此当我们要使用多个该内部类的对象时就应选择局部内部类
以上总结自《Java编程思想(Thinking in Java)》——YayayaHong