Java编程思想第四版读书笔记——第十章 内部类

第十章  内部类



1、创建内部类


内部类是嵌在外部类内部的类。

如果想从外部类的非静态方法创建一个内部类对象,一定要用“. ”,Outer Class . InnerClass

比如: Parcel2.Contents c = q.contents();



2、链接到外部类


内部类可以访问外围对象的所有成员。对外围类的所有元素都有访问权。

内部类的对象只有在于外部类的对象相关联时才能被创建。当某个外围类对象创建一个内部类对象,此内部类的对象必定会秘密的捕获一个指向那个外围类对象的引用。依赖于外部对象,也就是,必须是外部类对象 . 某方法()  的方式创建。



3、使用this和new


内部类创建外部类对象,return OuterClass.this;

在其它地方创建内部类,要用外围类对象创建,而不是用类名。 OuterClass.InnerClass inner = outer.new Inner();



4、内部类和向上转型


接口的所有成员自动被设置为public。

private内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了细节。因为是private,所以除了外围类不能访问它,只能通过outer.contents() 方法return的new出来。 因为上转型为接口,所以隐藏了原本的类型。

由于不能访问任何新增加的,原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给Java编译器提供了生成更高效代码的机会。因为转型只能转型成接口类型,收窄了。



5、在方法和作用域内的内部类


可以在一个方法或者任意的作用域内定义内部类,这样做有两个理由:

1、实现了某类型的接口,于是可以创建并返回对其的引用。

2、要解决一个复杂的问题,想创建一个类来辅助解决方案,但是不希望这个类是公用的。


在方法内定义内部类:

public Destination destination(String s) { 
           class PDestination implements Destination

            private String label; 
            private PDestination(String whereTo) { 
               label = whereTo; 
            } 
            public String readLabel() { return label; } 
          } 
          return new PDestination(s); 
       } 

调用destination(String s)函数return这个类。


在作用域内定义:

 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(); 
          } 
          // Can’t use it here! Out of scope: 
          //! TrackingSlip ts = new TrackingSlip("x"); 
       }

虽然这个内部类被嵌在if语句中,这并不代表这个类的编译是有条件的,它其实与别的类一起编译过了。



6、匿名内部类


形如:

public class Parcel7 { 
       public Contents contents() { 
          return new Contents() { // Insert a class definition 
            private int i = 11; 
            public int value() { return i; } 
          };
// Semicolon required in this case 
       } 
       public static void main(String[] args) { 
          Parcel7 p = new Parcel7(); 
          Contents c = p.contents(); 
       } 
     }

由此可以创建一个继承自Contents的匿名内部类。

不能给内部类用构造器初始化,因为匿名内部类没有名字。

如果定义一个匿名内部类,它需要使用外部定义的参数,那么次参数引用需要是final的,如:

public class Parcel9 { 
       // Argument must be final to use inside 
       // anonymous inner class: 
       public Destination destination(final String dest) { 
          return new Destination() { 
            private String label = dest; 
            public String readLabel() { return label; } 
          }; 
       } 
       public static void main(String[] args) { 
          Parcel9 p = new Parcel9(); 
          Destination d = p.destination("Tasmania"); 
       } 
     }

而如果这个参数并未直接被使用(如被基类构造器使用),那么就不需要是final的.,如下:

abstract class Base { 
       public Base(int i) { 
          print("Base constructor, i = " + i); 
       } 
       public abstract void f(); 
     } 


     public class AnonymousConstructor { 
       public static Base getBase(int i) { 
          return new Base(i)
            { print("Inside instance initializer"); } 
            public void f() { 
               print("In anonymous f()"); 
            } 
          }; 
       } 
       public static void main(String[] args) { 
          Base base = getBase(47); 
          base.f(); 
       } 
     }

可以对匿名内部类进行实例初始化,比如:

 public Destination 
       destination(final String dest, final float price) { 
          return new Destination() { 
            private int cost; 
            // Instance initialization for each object: 
           
               cost = Math.round(price); 
               if(cost > 100) 
                 System.out.println("Over budget!"); 
            } 

            private String label = dest; 
            public String readLabel() { return label; } 
          }; 
       } 
       public static void main(String[] args) { 
          Parcel10 p = new Parcel10(); 

          Destination d = p.destination("Tasmania", 101.395F); 
       } 
     }

实例初始化的效果就是构造器。

匿名内部类可以既可以扩展类(如抽象类),也可以实现接口,但不能二者兼容。而且如果是实现接口,只能实现一个接口。


