【转】Java抽象类和接口的区别(csdn评论)

来源:互联网 发布:iphone6短信群发软件 编辑:程序博客网 时间:2024/06/05 13:27

原帖:http://topic.csdn.net/t/20030921/23/2285054.html

“接口(Interface)是什么,为什么使用接口,如何恰当的使用接口?”

----------------------------------------------------------------------------------------------------------------------------

 

(wulemale)

一个类,当需要实现多重继承时就要用到接口了
如,定义一个多线程的小应用程序类,实现的方法之一如下:
public   class   ThreadTest   extends   Applet   implements   Runnable   
{     
            ThreadTest   myTest=new   ThreadTest();  
            Thread   myThread=new   Thread(myTest);  
            myThread.start(); 
}  

 

 

----------------------------------------------------------------------------------------------------------------------------

 

(聪明的一猪)

接口就是当你的模块需要外面提供一个功能的时候,你用来定义你期望的功能是什么样的。
它作为对你的需要的一个规范,一个描述。
你通过接口,精确描述你需要的“功能”,然后从外界接收一个该接口的对象。
然后,你就可以使用这个接口了。

它让你关注“接口”,关注“规范”,而不是“实现”。别人怎么实现,那是别人的事,与我无关。你可以对你自己需要实现的东西编码,编译,测试,即使你需要的功能别人还没有实现出来。

根据需要,别人可以向你的模块传递不同的接口实现,从而达到重用的目的。

举个例子:
你给人写一个旅游指南:“先乘车去北海,在此吃午餐,下午去故宫。”
至于乘bus还是taxi,   吃包子还是麦当劳,下午怎么去故宫,不要越俎代庖,使用者可以根据自己的客观情况决定。
于是,乘车,吃午餐,去故宫,全是接口。你只管写你关心的逻辑,至于一些你不关心的实现细节,用接口抽象出来。


最后:接口和多重继承没有关系。

----------------------------------------------------------------------------------------------------------------------------

 

(郑)

 

可以从复用的角度来看。

比如我现在有一个class   :   ClassA
有很多程序使用这个ClassA,   都写在代码里面。如果后来有一个ClassB,功能比ClassA要好一些,我要所有用ClassA的地方都变成ClassB,想必会非常麻烦。

但是,如果我事先ClassA   是实现了一个接口IClass,所有的代码都是针对IClass编成的,实例化ClassA的地方都用一个工厂类来实例化的,这样,当我需要把所有ClassA都换成ClassB的时候只要将ClassB实现IClass,然后在工厂类中将返回ClassA的实例换成返回ClassB的实例就可以了。

----------------------------------------------------------------------------------------------------------------------------

 

(csdnhbc20)

 

接口相对来说是不变化的,也就是稳定的,所以,定义接口的时候,要把本质的东西找出来,然后放在接口里面,这也就是所谓的接口的永恒,而实际上又不可能达到永恒的,因为具体的环境在变化,软件的实质是变化的,所以永远都没有方法能定义出一个永恒的接口。
      具体抽象出接口的方法,我觉得主要是几个常见的oo原则:
      1:ocp原则
      2:里氏代换原则(父类可以用的地方,子类肯定也可以)
      3:依赖倒转原则(依赖接口类,不依赖实现类)
      ....
      实际工作中,其实主要的还是要对所做的产品行业有细致的分析(也就是需求分析这一阶段)
,能够充分了解该系统领域的东西,这样才能预见出该系统中的变化,才能抽象出比较好的对象出来
这样你做的系统自然就是客户所满意的,所以,实际上是要扩大我们的横向思维能力,有深厚的人文,心里,社会知识,这样才能比较真实的把握系统的需求,同时也要求有很强的接收新问题的能力,自学能力。

----------------------------------------------------------------------------------------------------------------------------

 

(天才一样的白痴)

 

