java编程思想--9接口

)抽象类
class TV{ 
    public void show(){ 
        System.out.println("TV"); 
    } 

 
class LeTV extends TV{ 
    public void show(){ 
        System.out.println("LeTV"); 
    } 

class MiTV extends TV{ 
    public void show(){ 
        System.out.println("MiTV"); 
    } 

class SanTV extends TV{ 
    public void show(){ 
        System.out.println("SanTV"); 
    } 

public class EveryTV { 
    public static void tvshow(LeTV tv){ 
        tv.show(); 
    } 
    public static void tvshow(MiTV tv){ 
        tv.show(); 
    } 
    public static void tvshow(SanTV tv){ 
        tv.show(); 
    } 
    public static void main(String[] args) { 
        tvshow(new LeTV()); 
        tvshow(new MiTV()); 
        tvshow(new SanTV()); 
    } 
}

其实会发现,从TV一开始,并没有创建TV对象,因为TV对象没什么意义。
java提供了一种抽象方法的机制,C++中叫纯虚函数。
包含抽象方法的类为抽象类,如果一个类有一个或多个的抽象方法,那么类一定要定义为抽象类。
抽象的方法声明可以没有方法体。如下:
abstract show();

抽象类不可new,确保抽象类的纯粹性,这就是抽象类,其实这也是抽象类存在的一个原因。
上述的TV类可以改写成抽象类:
abstract class TV{ 
    abstract void show(); 
}

注意,子类同样需要继承方法,而且要有方法体。
其实电视本来就很抽象,TV本身不需要具体的show方法,具体的电视有具体的实现方法,new TV这个类也没什么用,TV就是一个抽象的概念。
看了一篇英文的回答——What is an abstract class, and when should it be used?
讲的是动物吃东西,动物本身也是一个抽象层次上的概念,但是动物有吃东西的方法,每种不同的动物有自己的吃法,吃肉,吃草,但是总得吃,动物这个概念性的东西,就可以声明为抽象类,如果是普通类,那么子类不重写也可以,那么默认就变成了和动物一样的吃法,但是动物本身并没有吃法,定义为抽象类,抽象方法在子类中需要有实现,这样扩展性也好,这应该就是设计者的初衷。


2)接口
public interface TV {}
不定义public的话,接口只具有包内的访问权限,而接口的方法不声明为public,也是public的,接口可以有域,隐式为static和final。

当然,我们完全可以将TV设计为一个接口。那么为什么有抽象类还要有接口呢?
接口有一个特定的地方,实现接口的类必须实现接口的所有方法,抽象类除外,抽象类可以选择性的实现。这个问题后面总结时解答。

3)完全解耦
耦合性是软件工程的一个概念,程序设计讲究高内聚低耦合,耦合性可以简单看成依赖性,就是粘合度过紧。

书先举了一个例子。
class TV{
    public String name(){
        return getClass().getSimpleName();//拿到类名
    }
    
    Object show(Object input){
        return input;
    }
}

class LeTV extends TV{
    String show(Object input){
        return (String)input+" in letv";
    }
}
class MiTV extends TV{
    String show(Object input){
        return (String)input+" in mitv";
    }
}
public class TVShow {
    public static void play(TV tv , Object o){
        System.out.println(tv.name());
        System.out.println(tv.show(o));
    }
    public static void main(String[] args) {
        TVShow ts = new TVShow();
        String mylove = "my love tv show";
        ts.play(new LeTV(), mylove);
        ts.play(new MiTV(), mylove);
    }
}

TVShow的play方法可以根据接收的具体的TV参数而调用不同的方法,这种模式称为—— 策略模式。

接下来有Computer,但是专门处理Game。
class Game{
    private static int count;
    private final int id  = count++;
    public String toString(){
        return "Game"+id;
    }
}

public class Computer{
    public String name(){
        return getClass().getSimpleName();//拿到类名
    }
   
