Java编程思想读书笔记——内部类

来源:互联网 发布:销售智慧软件 编辑:程序博客网 时间:2024/05/16 11:59

第十章 内部类

将一个类定义在另一个类的内部,这就是内部类。

10.1 创建内部类

创建内部类的方式就是将类的定义置于外部类的里面即可。
如果需要在外部类之外创建内部类的对象,需要显式地指明对象的类型。

OuterClassName.InnerClassName innerClassObject = new OuterClassName.InnnerClassName();

10.2 链接到外部类

内部类可以拥有对外部类的所有元素的访问权(包括private声明的元素)。

迭代器设计模式:

public class Sequence {    private Object[] items;    private int next = 0;    public Sequence(int size){        items = new Object[size];    }    public void add(Object x){        if(next < items.length){            items[next++] = x;        }    }    private class SequenceSelector implements Selector{        private int i = 0;        @Override        public boolean end() {            return i == items.length;        }        @Override        public Object current() {            return items[i];        }        @Override        public void next() {            if(i < items.length){                i++;            }        }    }    public Selector selector(){        return new SequenceSelector();    }    public static void main(String[] args){        Sequence sequence = new Sequence(10);        for(int i = 0; i < 10; i++){            sequence.add(Integer.toString(i));        }        Selector selector = sequence.selector();        while(!selector.end()){            System.out.print(selector.current() + " ");            selector.next();        }    }}interface Selector{    boolean end();    Object current();    void next();}

10.3 使用.this和.new

在内部类中,如果需要使用外部类对象自身,可以使用.this语法。

public class DotThis {    void f(){        System.out.println("DotThis.f()");    }    public class Inner{        public DotThis outer(){            return DotThis.this;        }    }    public Inner inner(){        return new Inner();    }    public static void main(String[] args){        DotThis dt = new DotThis();        DotThis.Inner dti = dt.inner();        dti.outer().f();    }}

在外部类的外部中,如果需要创建内部类的对象,需要首先创建外部类对象,然后使用.new语法创建内部类对象。

public class DotNew {    public class Inner{    }    public static void main(String[] args){        DotNew dn = new DotNew();        DotNew.Inner dni = dn.new Inner();    }}

10.4 内部类与向上转型

如果内部类被声明为private或者protected,那么客户端程序员对内部类的访问将会受到限制,同时就得在外部类中提供相应的方法来进行访问。

此外,如果内部类实现了外部的某个接口,则可通过向上转型,使用多态的特性,将实现细节完全隐藏。

public class TestParcel {    public static void main(String[] args){        Parcel4 p = new Parcel4();        Contents c =p.contents();        Destination d = p.destination("Tasmania");    }}class Parcel4{    private class PContents implements Contents{        private int i = 11;        @Override        public int value() {            return i;        }    }    protected class PDestination implements Destination{        private String label;        PDestination(String whereTo){            label = whereTo;        }        @Override        public String readLabel() {            return label;        }    }    public Destination destination(String s){        return new PDestination(s);    }    public Contents contents(){        return new PContents();    }}

10.5 在方法和作用域内的内部类

除了在类内外,还可以在方法内或者任意域内定义内部类。
定义在方法内的内部类(局部内部类):

public class Parcel5 {    public Destination destination(String s){        class PDestination implements Destination{            private String label;            private PDestination(String whereTo){                label = whereTo;            }            @Override            public String readLabel() {                return label;            }        }        return new PDestination(s);    }}

定义在任意作用域内的内部类:

public class Parcel6 {    private void internalTracking(boolean b) {        if (b) {            class TrackingSlip {                private String id;                TrackingSlip(String s) {                    id = s;                }                String getSlip() {                    return id;                }            }            TrackingSlip ts = new TrackingSlip("slip");            String s = ts.getSlip();        }    }    public void track(){        internalTracking(true);    }    public static void main(String[] args){        Parcel6 p = new Parcel6();        p.track();    }}

10.6 匿名内部类

