zoukankan      html  css  js  c++  java
  • Java编程思想:第10章 内部类

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

    1.可以实现隐藏

    2.内部类了解外围类,并能与之通信,很多时候可以写出更加优雅和清晰的代码

    10.1创建内部类

    public class Outer{

      class Inner{

      }

    }

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

    10.2链接到外部类

    内部类自动拥有对外围类所有成员的访问权,这是如何做到的呢?当外围类对象创建了一个内部类对象时,此内部类对象必定会秘密的捕获一个指向那个外围类对象的引用,在访问外部类对象成员时,就是用这个引用来访问。编译器会帮助我们处理这些细节,而我们使用时,就像是内部类本身带有的一样。所以非static内部类在创建时候必须要由外部类对象来创建。

    10.3使用.this与.new

    如果要使用外部类对象的引用,OuterClassName.this来获取,编译器会检查类型是否正确,无运行时开销。

    创建内部类对象:要先有外部类对象Outer o = new Outer();然后用外部类对象创建内部类对象Outer.Inner i = o.new Inner();不可以用o.new Outer.Inner();

    10.4内部类向上转型

    当把private内部类向上转型成基类,尤其是接口的时候,内部类就有了用武之地:这个接口的实现可以完全隐藏,完全不可见,因为返回的是接口引用

    参考java源码AbstractList类:

    public Iterator<E> iterator() {

      return new Itr();

    }

    private class Itr implements Iterator<E> {

      ...

    }

    当我们外界使用迭代器的时候

    Iterator iter = arrayList.iterator();

    只能拿到接口的引用,完全不知道里面是一个private内部类,因为Itr是private的,也不能创建它。

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

    局部内部类:定义在方法体或者某一作用域里,超出作用域不能使用。

    注意:不能使用意味着在外部不能被访问到类型,但是该类型在JVM里是实际存在的,即使这个作用域已经结束。

    10.6匿名内部类

    没有名字的内部类,可以有带参构造器,可以添加并初始化域。

    如果在内部类的内部要使用外部的一个对象,这个对象的引用需要是final的。构造器中使用的参数不需要是final的。

    匿名内部类里不能自己添加构造器(因为不能被访问到,而且没名字),需要做一些初始化的时候,可以用代码块来实现。

    匿名内部类与正规的继承相比,要么扩展类,要么实现接口,不能二者都有,并且只能实现一个接口。

    10.6.1再访工厂方法

    可以把工厂方法改成匿名内部类。

    interface Game{boolean move();}

    interface GameFactory{Game getGame();}

    class Checkers implements Game{

      private Checkers(){}

      private int moves = 0;

      private static final int MOVES = 3;

      public static GameFactory factory = new GameFactory(){

        public Game getGame(){return new Checkers();}  

      };

      public boolean move(){

        return ++moves != MOVES;

      }

    }

    10.7嵌套类-静态内部类

    1)创建静态内部类对象不需要外部类对象

    2)不能从静态内部类对象访问外围内的非静态成员

    普通内部类不可以有static字段,静态可以有

    10.7.1接口内部的类

    正常情况下,接口内部是不能放任何代码的,但是嵌套类可以放。放入接口中的任何类都是public static的。

    用途:如果你想创建某些公共代码,使得它们可以被这个接口的所有实现使用,那么接口中嵌套内部类就会非常方便。

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

    一个内部类被嵌套多少层并不重要,它可以透明的访问所有外围类的所有成员。

    class MMA{

      private void f(){}

      class A{

        private void g(){}

        public class B{

          void h(){

            f();//直接调用 没有问题

            g();

          }

        }

      }

    }

    10.8为什么需要内部类

    一般来说,内部类继承某个类或实现某个接口,并且可以操作外围类对象。

    核心原因:内部类可以提供多重继承的能力。

    比如: AbstractList里有Itr和ListItr两种迭代器,如果我们只是让AbstractList实现Iterator接口,那么只能写出一个迭代器,通过使用内部类方式可以添加多个迭代器。

    10.8.1闭包与回调

    如果有一个类需要实现一个接口来提供某个方法,但是发现这个方法已经被另外一个接口使用了,那么可以用内部类代替这个类去实现接口。

    回调的价值在于它的灵活性---可以在运行时动态决定需要调用什么方法。

    10.8.2内部类与控制框架

    设计一个控制框架,要求到了设定时间就执行相应的事件:

    //创建一个抽象事件类,有执行时间和延迟时间,在产生新事件的时候用延迟时间计算出执行时间,action方法由实现类实现。

    public abstract class Event{

      private long eventTime;

      protected final long delayTime;

      public Event(long delay){

        this.delayTime = delay;

        start();  

      }

      public void start(){

        eventTime = System.nanoTime() + delayTime;

      }

      public boolean ready(){

        return System.nanoTime >= eventTime;

      }

      public abstract void action();

    }

    //创建一个控制类,循环遍历所有事件,如果事件时间到了就执行action

    public class Controller{

      private List<Event> eventList = new ArrayList<>();

      public void addEvent(Event e){eventList.add(e);}

      public void run(){

        while(eventlist.size()>0){

          for(Event e : eventList){

            if(e.ready){

              e.action();

              eventlist.remove();

            }

          }

        }

      }

    }

    从上面这2个类的设计来看,我们完全不知道event具体做什么,但是我们整个控制框架的核心功能:"把变化的事物(event具体实现),与不变的事物(时间发生过程)"分开。而我们只需要创建不同的变化的Event,这正是内部类要做的事情:

    1)控制框架里的实现是由单个类创建的,从而使得实现细节被封装起来。内部类用来表示解决问题所必须的不同action。

    2)内部类可以很容易访问外部类成员,这种实现不会显得很笨拙。

    控制框架:

    public class GreenhouseControls extend Controller{

      private boolean light = false;

      public class LightOn extends Event{

        public LightOn(long delay){super(delay);}

        public void action(){

          //hardware control code to turn on light

          light = true;

        }

      }

      public class LightOff extends Event{..}

      private boolean water = false;

      public class WaterOn extends Event{

        public WaterOn(long delay){super(delay);}

        public void action(){

          //..

          water = true;

        }

      }

      //...其他内部类

    }

    控制框架中用内部类把实现细节都隐藏起来了。

    那么如何使用这个框架并执行需要的事件呢:

    public static void main(String[] args){

      GreenhouseControls gc = new GreenhouseControls();

      gc.addEvent(gc.new LightOn(100));

      gc.addEvent(gc.new LightOff(200));

      ...

      gc.run();

    }

    控制框架可以体现出内部类的价值,图形界面设计里更是让人信服。

    10.9内部类的继承

     内部类的构造器必须连接到外围类对象的引用,如果一个类继承了内部类,那么如何保证外围类对象存在呢?需要使用特殊语法:

    1.在导出类添加一个带外围类参数的构造器

    2.构造器里用outer.super()来提供外围类引用

    例如:

    class Outer{

      class Inner{}

    }

    public class InheritInner extends Outer.Inner{

      InheritInner(Outer o){

        o.super();

      }

    }

    10.10内部类可以被覆盖吗

    class Outer{

      class Inner{}

    }

    class Outer2 extends Outer{

      class Inner{}

    }

    在Outer里调用Inner并不会被Outer2里的Inner覆盖,即不会产生多态特性。

    10.11局部内部类

    在方法体里使用局部内部类和使用匿名内部类有什么区别呢?

    唯一的区别在于:局部内部类可以有命名的构造器,并且可以被重载用来初始化,可以产生多个对象。而匿名内部类只能提供一个该类型的对象。

    10.12内部类标识符

    匿名内部类: Outer类名 $ 数字

    成员内部类:  Outer类名 $ 内部类名

    局部内部类: Outer类名 $ 数字 内部类名

    10.13总结

    接口和内部类实现了C++的多重继承功能,但是更优雅,更简洁。

  • 相关阅读:
    HDOJ 1846 Brave Game
    并查集模板
    HDU 2102 A计划
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    POJ 1321 棋盘问题
    CF 999 C.Alphabetic Removals
    CF 999 B. Reversing Encryption
    string的基础用法
    51nod 1267 4个数和为0
  • 原文地址:https://www.cnblogs.com/superzhao/p/4817273.html
Copyright © 2011-2022 走看看