【Java编程思想笔记】第十章-内部类

  • 内部类对象可以直接访问外围对象的所有成员(包括私有的),而不需要任何特殊条件,就像调用自己的方法与属性成员一样。但外围类不能直接访问内部类中的方法,除非使用内部类的实例来访问(也能访问私有的)。

  • 内部类自动拥有对其外围类所有成员的访问权。这是如何做到的呢?当某个外围类的对象创建了一个内部类对象时,此内部对象会有一个指向外围类对象的引用,然后在你访问此外围类的成员时,就是用那个引用来选择外围类的成员,编译器会帮你处理所有的细节。注,这只限于非静态的内部类。

  • 构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。

  • 静态的内部类也叫嵌套类。

  • 匿名内部类与普通的内部类继承相比是有些受限的,虽然匿名内部类既可以继承类,也可以实现接口,但是不能两者兼备,而且如果实现接口,也只能实现一个接口。

  • 非静态的内部类中不能定义static成员,但final
    staitc成员是可以的。因为一个成员类实例必然与一个外部类实例关联,这个static定义完全可以移到其外部类中去。

  • 内部(类中或接口中)接口一定static的。

  • 接口中的内部类一定是public与static,但不一定是final(与数据成员不同),因为省略final时可以被继承

  • 非静态的内部类里不能定义接口,因为内部接口(嵌套接口)默认为static,是无法改变的,所以内部类里的接口只能定义在静态的内部类里面。

  • 方法与块里定义的内部类只能是非静态的,不能加static,所以局部内部类里只能定义非静态的成员。局部内部类也能直接访问外部内所有成员。

  • 静态的内部类可以定义非静态与静态的东西。

  • 不能在静态内部类中直接(实例化外部类再访问是可以的)访问外部类中的非静态成员与方法。而非静态内部类是可以访问外部类的静态成员。

  • 在方法与作用域内都可定义内部类。如果一个内部类在if条件语句中定义,则不管条件是否成立都会编译成类,但这个类的使用域只限于该if语句中,if外面不能访问。

  • 设计模式总是将变化的事物与保持不变的事物分离开,比如模板方法模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。

  • 内部类不能被重写:父类中的内部类不能被子类中的同名内部类重写。

  • 为什么加上final后的局部变量就可以在内部类中使用了?因为加上final后,编译器是这样处理内部类的:如果这个外部局部变量是常量,则在内部类代码中直接用这个常量;如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用,所以调用的实际是自己的属性而不是外部类方法的参数或局部变量。这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

  • 一个类对外提供一个公共接口的实现(接口与实现完全分离,而且隐藏了实现)是内部类的典型应用,以JDK
    Collection类库为例,每种Collection的实现类必须提供一个与其对应的Iterator实现,以便客户端能以统一的方式(Iterator接口)遍历任一Collection实例。每种Collection类的Iterator实现就被定义为该Collection类的私有的内部类

内部类作用:

1、内部类方法可以访问该类所在的作用域中的数据,包括私有数据。
2、内部类可以对同一个包中的其他类隐藏起来。
3、当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
4、实现多继承。

内部类机制

如果有如下内部类:

public class Outer {
   private boolean b;
   private int i;

   //内部类
   private class Inner {
      private boolean b;
      Inner(boolean b) {
         this.b = b;
      }
      public void print() {
         System.out.println(Outer.this.b & this.b);
      }
   }

   public void f(final String str) {
      class Inner {//局部内部类
         public void print() {
            System.out.println(Outer.this.i);
         }
      }
   }
}
  • 我们来使用Reflection反射外部及内部类,看看内部类是怎样访问外部类成员的: D:\work\Test\bin>java
    Reflection Outer
class Outer
{
//构造器
public Outer();
//字段
private boolean b;
private int i;
//方法
static boolean access$0(Outer);// 内部类Outer$Inner通过该方法访问外部类b成员,这样就可以访问一个私有成员了
static int access$1(Outer); // 局部类Outer$1Inner通过该方法访问外部类i成员
public void f(java.lang.String);
}