    Game show(Game input){
        return input;
    }
}

class LeComputer extends Computer{
    Game show(Game input){
        return input;
    }
}
class MiComputer extends Computer{
    Game show(Game input){
        return input;
    }
}

虽然书上举的是filter和processor的例子,但是书上这里有一点错误,虽然Computer和TV具有相同的接口元素,但是Computer不是继承TV,根本就是两个类,所以Computer用在TVShow的play方法正常运行肯定是错的。有这个例子也可以看出,TVShow的方法和TV耦合过紧了,只支持TV,要是智能电视呢?

所以,将TV设计为一个接口:
public interface TV {
    String name();
    Object show(Object input);
}

public class TVShow {
    public static void play(TV tv , Object o){
        System.out.println(tv.name());
        System.out.println(tv.show(o));
    }
}

public abstract class AbstractTV implements TV{

    public String name() {
        return getClass().getSimpleName();   
    }
    public abstract String show(Object input);
    
    public static void main(String[] args) {
        TVShow ts = new TVShow();
        String mylove = "my love tv show";
        ts.play(new LeTV(), mylove);
        ts.play(new MiTV(), mylove);
    }

}

class LeTV extends AbstractTV{
    public String show(Object input){
        return (String)input+" in letv";
    }
}
class MiTV extends AbstractTV{
    public String show(Object input){
        return (String)input+" in mitv";
    }
}

是不是觉得很麻烦,还要我们自己新建一个AbstractTV去实现TV接口。有什么用呢?看看Game的改变:
class Game{
    private static int count;
    private final int id  = count++;
    public String toString(){
        return "Game"+id;
    }
}
class ComputerAdapter implements TV{
    private Computer c ;
    ComputerAdapter(Computer c){
        this.c = c;
    }
    public String name() {
        return c.name();
    }

    public Game show(Object input) {
        return c.show((Game)input);
    }
   
}

public class ComputerShow{
    public String name(){
        return getClass().getSimpleName();//拿到类名
    }
   
    Game show(Game input){
        return input;
    }
   
    public static void main(String[] args) {
        Game g = new Game();
        TVShow.play(new ComputerAdapter(new LeComputer()),g);
        TVShow.play(new ComputerAdapter(new MiComputer()),g);
    }
}

class LeComputer extends Computer{
    Game show(Game input){
        return input;
    }
}
class MiComputer extends Computer{
    Game show(Game input){
        return input;
    }
}

class Computer{
    public String name(){
        return getClass().getSimpleName();//拿到类名
    }
    Game show(Game input){
        return input;
    }
}

发现没有,原来不能给Computer使用的TVShow的play方法现在可以用了,原因是ComputerAdapter实现了TV接口。

其实这种模式又是另外一种设计模式——适配器模式。ComputerAdapter接收不同的Computer同时实现TV接口,以便后面用于TVShow方法,不然单纯的Computer对象无法作为方法参数,适配器能将你所拥有的接口去产生所需要的接口,即这个例子的接口传入,其实很类似策略模式。ComputerAdapter本身就是代理,你只需要传入Computer对象,而看不到里面的方法实现。

还有一个有趣的地方,虽说是实现接口的所有方法,但是ComputerAdapter和AbstractTV实现接口的show方法时却与不同的类型,原因在Object上,如果TV接口的show方法不是Object类型而是某一具体的对象的话,那么实现方法的时候方法类型就要一致。

这种接口的实现用处就是降低耦合性,TVShow不仅能传入继承AbstractTV的对象,对于其他实现TV接口的对象也是可以使用的。刚开始看可能有点看懂,我看书也看了好久,一大堆东西,但总算搞明白了。

4)多重继承
由于只能继承一个类,但是可以实现多个接口,便拥有多个接口的功能。
public class A extends B implements C,D,E{}


5)接口的域
放在接口的域都是自动为static和final,同时也是public的。其实特性反过来证明也是可以的。练习题也有。
public interface T {
    int A=1;
}

public class Test implements T{
    public static void main(String[] args) {
        System.out.println(Test.A);
        //System.out.println(Test.A++);
    }
}

静态域才可以通过类名直接访问,final的域不能改变,在java5开始,就有了枚举类型了。

最后,就像书上说的“确定接口是理想选择,因而应该总是选择接口而不是具体的类。”这其实是引诱。
通篇下来,会发现接口真的很抽象,抽象要应需求而用,而不是为了用了用。