利用内部类可以更好的实现工厂模式。



7、嵌套类


将内部类声明为static,这通常称为嵌套类。

嵌套类意味着:

1、要创建嵌套类的对象,不需要其外围类的对象。

2、不能从嵌套类的对象中访问非静态的外围对象。

普通内部类不能有static数据和static字段,也不能包含嵌套类,而嵌套类可以包含。它不需要依赖外围类引用。如下:

public class Parcel11 { 
       private static class ParcelContents implements Contents { 
          private int i = 11; 
          public int value() { return i; } 
       } 
       protected static class ParcelDestination 
       implements Destination

          private String label; 
          private ParcelDestination(String whereTo) { 
            label = whereTo; 
          } 
          public String readLabel() { return label; } 
          // Nested classes can contain other static elements: 
          public static void f() {} 
          static int x = 10; 
          static class AnotherLevel { 
            public static void f() {} 
            static int x = 10; 
          } 
       } 
       public static Destination destination(String s) { 
          return new ParcelDestination(s); 
       } 
       public static Contents contents() { 
          return new ParcelContents(); 
       } 
       public static void main(String[] args) { 
          Contents c = contents(); 
          Destination d = destination("Tasmania"); 

       } 
     }

接口内部的类:

接口中的任何类自动是public和static的。

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

public interface ClassInInterface { 
       void howdy(); 
       class Test implements ClassInInterface { 
          public void howdy() { 
             System.out.println("Howdy!"); 
          } 
          public static void main(String[] args) { 
             new Test().howdy(); 
          } 
       } 
     } 

采用嵌套类来写每个类测试用的main()方法,就不必带着已编译过的额外代码,发布时删掉类就好。

public class TestBed { 
       public void f() { System.out.println("f()"); } 
       public static class Tester { 
          public static void main(String[] args) { 
             TestBed t = new TestBed(); 
             t.f(); 
          } 
       } 
     }



8、为什么需要内部类


内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。可以认为内部类提供了某种进入其外围类的窗口。

内部类最吸引人的原因是:

        内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效的实现了“多重继承”。也就是说,内部类允许继承多个非接口类型(类或抽样类)。(个人感觉一个外围类可以包含多个内部类,然后每个内部类通过多个方法分别return new baseClass1{.......};  return new baseClass2{.......};  return new baseClass3{.......};  在调用时使用 outer.方法() 可以返回多重类型,作为函数的参数,达到“多重继承”的效果。)

    如果拥有的是抽象类或者具体的类,而非接口,那就只有内部类才能实现多重继承。


如果使用内部类,可以获得一些特别的特性:

  1. 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
  2. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
  3. 创建内部类对象的时刻并不依赖于外围类对象的创建。
  4. 内部类没有令人迷惑的“is-a”关系,它就是一个独立的实体。

闭包与回调:

闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。内部类是面向对象的闭包。在此作用域内,内部类有权操作所有的成员,包括private成员。

回调:

接口{
g();
}

基类{
g();
}

类1 implents 接口{
g(){};
}

类2 extends 基类{
g(){};
内部类 implents接口{
g(){
类2.this.g();
}
}
}

回调类{

private 接口对象a;

传入接口对象函数(接口对象b){a=b;}

调用接口对象函数(){a.g();}

}


继承两个类,含有同名方法,使用内部类可以避免覆盖。当创建一个内部类,没有在外围类接口中添加东西,也没有修改外围类接口。

通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。

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


内部类与控制框架:


应用程序框架就是被设计用以解决某类特定问题的一个类或者一组类。

模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。

控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称作事件驱动系统


“变化向量”是各种不同的Event对象所具有的不同行为,通过创建不同的Event子类来表现不同的行为。

内部类允许:

  1. 控制框架的完整实现是由单个类创建的,从而使得实现的细节被封装起来。内部类用来表示解决问题所必需的各种不同的action()。
  2. 内部类可以很容易的访问外围类的任意成员,所以可以避免这种实现变得笨拙。

作为典型的应用程序框架,GreenHouseControls类继承自Controller:


package innerclasses.controller; 

public abstract class Event {            //模板方法
        private long eventTime; 
        protected final long delayTime; 
        public Event(long delayTime) { 
          this.delayTime = delayTime; 
          start(); 
        } 
        public void start() { // Allows restarting 
          eventTime = System.nanoTime() + delayTime;   //触发时间 = 系统当前时间+延迟时间
        } 
        public boolean ready() { 
          return System.nanoTime() >= eventTime; 
        } 
        public abstract void action(); 
     }




