Java中的abstract类和interface

Q:java里面有抽象类么?和接口的区别是什么?
A:java中有抽象类,用关键字abstract修饰。

 

抽象类和接口之间的区别和联系,网上有很多的文章,你可以参考一下。
以下是我对这个问题的看法。
首先,从语法上讲,抽象类是一个类,根据java的单继承体系。如果它有子类,那么它的子类只能继承它。
java允许实现多个接口。所以一个类可以实现多个接口
抽象类里面可以定义各种类型和访问权限的变量(就像普通类一样),也可以定义各种访问权限的方法(就像普通类一样)。
但是接口不可以。在接口里面定义的方法默认就是public abstract的,变量默认就是public static final的。(不管你有没有加权限控制符,不加,默认就是这些权限;加错了,权限缩小了,那么就会报错)

其次,从意义上讲,如果继承一个抽象类,那么抽象类和它的子类就有父子关系,即有类的层次关系(这关系到类的设计问题)。
接口,在我看来,是一种契约或者协议,是一层提供给另一层的接口(可以想象成OSI各层之间的关系)
在某一层中有多个类协作实现这个层的功能,通过接口暴露出去。但这些类之间会有层次关系(is a,has a)

 

Q:一个方法加abstract和不加abstract有什么区别?就是说不加有什么关系?加了又会怎样?
A:一方法要是加abstract,那么这个方法就是抽象的,须由子类去实现

抽象方法必须在抽象类当中,也就是说,一个类只要有一个方法是抽象的,那么这个类就必须是抽象类
抽象类里面的方法不一定要抽象的,也可以全都不是抽象的
抽象类不能实例化!
所以可以想到,当你不想让你的类被别人实例化,只想这个类的子类可以实例化,那么只要将这个类声明为abstract的就可以了
(和final的情况满相近,当你不想让这个类再有子类化,只要在类的声明前面添加final即可)

不过更多的情况下,我想,还是一个抽象类,他既有部分抽象方法,也有部分具体的方法。
具体的方法是已经定义好的,抽象方法需要由子类去实现。(个人感觉和C++的virtual很相近,不过abstract是纯虚的)

 

那么为什么要有抽象类呢?我说过这关系到你类的设计问题。(当然还有不想被实例化)
譬如经常举的一个例子

  1. abstract class Shape{
  2.     public abstract void draw();
  3.     public abstract void reDraw();
  4. }
  5. class Triangle extends Shape{
  6.     public void draw(){...}
  7.     public void reDraw(){...}
  8. }
  9. class Circle extends Shape{
  10.     public void draw(){...}
  11.     public void reDraw(){...}
  12. }

当然你可以这样设计

  1. class Shape{
  2.     public void draw(){}
  3.     public void reDraw(){}
  4. }

这样设计有2个不妥之处:
1.不符合逻辑。Shape是你针对具体的事物(Triangle,Circle)抽象出来的。它定义的方法应该到子类中去实现
2.在设计的时候就应该认识到,实例化Shape是没有意义的,因为Shape本身只是一种抽象。现在别人如果拿来就可以实例化了,这样的操作方式违背了当初设计的意愿。而且会存在潜在的危险。(当前的实现可能看不出危险)

那么,如果把Shape定义成Interface呢?
当然可以

  1. Interface Shape{
  2.     void draw();
  3.     void reDraw();
  4. }
  5. class Triangle implements Shape{
  6.     public void draw(){...}
  7.     public void reDraw(){...}
  8. }
  9. class Circle implements Shape{
  10.     public void draw(){...}
  11.     public void reDraw(){...}
  12. }

只不过现在子类Triangle,Circle和Shape之间的关系就不是父子了。
这样设计其实违背了常理,因为在我们的观念中,Triangle和Circle本来就应该是一种Shape
而不应该是他们都遵守一定的操作方式(void draw(),void reDraw())