接口可以看成是类似抽象类的东西,他只关心某种共性,但并不给出实现。
我觉得比较重要的是要搞清楚什么时候用抽象类,什么时候用接口。
一般来说抽象类就是   is   something   的关系,而接口要描述的是   has   something   的关系。
比如你可以定义一个接口锁,那么锁可能会有各种各样的锁,门锁   车锁   电脑锁等等,他们的特性都是可以lock   unlock诸如此类,但是具体锁的是门还是车还是电脑等等就要在这些锁的具体定义里面写了。甚至可能门锁是一个抽象类   车锁是一个抽象类   电脑锁是一个抽象类,它们都声明了锁这个接口,然后我就可以从这些抽象类里面继承出防盗门锁,电脑门锁诸如此类的具体类了

----------------------------------------------------------------------------------------------------------------------------

 

(xuefeng)

 

 

接口使得类的定义和实现完全分开。

类的提供者向客户提供一个接口,保证实现接口所定义的所有功能,至于如何实现,那是他的事,与你无关。

作为客户端,只需要关心接口提供的功能,使用接口就可以了。

这样做最大限度的降低了层与层之间的耦合,提供者可能会更改实现类,但只要接口不变,客户端无需改动。

java编程最重要的一点就是:只针对抽象编程。

----------------------------------------------------------------------------------------------------------------------------

 

 (蜀人)

interface   是关于的类型的,而class   是关于实现的,在class中有数据成员,如果修改了class,
那么client就必须要重新连接,甚至编译。而通过interface的话,就可以不用重新编译,连接。

----------------------------------------------------------------------------------------------------------------------------

 

(jeffyan77)

 

bruce007说的比较接近我的观点:interface   是关于类型的,而class   是关于实现的。

不要因为实现中的某种方便而使用interface。

前面几位讲到的使用interface   的好处,基本上就是使用纯类型,以及将纯类型和实现区分开的好处。都是正确的。

----------------------------------------------------------------------------------------------------------------------------

 

(赵明宇)

 

1.   从需求上来说:接口体显了你需求分析的结果。它是需求分析后的产物。你在做完需求分析后,完全可以得到一个系统各部所需要的方法。记住,它只反应了系统想要做些什么,而不是系统该怎么做。当你认为你所定义的接口完全满足了需求中的全部功能时,最好通过手工的方式模拟系统的运行。
举个例子,你可以一边看着你的用例图和序列图(不仅仅是这些),一边“调用”你的接口中的方法。这个调用是指在你的头脑中调用。你就直接假设所有的实现都已经完成,并且非常正确。    

如果在你的头脑中系统“运行”的很好。说明你完成了系统上层的设计工作(前提是你的需求是正确的)。接下来你可以针对你所设计的接口做应对于“在接口所定义的方法下不同需求的不同的实现”来实现对相同的业务不同的业务规责的多态性。记住接口是实现多态性的机制(当然,抽象类也可以)。

2.   从设计角度来分析:java提倡的是针对抽象编程(其实只要是面向对象的语言都应如此)。具体的意思就是客户端应该只依赖于抽象的东西。再具体一点来说,就是说白了,客户端应该只针对接口来编程。不要针对具体的实现,这样当实现这个接口的业务规则发生了变化,你只需要改动后面的实现,而不影响客户端,因为客户端根本不知道你已经“偷偷地”改变了方法的具体实现。这使得系统一处发生了变化,对系统的影响范围减至最少。顺便说一下“客户端”的概念,客户端不是用户端。客户端是相对的,A   调用了   B   ,A就叫做B的客户端,B是A的服务端.一般来讲,一个系统中,上层是下一层的客户端。下一层是上一层的服务端。一个系统的各部分可能相互调用,那它们之间也是客户端和服务端的关系。服务端只提供给客户端必要的方法。当系统各部分之间针对于抽象(具体就是通过接口),来相互调用的话,各部分之间就有了一个稳定的“契约”,它们都以这个契约为准相互沟通,而彼此都不知道对方是如何实现的。这样的话,你就有很大的余地来选择每部分的具体实现细节,怎么实现。从而一点都不影响它们的客户端。这种情况在实现规则发生改变你需要重写实现时体现的最为明显。这也是针对抽象编程的原因。