、interface不仅仅只是一个极度抽象的类,因为它允许人们通过创建一个能够被向上转型为多种基类型的类,来实现某种类似C++多重继承变种的特性。

2、像类一样,可以在interface关键字前面添加public关键字(但仅限于该接口在与其同名的文件中被定义),或者不添加它而使其只是具有包访问权限,这样它就只能在同一个包内可用。

3、可以选择在接口中显示地将方法声明为public的,但即使你不这么做,它们也是public的。因此当实现一个接口时,在接口中被定义的方法必须被定义为public。

4、Java中,在方法的继承过程中,其可访问权限不能被降低。

5、接口也可以包含字段,但是它们隐式为static和final的。

6、因为接口是根本没有任何具体实现的——也就是说,没有任何与接口相关的存储;因此,也就无法阻止多个接口的组合,也就是“多重继承”。如下例子:


interface CanFight{  
   void fight();  
}  
interface CanSwim{  
   void swim();  
}  
interface CanFly{  
   void fly();  
}  
class ActionCharacter{  
   public void fight(){  
      System.out.println("ActionCharacter.fight()");  
   }  
}  
class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly{  
   public void swim(){  
      System.out.println("Hero.swim()");  
   }  
   public void fly(){  
      System.out.println("Hero.fly()");  
   }  
   /**1.注意CanFight中的fight()方法并没在这里定义 
    * 2.这是因为CanFight接口与ActionCharacter类中的fight()方法的特征签名是一样的。 
    * 3.所以即使Hero没有显式地提供fight()的定义,其定义也因ActionCharacter而随之而来。 
    * 4.若把ActionCharacter的fight()改为private,就会出现提示说 
    *   “类型 Hero必须实现继承的抽象方法 CanFight.fight()”的错误。 
    * 5.若把ActionCharacter的fight()改为private,就会出现提示说 
    *   “继承的方法 ActionCharacter.fight()不能隐藏 CanFight中的公用抽象方法”的错误。 
    */  
    
}  
public class Adventure{  
   public static void t(CanFight x){x.fight();}  
   public static void u(CanSwim x){x.swim();}  
   public static void v(CanFly x){x.fly();}  
   public static void w(ActionCharacter x){x.fight();}  
   public static void main(String[] args){  
      Hero h = new Hero();  
      t(h);  
      u(h);  
      v(h);  
      w(h);  
   }  
}  
运行结果:


1).注意CanFight中的fight()方法并没有在Hero定义

2).这是因为CanFight接口与ActionCharacter类中的fight()方法的特征签名是一样的。

3).所以即使Hero没有显式地提供fight()的定义,其定义也因ActionCharacter而随之而来。

4).若把ActionCharacter的fight()改为private,就会出现提示说“类型 Hero必须实现继承的抽象方法CanFight.fight()”的错误。

5).若把ActionCharacter的fight()改为private,就会出现提示说“继承的方法 ActionCharacter.fight()不能隐藏CanFight中的公用抽象方法”的错误。


7、以上例子所展示的就是使用接口的核心原因:为了能够向上转型为多个基类型。

8、使用接口的第二原因:与使用抽象基类相同,防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。

9、应该使用接口还是抽象类:当要创建不带任何方法定义和成员变量的基类时,使用接口。

10、事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口,只有在强制你必须具有方法定义和成员变量时,才应该选择抽象类,或者在必要时使其成为一个具体类。

11、接口通过继承来扩展,如下:


interface Plant{  
   void grow();  
}  
interface Flower extends Plant{  
   void bloom();  
}  
interface Apple extends  Plant, Flower{  
   void fruit();  
}  
class RedApple implements Apple{  
   public void grow(){  
      System.out.println("RedApple isgrowing.");  
   }  
   public void bloom(){  
      System.out.println("RedApple isblooming.");  
   }  
   public void fruit() {  
      System.out.println("RedApple isfruiting.");  
   }  
}  
心细的人可能看到了Apple接口后extends  Plant, Flower。一般情况下,只可以将extends用于单一类,但是既然接口可以由多个其它接口产生,那么在创建一个新接口时,extends当然可以引用多个基类接口。此语法仅适用与接口的继承。

12、Java中构建如同C++的enum类型那样具备类型安全(变量的取值被限定在一个有限的范围内):