如何扩展这些设计呢?譬如说为Triangle和Circle添加erase方法?
如果是class Shape这样的设计,很简单,只要

  1. abstract class Shape{
  2.     public abstract void draw();
  3.     public abstract void reDraw();
  4.     public void erase(){
  5.         Window window=WindowContainer.getCurrentWindow();
  6.         window.clean();
  7.     }
  8. }

Triangle和Circle的代码可以原封不动!

你可能会问,为什么不把erase()设计成为abstract的。你当然可以把erase()设计成abstract的,然后在子类中重写这个方法,提供子类自己的实现。
但是,在现实当中,你可能更赞同是用上面的设计。的确,erase方法只要简单的进行擦除就可以了,不是吗?
这样一来,扩展就变的很容易了。

如果是Interface Shape的设计,那你做的事可就多了

  1. Interface Shape{
  2.     void draw();
  3.     void reDraw();
  4.     void erase();
  5. }
  6. class Triangle implements Shape{
  7.     public void draw(){...}
  8.     public void reDraw(){...}
  9.     public void erase(){ 
  10.         Window window=WindowContainer.getCurrentWindow();
  11.         window.clean();
  12.     }
  13. }
  14. class Circle implements Shape{
  15.     public void draw(){...}
  16.     public void reDraw(){...}
  17.     public void erase(){
  18.         Window window=WindowContainer.getCurrentWindow();
  19.         window.clean();
  20.     }
  21. }

所有相关的代码,你都要修改!即便他们都做同样的事情!
这样扩展实在是太麻烦了!
你也许和我想的一样,定义接口本身的目的就是告诉大家,任何实现此接口的类,都有自己的实现方法!
我们定义接口的目的是:你们的实现可以不一样,但是你们暴露的接口一定要一致

 

可以举java类库当中的一个设计,Thread
如果你要设计一个线程类,该怎么办?
两种方法:

  1. //通过继承Thread类来实现
  2. class MyThread extends Thread{
  3.     public void run(){}//覆盖Thread的run方法
  4. }
  5. //通过实现Runnable接口来实现
  6. class MyThread implements Runnable{
  7.     public void run(){}//实现Runnable的run方法,MyThread和YourThread的实现都不一样
  8. }

(看过Thread的源代码,你就会发现,其实Thread实现了Runnable)
到此,你应该可以看的出来,为什么要继承Thread和为什么要实现Runnable了

 

另外一个抽象类的使用方法是我最近在C++的项目中学习到的。可能你已经会了

 

  1. package com.pamigo.shp;
  2. abstract class Shape{
  3.     public abstract void draw();
  4.     public abstract void reDraw();
  5.     public void erase(){
  6.         doErase();
  7.         Window window=WindowContainer.getCurrentWindow();
  8.         window.clean();
  9.     }
  10.     protected abstract void doErase();
  11. }
  12. class Triangle extends Shape{
  13.     public void draw(){...}
  14.     public void reDraw(){...}
  15.     protected void doErase(){
  16.         MusicBox.sing("Amazing Grace");
  17.     }
  18. }
  19. class Circle extends Shape{
  20.     public void draw(){...}
  21.     public void reDraw(){...}
  22.     protected void doErase(){
  23.         WindowBox.popUp("I'll go!");
  24.     }
  25. }
  1. package com.pamigo.clnt;
  2. class User{
  3.     public static void main(String[] args){
  4.         Shape shp=new Triangle();
  5.         shp.draw();
  6.         shp.erase();
  7.     }
  8. }

//注意我的权限修饰符


我想表达的意思就是,对外界来说,User只能看到Shape的erase方法。
erase方法必须要做Window的清除工作,但为了给子类提供机会做自己的清除工作,所以设计了

  1. protected abstract void doErase();

因为是protected权限,所以User是看不见的。
不管是Circle还是Triangle,都会有自己的doErase方法。并且他们都会在用户调用shp.erase();时被正确的调用
并且不用关心Window是如何被清除的!
这类似于一种回调函数的方式,你可以这么认为。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值