1.为什么需要内部类
- 内部类继承自某个类或者实现某个接口,内部类可以访问创建它的外部类的对象或参数资源。
- 内部类能独立的实现某个接口,无论外围类是否实现了这个接口。
- 内部类使多重继承的解决方案变得更加完善。
下面的代码体现内部类实现多重继承,实现多个接口
从实现的观点来看,上面的外部类实现多重继承和内部类实现多重继承并没有什么区别interface A{} interface B{} //实现两个接口的单一类 class X implements A,B{} //实现两个接口的存在内部类的类 class Y implements A{ //在内部类实现另一个B接口 B makeB(){ //返回一个内部类 return new B() {}; } } public class MultiInterfaces { static void takesA(A a){} static void takesB(B b){} public static void main(String[] args) { //声明外部类 X x = new X(); Y y = new Y(); /** * 下面可以看到x和y都可以实现传入takes.()方法中,而这是两个接口 */ takesA(x); takesA(y); takesB(x); //获取内部类并传入takesB() takesB(y.makeB()); } }
- 上面是接口,所以实现多重继承没有问题,但如果要对抽象类或实际类实现多重继承,就需要使用内部类来实现
class D{} abstract class E{} //一个外部类继承了D就不能再继承其他的类了 class Z extends D{ //内部类实现多重继承 E makeE(){return new E(){};} } public class MultiImplementation { static void takesD(D d){} static void takesE(E e){} public static void main(String[] args) { Z z = new Z(); takesD(z); //获取Z中的E内部类对象 takesE(z.makeE()); }
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类相互独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口(多个内部类的实现)。
- 创建内部类的时刻并不依赖于外围类对象的创建。【这句好像与前面矛盾,楼主也不解】
- 内部类没有“is-a”关系,它就是一个实体。
2.闭包
- 闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。闭包包含的自由变量来自于定义闭包的环境。
- java中的闭包是一个对象,它记录了一些信息,这些信息来自于创建它的作用域。
- 通过闭包实现java中的回调
在Android中,闭包回调多用于Activity和其Fragment之间的传参,当然adapter有时也会与Activity传参使用回调接口的方法。interface Incrementable{ void increment(); } //外围类实现一个接口 class Callee1 implements Incrementable{ private int i = 0; //回调方法 @Override public void increment() { i++; System.out.println(i); } } class MyIncrement{ //一个同上面接口中的方法相同的方法 public void increment(){System.out.println("Other operation");} static void f(MyIncrement mi){mi.increment();} } //继承自上面的MyIncrement,因为MyIncrement中的方法与Incrementable接口中方法相同,实现回调接口的功能就不可以用普通的方式实现了 class Callee2 extends MyIncrement{ private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } //一个内部类,实现了一个回调接口,这是一个<span style="color:#FF0000;">闭包</span>,信息来自于创建它的作用域 private class Closure implements Incrementable{ //回调方法,也叫钩子 @Override public void increment() { /** * 执行外部类的方法,这样只要其他的对象获取到这个内部类接口,就可以调用这个回调方法,回调方法里面调用外部类的private参数, * 实现与外界的相通 */ Callee2.this.increment(); //使用一个private i ++; } } //返回一个回调接口,与外界相联系 Incrementable getCallbackReference(){ return new Closure(); } } class Caller{ private Incrementable callbackReference; public Caller(Incrementable cbh) {callbackReference = cbh;} void go(){callbackReference.increment();} } public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); //传入一个实现了回调接口的对象 Caller caller1 = new Caller(c1); //得到Callee2内部类声明的回调接口,并传入Caller对象,供Caller对象回调接口中的方法 Caller caller2 = new Caller(c2.getCallbackReference()); //执行回调 caller1.go(); caller1.go(); caller2.go(); } }
- 回调的价值在于它的灵活性,可以在程序运行时动态的决定调用什么方法。
3.内部类与继承
- 集成过程中的问题在于:内部类的构造器必须链接到只想外围类对象的引用,而到导出类(子类)不存在可连接的默认对象引用。
要解决这个问题,必须使用特殊的语法
class WithInner{ class Inner{} } //继承一个内部类 public class InneritInner extends WithInner.Inner{ public InneritInner(WithInner wi) { /** * 构造期内必须引用所继承的外围类对象 * 编译器才不会报错 */ wi.super(); } public static void main(String[] args) { //调用的时候,总之内部类必须与其外围类链接才能使用,即使继承 WithInner wi = new WithInner(); InneritInner ii = new InneritInner(wi); } }
4.覆盖(重写)内部类,像重写方法一样
- 子类可不可以覆盖父类中的内部类呢
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() */
- 当继承某个外围类的时候,内部类并没有发生特别的变化。这两个内部类是完全独立的两个实体,在各自的命名空间内。
- 明确的继承某个内部类,子类内部类继承父类的内部类,构造器的执行过程是先执行外围类的,再执行相关内部类的
class Egg2{ protected class Yolk{ public Yolk(){System.out.println("Egg2.Yolk()");} public void f(){System.out.println("Egg2.Yolk.f()");} } private Yolk y = new Yolk(); public Egg2(){System.out.println("New Egg2()");} public void insertYolk(Yolk yy){y = yy;} public void g(){y.f();} } public class BigEgg2 extends Egg2{ public class Yolk extends Egg2.Yolk{ public Yolk(){System.out.println("BigEgg2.Yolk()");} public void f(){System.out.println("BigEgg2.Yolk.f()");} } public BigEgg2() {insertYolk(new Yolk());} public static void main(String[] args) { Egg2 e2 = new BigEgg2(); e2.g(); } } /** * Egg2.Yolk() //这儿很奇怪,其实是先初始化最终父类的class域内的变量,这个是Egg2中的private Yolk y = new Yolk(); * New Egg2() * Egg2.Yolk() * BigEgg2.Yolk() * BigEgg2.Yolk.f() * */
5.局部内部类
- 在代码块里创建的内部类,比如在一个方法体内创建一个内部类
- 局部内部类不能有访问修饰词(private 等),因为它不是外围类的一部分。
- 可以访问当前代码块内的常量,以及此外围类的所有成员。
局部内部类的名字在方法外是不可见的,和匿名内部类没有名字是一样的,那为什么要用局部内部类??interface Counter{ int next(); } public class LocalInnerClass { private int count = 0; Counter getCounter(final String name){ //局部内部类 class LocalCounter implements Counter{ //重写构造器 public LocalCounter() { System.out.println("LocalCounter"); } @Override public int next() { System.out.println(name); return count ++; } } //返回此局部内部类 return new LocalCounter(); } Counter getCounter2(final String name){ //匿名内部类 return new Counter() { //匿名内部类的高仿构造器 {System.out.println("Counter");} @Override public int next() { System.out.println(name); return count ++; } }; } public static void main(String[] args) { LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Loc inner"), c2 = lic.getCounter2("Anonymous inner"); for (int i = 0; i < 5; i++) System.out.println(c1.next()); for (int i = 0; i < 5; i++) System.out.println(c2.next()); } }
唯一的理由是内部类需要一个已命名的构造器或者重写构造器,而匿名内部类并不能实现构造器,它只能用于实例初始化。
另一个理由是需要不止一个该内部类的对象。
6.内部类标识符
- 每个类都会产生一个.class文件,其中包含了如何创建该类的对象的全部信息。
- 内部类也必须生成一个.class文件已包含他们的Class对象信息。
- 类文件的命名有严格的规则。
外围类的名字,加上$,在加上内部类的名字。例如LocallnnerClass生成的.class文件包括:
Counter.class
LocalInnerClass$1.class
LocalInnerClass$1LocalCounter.class
LocalInnerClass.class - 如果内部类是匿名的,编译器会简单的产生一个数字作为其标识符。
如果内部类是嵌套在别的内部类之中,只需直接将它们的名字简单加在其外围类标识符与$的后面
7.应用程序框架
- 应用程序框架——解决特定的问题的框架。
- 应用程序框架就是被设计用以解决某类特定问题的一个类或一组类。
- 要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。
在覆盖的方法中编写代码定制应用程序提供的通用解决方案,以解决特定问题。——模板方法设计模式。
设计模式总是将变化的事物和不变的事物分离开来。其中模板方法是保持不变的事物,可覆盖的方法就是变化的事物。
8.内部类和控制框架
- 控制框架是一类特殊的应用程序框架,用来解决响应事件的需求。
- 驱动系统:用来响应事件的系统。
public abstract class Event { private long eventTime; protected final long delayTime; public Event(long delayTimg){ this.delayTime = delayTimg; start(); } /** * 启动计时器的方法,调用此方法可以重新启动计时器,也能够重复使用Event对象。 * 重复一个事件,就在action中调用start()方法 */ public void start(){ eventTime = System.nanoTime() + delayTime; } //何时可运行action()方法。覆盖此方法,实现Event除基于事件以外的其他因素出发action方法 public boolean ready(){ return System.nanoTime() >= eventTime; } //动作执行方法 public abstract void action(); }
public class GreenHouseControls extends Controller{ private boolean light = false; //一个内部类 继承Event事件类控制开灯 public class LightOn extends Event{ public LightOn(long delayTimg) {super(delayTimg);} @Override public void action() { light = true; } public String toString(){return "Light is on";} } //一个内部类 继承Event事件类控制开灯 public class LightOff extends Event{ public LightOff(long delayTimg) {super(delayTimg);} @Override public void action() { light = false; } public String toString(){return "Light is off";} } }
public class GreenhouseController { public static void main(String[] args) { GreenHouseControls gc = new GreenHouseControls(); //添加一个事件 1秒后执行 gc.addEvent(gc.new LightOff(1000)); } }
总结:
- 内部类使得多重继承更加的完善。