3.   从面向对象的角度看:面向对象技术为我们提供了许多好的特征。比如:信息隐藏,多态。实现信息隐藏的手段是封装,而实现多态的手段就是继承。(接口的实现可以看做是一种继承,虽然它没有从“父亲”那里继承任何东西,但它还是你的父亲   ^_^)。运用接口我们可以充份运用多态的特性。要知道,多态是面向对象中最为重要的特性之一。如果你不用接口或抽象类,而只是直接用每个具体的类做事情,那可以说你大概从面向对象这里仅仅取得了一点封装信息的好处,而失去了多态这个最大的好处。这样的程序并不是真正运用了面向对象的技术。所以用面向对象,最主要的就是在用它的多态性质。而使用接口正是通往多态大道。

以上是我从我的经验中总结的一点个人意见。大家继续讨论一下。

 

(赵明宇)

 

对了,顺便说一下多继承的事情。接口的存在并不是因为多继承的原因,接口最重要的用途也不是多继承。只是因为java不支持多继承,而运用接口可以间接地实现这种性质,所以总让人感觉接口是因为多继承的原因而出现的。这是大错特错了。
我个人感觉多继承这种特性大大的增加了设计的复杂度,而它带来的好处却不像它带来的坏处那样大。设计的时候尽量不要使用多继承。java取消了多继承的这种性质应该是有她的原因的。

 

(赵明宇)

继承是应该用的,但不要过渡依赖,就像     ajoo(聪明的一猪)     说的一样。    

设计时应该首先考虑一下聚合和组合的情况,其次考虑继承。   继承是静态的复用方式,对象之间的聚合关系是动态的复用。不要只因为一个类已经提供了大部分你需要的代码,而你又想增加一些就去继承那个类。这往往都是错误的想法。继承一定要满足   is-a   的关系。也就是说只有当子类是一种父类的时候你才可以去做继承。   is-a   关系是里式替换的必要条件。

 

(赵明宇)

 

//这是一个接口,Vehicle是交通工具的意思,这个接口定义了交通工具的一个共有的方法
//drive()驾驶
public   interface   InfVehicle   {
        public   void   drive();
}    

//小汽车是交通工具,实现交通工具的接口
public   class   Car   implements   InfVehicle   {
        public   void   drive()   {
                //这里具体实现小汽车的驾驶方法
        }
}
//自行车也是交通工具,实现了交通工具的接口
public   class   Bicycle   implements   InfVehicle   {
        public   void   drive()   {
                //这里具体实现自行车的驾驶方法
        }
}

//这是一个“人”类,它有一个方法是   goHome回家,回家需要一种交通工具,所以他有一个交通工具,他回家时使用这个工具
public   class   Man   {
        private   InfVehicle   vehicle;
       
        public   InfVehicle   getVehicle()   {
                return   this.vehicle;
        }

        public   void   setVehicle(InfVehicle   vehicle)   {
              this.vehicle   =   vehicle;
        }
        //回家方法
        public   void   goHome()   {
                this.vehicle.drive();
        }
}

现在我们来看怎么使用这些类

Man   aMan   =   new   Man();       //创建一个 "人 "                      
InfVehicle   car   =   new   Car();   //创建一个小汽车
InfVehicle   bicycle   =   new   Bicycle();//创建一个自行车

//比如今天这个人想开车回家,我们就
aMan.setVehicle(car);
aMan.goHome();
//如果他开车开腻了,想换一种方式,他这可以骑车回家
aMan.setVehicle(bicycle);
aMan.goHome();

-----------------------------------------------------------------

这样的代码的好处就是,如果有一天那个人说:“我不想开小汽车了,没意思,我也不想骑车,太累。我想开大卡车下班回家,那样够帅!”(注意:客户完全可以提出类似的要求)那我们怎么办呢?   在这里,人这个类使用交通工具的服务,人即是客户端,交通工具是服务端(上面我的一个贴子说了客户端和服务端的事情),那我们不想对客户端造成影响。因为在这个例子中使用了接口,我们就很好办。   比如我们可以添加一个卡车类Sixby实现交通工具的接口,这样如果这个人想开卡车下班回家,就传给它一个Sixby的实例,他就可以够帅了^_^
当然,这里有一个问题,但它本贴讨论的接口无关。就是我们怎么来创建具体的交通工具?   是不是每次要换交通工具的时候都要重新new一个?     对这个问题你就可以考虑一下工厂的设计模式了。这话就说远了,已经与本主题无关了。

