Java编程思想之十 内部类

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

10.1 创建内部类

创建内部类就是把类的定义置于外部类里面。

public class Parcell {
    class contents{
        int i=0;
        public void GetI(){
            System.out.println("contents"+i);
            i++;
        }

    }
    class Destintion{
        private String label;
        public void GetI(String s){
            System.out.println(s);
        }
    }
    public contents GetontentsC(){
        return new contents();
    }
    public void ship(String dest){
        contents c=new contents();
        c.GetI();
        Destintion d=new Destintion();
        d.GetI(dest);
    }

    public static void main(String[] args){
        Parcell p=new Parcell();
        p.ship("dwdwdw");
        Parcell.contents c=p.GetontentsC();
        c.GetI();
    }
}

如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体的指明这个对象的类型:OuterClassName.InnerClassName.

10.2 链接到外部类

当生成一个内部类对象时,此对象和制造它的外围对象之间有一种联系,所有,它能访问它外围对象的所有成员,而不需要特殊条件。

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;
    }
    public class SequenceSelector implements Selector{
private int i=0;
        @Override
        public boolean end() {
            if (i==items.length)
                return false;
            return true;
        }

        @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 s=sequence.selector();
        while (s.end()){
          System.out.print(s.current());
            s.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 d=new DotThis();
        DotThis.Inner in=d.inner();
        in.outer().f();
    }
}

创建某个内部类对象时,需要使用.new。

public class Parcel3 {
    class Contents{
        private int i=11;
        public int value(){return i;}
    }
    public class Destination{
        private String label;
        Destination(String whereTo){
            label=whereTo;
        }
        String readLabel(){
            return label;
        }
    }
    public static void main(String[] args){
        Parcel3 p=new Parcel3();
        Parcel3.Contents c=p.new Contents();
        Parcel3.Destination d=p.new Destination("123");
        System.out.println(d.label);
    }
}
10.4 内部类与向上转型

当将内部类向上转型为基类,尤其是一个接口的时候,内部类就很有用了。因为内部类的某个接口的实现是可以完全不可见并且不可用的。

public class TestParcel {
    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Contents c = p.contents();
        Destination d = p.destination("tasmaina");
    }
}

interface Destination {
    String readLabel();
}

interface Contents {
    int value();
}

class Parcel4 {
    private class PContents implements Contents {
        private int i = 11;

        @Override
        public int value() {
            return i;
        }
    }

    protected class PDestionation implements Destination {
        private String label;

        private PDestionation(String whereTo) {
            label = whereTo;
        }

        @Override
        public String readLabel() {
            return null;
        }
    }

    public Contents contents() {
        return new PContents();
    }

    public Destination destination(String s){
        return new PDestionation(s);
    }
}

PDestination是protected,所以只要Parce4及其子类,还有与Parce4同一个包中的类能访问PDestination。
private内部类给类设计者提供了一条途径,通过这种方式,可以完全阻止任何依赖于类型的编码,并且完全隐藏实现细节。

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

在方法的作用域内,创建一个完整的类

public class Parcel5 {
    public Destination destination(String s){
        class PDestination implements Destination{
            private String label;
            private PDestination(String whereTo){
                System.out.println(whereTo);
                label=whereTo;
            }
            @Override
            public String readLabel() {
                return label;
            }
        }
        return new PDestination(s);
    }
    public static void main(String[] args){
        Parcel5 p=new Parcel5();
        Destination d=p.destination("sa");
    }
}

在任意的作用域嵌入一个内部类

public class Parcel6 {
    private void internalTracking1(boolean b) {
        if (b) {
            class TrackingSlip {
                private String id;

                TrackingSlip(String s) {
                    id = s;
                    System.out.println(id);
                }

                String getSlip() {
                    return id;
                }
            }
            TrackingSlip trac=new TrackingSlip("asd");
           // TrackingSlip ts = new TrackingSlip("slip");
            //String s = ts.getSlip();
        }
}
    public void track() {
        internalTracking1(true);
    }
    public static void main(String[] args){
        Parcel6 p=new Parcel6();
        p.track();
    }
}

TrackingSlip类被嵌入在if语句的作用域内,这不是说该类创建是有条件的,它其实与别的类一起编译过。但是在TrackingSlip的作用域之外,它是不可用的。

10.6 匿名内部类

public class Parcel7 {
    public Contents contents(){
        return new Contents() {
            private  int i=11;
            @Override
            public int value() {
                System.out.println(i);
                return i;
            }
        };
    }
    public static void main(String[] args){
        Parcel7 p7=new Parcel7();
        p7.contents().value();
    }
}

contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起。
通过new表达式返回的引用被自动向上转型。

public class Parcel7b {
    class ABContents implements Contents{
        private  int i=11;
        @Override
        public int value() {
            System.out.println(i);
            return i;
        }
    }
    public Contents contents(){
        return new ABContents();
    }
    public static void main(String[] args){
        Parcel7b b=new Parcel7b();
        Contents c=b.contents();
        c.value();
    }
}

下面的基类需要一个有参数的构造器

