抽象类和接口又什么好处
从这个问题我们可以衍生出两个问题:
1. 为什么要用抽象类?
2. 为什么要使用接口?
企业单位在面试的时候经常会问到!
答案综合考虑以下方面:
1:强调不但应该具有什么能力,而且应该具有什么特性,并且一些能力有共同的处理细节的时候,可以用抽象类;
2:只强调应该具有什么能力的时候,并且处理细节可能完全不同,就考虑用接口;
3:抽象类考虑从上到下的继承和拥有,接口考虑从左到右的规范;
==============================================================
使用接口定义行为,由于接口是特殊的抽象类,也就可以使用抽象类定义行为,抽象类同时能够实现行为;但仅是定义行为时,就可使用接口,因为接口能够更有效地分离定义与实现,为代码的维护和修改带来方便。
==============================================================
1, 抽象类提供了一部分功能的实现,如果子类的功能实现和父类的功能实现是相同的,可以使用抽象
2, 接口只规定了类的函数规范,没有具体实现。
==============================================================
面向对象的经典是面向抽象编程。
所以,用抽象的型别统一类型,来进行操作,有利于以后的扩展,移植,复用!!
==============================================================
其实抽象类的一个好处是类不能被实例化,最大的好处就是通过方法的覆盖来实现多态的属性。也就是运行期绑定
==============================================================
我想用比较通俗的方法告诉你:
假如有两个程序员,两个在两个程序里都要用到一种功能,比如要取一个方法名。
甲自己做了一个方法叫getname,乙也作了一个方法叫qumingzi。如果两个人要去看对方的程序,那么
这个方法要读懂是不是要有一个过程?
如果在公司里,有个抽象类,里面有个抽象方法getName,公司规定,凡遇到这样的问题就实现这个方法。
那么这两个人要读对方的代码是不是就容易了??
==============================================================
假如很多人要买水果吃,吃的动作只有一个,但是有的人要用现金买,有的人用信用卡,有的人赊帐。要为每个人定义一个类,就要定义多个相同的吃的方法。如果定义一个抽象类,在里面实现吃的方法,再做一个买的抽象方法。那每个人的类都从这个抽象类派生下来,只要实现买的方法即可,吃的方法就可以直接用父类的方法了。如果要改吃的方法就不用改多个,只要改这个抽象类里的就行了。
==============================================================
抽象类将事物的共性的东西提取出来,抽象成一个高层的类。子类由其继承时,也拥有了这个超类的属性和方法。---也就实现了代码的复用了。
子类中也可加上自己所特有的属性和方法。----也就实现了多态
==============================================================
以学生为列,博士和硕士都是学生,那么学生就成为了一个父类了,他们都具有学生的特征,那些共同的特点就可以继承,实现代码重用
但是如果还有一种类型的学生,它既具有博士,又具有硕士的特点,那我岂不是要继承两个类,但是这两个类里又有很多相同的特征啊,那就现的代码冗余了,是吗?
那么我现在的目的就是要使它简洁,但是要达到那个效果,我就会用到接口,所谓的接口有两个含义
1。方法的集合
2。概念
第二中就是我们JAVA中用的,它把类中某些独有的方法用接口的形式表现出来,然后你就直接implement接口就是拉,再继承学生那个类,就可以拉
==============================================================
接口的好处之一:
就是可以多重继承, 而类不可以~
子类,可以同是继承至一个接口和一个抽象基类
==============================================================
例子:
有以下类:
类(动物) 类(狗) 类(大象)
类(狗)和类(大象)是类(动物)的子类。
如果我希望别人只能创建类(狗)和类(大象)的对象,绝不希望别人能直接创建类(动物)的对象(直接创建类(动物)是无意义的,因为每一种动物都有它的真正类),于是我就可以把类(动物)做成抽象类
==============================================================
就象建立房子的框架
里面的房间可以分别设计,但是框架不变
==============================================================
面向对象的经典是面向抽象编程
抽象的意思,就是你不必为每个插销定义一个插座。
你不用为每个CPU定义一个主板
抽象类和接口的意思,就是规范,大家都遵守。这样符合规范的东西,大家都不用了解各自的细节。插座不会了解插销怎么做的。插销也不必了解插座怎么做的。
抽象类和接口的意思,就是规范,大家都遵守。这样符合规范的东西,大家都不用了解各自的细节。插座不会了解插销怎么做的。插销也不必了解插座怎么做的。
==============================================================
JAVA是基于C/C++开发出来的,也可以称作是二者综合体的简化版,去处其中的一些过于复杂的控制功能或改由计算机自动处理。其中就包括去除了C++中的多重继承功能,而改由不计数量的接口来实现。
举个简单的例子:古希腊神话中的飞马。它既能奔跑(马的方法),又可以飞(鸟的方法)。在C++中可以通过多重继承来实现,但在JAVA中则必须通过接口来实现。或者继承马的方法,并实现一个鸟飞的接口。或者同时实现一个跑的接口和飞的接口。
接口具有良好可扩展性。有了跑和飞的两个借口,你可以轻松的做出飞猪,飞驴等具有类似功能的对象。
接口中不得实现任何方法。一个类一旦要实现一个接口,则必须实现改接口的所有方法,即使在该方法中不执行任何操作(即方法体内为空)。
接口与抽象类具有类似的功能。在使用上,一般接口用于定义比较普遍的方法,而抽象类主要用于比较具体的方法。
面向对象的本质:万物皆对象!
封装:封装就是指利用抽象数据类型将数据和基于数据的操作封装在一起,数据被保护在抽象类型的内部,系统的其他部分只有通过包裹在数据外面的被授权的操作,才能够与这个抽象数据类型交流与交互!
继承:继承实际上是存在于面向对象程序中的两个类之间的关系。当一个类拥有另一个类的所有数据和操作时,就称这两个类之间具有继承关系!
多态:多态是指一个程序中同名的不同方法共存的情况。面向对象的程序中多态的情况有多种,可以通过子类对父类方法的覆盖实现多态,也可以利用重载在同一个类中定义多个同名的不同方法!
OO=Objects+Classes+Inheritance + Communication With messages
那还是教科书上说的,
多态,解释就是一个方法被多次构造,,经常出现在,继承关系类(抽象类,接口)
回忆一下接口使用
interface I_Test{
public void Print (){}
}
class C_A implment I_Test{
public void Print(){ System.out.println("C_A")}
}
class C_B implment I_Test{
public void Print(){System.out.println("C_B")}
}
再来看看class Test{
public void main(){
I_Test i= new C_A();
i.Print(); //打印出C_A
i= new C_B();
i.Print();//打印出C_B
}
//,"多态性是站在类的角度完成了接口与实现的分离",多态调用Print()方法,不知道能不能解释这句话,个人是这样一来理解的。
我个人认为“多态”不单是对父子类,接口,抽象类的理解。。关键还是要对 protect,private正确使用中去体味。
这样才能更好隔离,综合使用多态才会清晰。
接口(Interface)是用来定义行为的!
抽象类(Abstract Class)是用来实现行为的!
具体类()是用来执行行为的!
支持多重继承, 支持抽象函数, 允许实现 ,允许创建实体,允许部分实现
interface 支持 支持 不允许 不允许 不允许
abstract class不支持 支持 允许 不允许 允许
class 不支持 不支持 允许 允许 不允许
所以打算多继承,又并不想马上实现它的时候,就用接口。需要实现它的类需要。比如我上述例子中的Print(),,通常对应多个实现类的。(记得所有的方法都必须是public的,也可以用有static final)
如果只需要实现部分的部分方法,又不需要全部实现,那就可以用抽象类。对于抽象方法,需要overrider 就是要重写一个方法来实现 所定义好的abstract 方法。(这个和接口一样)
如果全部实现,又全部要执行的。。那么就用直接类吧,父子关系一带被调用它的时候,比如子类继承了父类的方法时候,一调用就执行了。
其实,要从宏观上讲,那就牵涉到软件工程理论了。
个人看法:
事物的出现,总是有它出现的理由(需求)!
但是,事物的生存,需要有适应性(灵活)!
比如:长颈鹿,脖子长,这是个抽象的概念;
能吃草(是非肉食性动物的特征,接口)。
所以,当需要描述长颈鹿的时候,我们就可以用接口(非肉食性动物)和抽象(脖子长)的方法来定义它,
然后,具体是什么种类/或者颜色的长颈鹿,怎么个吃草法,这就是具体类了。
=============================================================
经典解释是:使用了对象就叫基于对象,基于对象的基础上增加了继承从而变成了面向对象。
基于对象的最大特点就是封装,至于private,protected,public,并不是基于对象语言的必须要求。同时应该注意到的是,封装对于应用本身并没有提供太大的帮助;换句话说封装仅仅改变了方法的表达形式,除了析构函数能够提供一些资源安全上的保证,当然java没有析构函数。比如某些网络应用,包装了socket,然而必须的accept,listen,connect,read,write还是需要你自己去调用,只不过调用方式有所变化而已;更麻烦的是处理错误,可能的错误你必须完全接管,那就需要对问题本身有很深入的了解。基于对象让你不容易犯错误,但是不能改变对问题本身的理解。
于是出现了面向对象。继承,多态只是它的特点而已,一种表象。而关键优越性在于它可以改变对问题的认识。比如处处可见的画图的例子,p.Draw(),就能画出一个形状,都是Draw()方法,但是可以根据p的不同,动态选择。无可否认,这个是多态性的优越性的例子。然而继承又如何呢,可以这样说,如果不使用多态,继承就没有意义了。因为没有多态,继承事实上导致了较高层次的包装而已,与基于对象相同,它不能改变对问题本身的理解。很多时候继承被包裹所代替,COM的对象重用,就是典型的包裹,不过它叫聚集而已。类层次越深管理越困难。
面向对象提供了极大的灵活性,然而作为一个概念,外延太丰富,反而不容易把握。看看JFC的文档,实在太灵活了,感觉上这样那样几个东西拼合起来,窗口就可以做成了。事实却不是如此,你必须去了解layer,了解listener,了解很多东西,然后按照固定的规则进行拼装才能达到效果。简单说,看看javadoc的JFC文档,很难学会如何去实现一个可用的窗口应用。这一点明显比不上VB,其实这就和使用面向对象方法是为了接口易用性这一个基本原则背离了。外延被扩大化到难以接受的规模,这是使用面向对象方法很容易犯的毛病。而设计模式提供了一些方法缓和这些矛盾,比如用静态函数创建对象,私有构造,使得对象不能被任意创建,这就大大压缩了这些对象的外延。简单看看MessageDigest,可能在内部有md2,md4,md5,sha1的类供我创建对象,但是这些我都不让你看到,你只要知道一个MessageDigest就足够了,免得产生误会。
先说那么多了。
回复人: Leemaasn(我给大家拜早年啦!新春快乐!!!) ( ) 信誉:101 2004-1-12 18:47:34 得分:0
zengpan_panpan()
牛,,,很彻底地分析了“何谓对象”!!!
回复人: Leemaasn(我给大家拜早年啦!新春快乐!!!) ( ) 信誉:101 2004-1-12 20:40:23 得分:0
对于 zengpan_panpan()所说的“而关键优越性在于它可以改变对问题的认识。”!
个人看法如下:
通常,我们要使用一个对象的paint方法时,如果我们很具体的考虑这个Paint问题时,我们也许会从想要画什么开始。然后,考虑从该画什么东西,又考虑对于这个东西我们应该怎么画。。。。
一层层深入时,,,就会陷入到Windows底层API函数(我以Windows平台为例。),楼主是搞Delphi的。这点应该会明白。。。
如果只是简单几个类,那也无所谓,当大规模生产时,还是这么做,后果可想而知的(比如:Windows的MFC、或者VCL)。
如果我们以对象和接口的角度来考虑这个Paint问题。
那么,我们可以先定义好一些接口,比如paint这个接口。
然后再来设计对象,如果某个对象,需要paint这个方法,那么,我们可以让它实现paint这个接口。
具体怎么实现,这就是抽象类或者具体类的事情了。
然后,我们在其他地方调用这个对象的paint方法时,
我们就只需要按照这个paint这个接口的规定来调用就行了。
在这里,“改变对问题的认识”改变在什么地方呢?我们从对象的认识从一个具体到抽象、封装的方向变了。
就像人,又分为黄色人种、白色人种、黑色人种(抽象类)等。。。
中国人、印度人。。。(接口)等。。。
如果再抽象,可以看作:脊椎动物。。。。
哈,扯远了。。。
其实,真正的解答,应该请zengpan_panpan()来说明!!!
有请我们真正的主角。。。zengpan_panpan()隆重登场。。
回复人: iamwls(-----魔高一尺,道高一丈-----) ( ) 信誉:96 2004-1-12 20:43:27 得分:0
to: zengpan_panpan()
你所说的:使用了对象就叫基于对象,基于对象的基础上增加了继承从而变成了面向对象。
好象并不太准确
记得(好象是,呵呵)有人问JAVA的创始人:如果你想重来一次JAVA,你改变的是什么?
答:去掉继承
也许应该是增加了抽象多态, 从而变成了面向对象可能更准确点
回复人: Leemaasn(我给大家拜早年啦!新春快乐!!!) ( ) 信誉:101 2004-1-12 21:06:27 得分:0
哈,好象这句话,我也有看过。。。
记得(好象是,呵呵)有人问JAVA的创始人:如果你想重来一次JAVA,你改变的是什么?
答:去掉继承
但没有继承,总觉得。。。似乎世界不太完美。。。。
回复人: zengpan_panpan() ( ) 信誉:100 2004-1-12 21:45:13 得分:0
基于对象和面向对象的定义也不是我说的,以前是这么定义的。
至于JAVA要去掉继承,从方法角度讲是可行,不过至少还得保留接口,否则多态无从谈起。不过这需要推翻整个jdk,肯定就不现实了。另外,看看Object类的实现可以发现,里面的notify, wait系列方法,有多少时候用到了,要知道任何一个对象都有这样的方法,而这两个方法明显是线程相关的;然而线程类仅仅是它的一个子孙而已。这会让人觉得别扭。其实这个问题的关键在于java不仅仅是一门语言,还是一个运行环境(jvm),它们是紧紧捆绑在一起的,Object如此定义是一个妥协的结果。
呵呵,扯远了,回到面向对象问题上来:
其实,面向对象也好,面向过程也好,现在喊得火热的GP也好,争来争去,没有太大意义。不管什么应用,都是面向需求的,这样那样的方法都是手段而已,目的才是最重要的。理论说多了乏味得很。
关于面向对象方法,有一个比较经典的例子。我回了最后一篇,不过后来沉下去了。
欢迎提出不同意见。
另外,我来出个题目:
需求很简单:设计个线程池。
于是现在假设出两种方案,互相对比一下。从需求入手:
1. 方案一:假设有一线程池对象 tpool;
Thread t = tpool.get();
t.start(myrunner);
tpool.put(t);
2. 方案二:假设有一线程池类 TPool;
TPool.start(myrunner);
你觉得哪个好,好在哪里,哪个不好,不好又在哪里?
zengpan_panpan() ( ) 太精彩了,你的解释,我也随便拜读你的连接文章。:) 果然精彩,请您继续为我们讲解啊。
我觉得是第二种好。
虽然两个都是基于对象的。
但是,线程中,反复的创建销毁对象是非常耗资源的。
我具体也不怎么会解释,这些池化类方法是比较好的,,
让我想到了数据库的连接池。。。。
请您给我们答案吧。。。期待中。
灯光,,掌声。。。。大家把手机关了:)
回复人: zengpan_panpan() ( ) 信誉:100 2004-1-15 14:56:07 得分:0
其实第一种方法不好。
虽然符合一般池子的概念,比如,向池子申请资源,归还资源。
但是就因为这样将造成线程使用者和线程池之间的强耦合。
比如向线程池请求一个线程,就不得不考虑,它还有没有线程可以提供给我用?如果没有到底是返回错误,还是还是把我挂起?另外线程运行了我的runner,我什么时候知道知道它结束,如果它还没有结束,我又怎么能把线程归还给池子。考虑这些问题自然就很麻烦了,不但设计应用麻烦,设计线程池也麻烦。
而第二种就很简单了。
把我的runner直接提供给池子,如果它暂时没有线程可用,那么排队就是了。如果不允许排队可以考虑增加一个addTask,提供个参数说明我不排队,要是没有直接报错。更多的通过这个参数甚至可以控制线程优先级一系列的问题,而不需要对池子有多大改动。
线程池本身,涉及到很多方面的问题,比如操作系统调度之类的问题,里面也会包含一些对象化的思考,这些思考并不在于用什么具体语言去实现。现在给出第二种的最简单实现,可以去分析一下里面到底有多少对象化概念,有什么模式,也可能像这种模式,不过似是而非,但是这些都不重要,并不见得一定要符合什么模式才是对的。前面已经说过,不管面向什么,不管什么方法,最终都是面向需求的。最后应该把你的程序看成一块胶泥,想捏成圆的就圆的,想捏成方的就方的。
// TPool.java
import java.lang.*;
import java.util.*;
import java.io.*;
class MyRunner implements Runnable
{
String name;
public MyRunner(String x)
{
name = x;
}
public void run()
{
System.out.println("My name is " + name);
try { Thread.sleep(50); } catch (Exception e) {}
}
}
public class TPool implements Runnable
{
private int id;
private static LinkedList pool = new LinkedList();
private TPool (int i) { id = i; }
public void run()
{
for (;;)
{
Runnable r;
synchronized(pool)
{
System.out.println("[Thread " + id + "] Enter Sleep");
while (pool.size() == 0)
{
try
{
pool.wait();
} catch(InterruptedException e)
{
}
}
r = (Runnable)pool.getFirst();
pool.removeFirst();
System.out.println("[Thread " + id + "] Exec Runner");
}
r.run();
}
}
static void addTask(Runnable r)
{
synchronized(pool)
{
pool.add(r);
pool.notifyAll();
}
}
static void init (int size)
{
for (int i = 0; i < size; i++)
(new Thread(new TPool(i))).start();
}
static void main(String args[])
{
TPool.addTask(new MyRunner("A"));
TPool.addTask(new MyRunner("B"));
TPool.addTask(new MyRunner("C"));
TPool.init(3);
for(;;)
try
{
Thread.sleep(1000);
int n = (int)(Math.random() * 5);
System.out.println("\nGenerate " + n + " Tasks\n");
for (int i = 0; i < n; i++)
TPool.addTask(new MyRunner( (new Double(Math.random())).toString() ));
} catch(Exception e)
{
}
}
}
接口(Interface)是用来定义行为的!
抽象类(Abstract Class)是用来实现行为的!
具体类()是用来执行行为的!
这个不错
抽象类用来实现公用行为更好一点
其实除了interface外,还有adapter模式可以参照
对于接口、抽象类,我的理解是:从类图出发,根据实际情况定义接口或抽象类,接口是定义行为的,任何一个类都可以看作有一个接口,如果需要可以定义接口,否则不需要定义,而抽象类是可以定义和实现行为,但不能执行行为,因为它不能实例化,但可以定义对象,这个对象的行为是抽象类的实现类的实例的行为。
从设计上说抽象类的好处有二:
一、抽象类提供了部分实现,使子类可以重用部分抽象类的代码
二、抽象类为其子类提供了共同的接口(注意:这里说我接口与 java 中的 interface 是不同的)
也就是别人可以将各个子类的对象当成抽象类的对象使用。
我觉得“为了方便扩展”的说法是荒谬的,如果可以实现,为什么要代码写到别的类中去呢?
抽象类的关键是抽象方法,而设计抽象方法的原因是在于抽象类自己没有办法实现这个方法,
或者说在抽象类这一层,我们不知道如何实现这个方法,只有到了具体类才有办法知道实现。
如“动物”这个抽象类,有一个抽象方法“吃”,但是动物这个类是不能实现“吃这个方法的,
只有到了“人”这个类,才能有“用手将东西放进嘴里然后吞下去”这样的实现。
“动物”这个类的“吃”方法也不可能有默认的实现,因为现实中没有一个纯“动物”的实例,
它要么是“人”类的实例,要么是其它“动物”类子类的实例!
但我们一旦碰到一个“动物”对象,就可以调用“吃”这个方法,
当然我们也要预期不同对象的实现不是一样的
interface表述“has a”关系,用于描述功能
是用来说明它的是实现类是“做什么?”的
至于“怎么作?”,interface并不进行约束
而abstract class表述“is a”关系,它在描述功能的同时
也通过具体实现部分功能的方式,来约束“怎么作?”
只有接受了这个约束,才应该去继承它
从而可以将“其中被具体实现的部分功能”,在它的子类中得到重用
实际使用当中,interface与而abstract class各有优势,
需要权衡。
不过,有时候,两者也可以兼用
《effective java》中提出,
可以声明一个interface描述功能,
再提供一个该接口的骨架(skeleton)实现(实现该接口的abstract class)
固化对功能实现方式的约束,从而尽可能地实现重用
于是就有了另一个问题:
在使用当中,可能发现这个skeleton实现不能满足现有需求
这时可以在原有的interface基础上,派生出另一个skeleton实现
抽象类作为超类,可以将子类的公用逻辑用方法实现,而接口只是定义了实现类的规范(其实就是定义了实现类该做什么)
因此子类有公用的逻辑部分就在抽象类里实现该方法的细节,子类中有共性但实现细节不同则定义为抽象方法。抽象类只能被一个子类继承,而接口可以被多个实现类实现,所以同意楼上的接口的多态性更好
接口: 只定义方法名称,没有方法的具体实现。
抽象类: 只定义方法名称,没有方法的具体实现。 (与 接口 相同)
定义方法名称,并实现这些方法。 (与 类 相同)
以上两者都有。(真正的抽象类大多是这种方式. 可参看API源代码)
类:定义的方法必须都有实现。
抽象类与接口在设计时各有什么好处?2007-04-13 13:37接口不能执行任何的方法,而抽象类可以。
类可以执行许多接口,但只有一个父类。 这个也有人说用接口可以实现多重继承。
接口不是类分级结构的一部分。而没有联系的类可以执行相同的接口。
具体说说:
接口是对象属性和方法的描述(但不包括他们具体实现),比如Int32类型实现了IConvertible接口,就说明Int32具有 IConvertible接口所描述的ToDouble()方法。但IConvertible并没有给出实现ToDouble()的内容,需要由 Int32类型自己实现。
接口用于实现多态。比如Int32,Int64和Single类型都实现了IConvertible借口,那么就说明他们一定都具有ToDouble()方法。所以,定义一个变量是IConvertible类型:
IConvertible c;
然后,无论是给c赋任何实现IConvertible类型的变量,我都能够保证,c至少有一个ToDouble()方法可用,而不用关心c具体是什么类型的变量。如
int i = 3; //Int32
long j = 6L; //Int64
float k = 4F; //Single
则
c = i;
c.ToDouble();
c = j;
c.ToDouble();
c = k;
c.ToDouble();
都不会发生错误。
实现接口的类可以显式实现该接口的成员。当显式实现某成员时,不能通过类实例访问该成员,而只能通过该接口的实例访问该成员。
接口的应用大多数是在DesignPattern时才用到。
抽象类,从多个对象中抽出来的“共性”,而他的后代,既有共性、又有特性。例如:“图形”是抽象的,没有形状,由点线组成;正方形、圆形是他的派生,可以是对象。