class Outer$Inner
{
//构造器
Outer$Inner(Outer, boolean);// 在编译期会自动传入外部类实例
//字段
private boolean b;
final Outer this$0;//指向外部类实例
//方法
public void print();
}

class Outer$1Inner
{
//构造器
  Outer$1Inner(Outer, java.lang.String);//第二个参数是引用的final类型的局部变量,也是通过构造器传入的
//字段
final Outer this$0;
private final java.lang.String val$str;//存储引用的局部final类型变量
//方法
public void print();
}
  • 非静态内部类创建方式:
OuterClassName.InnerClassName inner = new OuterClassName().new InnerClassName();
  • 静态内部类创建方式:
OuterClassName.InnerClassName inner = new OuterClassName.InnerClassName();

继承内部类语法规则:

class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {
  //! InheritInner() {} // 不能编译
  /*
   * 这里的super指InheritInner类的父类WithInner.Inner
   * 的默认构造函数,而不是WithInner的父类构造函数,这
   * 种特殊的语法只在继承一个非静态内部类时才用到,表示
   * 继承非静态内部类时,外围对象一定要存在,并且只能在
   * 第一行调用,而且一定要调用一下。为什么不能直接使用
   * super()或不直接写出呢?最主要原因就是每个非静态的内部类
   * 都会与特有的一个外围类实例对应,这个外围类实例是运行时传到内部类里去的,所以在内部类里可以直接使用那个对象(比如Outer.this),但这里是在外部内外,使用时还是需要存在外围类实例对象,所以这里就显示的通过构造器传递进来,并且在外围对象上显示的调用一下内部类的构造器,这样就确保了在继承至一个类部类的情况下,外围对象一类会存在这样一个约束。
   */
  InheritInner(WithInner wi) {
    wi.super();
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}

静态内部类里可以使用this(而不像静态方法或块里是不能使用this的),此时的this指向静态内部类,而不是外部类,下面为LinkedList类里的内部类,代表一个节点的实现:

private static class Entry {
   Object element;
   Entry next;
   Entry previous;

       Entry(Object element, Entry next, Entry previous) {
       this.element = element;
       this.next = next;
       this.previous = previous;
   }
}

在非静态的内部类里访问外围类相同属性成员时,需在this前加上外围类型(采用Outer.this.XX 来访问,其中Outer为外围类的类型),一般在访问自己成员时,可以省略this前自身的类型,但默认应该是有的:

public class Outer {
   private int i = 1;
   public void f(){
      System.out.println("f Outer.this.i=" + Outer.this.i);//1
   }

   private /*static*/class Inner {
      private int i = 2;
      public void p() {
         System.out.println("p this.i=" + this.i);//2
         System.out.println("p Inner.this.i=" + Inner.this.i);//2
         //!!注,如果是静态的内部类时,下面语句不能编译通过,因为静态的内部类没与外部类实例关联
         System.out.println("p Outer.this.i=" + Outer.this.i);//1
      }
   }

   public static void main(String[] args) {
      Outer outer = new Outer();
      outer.f();
      Outer.Inner inner = outer.new Inner();
      inner.p();
   }
}

匿名内部类(方法或块中的内部类一样)使用外部类作用域内的局部变量需定义成final,但这个变量需在匿名内部类中直接使用,如果是作为匿名构造器的参数时不需要:

public class Wrapping {
  private int i;
  public Wrapping() {}
  public Wrapping(int x) { i = x; }
  public int value() { return i; }
}

public class Parcel {
    // 可以直接内部类或匿名的内部类访问,不需要定义成final
    private int ii = 1;
   /*
    * 这里的参数 x 不需要定义成final,因为它只是
    * 作为构建匿名对象时传递的一个参数,而没有直接
    * 在匿名内部类中使用
    */
   public Wrapping wrapping1(int x) {
              // 调用匿名内部类的基类的带参构造函数
      return new Wrapping(x) { // 传递参数
         public int value() {
            //调用外部内的域成员时可直接调用,但加this时就需在
            //this前加外部类名,因为该内部类没有定义ii
            return super.value() * 47 * Parcel.this.ii;
         }
      };
   }