public final class Month{  
   private String name;  
   //private构造器,无法创建它的任何实例,所有实例都是在类的内部有其身创建的finalstatic实例  
   private Month(String name){  
      this.name = name;  
   }  
   public String toString(){  
      return name;  
   }  
   public static final Month  
      JAN = new Month("January"),  
      FEB   = new Month("February"),  
      MAR   = new Month("March"),  
      APR   = new Month("April"),  
      MAY   = new Month("May"),  
      JUN   = new Month("June"),  
      JUL   = new Month("July"),  
      AUG   = new Month("August"),  
      SEP   = new Month("September"),  
      OCT   = new Month("October"),  
      NOV   = new Month("November"),  
      DEC   = new Month("December");  
   public static final Month[] month = {  
      JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC  
   };  
   public static final Month number(int order){  
      return month[order - 1];  
   }  
   public static void main(String[] args){  
      //curMonth是一个Month对象,它仅可以被赋予一个Month对象。这就体现了类型安全性。  
      Month curMonth = Month.JAN;  
      System.out.println(curMonth);  
      curMonth = Month.number(12);  
      System.out.println(curMonth);  
      //这种方式同样也允许你可互换地使用==或equal(),因为Month的每一个值都仅有一个实例。  
      System.out.println(curMonth == Month.DEC);  
      System.out.println(curMonth.equals(Month.DEC));  
      System.out.println(Month.month[3]);  
   }  
}  
运行结果:


13、接口可以嵌套在类或其他接口中。嵌套接口可以拥有public和“包访问”两种可视性。同时,public和包访问的嵌套接口都可以被实现为public、包访问以及private的嵌套类。

class A{  
   interface B{  
      void fromB();  
   }  
   interface D{  
      void fromD();  
   }  
   //嵌套接口可以是private访问权限。  
   //它带来的好处是:强制接口中的方法定义不要添加任何类型信息(也就是说,不允许向上转型),不明白,请看看最后的解说。  
   private interface I{  
      void fromI();  
   }  
   //private的嵌套接口可以被实现为private  
   private class J implements I{  
      public void fromI() {  
        System.out.println("E.J.fromI()");  
      }  
   }  
   //private的嵌套接口也可以被实现为public  
   public class J1 implements I{  
      public void fromI() {  
        System.out.println("E.J1.fromI()");  
      }  
   }  
   //结合getI()和setI()方法实现“向上转型”?!  
   public I getI(){  
      return new J1();  
   }  
   public void setI(I i){  
      i.fromI();  
   }  
   //接口之间可以嵌套,嵌套在另一个接口中的接口只能是public的  
   interface K{  
      interface L{//此处也默认是public的  
        void fromL();  
      }  
   }  
}  
public class E implements A.D{  
   public void fromD() {  
      System.out.println("E.fromD()");  
   }  
   //内部类F public  
   public class F implements A.B{  
      public void fromB() {  
        System.out.println("E.F.fromB()");  
      }  
   }  
   //内部类G protected  
   protected class G implements A.B{  
      public void fromB() {  
        System.out.println("E.G.fromB()");  
      }  
   }  
   //内部类I private  
   private class H implements A.B{  
      public void fromB() {  
        System.out.println("E.I.fromB()");  
      }  
   }  
   public static void main(String[] args){  
      E e = new E();  
      e.fromD();  
      //内部的实例化  
      E.F f = e.new F();  
      f.fromB();  
       
      E.G g = e.new G();  
      g.fromB();  
       
      E.H h = e.new H();  
      h.fromB();  
      //"实现了private接口I"的"public类J1"的实例化。  
      A.J1 j1 = new A().new J1();  
      j1.fromI();  
       
      A a = new A();  
      //A.I i = a.getI();//类型 A.I 不可视  
      //A.J1 j = a.getI();//不能从 A.I转换为 A.J1  
      //a.getI().fromI();//类型 A.I 不可视  
      a.setI(a.getI());  
   }  
}  
运行结果:

getI(),返回private接口的引用的public方法。在main()中,数次使用返回值的行为都失败了。只有一种方式可以成功,那就是将返回值交给有权使用它的对象。在上例中,是一个A的对象a同过setI()方法来实现的。

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
9.1抽象类和抽象方法
创建抽象类是想通过这个接口来操作一系列类。抽象方法只有声明没有方法体,
抽象类不能直接实例化,只能继承。
9.2接口
interface关键字产生一个完全抽象的类,没有提供任何具体实现。
抽象类和接口的区别
1、抽象类里面可以有非抽象方法
但接口里只能有抽象方法
声明方法的存在而不去实现它的类被叫做抽像类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的所有抽像方法提供实现,否则它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
2、接口(interface)是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可以定义static final成员变量 。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对像上调用接口的方法。由于有抽像类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口 9.3完全解耦
策略设计模式method(class,param)
适配器策略模式:如果无法修改你想要使用的类时,适配器中的代码将接受你拥有的接口,并产生你需要的接口。
X
这个类图也很容易理解,Target是一个类(接口),Adaptee是一个接口,通过Adapter把Adaptee包装成Target的一个子类(实现类)。
9.4java中的多重继承

如果要创建不带任何方法和成员变量的基本时,应选择接口。
9.5通过继承来扩展接口
通过继承可以在新接口中组合数个接口,extends后面逗号隔开。
9.7接口中的域
接口中的任意域都是static和final的,所有接口是一个很便捷的创建常量数组的工具。
9.8嵌套接口
9.9接口与工厂
工厂方法设计模式;

抽象类和抽象方法
抽象方法:不完整的,仅有声明而没有方法体。

   abstract void f();
抽象类:包含抽象方法的类。(若一个类包含一个或多个抽象方法,则该类必须限定为抽象的。)

1.用抽象类直接创建对象是不安全的,因为这个抽象类是不完整的。编译器通过这种方式保证了
抽象类的纯粹性。

public abstract class Person {
    public abstract void eat();
    public abstract void pee();
    public abstract void sleep();
}



//不能直接用抽象类创建对象
//Person p = new Person();//错误
2.一个类从抽象类中继承,若想创建改类的对象,就必须实现基类中的所有抽象方法。否则将无法创建该类的对象,
并且导出类也是抽象类。

//一个类继承自抽象类,必须实现这个抽象类中的所有抽象方法
public class Male extends Person {
    @Override
    public void eat() {
        System.out.println("Male eat a lot!");
    }

    @Override
    public void pee() {
        System.out.println("Male pee by standing up");
    }

    @Override
    public void sleep() {
        System.out.println("Male sleep");
    }
}


//创建改导出类的对象
Person p = new Male();
3.可以创建一个没有任何抽象方法的抽象类。(若有这样一个类,包含任何抽象方法都显得没有实际意义,而且我们也想阻止产生这个类的任何对象)

4.抽象类并不需要所有的方法都是抽象的。

作用:
1.明确类的抽象性,并告诉用户和编译器打算怎样来使用它们。
2.抽象类是很有用的重构工具,使我们能很容易的将公共方法沿着继承层次向上移动(也就是将公共方法抽取重构到抽象类中)

2.接口
interface关键字使抽象更进一步,产生一个完全抽象的类,没有提供任何具体的实现。(只有方法名,参数列表和返回类型,没有任何方法体。接口只提供了形式,而未提供任何具体实现)

public interface People {
    void eat();
    void sleep();
    void pee();
}
一个接口表示:“所有实现了该特定接口的类看起来都像这样”

接口被用来建立类和类之间的协议(protocol)。

接口不仅仅是一个极度抽象的类,因为接口允许我们通过创建一个能够被向上转型为多种基类的类型
来实现类似于多重继承的特性。(Java只支持单继承,但可以通过实现接口来达到多继承的效果)

接口中可以包含域,但是这些域隐式地是static和final的。

使用implements关键字让一个类遵循某个接口(或是一组接口)。它表示:"接口只是它的外貌(what it looks like),我们用类实现接口来声明它是如何工作的(how it works)"

3.完全解耦
策略设计模式: 创建根据所传递的参数对象的不同而具有不同的行为的方法(这类方法包含所要执行的算法中
固定不变的部分,而策略包含变换的部分,策略就是传递进去的参数对象)