(赵明宇)

 

(珂儿)

也许我有点笨,上面高手讲了那么多,我却还是没有理解接口和抽象类的区别,抽象类不也可以实现多态吗?就像   truezerg(赵明宇)   写的例子,我感觉如果声明一个抽象类,那么在具体运用的时候,可以重载这些方法,一样可以达到这种效果呀。
刚才问了一下同事,他说接口比较灵活,给你一个接口可以访问远程的,有点迷茫,不太理解。

 

(赵明宇)

to:   yangjuanli(珂儿)
向我上面写的那个例子是非常简单的,只是想表达一下怎么来使用接口。当然在那个例子中如果你能找到所有的交通工具在驾驶上的一个共同点的话,使用抽象类也是可以的。这种情况你就可以把共同的地方向上转移到父类中。这种情况你就可以使用抽象类了。
你要注意的一点是,接口是与抽象更密切的,抽象类是与实现更密切的(和接口相比较,虽然它叫抽象类)。所以一旦触及到实现,你就要特别注意和小心。你必须确认你所放在抽象类中的代码一定是适合所有它的子类的,如果不是这样的话或是你无法确定,你就使用接口比较好一点。
另外我个人的建议是不管你有没有抽取出有共性的代码,不管你有没有用到抽象类,你最好总是定义一个接口给用户。即便是你有抽象类,你也要提供一个接口,让你的抽象类实现你的接口,你的子类继承你的抽象类。把接口给用户。   这让比较好一点。

 

breakpoint_fish@hotmail.com

欢迎同行加入!

 

 

----------------------------------------------------------------------------------------------------------------------------

 

 

(秋天的树)

 

举个例子,如J2EE,sun发布了一个新的规范,全部使用interface,而IBM,   BEA等其他人只要按照sun的文档实现这些接口就可以了。如果sun发布的不是接口而是实现,如一个class,那么ibm说我讨厌sun的实现,它的效率太低下,而想改用自己的实现就不是那么容易了。interface是抽象规范的好工具。

----------------------------------------------------------------------------------------------------------------------------

 

(聪明的一猪)

 

我在写一个thread   pool的时候,就发现Thread是个类而不是interface让我很不爽。很多东西得绕个大弯子做,还做的不干净。
感觉这是java的一个缺点。

另外,同意赵明宇对继承的批评。对继承的过渡依赖(甚至认为OO就是继承),来自于Smalltalk,   Simula等语言的影响,以及认为继承这种“inremental   design”就可以自然地描述世界的天真。

----------------------------------------------------------------------------------------------------------------------------

 

(CSDN ,随时随地,想上就上)

 

举个荤例说明接口的多态性

女人有个口子,叫私处
男人有根棒子,可以操作接口。同一个男人的棒子,可以操作不同的女人(只要是女人,有口子),但男人只知这个女人是女人,不知是张三妹,还是李四妹,这个时候,说女人是多态的(想想关灯后的多人成人派对,大家都不说话,抓住就上了)。对色狼来说,他只认有口子的人,不去挑张三妹还是李四妹。

对比:
男人:客户,消费者
女人定义:有口子的都是女人
男人消费女人:有口子的都可以被消费

 

上面一再强调有口的就是女人,那么母狗母猪母牛也有口子,是不是男人也或以消费它们呢?答案是肯定的,这样,就不能推论说因为女人是人,母狗母猪母牛也是人,只能说,她(它)们是母的。这个母的就是接口。女人可以实现,母狗母猪母牛也可以实现,这就是角色的意思。

人可以用手制造工具,这是人的一个技能,因此,女人也可以制造工具,但母猪母狗就不行了,因此,人到女人是功能实现(都是人),母到女人,母狗母猪母牛是角色实现(都是女的)。

 

(郑关西)

 