   /*
    * 注,这里的参数 x 一定要定义成final,因为
    * 它被匿名内部类直接使用了
    */
   public Wrapping wrapping2( final int x ) {
      final int y = 1;
      return new Wrapping() {
         //不管是在定义时还是方法中使用都需要定义成final
         private int i=y;
         public int value() {
            return i * x * 47;
         }
      };
   }
   public static void main(String[] args) {
      Wrapping w = new Parcel().wrapping1(10);
      w = new Parcel().wrapping2(10);
   }
}

匿名类中不可能有命名的构造器,因为它根本没有名字。但通过块初始化,就能够达到为匿名内部类创建一个构造器的效果,当然它受到了限制——你不能重载块方法,这不像普通内部类的构造函数:

abstract class Base {
  public Base(int i) {
    System.out.println("Base constructor, i = " + i);
  }
  public abstract void f();
} 

public class AnonymousConstructor {
  public static Base getBase(int i) {
    return new Base(i) {
      {   //初始化块
         System.out.println("Inside instance initializer");
      }
      public void f() {
         System.out.println("In anonymous f()");
      }
    };
  }
  public static void main(String[] args) {
    Base base = getBase(47);
    base.f();
  }
} /* Output:
Base constructor, i = 47
Inside instance initializer
In anonymous f()
*///:~
  • 实现接口的匿名内部类:

public class Outer {
public Comparable getComp() {
return new Comparable() {// Comparable为比较器接口
//实现接口
public int compareTo(Object o) {
return 0;
}
};
}
但要注意,匿名内部类实现一个接口时,构造时不能带参数:

interface InnerI {}
   public InnerI getII() {
      // 匿名内部内实现一个接口时,构造器不能带参数,
      // 因为接口根本就没有构造器,更没有带参的构造器
      // !!return new InnerI(int i) {};
   }
}
  • java实现多重继承
  • java不支持多继承。即没有extends
    Class1,Class2的语句形式。这里的多继承是指继承类的属性和行为,并且是编译时就决定的静态行为。
  • 广义的继承是指除了组合之外的的第二种代码复用方法,只要满足“像一个”或者“像是一个”、“里面有一个”条件都可以看做继承。

  • java的非静态内部类可以使用外部类的所有成员方法和变量。这给继承多个类的同名成员并共享带来可能。同时非匿名内部类可以继承一个父类和实现多个接口,因此外部类想要多继承的类可以分别由内部类继承,并进行Override或者直接复用。然后外部类通过创建内部类的对象来使用该内部对象的方法和成员,从而达到复用的目的,这样外部内就具有多个父类的所有特征。

  • 这里的多继承可以说是外部类继承自一个内部类对象,而不是类,内部类 is in a 外部类,外部内的所有行为都是通过内部类对象动态获得的。

下面是采用组合多个内部类的方式模拟多继承的实例(基于对象层面,即组合多个内部类对象):

//手机
abstract class Mobile {
   public abstract void call();
}
// MP3播放器
abstract class Mp3Palyer {
   public abstract void play();
}
// 智能手机
class SmartPhone {
   private Mobile mb = new SmartMobile();
   private Mp3Palyer mp3 = new PhoneMp3();