适配器设计模式: 适配器中的代码将接受所拥有的接口,并产生所需要的接口。
将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具复用性。                                              

 4.抽象类和接口的区别

尽管接口和抽象类看起来很是相似,但他们之间还是存在很多的不同点. 
(1).抽象类毕竟还是类,所以用class关键字声明然后用extends关键字继承.接口用interface声明,implements继承. 
(2).接口中方法权限必须是public,变量是static final型常量;抽象类中的方法权限可以自己设定,变量也一样. 
(3).接口中的所有方法都必须是抽象的而抽象类中可以是抽象的也可以是普通的. 
(4).最后也是最重要的一点区别是:接口支持多重继承而抽象类是不可以的!这一点带来的一个好处就是,我们能够将一个类向上转型成多个基类.

看起来像是一种简单的代码隐藏机制,将某些类置于另外一些类的的内部,内部类了解它的外围类(surrounding class),且能够与之通信。

1. 接口:接口可以包含数据成员,它们隐含都是static和final的。接口只提供了形式,而未提供任何具体实现。

接口被用来建立类与类之间的“协议(protocol)”。

接口中被定义的方法必须是public的。

2. java的多重继承:接口的组合使用。

使用接口的核心原因:1)为了向上转型为不止一个的基类型,2)与抽象基类相同,防止客户端程序员创建该类的对象,并确保仅仅是建立一个接口。

3. 组合接口时带来的名字冲突:避免想要组合的不同接口中使用相同的方法名。(被重载的方法仅通过返回类型是不能区分开的)。

4. 通过继承来扩展接口:使用extends来引用多个基类接口,用逗号将接口名一一分隔开即可。

5. 群组常量:接口用来创建常量组的工具,使用大写字母的风格

6. 初始化接口中的数据成员:在接口中的数据成员,自动是static和final的,可以被非常量表达式初始化。这些数据成员不是接口的一部分,只是被存储在该接口的静态存储区域内。

7. 嵌套接口:接口嵌套在类或其他接口中。

8. 内部类:从外部类的非静态方法之外的任意位置创建某个内部类的对象,指明这个对象的类型:OuterClassName.InnerClassName.

9. 内部类与向上转型:内部类向上转型为基类,转型为一个接口的时候,使用内部类。所得到的只是指向基类或接口的一个引用,所以能够很方便地隐藏实现细节。

Private内部类给类的设计者提供了一种途径,以完全阻止依赖于类型的编码,并且完全隐藏了实现的细节。

普通的(非内部的)类,不能声明为private或protected;它们只可以被赋予public或者包访问权。

10. 在方法和作用域内的内部类:理由1)实现了某类型的接口,于是可以创建并返回对其的引用。2)要解决一个负杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。

11. 匿名内部类:在匿名内部类中,要使用一个在它的外部定义的对象,编译器会要求其参数引用是final型的。通过实例初始化来达到构造器的效果,由于不能重载实例化,所以只能有一个构造器。

12. 链接到外部类:内部类拥有其外围类的所有元素的访问权。

13. 嵌套类:将内部类声明为static,这成为嵌套类(nested class)。这意味着:1)要创建嵌套类的对象,并不需要其外围类的对象。2)不能从嵌套类的对象中访问非静态的外围类对象。

普通的内部类的属性与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static属性,也不能包含嵌套类,但是嵌套类可以包含所有这些东西。

可以将嵌套类置于接口的命名空间内,并不违反接口的规则。

14. 引用外围类的对象:在此外围类名称后面跟一个句点和this关键字来获得。

生成内部类的对象时,必须使用此外围类的一个对象来创建其内部类的一个对象。因为此内部类的对象会悄悄地链接到创建它的外围类的对象。

15. 从多层嵌套类中访问外部:它能透明地访问所有它所嵌入的外围类的所有成员。

16. 内部类的继承:继承内部类时,必须将那个“秘密的”外围类对象的引用初始化,而被继承的类中并不存在相联接的缺省对象,需使用专门的语法来明确说清它们之间的关联。

在继承类的构造器中,使用enclosingClassReference.super(),来提供必要的引用,使程序编译通过。

