抽象类、接口

来源:互联网 发布:java并发实战 epub 编辑:程序博客网 时间:2024/06/07 08:06

抽象类及抽象方法必须用abstract修饰。抽象类中可以含具体方法,但一旦含有抽象方法则该类必须定义为abstract。抽象类可以含属性、方法、构造器、初始化块、内部类、枚举类,但抽象类的构造器只能被其子类使用,而不能用于创建其本身的实例(不能创建抽象类的实例)。抽象方法即只有声明而没有方法体,以逗号结束。

抽象类虽不能创建它本身的实例,但它却可以作为指向其子类的引用,从而利用多态调用子类中的方法。因为抽象类就是被继承的,所以抽象类的定义中不能有final关键字。

abstract不能用于修饰属性和局部变量及构造器。


接口可以理解为一套规范,它更抽象更广泛,抽象类相当于其子类的模版,而接口就是一套规范,实现了该规范的类就可以完成某种功能。比如主板上的PCI插槽,它的设计遵循了PCI接口规范,同样遵循了PCI接口规范的显卡就可以插入该PCI插槽与主板进行通信。这样说来,双方必须都实现PCI接口才可。

因为接口是一套规范,是专门用来被实现的,所以它的修饰符只能是public,或省略,省略则只能被同一包下的类访问。一个接口可以有多个父接口,但不能去继承父类,当然不可以了,都不是一回事儿。一个类可以实现多个接口,但只能继承一个直接父类,这也是接口的优势之一。因为接口是一种规范,所以很自然它里面不能包含构造器和初始化块,且只能包含常量属性、抽象方法、内部类内部接口、枚举类定义。再想一下,接口中的常量属性必须是public static final,即可以被公共访问但不能修改,系统会自动添加public static final修饰符,而剩下3中成员默认只能是public修饰符。

和抽象类一样,接口不能用于创建其本身的实例,但可以声明实现了该接口的对象。

实现某个接口用implements声明。

书上关于接口的一个用法:

interface Product
{ int getProduceTime();
  }

interface Output
{ int MAX_CACHE_LINE=50;
  void out();
  void getData(String msg);
}
public class Printer implements Output,Product
{ private String[] printData=new String[MAX_CACHE_LINE];//实现了接口之后就可以直接使用里面的常量
  private int dataNum=0;
  public void out() //这个是实现接口中的方法,但可以看到这已经完全是该类自己的方法,可以用自己类的变量
  { while(dataNum>0)
     { System.out.println("打印机打印歌词:  "+printData[0]);
       System.arraycopy(printData,1,printData,0,--dataNum);
      }
   }
public void getData(String msg)
{ if(dataNum>=MAX_CACHE_LINE)
    { System.out.println("输出队列已满,添加失败");
     }
   else
     { printData[dataNum++]=msg;
      }
}
public int getProduceTime()
 { return 45;
  }
public static void main(String[] args)
{ Output o=new Printer();
  o.getData("到底为什么");
  o.getData("都是谁的错");
  o.out();
  o.getData("你用爱换走青春");
  o.getData("我还剩下什么");
  o.out();
  Product p=new Printer();
  System.out.println(p.getProduceTime());
  Object obj=p;
 }
}

有一点要注意,虽然定义接口时的修饰符可以省略,系统会自动给接口添上public修饰符,但如果你显式的给接口添上

public修饰符,编译时会报错,因为虚拟机会把这个接口当作类来处理。因为一个文件中只能有一个public修饰符。
另arraycopy的用法:

arraycopy(被复制的数组, 从第几个元素开始复制, 要复制到的数组, 从第几个元素开始粘贴, 一共需要复制的元素个数);

关于面向接口编程:

这个问题是考虑到下述情况:如果一个类要使用另一个类的方法和属性,可以把另一个类的对象组合到该类中,但如果另一个类是一个比较通用的类,即很多类都会用到这个类,那么很多类都要组个这个类的对象,但如果一旦这个类的内容改变了或升级了,比如说要改变类的名称,或改变方法的名称,新增加方法了等,那么所有组合这个类的类都要重新改写它的代码,所以才选择让这些类组合一个接口属性,而不是组合具体的一个类的对象,从另一方面来说,组合一个接口更合理,因为接口就是代表的一种规范,既然很多类都要用到同一种规范,那么自然应该用接口了。下面是李刚老师书上的一个程序,我改动了一下,并新增加了一个类Computer2,代码如下:

public class Computer//可能有很多类都组合了Output接口,Computer类只是其中一个
{ private Output out;//这个定义的是一个对象引用
  public Computer(Output out)
   { this.out=out;
    }
  public void keyIn(String msg)
    { out.getData(msg);
     }
  public void print()
  { out.out();
   }
}
public class Computer2//它也要使用接口Output中的方法
{ private Output out;
  public Computer2(Output out)
   { this.out=out;
    }
  public void myworld(String msg)  
   { out.getData(msg);
    }
  public void myprint()
   { out.out();
    }
}
public class BetterPrinter implements Output//实现Output接口的具体方法
{ private String[] printData=new String[MAX_CACHE_LINE*2];
  private int dataNum=0;
  public void out()
   { while(dataNum>0)
      { System.out.println("高速打印机正在打印: "+printData[0]);
        System.arraycopy(printData,1,printData,0,--dataNum);
       }
    }
public void getData(String msg)
{ if(dataNum>=MAX_CACHE_LINE*2)
    { System.out.println("输入队列已满,添加失败");
     }
  else
     { printData[dataNum++]=msg;
      }
 }
}
public class OutputFactory
{ public Output getOutput()
   { return new BetterPrinter();
    }
  public static void main(String[] args)
  { OutputFactory of=new OutputFactory();
    Computer c=new Computer(of.getOutput());
    c.keyIn("轻量级Java应用");
    c.keyIn("structs权威指南");
    c.print();
    System.out.println();
    Computer2 cc=new Computer2(of.getOutput());
    cc.myworld("马特达蒙好帅");
    cc.myworld("谍影重重4看着还不错");
    cc.myprint();
   }
}
运行上面的程序可以运行。但再仔细想想,发现还有一问题值得思考:Computer类和Computer2类虽然都组合了Output接口,但它们可能、而且很可能是独立的类,但在上面程序中这两个类中都没有主函数,而是主函数放在了类OutputFactory类中,能不能把主函数放到这两个类本身里面呢?能不能呢?不能,因为主函数要新建Computer类和Computer2类的对象,而新建时要传入实现了Output接口的对象,这样又将具体的类对象放进类中了,如果这个具体类对象要升级,还是要修改本类的代码。所以把主函数功能的代码都放在了OutputFactory中了,但这样子的话,又导致一个问题,即一旦运行OutputFactory中主函数,所以类都会运行一遍,但我们可能只是想运行某一个类,我觉得有一种方法就是在运行OutputFactory类时传入一个参数,然后让其主函数决定运行哪个类的那段控制代码。


我上面的话都对吗?真的不能把主函数放到Computer类和Computer2类里面吗?我刚刚发现其实也是可以的:只用改动下面这两个类,实现接口的那个类不用管:

public class Computer4 
{ private Output out; 
  public Computer4(Output out)
   { this.out=out;
    }
  public void keyIn(String msg)
    { out.getData(msg);
     }
  public void print()
  { out.out();
   }
public static void main(String args[])
  { Computer4 dd=new Computer4(OutputFactory4.getOutput());
    dd.keyIn("这样子把主函数放到类本身里面可以吗?");
    dd.print();
 }
}
public class OutputFactory4
{ public static Output getOutput()
   { return new BetterPrinter();
    }
}
上面的程序编译运行通过。


但我又想到一个问题,可以让这些类组合一个接口属性,那为什么不能组合一个抽象类属性呢,抽象类也是抽象的,而且抽象类由于是父类,可以直接指向重写的子类,如果需要修改或升级了,重新修改一下或新建一个该抽象类的子类不就行了吗?我尝试了下,代码如下:

abstract class Output //这样把抽象类的具体代码都写出来了不太好,只是为了说明情况
{ private String[] printData=new String[MAX_CACHE_LINE*2];
  private int dataNum=0;
  public void out()
   { while(dataNum>0)
      { System.out.println("高速打印机正在打印: "+printData[0]);
        System.arraycopy(printData,1,printData,0,--dataNum);
       }
    }
public void getData(String msg)
{ if(dataNum>=MAX_CACHE_LINE*2)
    { System.out.println("输入队列已满,添加失败");
     }
  else
     { printData[dataNum++]=msg;
      }
 }
}
public class BetterPrinter extends Output//扩张了抽象类Output

}
public class Computer3
{ private Output out;
  public Computer2(Output out)
   { this.out=out;
    }
  public void myworld(String msg)  
   { out.getData(msg);
    }
  public void myprint()
   { out.out();
    }
}
public class OutputFactory3
{ public Output getOutput()
   { return new BetterPrinter();
    }
  public static void main(String[] args)
  { OutputFactory of=new OutputFactory();
    Computer c=new Computer(of.getOutput());
    c.keyIn("轻量级Java应用");
    c.keyIn("structs权威指南");
    c.print();
    System.out.println();
    Computer2 cc=new Computer2(of.getOutput());
    cc.myworld("马特达蒙好帅");
    cc.myworld("谍影重重4看着还不错");
    cc.myprint();
   }
}
程序编译运行通过。这样子也完全实现了使用接口所达到的效果。虽然是这样,但还是使用接口更合适一点吧,因为接口就是表达一种规范,而抽象类只是一个模版。(还有别的原因吗,为什么不用抽象类??)


上面的整个分析有哪些地方错了或不合适,希望看到的网友更正一下,分享学习才会进步。我每隔一段时间也会再回过头来看看自己以前的笔记或总结,看看有什么错误或从中又有新的学习发现。