内部类之二_高级篇
来源:互联网 发布:php 小项目 不用 mvc 编辑:程序博客网 时间:2024/06/08 06:38
1. 嵌套类
如果不需要内部类与外围类对象之间有联系,那么可以将内部类声明为static–这就是嵌套类。
当内部类是static时就意味着:
1)要创建嵌套类的对象,并不需要外围类对象。
2)不能从嵌套类的对象中访问非静态的外围类对象。
3)普通内部类不能有static数据和字段。
public class parcel11 { private static class ParcelContents implements Contents{ private int i = 11; public int value() {return i;} } protected static class ParcelDestination implements Destination{ private String label; private ParcelDestination(String whereto){label = whereto;} @Override public String ReadLable() { return label;} public static void f(){} static int x = 10; static class AnotherLevel{ public static void f(){} static int x = 10; } } public static Destination destination(){return new ParcelDestination("java");} public static Contents constents(){return new ParcelContents();} public static void main(String[] args) { Contents c = constents(); Destination d = destination(); }}
在main函数中没有定义parcel11对象的必要,而是使用static方法返回Contents和Destination对象的引用。嵌套类不能使用this来连接到外部类的引用,这样这个内部类就像一个static方法。
1.1 接口内部的类
一般在接口中不能放置任何代码,如果嵌套类在接口的内部会自动转换为public和static。只是将嵌套类放置接口的命名空间内,所以不违反接口的规则。甚至可以在内部类中实现其外围接口。如:
public interface ClassInInterface { void howdy(); class Test implements ClassInInterface{ @Override public void howdy() { // TODO Auto-generated method stub System.out.println("java"); } public static void main(String[] args){ new Test().howdy(); } }}
如果你想要创建某些公共代码,使得他能被某个接口的所有不同实现所公用,那么使用内部类就很方便。
嵌套类还可以用来测试代码
public class TestBed { public void f(){System.out.println("f()");} public static class Tester{ public static void main(String[] args) { // TODO Auto-generated method stub TestBed t = new TestBed(); t.f(); } }}//output:f()
这样会生成一个独立的类TestBed$Tester,发布这个程序的时候删除这个类就行了。
1.2 从多层嵌套类中访问外部类的成员
一个内部类嵌套多少层并不重要,他能透明的访问所有它所嵌入的外围类的成员。
class MNA{ private void f(){} class A{ private void g(){} public class B{ void h(){ g(); f(); } } } }public class MultiNestAccess { public static void main(String[] args) { // TODO Auto-generated method stub MNA mna = new MNA(); MNA.A mnaa = mna.new A(); MNA.A.B mnaab = mnaa.new B(); mnaab.h(); }}
2.为什么需要内部类
内部类最吸引人的原因是:每个内部类都能独立的继承一个接口的实现,所以无论外围类是否已经继承某个接口的实现,对内部类没有影响。
内部类允许继承多个没接口类型(类或者抽象类)
interface A{}interface B{}class X implements A,B{}class Y implements A{ B getB(){ return new B(){}; }}public class MultiInterface { static void taskA(A a){} static void taskB(B b){} public static void main(String[] args) { // TODO Auto-generated method stub X x = new X(); Y y = new Y(); taskA(x); taskA(y); taskB(x); taskB(y.getB()); }}
这里看到了使用单一类和使用内部类的两种方式,看上去并没有什么不同,都能实现。
class D{}abstract class E{}class Z extends D{ E getE(){return new E(){};}}public class MultiImplementation { static void tasksD(D d){} static void tasksE(E d){} public static void main(String[] args) { // TODO Auto-generated method stub Z z = new Z(); tasksD(z); tasksE(z.getE()); }}
这样就实现了多继承。
使用内部类还可获得其他一些特性:
1)内部类可有有多个实例,每个实例都有自己的状态信息,与外围类对象的信息相独立。
2)在单个外围类中,可以让内部类以不同的方式实现同一个接口,或继承同一个类。稍后介绍。
3)创建内部类的对象时刻并不需要外围类对象的创建。
4)内部类没有“is-a”关系,他是独立的实体。
2.1 闭包与回调
闭包:是一个可以调用的对象,它记录了一些信息,这些信息来源于创建它的作用域。内部类就是面向对象的闭包,它包含外围了对象的信息和对外围类对象的应用,内部类有权操作所以成员包括private成员。
通过回调,对象能携带一些信息,这些信息允许在稍后的某个时刻调用初始的对象。
闭包示例:
package inner_class;import static net.lijun.util.Print.*;import jdk.nashorn.internal.codegen.CompilerConstants.Call;interface Incrementable{ void increment();}//这里只是实现了Incrementableclass Callee1 implements Incrementable{ private int i = 0; public void increment() { i++; println(i); }}class MyIncrement{ public void increment(){println("Other operation");} static void f(MyIncrement mi){mi.increment();}}//如果你的类需要实现Incrementable接口就必须使用内部类class Callee2 extends MyIncrement{ private int i = 0; public void increment(){ super.increment(); i++; println(i); } private class Closure implements Incrementable{ public void increment() { Callee2.this.increment(); } } Incrementable getCallbackReference(){return new Closure();}}class Caller{ private Incrementable callbackReference; 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); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); }}
这个例子解释了外围类实现一个接口与内部类实现一个接口的区别,Calee1是简单的解决方式。Calee2继承自‘MyIncrement’后者有个完全不同的’increment’,并且与Incrementable接口期望的方法increment完全不相干,所以Calee2继承了MyIncrement就不能为了Incrementable的用途而覆盖了increment方法,于是只能用内部类独立的实现Incrementable。
内部类Closure实现了Incrementable,以提供返回Callee2的一个钩子。
Caller的构造器需要一个Incrementable的应用作为参数,然后在以后的某个时刻,Caller可以回调Calee类。
2.2内部类与控制框架
应用程序框架是被设计用来解决某类特定问题的一个类或者一组类。
控制框架是特殊的应用程序框架,它用来解决相应事件的需求。在GUI中用的比较多。
首先接口描述了要控制的事件,因为默认的行为是基于时间去执行控制的,所以使用抽象类代替接口。
例:
public abstract class Event { private long eventTime; protected final long delayTime; public Event(long delayTime){ this.delayTime = delayTime; start(); } public void start(){ eventTime = System.nanoTime()+delayTime; } public boolean ready(){ return System.nanoTime()>=eventTime; } public abstract void action();}
当运行Event时随即运行start()方法,那么就可以捕获当前时间加上延时时间,生成触发时间,
ready()告诉你何时可以运行action()方法,
下面的文件包含了一个用来管理并触发事件的实际控制框架。Event对象被保存在List中.
import java.util.*;public class Contorller { private List<Event> eventlist = new ArrayList<Event>(); public void addEvent(Event e){eventlist.add(e);} public void run(){ while(eventlist.size()> 0){ for(Event e:new ArrayList<Event>(eventlist)){ if(e.ready()){ System.out.println(e); e.action(); eventlist.remove(e); } } } }}
run()方法循环遍历eventlist寻找就绪的ready,要运行的对象。对每一个就绪的事件,使用toString打印对象,调用其action方法,然后冲队列中移除。
到现在你并不知道Event的action方法是什么。这就是这个设计的关键所在。使变化的事物与不变的事物分离开来。各种不同的Event所具有的不同行为,而你通过创建Event表现这些不同的行为。
这就是内部类要做的事情:
1)控制框架的完整实现是由单个类创建的,从而使实现的细节被封闭了起来。内部类用来表示解决问题所必须的各种不同的action()。
2)内部类可以访问外围类的任意成员,所以可以避免这种实现变得笨拙。
下面是一个温室系统的例子:
使用内部类可以在单一的类里面产生对同一基类Event的多种导出版本。
package inner_class;public class GreenhouseControls extends Contorller { private boolean light = false; public class LightOn extends Event{ public LightOn(long delayTime) { super(delayTime); } @Override public void action() {light = true;} public String toString(){return "Light is on";} } public class LightOff extends Event{ public LightOff(long delayTime) {super(delayTime);} @Override public void action() {light = false;} public String toString(){return "Light is off";} } private boolean water = false; public class WaterOn extends Event { public WaterOn(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here. water = true; } public String toString() { return "Greenhouse water is on"; } } public class WaterOff extends Event { public WaterOff(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here. water = false; } public String toString() { return "Greenhouse water is off"; } } private String thermostat = "Day"; public class ThermostatNight extends Event { public ThermostatNight(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here. thermostat = "Night"; } public String toString() { return "Thermostat on night setting"; } } public class ThermostatDay extends Event { public ThermostatDay(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here. thermostat = "Day"; } public String toString() { return "Thermostat on day setting"; } } public class Bell extends Event { public Bell(long delayTime) { super(delayTime); } public void action() {addEvent(new Bell(delayTime));} public String toString() { return "Bing!"; } } public class Restart extends Event { private Event[] eventList; public Restart(long delayTime, Event[] eventList) { super(delayTime); this.eventList = eventList; for(Event e : eventList) addEvent(e); } public void action() { for(Event e : eventList) { e.start(); // Rerun each event addEvent(e); } start(); // Rerun this Event addEvent(this); } public String toString() { return "Restarting system"; } } public static class Terminate extends Event { public Terminate(long delayTime) { super(delayTime); } public void action() { System.exit(0); } public String toString() { return "Terminating"; } }}
下面通过创建一个GreenhouseControls的一个对象,并添加各种不同的Event对象来配置该系统。
public class GreenhouseControl { public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); gc.addEvent(gc.new Bell(900)); Event[] eventList = { gc.new ThermostatNight(0), gc.new LightOn(200), gc.new LightOff(400), gc.new WaterOn(600), gc.new WaterOff(800), gc.new ThermostatDay(1400) }; gc.addEvent(gc.new Restart(2000, eventList)); if(args.length == 1) gc.addEvent(new GreenhouseControls.Terminate(new Integer(args[0]))); gc.run(); }}/* Output:Bing!Thermostat on night settingLight is onLight is offGreenhouse water is onGreenhouse water is offThermostat on day settingRestarting systemTerminating
3.内部类的继承
因为内部类的构造器必须链接到其指向外围类的应用,所以在继承内部类是事情会变得复杂。问题在于那个指向外围类的“秘密”应用必须被初始化。而在导出类中不再存在可链接的默认对象。解决这个问题需要用到特殊的语法来说清楚他们之间的关联。
class WithInner{ class Inner{}}public class InheritInner extends WithInner.Inner{ //InheritInner( ){}不能被编译 InheritInner(WithInner wi){ wi.super(); } public static void main(String[] args) { // TODO Auto-generated method stub WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); }}
可以看到,InheritInner 只继承内部类,不是外围类。但是要生成构造器是默认的构造器不好用。而且不能只传一个外围类对象的应用,必须在构造器中使用encolsingClassReference.super();这样才提供了必要的引用,程序才能编译通过。
4.内部类可以被覆盖吗
覆盖内部类就好像内部类是外围类的一个方法,其实并不起什么作用。
import static net.lijun.util.Print.*;class Egg{ private Yolk y; protected class Yolk{ public Yolk(){println("Egg.Yolk()");} } public Egg(){ println("new Egg"); y = new Yolk(); }}public class BigEgg extends Egg{ public class Yolk{ public Yolk(){println("BigEgg.Yolk()");} } public static void main(String[] args) { new BigEgg(); }}
可以看到当继承某个外围类时内部类并没有发生任何变化,这两个内部类是完全不同的两个实体,各自在自己的命名空间。明确的继承某个内部类是可以的。
import static net.lijun.util.Print.*;class Egg2 { protected class Yolk { public Yolk() { println("Egg2.Yolk()"); } public void f() { println("Egg2.Yolk.f()");} } private Yolk y = new Yolk(); public Egg2() { 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() { print("BigEgg2.Yolk()"); } public void f() { print("BigEgg2.Yolk.f()"); } } public BigEgg2() { insertYolk(new Yolk()); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Egg2 e2 = new BigEgg2(); e2.g(); }}
5.局部内部类
前面说过,可以在代码块里创建内部类,典型的创建方式是在一个方法体里创建。局部内部类里面不能有访问说明符,因为不是外围类的一部分;但是可以访问当前代码块里的常量,以及此外围类的所以成员。
下面的例子对局部内部类与匿名内部类的创建进行了比较。
import static net.lijun.util.Print.*;interface Counter{ int next();}public class LocalInnerClass { private int count = 0; Counter getCounter(final String name){ class LocalCounter implements Counter{ public LocalCounter(){ println("LocalCounter()"); } public int next() { print(name); return count++; } } return new LocalCounter(); } Counter getCounter2(final String name) { return new Counter() { { println("Counter()"); } public int next() { print(name); return count++; } }; } public static void main(String[] args) { LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner "); for(int i = 0; i < 5; i++) println(c1.next()); for(int i = 0; i < 5; i++) println(c2.next()); }}
Counter返回序列中的下一个值。我们使用了局部内部类和匿名内部类,他们有相同的行为和能力,既然局部内部类的名字在方法外是不可见的,那我们为什么不使用匿名内部类呢?唯一的理由是,我们需要一个已命名的构造器,或者需要重载构造器,而匿名内部类只适用于实例初始化。
另一个理由是:需要不止一个该内部类对象。
6.内部类标识符
内部类的命名规则是外围类的名字加上’$’再加上内部类的名字。如果内部类是匿名的,编译器会随机产生一个数字作为其标识符,
7.总结
接口和内部类可以解决C++中多继承的问题,此时读者应该了解到他们的语法和语言,在遇到实际情况时我们会最终理解它。
- 内部类之二_高级篇
- 内部类之二
- Java高级特性之---内部类
- 【面向对象(高级)】_匿名内部类笔记
- 【面向对象(高级)】_匿名内部类笔记
- C++ _内部类
- 0009_内部类
- 面向对象(高级篇之Object类与包装类与匿名内部类)
- Java高级特性之继承一个内部类
- Java高级特性之new一个内部类
- Java类高级之匿名内部类总结(转载)
- Java类高级特性之静态内部类
- 高级类特性----内部类
- 内部类学习(二)
- 内部类(二)
- 内部类(二)
- 二,java内部类
- 内部类(二)
- MTK android 常用修改点
- MyEclipse中报错:Communications link failure due to underlying exception
- sizeof('\0') strlen("\0")
- C++ 开发 Web 服务框架
- LinkedTransferQueue原理理解
- 内部类之二_高级篇
- bzoj2763[JLOI2011]飞行路线 分层图最短路
- Java中 super关键字
- BZOJ P3212 Pku3468 A Simple Problem with Integers
- 杭电oj(java版)——1092 A+B for Input-Output Practice (IV)
- 前后端共享文件的批处理
- 在ubuntu下,把安装的中文输入法切换出来
- 导出Excel功能-从服务端到浏览器的简单处理
- CSS属性