17. 内部类不能像方法一样进行重载。因为这两个内部类是完全独立的两个实体,各自在自己的命名空间内。当然,明确地继承某个内部类是可以的。

18. 局部内部类(Local inner classes):可以访问当前代码块内的常量,和此外围类所有的成员。

使用局部内部类,原因是需要一个已命名的构造器,或者需要重载构造器,而匿名类只能用于实例初始化。

需要不止一个此内部类的对象时使用。

19. 内部类标识符:外围类的名字,加上‘$’,再加上内部类的名字。内部类是匿名的时候,编译器简单地产生一个数字作为其标识符。

20. 为什么需要内部类:不是总能享用到接口带来的方便,有时你需要与接口的实现进行交互,所以使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

内部类有效地实现了“多重继承”,也就是说,内部类允许你继承多个非接口类型(类或抽象类)。

21. 闭包(closure)与回调:闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。内部类是面向对象的闭包。

回调(callback):通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。通过内部类提供内闭包的功能。

22. 内部类与控制框架:1)用一个单独的类完整地实现一个控制框架,从而将实现的细节封装起来。内部类用来表示解决问题所必须的各种不同的action()。2)内部类能够轻易地访问外围类的任意成员,所以可以避免这种实现变得很笨拙。如果没有这种能力,代码将变得很令人讨厌,以至于你肯定会选择别的方法。

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

9.1抽象类和抽象方法

如果我们的基类只是希望它被继承从而通过它的接口操作导出类,它本身创建对象没有什么意义,那么我们可以把这个基类设计为抽象类。把在基类中不好实现的方法标记为abstract。这样做可以使类的抽象性更加明确。

9.2接口

interface关键字使得抽象概念更加彻底,只提供了形式,没有任何实现。但它不仅仅是极度抽象的类,它还允许创建出一个可以向上转型成多种类型的类,来实现类似于多重继承的特性。

接口中也可以包含域,但这些域是隐式static final的。

接口中的方法必须是public(可省略不写),实现类中实现方法也必须是public的。

9.3完全解耦

只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果这个方法还想操作其他类型,就会发生错误。而接口就可大大放宽这种限制,使得我们可以编写复用性高的代码。例如:有一个手机类,接口是播放器play(),有不同的导出类继承这个手机类,一个方法接收手机对象,调用play方法播放音乐。但是这个方法只能接收手机对象和导出类对象。如果我想让电脑播放音乐,由于电脑不属于手机继承体系,所以不能实现这种功能。但是如果我们把播放功能抽象到一个接口里,让手机实现这个接口,电脑实现这个接口,那么这个方法只要接收接口参数即可。实现了解耦合。

如果手机类是我们创建的,我们可以直接添加implements,但是如果它是jar里的或者别人的,我们不能直接修改它怎么办呢?

可以使用适配器模式,创建一个适配器类,实现接口,组合所需要的对象,在方法里调用该对象相应的方法。

9.4Java中的多重继承

一定要记住:使用接口的核心原因就是为了能向上转型成多个基类(以及由此带来的灵活性),第二个原因就是与使用抽象类相同:防止客户端创建该类的对象。

9.5通过继承扩展接口

利用extends关键字可以扩展接口里的方法

9.6适配接口

使用适配器模式来给现有操作接口方法添加新的操作对象是非常有用的。

9.7接口中的域

隐式的static、final的,在enum出现之前(Java SE5)用它当做常量。

9.8嵌套接口

...

9.9接口与工厂

接口是实现多继承的途径,而生成遵循接口对象的典型方式就是工厂方法设计模式。

原始方式:

方法以接口为参数,void action(Service s){...}

调用的时候if(xxx)action(new SImp1());else if(xxx)action(new SImp2());else if....每个调用地方都需要直接提供实现类对象。如果需要修改/增加实现类代码就会很冗余。

工厂方式:

void action(ServiceFactory fac){Service s = fac.getService();....}

只需要在factory里改变即可。

9.10总结

任何抽象性都应该是应真正需求而产生的。应该优先选择类而不是接口。如果接口的必需性变得非常明确,那么就进行重构。接口是重要的工具,但是不要滥用它。


o







猜你喜欢

转载自zhyp29.iteye.com/blog/2306697