   public Mobile getMobile() {
      return mb;
   }
   public Mp3Palyer getMp3() {
      return mp3;
   }
   public class SmartMobile extends Mobile {
      @Override // 不同的智能机有的call方式
      public void call() {
         System.out.println("Call phone!");
      }
   }
   public class PhoneMp3 extends Mp3Palyer {
      @Override // 不同的智能机有的play方式
      public void play() {
         System.out.println("Play music!");
      }
   }
}
public class MutiImpTest1 {
   static void call(Mobile m) {
      m.call();
   }
   static void play(Mp3Palyer p) {
      p.play();
   }
   public static void main(String[] args) {
      SmartPhone sp = new SmartPhone();
      call(sp.getMobile());// 智能手机具有手机功能
      play(sp.getMp3());// 又具有Mp3的功能
   }
}

下面采用继承与匿名类的方式模拟(一个是类层面的,一个是对象层面,即外部类首先继承一个类,然后通过引用内部类对象来继承另外一个类):

//手机
abstract class Mobile {
   public abstract void call();
}
// MP3播放器
abstract class Mp3Palyer {
   public abstract void play();
}
// 智能手机,继承自Mobile
class SmartMobile extends Mobile {
   @Override // 不同的智能机有的call方式
   public void call() {
      System.out.println("Call phone!");
   }
   // 智能手机也有播放音乐的功能
   public Mp3Palyer getMp3() {
      return new Mp3Palyer() {
         @Override // 不同的智能机有的play方式
         public void play() {
            System.out.println("Play music!");
         }
      };
   }
}
public class MutiImpTest2 {
   static void call(Mobile m) {
      m.call();
   }
   static void play(Mp3Palyer p) {
      p.play();
   }
   public static void main(String[] args) {
      // 现在智能手机即是手机类型,又具有Mp3的功能
      SmartMobile sp = new SmartMobile();
      call(sp);// 智能手机即是手机类型
      play(sp.getMp3());// 又具有Mp3的功能
   }
}

上面的多继承是站在外部类的角度来看的,即它们是通过外部类引用内部类来达到多态与复用的目的。反过来,内部类继承了一个类,同时拥有了外部类的所有成员方法和属性,我们是否可以认为内部类集成了两个类呢?——一个是类层面的,一个是对象层面的(因为非静态内部类使用前一定有外部类的对象来创建它,它持有外部类某个对象的引用)。如果外部类还继承了其他类呢?内部类还是可以访问其他类的方法与属性。现在从内部类的角度来模拟多继承:

//智能机抽象类
abstract class SmartMobile {
   public abstract void call();

   public void play() {
      System.out.println("Play music!");
   }
}

// 手机
class Mobile {
   /*
    * 该方法是私有的,与SmartMobile类中方法同名,所以
    * Mobile不能直接继承自SmartMobile,因为重写时不能
    * 缩小访问权限,所以只能使用一个内部类来重写。
    */
   private void call() {
      System.out.println("Call phone!");
   }

   public SmartMobile getSmartMobileImp() {
      // 智能机的实现,好比多继承(继承外部类与SmartMobile)
      return new SmartMobile() {
         // 调用“继承”自外部类的相应方法来实现call
         public void call() {
            // 回调外部类真真的实现
            Mobile.this.call();
         }
      };
   }
}

public class MutiImpTest3 {
   static void call(SmartMobile m) {
      m.call();
   }
   static void play(SmartMobile p) {
      p.play();
   }
   public static void main(String[] args) {
      // 智能机即是手机也是Mp3播放器
      SmartMobile sp = new Mobile().getSmartMobileImp();
      call(sp);// 智能机即是手机
      play(sp);// 也是是Mp3播放器
   }
}

另外,从上面程序可看出内部类的另一作用:如果你想继承一个类或实现一个接口,但是这个接口或类中的一个方法和你构想的这个类中的一个方法的名称,参数相同,但访问权限缩小了,所以你不能直接继承与实现它,你应该怎么办?这时候,你可以建一个内部类继承这个类或实现这个接口(当然你可以修改访问权限是可以的)。由于内部类对外部类的所有内容都是可访问的,内部类可以通过调用外部类的这个方法来重写那个类或接口。上面的Mobile类中的call方法就是这种情况,所以你不能直接让Mobile去继承SmartMobile,固只能采用内部类来达到重写的目的。

闭包与回调