无参匿名内部类:

package com.mzm.chapter10;/** * 匿名内部类 *  */public class Parcel7 {    public Contents contents(){        return new Contents() {            private int i = 11;            public int value() {                return i;            }        };    }    public static void main(String[] args){        Parcel7 p = new Parcel7();        Contents c = p.contents();    }}

有参匿名内部类:

package com.mzm.chapter10;/** *  */public class Parcel8 {    /**     * 返回的是Wrapping类的子类对象,然后向上转型成为Wrapping对象     * @param x     * @return     */    public Wrapping warpping(int x){        return new Wrapping(x){            public int value(){                return super.value() * 47;            }        };    }}

匿名内部类中对字段的初始化,注意如果使用外部参数,那么这个参数必须被声明为final:

package com.mzm.chapter10;/** *  */public class Parcel9 {    /**     * 对匿名内部类进行初始化操作     * @param dest     * @return     */    public Destination destination(final String dest){        return new Destination() {            private String label = dest;            @Override            public String readLabel() {                return label;            }        };    }    public static void main(String[] args){    }}

利用抽象类,来打造匿名内部类的“构造器”,与利用拥有具体实现的类是相类似的:

package com.mzm.chapter10;/** *  */public class AnonymousConstructor {    public static Base getBase(int i){        return new Base(i){            {                System.out.println("Inside instance initializer");            }            @Override            public void f() {                System.out.println("In anonymous f()");            }        };    }    public static void main(String[] args){        Base base = getBase(47);        base.f();    }}abstract class Base{    public Base(int i){        System.out.println("Base constructor, i = " + i);    }    public abstract void f();}

不过在这种情况下,这个外部参数就不必声明为final了,因为这个参数实际上是被传给父类的构造器使用的。

匿名内部类与正常的类相比较:
1) 匿名内部类没有命名构造器,但可以通过继承普通的类或者抽象类来获得构造器的效果,但是只能选择一种初始化方式,不能再重载;
2) 匿名内部类只能实现一个接口,或者继承一个普通类/抽象类。

10.6.1 再访工厂方法
package com.mzm.chapter10;/** *  */public class Factories {    public static void serviceConsumer(ServiceFactory fact) {        Service s = fact.getService();        s.method1();        s.method2();    }    public static void main(String[] args) {        serviceConsumer(Implementation1.factory);        serviceConsumer(Implementation2.factory);    }}interface Service {    void method1();    void method2();}interface ServiceFactory {    Service getService();}class Implementation1 implements Service {    private Implementation1() {    }    @Override    public void method1() {        System.out.println("Implementation1 method1");    }    @Override    public void method2() {        System.out.println("Implementation1 method2");    }    public static ServiceFactory factory = new ServiceFactory() {        @Override        public Service getService() {            return new Implementation1();        }    };}class Implementation2 implements Service {    private Implementation2() {    }    @Override    public void method1() {        System.out.println("Implementation2 method1");    }    @Override    public void method2() {        System.out.println("Implementation2 method2");    }    public static ServiceFactory factory = new ServiceFactory() {        @Override        public Service getService() {            return new Implementation2();        }    };}

不再创建作为工厂的具名类,因为工厂往往只需要一个就够了。

10.7 嵌套类

如果将内部类声明为static,常称为嵌套类。

普通的内部类对象隐式地保存了一个指向创建它的外部类对象的引用,而静态内部类则不具备这样一个引用。

静态内部类与普通内部类的区别:
1) 创建静态内部类的对象,并不需要外部类对象,而普通内部类对象的创建则需要先创建外部类对象才可;
2) 不能从静态内部类对象访问非静态的外部类对象;
3) 普通内部类不能含有static成员,而静态内部类则可以包含静态成员和非静态成员。

10.7.1 接口内部的类

在接口中可以定义静态内部类:

package com.mzm.chapter10;/** *  */public interface ClassInInterface {    void howdy();    class Test implements ClassInInterface{        @Override        public void howdy() {            System.out.println("Howdy!");        }        public static void main(String[] args){            new Test().howdy();        }    }}