public class Parcel8 {
    public Wrapping wrapping(int i){
        return new Wrapping(i){
        };
    }
    public static void main(String[] args){
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    }
}
class Wrapping{
    public Wrapping(int i){
        System.out.println(i);
    }
}

Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当作公共"接口"来使用。
在匿名内部类尾部的分号,不是用来标记内部类结束的,它标记的是表达式的结束,只不过这个表达式正好表示匿名类。
如果定义一个匿名内部类,并希望它使用一个外部定义的对象,那么就要求参数引用是final的。

public class Parcel9 {
    public Destination wrapping(final int i){
        return new Destination(){
            private int label=i;
            @Override
            public String readLabel() {

                System.out.println(label);
                return "i";
            }
        };
    }
    public static void main(String[] args){
        Parcel9 p9=new Parcel9();
        Destination wr=p9.wrapping(4);
        wr.readLabel();
    }
}

在匿名类中不可能有命令构造器,但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。

public class Parcel8 {
    public Wrapping wrapping(int i){
        return new Wrapping(i){
            {System.out.println(i+1);}
        };
    }
    public static void main(String[] args){
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    }
}
class Wrapping{
    public Wrapping(int i){
        System.out.println(i);
    }
}

匿名内部类与正规的继承相比有些受限,因为匿名类既可以扩展类,也可以是实现接口,但不能两者兼备,如果是实现接口,也只能实现一个。

10.6.1 再访工厂模式

优先使用类而不是接口。如果设计里面需要某个接口,那么必须了解它。

10.7 嵌套类

如果不需要内部类对象与其外围对象有联系,可以使用内部类声明为static。这称为嵌套类。
普通的内部类对象隐试地保存了一个引用,指向创建它的外围类对象。当内部类是static的时,就不是这样的了。
嵌套类意味着:

  • 要创建嵌套类的对象,并不需要其外围类的对象。
  • 不能从嵌套类的对象访问非静态外围对象。

普通内部类的字段与方法,只能放在类的外部层次上,所有普通的内部类不能有static数据和static字段,也不能包含嵌套类。

public class Parcel11 {
    private static class parceContents implements Contents{

        @Override
        public int value() {
            return 0;
        }
    }
    public static Contents contents(){
        return new parceContents();
    }
    public static void main(){
        Contents c=contents();
    }
}

在一个普通的内部类中,通过一个特殊的this引用可以链接到其外围类对象,嵌套类就没有这个特殊的this引用,这使得它类似一个static方法。

10.7.1 接口内部的类

正常情况下,不能再接口中放置任何代码,但嵌套类可以作为接口的一部分,放到接口中的任何类都自动是public和static的。

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();
        }
    }
}

如果想要创建谋和代码,使得它们可以被某个接口的所有不同实现所公用,那么使用接口内部的嵌套类会显得很方便。

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

一个内部类被嵌套多少层,它都能透明的访问所有它所嵌入的外围类对象。

class MNA {
    private void f(){System.out.println("f");}
    class A{
        private void g(){System.out.println("g");}
        public class B{
            void h(){
                g();
                f();
            }
        }
    }
}
public class Multi{
    public static void main(String[] args){
        MNA mna=new MNA();
        MNA.A maa=mna.new A();
        MNA.A.B maab=maa.new B();
        maab.h();
    }
}

.new 语法能参数正确的作用域,所有不必再调用构造器时限定类名。

为什么需要内部类

内部类继承自某个类或实现某个接口,内部类的代码操作创建它外围类的对象,所有可以认为内部类提供了某种进入外围类的接口。
每个内部类都能独立地继承自一个实现,所有无论外围类是否已经继承了某个实现,对于内部类都没有影响。
在一个类中,以某种形式实现两个接口,有两种选择:使用单一类,或者使用内部类。
但是对于抽象类或者具体的类,就只能使用内部类实现多重继承了。

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() {
        };
    }
}
10.8.1 闭包与回调

闭包是一个可调用对象,它记录了一些信息,这些信息来自于创建它的作用域,通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员。
通过回调,对象能够携带一些信息,这些信息允许它在稍后某个时刻调用初始的对象。
通过内部类实现回调:

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 static int i=1;
    public void increment(){
        System.out.println("Other operation");
    }
    public 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 cbh){
        callbackReference=cbh;
    }
    void go(){callbackReference.increment();}
}
10.8.2 内部类与控制框架

应用程序框架就是被设计用以解决某类特定问题的一个类或一组类。要应用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决特定的问题。
控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称为事件驱动系统
首先,接口描述了要控制的事件:

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();
}

触发事件的实际控制框架:

import java.util.ArrayList;
import java.util.List;

public class Controller {
    private List<Event> eventList=new ArrayList<Event>();
    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到底做了什么。但是这个设计关键就是使变化的事物与不变的事物相互分离。而这就是内部类要做的事情。
内部类允许:

  • 控制框架的完整实现是由单个的类创建,从而使得实现的细节被封装了起来。内部类用来表示解决问题所必须的各种不同的action()。
  • 内部类能够很容易的访问外围类的任意成员,所以可以避免这种实现变得很笨拙。
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";
        }
    }

    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();
                addEvent(e);
            }
            start();
            addEvent(this);
        }

        public String toString() {
            return "Restarting system";
        }
    }

    public static void main(String[] args){
        GreenhouseControls gc=new GreenhouseControls();
        Event[] eventList={
               gc.new LightOn(20000),
               gc.new LightOff(40000)
    };
        //gc.addEvent(gc.new LightOn(20000));
        //gc.addEvent(gc.new LightOff(40000));

        gc.addEvent(gc.new Restart(200000,eventList));
        gc.run();
    }
}

10.9 内部类的继承

内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,那个指向外围类对象的秘密的引用必须被初始化,而在导出类中,不再存在可连接的默认对象。

public class InheritInner extends WithInner.Inner{
    InheritInner(WithInner wi){//必须指向一个外围对象的引用
        wi.super();
    }
    public static void main(String[] args){
        WithInner wi=new WithInner();
        InheritInner i=new InheritInner(wi);
    }
}
class WithInner{
    class Inner{}
}

10.10 内部类可以被覆盖吗

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();
    }
}


当继承了某个外围类时,内部类并没有发生变化,这两个内部类是完全独立的个体,各自在各自的命名空间内。

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 局部内部类

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.print(name);
                return count++;
            }
        }
        return new LocalCounter();
    }
    Counter getCounter2(final String name)
    {
        return new Counter()
        {
            {
                System.out.println("Counter()");
            }
            @Override
            public int next()
            {
                System.out.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++)
            System.out.println(c1.next());
        for (int i=0;i<5;i++)
            System.out.println(c2.next());
    }
}
interface Counter
{
    int next();
}

原文地址:https://www.cnblogs.com/Tan-sir/p/11235128.html

时间: 2024-10-29 12:31:14

Java编程思想之十 内部类的相关文章

【Java编程思想】10.内部类

将一个类的定义放在另一个类的定义内部,这就是内部类. 10.1 创建内部类 内部类的名字是嵌套在外部类里面的 外部类可以有方法,返回一个指向内部类的调用.(外部类中可以调用内部类) 如果在外部类中,希望能在除了静态方法之外的任意位置创建某个内部类对象,那么可以向下面这样指明对象类型. OuterClassName.InnerClassName x = new InnerClassName(); 10.2 链接到外部类 在创建了一个内部类的对象后,内部类与制造它的外围对象(enclosing ob

Java编程思想---第十二章 通过异常处理错误(中)

第十二章  通过异常处理错误(中) 12.4 创建自定义异常 我们不必拘泥于Java中已有的异常类型,Java提供的异常体系不可能预见所有的错误,所以可以自己定义异常类来表示程序中可能会遇到的特定问题:要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承,建立新的异常类最简单的方法就是让编译器为你产生默认构造器,所以这几乎不用写多少代码: class SimpleException extends Exception { } public class InheritingEx

Java编程思想学习(十六) 并发编程

线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是真正的并行运行,而是通过时间片切换来执行,由于时间片切换频繁,使用者感觉程序是在并行运行.单核心CPU中通过时间片切换执行多线程任务时,虽然需要保存线程上下文,但是由于不会被阻塞的线程所阻塞,因此相比单任务还是大大提高了程序运行效率. 1.线程的状态和切换: 线程的7种状态及其切换图如下: 2.多线

Java编程思想之十二 通过异常处理错误

Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者--该接收者将知道如何正确处理这里问题. 12.1 概念 使用异常所带来的另一个相当明显的好处,它往往能够降低错误处理代码的复杂度. 12.2 基本异常 异常情形是指阻止当前方法或作用域继续执行的问题.把异常情形与普通问题相区分很重要,普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误.而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解

Java编程思想---第十章 内部类(下)

第十章 内部类(下) 10.9 内部类的继承 因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候事情会变得有些复杂,问题在于那个指向外围类对象的引用必须被初始化,而在导出类中不再存在可连接的默认对象,要解决这个问题,必须使用特殊的语法来明确说清他们之间的关联: class WithInner { class Inner {} } public class InheritInner extends WithInner.Inner { InheritInner(WithInn

Java编程思想笔记(内部类)

      1.创建内部类       2.链接到外部类:当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件.   3.使用.this()与.new():1.如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this.2.DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); //要想直接创建内部类的对象,你不能按照你想象的方式,去引用外部

Java编程思想学习(十四) 枚举

关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返回enum实例数组,且数组中元素保持在enum声明时的顺序. 1 public class TestEnum { 2 public static void main(String[] args) { 3 Fruit[] values = Fruit.values(); 4 for (Fruit fr

Java编程思想学习(十二) 数组和容器

一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的话就是对象数组默认值为null,而基本数组视本身情况而定. 1 package lkl; 2 3 import java.util.Arrays; 4 5 ///下面演示了数组的初始化 6 //包括对象数组和普通数组 7 class Base{ 8 private static long count

Java编程思想---第十章 内部类(上)

第十章  内部类(上) 可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一种非常有用的特性,它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性,但内部类与组合是完全不同的概念. 10.1 创建内部类 创建内部类的方法就是,把类的定义置于外围类的里面: public class Parcel1 { class Contents { private int i = 11; public int value() { return i; } } class Destina