  • 动态语言的闭包是一个永恒的话题。闭包在编码过程的方便和快捷使得动态语言的拥护者对它津津乐道,而静态语言特别是Java语言的扇子们会拿出匿名内部类来说Java语言也有类似的功能。
  • JavaScript 中闭包的产生是由于 JavaScript 中允许内部 function,也就是在一个 function 内部声明的function 。
  • 内部 function 可以访问外部 function 中的局部变量、传入的参数和其它内部 function 。
  • 当内部function可以在包含它的外部 function 之外被引用时,就形成了一个闭包。这个时候,即便外部 function已经执行完成,该内部 function 仍然可以被执行,并且其中所用到的外部 function 的局部变量、传入的参数等仍然保留外部function 执行结束 时的值。下面是一个例子:
function Outer(){
var i=0;
function Inner(){
alert(++i);
}
return Inner;
}
var inner = Outer();
inner();
  • 因为函数Outer外的变量inner引用了函数Outer内的函数Inner,就是说:当函数Outer的内部函数Inner被函数Outer外的一个变量inner引用的时候,就创建了一个闭包。
  • 闭包有什么作用:简而言之,闭包的作用就是在Outer执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回Outer所占用的资源,因为Outer的内部函数Inner的执行需要依赖Outer中的变量。
  • 闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出Java中内部类是面向对象的闭包,因为它不仅包含创建内部类的作用域的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。
  • C++有指针函数,可以实现回调。通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。Java中没有指针,回调是通过匿名类来实现的。

以下是TIJ中的闭包与回调实例:

//具有自增能力的接口,也是回调接口
interface Incrementable {
  void increment();
}

// 回调框架
class Caller {
  // 回调接口对象引用
  private Incrementable callbackReference;
  // 动态的传进回调接口实现
  Caller(Incrementable cbh) { callbackReference = cbh; }
  void go() {
      callbackReference.increment();//回调
  }
}

//自增接口简单实现:
class Callee1 implements Incrementable {
  private int i = 0;
  public void increment() {
    i++;
    System.out.println(i);
  }
}  

//另一个具有类似功能的自增具体类,但功能不是Incrementable所要现实的功能
class MyIncrement {
  //与类Callee1中的increment方法具有相同的签名
  public void increment() { System.out.println("Other operation"); }
  static void f(MyIncrement mi) { mi.increment(); }
}

// 如果你想实现Incrementable接口而功能又不是MyIncrement的功能实现,
// 即我们又想要MyIncrement中的increment功能实现,同时又要想实现Incrementable接口,
// 此时我们只能使用一个类内部类来实现Incrementable接口
class Callee2 extends MyIncrement {
  private int i = 0;
  // 重写了父类的功能
  public void increment() {
    super.increment();//同时又保留了父类的功能
    // 以下是Callee2具体类自己的实现
    i++;
    System.out.println(i);
  }
  // 内部类,与外部类形成一个闭包,因为内部类有一个指向外围类的引用,且在内部类作
  // 用域内可以调用外围类对象一切成员
  private class Closure implements Incrementable {//实现Incrementable接口
    public void increment() {
      // 指定调用外部类的increment方法,否则会发生递归调用
      Callee2.this.increment();//闭包,可以访问其作用域以外的外围类对象信息
    }
  }
  // 提供回调对象引用
  Incrementable getCallbackReference() {
    return new Closure();
  }
}  


// 调用
public class Callbacks {
  public static void main(String[] args) {
    Callee1 c1 = new Callee1();//简单实现者
    Callee2 c2 = new Callee2();//MyIncrement子类,但同时实现了Incrementable功能
    MyIncrement.f(c2);
    Caller caller1 = new Caller(c1);//动态传进回调对象
    Caller caller2 = new Caller(c2.getCallbackReference());//动态传进回调对象
    caller1.go();//开始执行回调
    caller1.go();
    caller2.go();
    caller2.go();
  }
} /* Output:
Other operation
1
1
2
Other operation
2
Other operation
3
*/

以下是孙妹妹在《JAVA面向对象编程》写的回调,基本上是模仿上面那个例子,但觉得她对回调理解的有问题,因为根本不像上面有回调框架类Caller存在,唉,国人写的书啊,这里那能看的出是回调?我咋就没有悟出来呢?我看到的只是一种闭包。请看她所列举的例子:
在以下Adjustable接口和Base类中都定义了adjust()方法,这两个方法的参数签名相同,但是有着不同的功能。

interface Adjustable{//个人认为这是回调接口
   //功能:调节温度
   public void adjust(int temperature);
}
class Base{
   private int speed;
   //功能:调节速度
   public void adjst(int spped){
      this.speed = speed;
   }
}
如果有一个Sub类同时具有调节温度和调节速度的功能,那么Sub类需要继承Base类,并且实现Adjustable接口,但是以下代码并不能满足这一需求:
class Sub extends Base implements Adjustable{
   private int temperature;
   public void adjust(int temperature) {
      this.temperature = temperature;
   }
}

以上Sub类实现了Adjustable接口中的adjust()方法,并且把Base类中的adjust()方法覆盖了,这意味着Sub类仅仅有调节温度的功能,但失去了调节速度的功能。或以使用内部类来解决这一问题:

class Sub extends Base {
   private int temperature;