接口中的元素默认就是public和static的

10.7.2 从多层嵌套类中访问外部类成员

无论一个内部类被嵌套多少层,它都能显式地访问所有它所嵌入的外部类的所有成员。

public class MultiNestingAccess {    public static void main(String[] args){        MNA mna = new MNA();        MNA.A mnaa = mna.new A();        MNA.A.B mnaab = mnaa.new B();        mnaab.h();    }}class MNA{    private void f(){    }    class A{        private void g(){        }        public class B{            void h(){                g();                f();            }        }    }}

10.8 为什么需要内部类

每个内部类都能独立地继承自一个接口实现,而无论外部类是否已继承了某个实现,对内部类都没有影响。

内部类使得多重继承的解决方案变得完善。

针对接口的多实现,既可以使用单一类,也可使用内部类:

package com.mzm.chapter10;/** * 多重继承之实现多个接口 *  */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();        takesA(x);        takesA(y);        takesB(x);        takesB(y.makeB());    }}interface A{}interface B{}class X implements A, B{}class Y implements A{    B makeB(){        return new B(){        };    }}

针对继承多个类或者抽象类,则只能使用内部类来达到多重继承的效果:

package com.mzm.chapter10;/** * 多重继承之“继承”多个类(抽象类) *  */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);        takesE(z.makeE());    }}class D{}abstract class E{}class Z extends D{    E makeE(){        return new E(){        };    }}

使用内部类还可以获得其他特性:
1) 内部类可以有多个实例,每个实例都有自己的状态信息,并且与外部类对象的信息相互独立;
2) 单个外部类中,可以让多个内部类以不同的方式实现同一个接口或者继承同一个类;
3) 创建内部类对象的时刻并不依赖于外部类对象的创建;
4) 内部类对象是一个实体,并没有”is-a”关系。

10.8.1 闭包与回调

内部类是面向对象的闭包,它包含外部类对象的信息,还自动具有指向此外部类对象的引用,在这样一个作用域内,内部类对象有权对所有成员进行操作,包括private成员。

package com.mzm.chapter10;/** *  */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();    }}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();    }}class Callee2 extends MyIncrement{    private int i = 0;    public void increment(){        super.increment();        i++;        System.out.println(i);    }    /**     * 钩子?     */    private class Closure implements Incrementable{        @Override        public void increment() {            Callee2.this.increment();        }    }    Incrementable getCallbackReference(){        return new Closure();    }}class Caller{    private Incrementable callbackReference;    Caller(Incrementable cbn){        callbackReference = cbn;    }    void go(){        callbackReference.increment();    }}
10.8.2 内部类与控制框架

应用程序框架(application framework):被设计用来解决某类特定问题的一个类或者一组类。

运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的某些方法中编写代码定制应用程序框架提供的通用解决方案,以解决特定问题。之后,模板方法(应用程序框架中保持不变的部分)会调用一个或多个可覆盖方法,来完成既定的算法动作。

控制框架(control framework)是一类特殊的应用程序框架,它用来响应事件的需求。

事件驱动系统是主要用来响应事件的系统。