littlecpu(CSDN,   随时随地,想上就上。)  
说的虽粗,但是经典!!!
从他的例子可以看出,男人、女人就是类,可以继承,公猪、母猪也是类也可以继承,
但是所有雌性的哺乳动物都实现了那么个插座,所以所有雄性的哺乳动物都实现了插销。
这样生物才能繁衍,由于都实现了插座、插销的接口,这些哺乳动物就可以随便找一个异性同类(class)来工作。
其实这里面挺复杂,雌性还实现了另一个接口,生小孩,虽然都是一个口里的,但实现的功能不同。后者又跟继承扯上了关系。
那么我们可以定义接口的两个方法。一个是交配,一个是生小孩。

例如:
Ifemale   i=new   女人();
i.生小孩();
一个孩子诞生;

Ifemale   i=new   母猪();
i.生小孩();
一只小猪诞生了;

这就实现了面向对象的多态。

 

(郑关西)

抽象类是用于组件内部的。
接口是用于组件外部的。

至于人和猪接口的具体实现,大家不用去管,那是上帝实现的。

----------------------------------------------------------------------------------------------------------------------------

(山不在高)

 

我也凑热闹,
上面大家讲了很多接口的好处了。
不过我觉得那些都比较虚,
不是说每次写东西先来个接口,然后实现就会有那样的好处,
必须能真正抓住事物的本质,要不然,写出来的接口只能是一遇到变化
就修改,一遇到麻烦就扩充,根本就成了累赘,因为一扩充,那扩展了这个接口的
类都要改,SWEAT。

理解接口,关键在于学会抽象,抽象是一个分析的过程,
说白了还是面向对象的分析问题,这个话就长了,也非我所长(不到家的人就
这样,有时候好像明白点,可是却说不出来,见笑了)。
一个朋友和我说,每个对象都是有生命力的,开始不明白,后来仔细想来很有道理。

----------------------------------------------------------------------------------------------------------------------------

 

(刘瑜江)

 

你可以试作把接口详陈一个信封,信封上的地址,邮编,收件人姓名和信中的内容都相当于接口
中的提供的方法,这些方法是在从发件到邮局到收件人都采用的通用的标准,你只要把信封上的
各个元素填充完整(即接口实现)了,那么这封信就能在很大范围被识别和使用,当然你也可以在
信封的一个下角做一个符号(只有发件人和指定的收件人能能理解),那么你可以在一个信封实现时在信封上加一个元素,就行了.
(级别不高,理解可能有误,请谏量)

----------------------------------------------------------------------------------------------------------------------------

(独孤求败)

 

我还是比较支持把接口理解为‘规范’或者‘标准’的观点。接口就是一张纸,纸上严格的规定了许多条目。
truezerg(赵明宇)举的例子没有错,但是这里只体现了Interface的部分内涵,而且抽象类也有这个功能。
我认为最好的例子还是   javawings(JavaWings)   举的插座的例子,比如某个大楼的电源插座是三孔的,要使用时,所有的插头,必须使用三孔

----------------------------------------------------------------------------------------------------------------------------

 

(狗贼)

 

在下刚开始学习接口的实际编写,因为发现采用了好的面向对象设计方法后还是回存在细节上的疏忽和错误,例如类定义没有太大问题,方法也如此,但在重载中发现我在不断的加参数、以至于一个方法可能要被重载许多次,每一次都需编译所有的调用,请问各位前辈,如果我定义这个调用的接口类后,在声明时是否不用考虑参数或返回值的一致性,只需方法名一致即可?或这个问题可以用抽象类来解决?

 

(赵明宇)

 

不行,只考虑方法名不考虑参数类型和参数个数那接口的作用就没有什么了。你在重载中发现在不断的加参数,以至于一个方法可能被重载了好多次,这说明你的接口定义的有问题。接口如果不满足用户的话,你在写类的时候就想去重载已有的方法,加参数什么的。即使你这样做了,也没有什么用。   因为接口里没有后添加的方法。你一样无法通过接口来使用。

在确定接口是否定义完成后你试着测试一个接口是否满足了要求,然后再决定是否开始实现它

 

(狗贼)

 