   // 为了防止覆盖父类的adjst,所以取了不同的名字
   private void adjustTemperature(int temperature) {
      this.temperature = temperature;
   }

   // 实现回调接口
   private class Closure implements Adjustable {
      public void adjust(int temperature) {
         // 这里是回调?
         adjustTemperature(temperature);
      }
   }

   // 提供回调对象引用
   public Adjustable getCallBackReference() {
      return new Closure();
   }

   public static void main(String[] args) {
      Sub sub = new Sub();
      // 具有调节速度的功能
      sub.adjst(1);

      //又具有调节温度的功能,这里就是回调?
      sub.getCallBackReference().adjust(2);
   }
}

上面使Sub类既不覆盖Base类的adujst()方法,又实现了Adjustable接口的adjust()方法。
客户类先调用sub实例的getCallBackReference()方法,获得内部类的Closure实例,然后再调用Closure实例的adjust()方法,该方法又调用Sub实例的adjustTemperature()方法。这种调用过程称为回调(这就叫回调?不理解)。
回调实质上是指一个类尽管实际上实现了某种功能,但是没有直接提供相应的接口,客户类可以通过这个类的内部类的接口来获得这种功能。而这个内部类本身并没有提供真正的实现,仅仅调用外部类的实现。可见,回调充分发挥了内部类所具有的访问外部类的实现细节的优势。

以下回调来源于网络:
回调的基本原理跟好莱坞原则一样,Don’t call me,I’ll call you.
编程上来说,一般使用一个库或类时,是你主动调用人家的API,这个叫Call,有的时候这样不能满足需要,需要你注册(注入)你自己的程序(比如一个对象),然后让人家在合适的时候来调用你,这叫Callback。设计模式中的Observer就是例子:所有的观察者都需要向自己关心的主题Observable注册,然后主题在适当时机(主题类对象的属性发生变化时)通知所有订阅它的观察者并更新,其中观察者都实现了一个统一的Observer接口中的Update方法。

/**下面应用中ICallBack接口与Printer类好比是别人提供的API,*/
public interface ICallBack {//回调接口
public void print();
}
public class Printer {
    ICallBack ic;

    void setCallBack(ICallBack ic) {
        this.ic = ic;  
    }
  /*
供外界调用,即自己提供一个接口ICallBack,由外界PrintHandler去实现,再在适当时机回头调用外界所提供的实现print方法。
我没有实现接口,但是我取得了一个实现接口的对象,而这个对象是外界类调用我的方法setCallBack()时所赋给我的,因此我可以在业务需要的地方来调用外界所提供的实现print方法
*/
void execute() {
   //固定算法do some thing…
        ic.print(); //抽取变化的部分,由外界去实现
//固定算法 do some thing…
    }
}

/**下面是外界应用*/
public class PrintHandler {
    public static void main(String[] args) {
        Printer printer = new Printer();
/*注意下面的这项代码片段,它给printer对象传递了一个实现ICallBack接口的匿名类,这样Printer类的对象就取得了一个实现回调接口的类,因此Printer可以在任何时候调用接口中的方法*/
        printer.setCallBack(new ICallBack() {
         /* print 方法在PrintHandler类中实现,但不在PrintHandler 类对象中调用,而是在Printer类对象中调用,这就是回调*/
            public void print() {
                System.out.println("This is a callback");
            }
        });
         //  这句话可以设置成当满足某条件时再执行  
        printer.execute();  
    }  
}

内部类生成的class文件名规则:

public class A {//A.class
   class B {//A$B.class
      class C {}//ABC.class
   }

