• 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++的多重继承功能,但是更优雅,更简洁。

  • 相关阅读:
    Javascript 基础知识整理
    设计模式
    Flex 布局
    CSS选择器,属性前缀,长度单位,变形效果,过渡效果,动画效果
    CSS中一些重要概念
    性能优化(CSS优化)
    CSS定位走一波(定位学习续)
    定位布局
    浮动布局
    Display属性学习总结
  • 原文地址:https://www.cnblogs.com/superzhao/p/4817273.html
Copyright © 2020-2023  润新知