谢谢   truezerg(赵明宇),您的意思是否可以这样理解,在接口定义时必须对所有方法的重载情况都声明,至于实现可以放到下一步,但规划好这些方法是前提条件,否则表示类的抽象分析过程存在问题。

 

(赵明宇)

 

可以这么说,如果你在实现接口的过程中经常发现少了一个方法,或是需要一个重载方法,然后你在到接口中添加上,然后再去实现它。   这就说明你接口定义时就没有定义好。直到你去实现时才发现不满足需求,于是你就去改。这样就不好了。

我个人经验,接口里如果出现过多的重载方法很有可能也没有设计好。一般接口体现对上一层的服务。服务一般是较稳定的,过多的重载方法,参数的不稳定性导制接口也比较脆弱。而且从服务的角度来说好像也不是非常必要。     只是我个人的想法。

 

----------------------------------------------------------------------------------------------------------------------------

 

(sagaman)

 

请问:象java.sql包里面的那些接口的实现在哪里?PreparedStatement对象的getConnection方法返回Connection对象,然后就可以直接使用该Connection对象的方法,并没有像赵明宇大虾所说的那样应用。

另外,如何把一个接口的所有常量属性都枚举出来?例如,我把学历的所有类别防到接口Education中,每个学历作为Education接口一个属性,我在某处需要全部列出,怎么实现?是不把所有的学历作为接口的一个数组类属性?

 

(赵明宇)

to:   sagaman(sagaman)  
---------------------------------------------------
请问:象java.sql包里面的那些接口的实现在哪里?PreparedStatement对象的getConnection方法返回Connection对象,然后就可以直接使用该Connection对象的方法,并没有像赵明宇大虾所说的那样应用。
---------------------------------------------------
事实上你用的不正是Connection的接口吗?     而getConnection返回的正是一个实现了这个接口的具体的类的对象啊。   你说在哪里实现的?   在你用的每个具体的数据库的驱动程序里。你做数据库连接前一定会用到这句话吧。Class.forName(driver).newInstance();   这就是在加载一个具体的数据库驱动程序。

JDBC正是一个使用接口的极好的例子啊。   你只管用接口里的方法就行了,不管你用什么数据库的驱动程序。   就像你只管用交通工具的驾驶方法不管你是在开什么车。

 

(sagaman)

to   赵大:
第一个问题:如何隐藏接口的实现?例如你并不知道Resultset、Statement接口怎么实现的,这些接口的实现对你来说是隐藏的。

 

(赵明宇)

 

隐藏接口的实现?     你知道Oracle的JDBC是怎么实现的吗?   我想大多数人都不关心这个吧。   但我们都用过它的驱动来做Oracle的数据库应用,     你用的时候就是在用jdbc的接口,就是在对接口说话。   这是不是你所说的隐藏?  

其实对接口的实现没有必要非得隐藏。   也不必隐藏。

----------------------------------------------------------------------------------------------------------------------------

 

(火山)

说一点个人的理解。
接口是某种外在特性的描述。因此private,protect都是无效的。因为它不涉及具体的实现,因此变量定义也是无效的。
如interface   消化功能   {
    public   吸收()   ;
    public   排泄()   ;
}
类是某种实体规则的定义。在类中可以实现接口。
如class   人impliment消化功能   {
    吃();
    喝();
    拉();
    撒();
    public   吸收()   {
        吃或喝
    }
    public   排泄()   {
        拉或撒
    }
}
从代码的角度来说,类继承可以减少子类代码,而接口的实现只会增加代码。
最后,接口和接口之间是没有继承关系的,只有扩展关系

----------------------------------------------------------------------------------------------------------------------------

 

(魔之眼)

接口就是对客户的承诺

----------------------------------------------------------------------------------------------------------------------------

 

(mozart2000)

其实用电脑DIY来比喻最直观了

主板上那些插槽都是接口。
只要符合接口的规范,你可以随意使用产品。
比如,你可以用DLink的网卡,也可以用3Com的网卡,
而集成网卡的主板,你就没有替换的余地了。

用接口来编程,可以提高软件的扩展性。

----------------------------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------------

原创粉丝点击