package innerclasses.controller; 
      import java.util.*;

public class Controller {                              //组合Event类,用以管理并触发事件的实际框架
        // A class from java.util to hold Event objects: 
        private List<Event> eventList = new ArrayList<Event>(); 
        public void addEvent(Event c) { eventList.add(c); } 
        public void run() { 
           while(eventList.size() > 0) 
              // Make a copy so you’re not modifying the list 
              // while you’re selecting the elements in it: 
              for(Event e : new ArrayList<Event>(eventList)) 
                 if(e.ready()) { 
                    System.out.println(e); 
                    e.action(); 
                    eventList.remove(e); 
                 } 
        } 
      }




import innerclasses.controller.*; 

public class GreenhouseControls extends Controller { 
      private boolean light = false; 
      public class LightOn extends Event {                                 //点灯内部类
         public LightOn(long delayTime) { super(delayTime); } 
         public void action() { 
           // Put hardware control code here to 
           // physically turn on the light. 
           light = true; 
         } 
         public String toString() { return "Light is on"; } 
      } 
      public class LightOff extends Event {                                    //关灯内部类
         public LightOff(long delayTime) { super(delayTime); } 
         public void action() { 
           // Put hardware control code here to 
           // physically turn off the light. 
           light = false; 
         } 
         public String toString() { return "Light is off"; } 
      } 
      private boolean water = false; 
      public class WaterOn extends Event {                                   //开水内部类
         public WaterOn(long delayTime) { super(delayTime); } 
         public void action() { 
           // Put hardware control code here. 
           water = true; 
         } 
         public String toString() { 
           return "Greenhouse water is on"; 
         } 
      } 
      public class WaterOff extends Event {                                  //关水内部类
         public WaterOff(long delayTime) { super(delayTime); } 
         public void action() { 
           // Put hardware control code here. 
           water = false; 
         } 
         public String toString() { 
           return "Greenhouse water is off"; 
         } 
      } 
      private String thermostat = "Day"; 
      public class ThermostatNight extends Event {                          //晚上温度调节内部类
         public ThermostatNight(long delayTime) { 
           super(delayTime); 
         } 
         public void action() { 
           // Put hardware control code here. 
           thermostat = "Night"; 
         } 
         public String toString() { 
           return "Thermostat on night setting"; 
         } 
      } 
      public class ThermostatDay extends Event {                             //白天温度调节内部类
         public ThermostatDay(long delayTime) { 

 super(delayTime); 
          } 
          public void action() { 
            // Put hardware control code here. 
            thermostat = "Day"; 
          } 
          public String toString() { 
            return "Thermostat on day setting"; 
          } 
       } 
       // An example of an action() that inserts a 
       // new one of itself into the event list: 
       public class Bell extends Event {                                          //响铃内部类
          public Bell(long delayTime) { super(delayTime); } 
          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); 
          } 
          public void action() { 
            for(Event e : eventList) { 
               e.start(); // Rerun each event 
               addEvent(e); 
            } 
            start(); // Rerun this Event 
            addEvent(this); 
          } 
          public String toString() { 
            return "Restarting system"; 
          } 
       } 
       public static class Terminate extends Event {                               //暂停内部类
          public Terminate(long delayTime) { super(delayTime); } 
          public void action() { System.exit(0); } 
          public String toString() { return "Terminating";  } 
       } 
     }

如上,使用内部类,在单一的类里面产生对同一个基类Event的多种导出版本,action()方法通常涉及某硬件控制

下面是命令设计模式的例子:

import innerclasses.controller.*; 


     public class GreenhouseController { 
       public static void main(String[] args) { 
          GreenhouseControls gc = new GreenhouseControls(); 
          // Instead of hard-wiring, you could parse 
          // configuration information from a text file here: 
          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(); 
       } 
     } /* Output: 
     Bing! 
     Thermostat on night setting 
     Light is on 
     Light is off 
     Greenhouse water is on 
     Greenhouse water is off 
     Thermostat on day setting 
     Restarting system 
     Terminating 



9、内部类的继承


10、内部类可以被覆盖吗


11、局部内部类


12、内部类标识符


13、总结


这些特性的使用应该是设计阶段需要考虑的问题,孰能生巧,加油~!




你可能感兴趣的:(java,编程,对象)