   {
      class B {}//A$1B.class
   }

       B f() {
      class D {}//A$1D.class
      return new B() {};//A$1.class
   }

       B g() {
      class E {//A$1E.class
                     B h() {
            return new B() {};//A1E1.class
         }
      }
      return new B() {};//A$2.class
   }
   static class F{}//A$F.class

   public static void main(String[] args) {
      A a = new A();
      System.out.println(a.f().getClass().getName());
      System.out.println(a.g().getClass().getName());
   }
}

接口与工厂模式

接口典型的应用就是多继承,而工厂方法设计模式能生成实现同一接口的对象,这与我们直接在使用的地方new某个实现对象是不同的,我们通过工厂对象上调用是业务实现对象创建方法,而该工厂对象将生成接口的某个业务实现的对象,理念上,我们的代码将完全与接口分离,这使得我们可以透明地将某个实现替换为另一个实现。下面的实例展示了工厂方法的结构:

// 业务接口
interface Service {
  void method1();
  void method2();
}
// 业务工厂
interface ServiceFactory {
  Service getService();
}
// 业务1的实现
class Implementation1 implements Service {
  Implementation1() {} // 包访问
  public void method1() {System.out.println("Implementation1 method1");}
  public void method2() {System.out.println("Implementation1 method2");}
} 
// 业务1的工厂
class Implementation1Factory implements ServiceFactory {
  public Service getService() {
    return new Implementation1();
  }
}
//业务2的实现
class Implementation2 implements Service {
  Implementation2() {} // 包访问
  public void method1() {System.out.println("Implementation2 method1");}
  public void method2() {System.out.println("Implementation2 method2");}
}
//业务2的工厂
class Implementation2Factory implements ServiceFactory {
  public Service getService() {
    return new Implementation2();
  }
} 
//工厂应用
public class Factories {
  public static void serviceConsumer(ServiceFactory fact) {
   // 通过不同的工厂获得不同的业务实现
    Service s = fact.getService();
    s.method1();
    s.method2();
  }
  public static void main(String[] args) {
    serviceConsumer(new Implementation1Factory());
    // 业务实现可以透明的改变:
    serviceConsumer(new Implementation2Factory());
  }
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/

如果不使用工厂方法,你的代码就必须在使用处指定将要创建的Service的确切类型,以便调用合适的构造器。为什么需要这种额外的间接性呢?一个常见的原因就是想要创建框架,业务实例的生产过程对客户是透明的,而且还可以在工厂方法返回实例前对业务对象进行额外处理。

内部类与工厂模式

看看再使用匿名内部类来修改上面的程序:

interface Service {
  void method1();
  void method2();
}
interface ServiceFactory {
  Service getService();
} 
class Implementation1 implements Service {
  private Implementation1() {}// 私有的,只能通过工厂方法来返回
  public void method1() {System.out.println("Implementation1 method1");}
  public void method2() {System.out.println("Implementation1 method2");}
  public static ServiceFactory factory = // 静态成员,只需一个工厂即可
    new ServiceFactory() {// 使用匿名类来实现工厂接口
      public Service getService() {
       // 但需多个业务对象,每次调用工厂方法都会获得一个业务实现对象
        return new Implementation1();
      }
    };
} 
class Implementation2 implements Service {
  private Implementation2() {}// 私有的,只能通过工厂方法来返回
  public void method1() {System.out.println("Implementation2 method1");}
  public void method2() {System.out.println("Implementation2 method2");}
  public static ServiceFactory factory =
    new ServiceFactory() {
      public Service getService() {
        return new Implementation2();
      }
    };
} 
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);
  }
}

修改后Implementation1与Implementation2的构造器都可以是private的,并且没有任何必要去创建具有具体名字的工厂类,另外,你经常只需要一个工厂对象即可,因此在本例中它被创建为Service实现的一个static域,这样更具有实际意义。修改后的程序是多么的完美!

你可能感兴趣的:(java编程思想笔记)