package com.mzm.chapter10;/** * 事件对象 *  */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;    }    /**     * 判断当前时间下能否运行这一事件对象的action,可在子类覆盖该方法,以添加基于时间之外的触发因素     * @return 当前时间下能否运行这一事件对象的action     */    public boolean ready(){        return System.nanoTime() >= eventTime;    }    public abstract void action();}
package com.mzm.chapter10;import java.util.ArrayList;import java.util.List;/** * 管理并触发事件的实际控制框架 */public class Controller {    //事件队列    private List<Event> eventList = new ArrayList<>();    /**     * 向事件队列中添加事件     * @param c 事件     */    public void addEvent(Event c){        eventList.add(c);    }    /**     * 运行事件队列     */    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);                }            }        }    }}

在上面的框架中,Event做了什么并不知道,需要框架使用者继承Event来进行具体的操作。

package com.mzm.chapter10;/** * 温室控制系统 */public class GreenhouseControls extends Controller {    //灯的状态    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);        }        @Override        public void action() {            water = true;        }        public String toString(){            return "Greenhouse water is on";        }    }    /**     * 关闭水源事件     */    public class WaterOff extends Event{        public WaterOff(long delayTime){            super(delayTime);        }        @Override        public void action() {            water = false;        }        public String toString(){            return "Greenhouse water is off";        }    }    //昼夜的状态    private String thermostat = "Day";    /**     * 夜晚     */    public class ThermostatNight extends Event{        public ThermostatNight(long deleyTime){            super(deleyTime);        }        @Override        public void action() {            thermostat = "Night";        }        public String toString(){            return "Thermostat on night setting";        }    }    /**     * 白天     */    public class ThermostatDay extends Event{        public ThermostatDay(long delayTime){            super(delayTime);        }        @Override        public void action() {            thermostat = "Day";        }        public String toString(){            return "Thermostat on day setting";        }    }    /**     * 响铃事件     */    public class Bell extends Event{        public Bell(long delayTime){            super(delayTime);        }        /**         * 将响铃事件添加到事件队列中         */        @Override        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);            }        }        @Override        public void action() {            //重新运行每个事件            for(Event e : eventList){                e.start();                addEvent(e);            }            //重启事件本身也需要重新运行并添加到系统的事件队列中            start();            addEvent(this);        }        public String toString(){            return "Restarting system";        }    }    public static class Terminate extends Event{        public Terminate(long delayTime){            super(delayTime);        }        @Override        public void action() {            System.exit(0);        }        public String toString(){            return "Terminating";        }    }    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();    }}

light、water和thermostat都属于外部类,表示状态,而内部类只是对其进行修改。

Bell控制响铃,然后在事件列表中增加Bell对象,表示在这一次的响铃事件结束后,过一会儿可以再次响铃。

Restart控制重启,该对象维护一个Event数组,在初始化重启事件对象时,将这一数组内的每个事件添加到外部类对象的事件队列中。一旦这个事件被触发,Event数组中的每一个事件都会重新生成触发时间,并添加到外部类的事件队列中,最后再将重启事件对象本身也重新生成触发时间,添加到外部类的事件队列中。

10.9 内部类的继承

一个类继承内部类,在其构造方法中必须传入外部类对象,否则编译会报错。

enclosingClassReference.super()
package com.mzm.chapter10;/** * 内部类的继承 *  */public class InheritInner extends WithInner.Inner{    InheritInner(WithInner wi){        wi.super();    }    public static void main(String[] args){        WithInner wi = new WithInner();        InheritInner ii = new InheritInner(wi);    }}class WithInner{    class Inner{    }}

10.10 内部类可以被覆盖吗

当一个类继承外部类,并拥有与该外部类相同的内部类时:

package com.mzm.chapter10;/** *  */public class BigEgg extends Egg{    public class Yolk{        public Yolk(){            System.out.println("BigEgg.Yolk()");        }    }    public static void main(String[] args){        new BigEgg();    }}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();    }}

两个内部类Yolk是两个独立的类,各自在各自的命名空间内。

当两者都实现继承时,即外部类继承外部类,内部类继承内部类:

package com.mzm.chapter10;/** *  */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();    }}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();    }}

与普通的类的继承没有什么不同。

10.11 局部内部类

在方法或者代码块内创建的内部类称为局部内部类,这种内部类不能有访问修饰符,因为它不是外部类的一部分,但是它可以访问当前代码块或者方法体内的常量,也可以访问外部类的所有成员。

使用局部内部类的原因在于,需要不止一个内部类对象。

10.12 内部类标识符

内部类也有.class文件,只是它的文件名如下:

OuterClassName$InnerClassName.class
原创粉丝点击