论坛首页 Java企业应用论坛

面向对象的三个基本特征

浏览 15567 次
精华帖 (2) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (2)
作者 正文
   发表时间:2011-07-31  
OO
面向对象的三个基本特征是:封装、继承、多态。
封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。


通过继承创建的新类称为“子类”或“派生类”。


被继承的类称为“基类”、“父类”或“超类”。


继承的过程,就是从一般到特殊的过程。


要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。


在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。




继承概念的实现方式有三类:实现继承、接口继承和可视继承。


Ø         实现继承是指使用基类的属性和方法而无需额外编码的能力;


Ø         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;


Ø         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。


在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。


抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。


OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。




多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。


实现多态,有二种方式,覆盖(override),重载(overload)。


覆盖,是指子类重新定义父类的虚函数的做法。它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。



重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响;
4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。



其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”


那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了代码重用。而多态则是为了实现另一个目的??接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

   发表时间:2011-08-01  
写得不错!
0 请登录后投票
   发表时间:2011-08-03  
写的很详细也很用功!对初学者很有帮助
0 请登录后投票
   发表时间:2011-08-07  
面向对象只有一个基本特征,就是封装。

继承会破坏封装,不能算是基本特征。
多态是隐藏实现细节的方式,目的也是为了programming to interface,实现封装。
0 请登录后投票
   发表时间:2011-08-09  
JamesFSH 写道
面向对象只有一个基本特征,就是封装。

继承会破坏封装,不能算是基本特征。
多态是隐藏实现细节的方式,目的也是为了programming to interface,实现封装。

我觉得楼上的,把封装理解成封闭了。
0 请登录后投票
   发表时间:2011-11-18   最后修改:2011-11-18
http://www.infoq.com/cn/articles/ck-art-of-language-design
引用
面向对象的设计方法已经深入人心,大多数现代语言都支持面向对象编程。多态性、数据抽象和继承是面向对象编程的三个基本原则。


http://www.infoq.com/cn/news/2010/07/objects-smalltalk-erlang
引用
我开始想知道面向对象编程到底是什么,而且我认为Erlang不是面向对象的,它是函数式编程语言。那我的论文导师就说了:“你是错的,Erlang是完全面向对象的”。他说面向对象的语言其实不是去面向对象。我在想,虽然我不能确信是不是能相信这句话,但Erlang可能是唯一的面向对象语言,这时因为Erlang具备面向对象编程的三原则:基于消息传递机制,对象分离和多态。


这两种说法哪个靠谱?
0 请登录后投票
   发表时间:2011-12-14   最后修改:2011-12-15
写的还可以
对封装理解并不是很到位,请看《漫谈设计模式》第一章关于其中的讲述:http://redhat.iteye.com/blog/1007884,封装不仅是数据和方法的隐藏。并且可以参见《设计模式精解》相关的部分对封装的阐述,此外,记得infoq上有一个视频也讲述了oob的封装特性,老外讲的,忘记了在哪里,你可以找找。
另外
引用
继承会破坏封装,不能算是基本特征。

此言差异,有了继承,面向对象才有了很多扩展,详细还是请浏览《漫谈设计模式》第一章关于其中的讲述。
封装是最重要的一条。
引用
多态是隐藏实现细节的方式,目的也是为了programming to interface,实现封装

这句理解的不错,其实更加准确一点,就是使用统一的方式处理不同行为能力。但是特别注意:多态并未实现封装,目的也不只是programming to interface,也不只是programming to abstract/interface.只是为programming to abstract/interface提供了很大的方便,即使没有interface、abstract,撇过设计的好坏,多态照用无误。所以不能单纯的说目的就是它,这是它的非常重要的一个特性。
0 请登录后投票
   发表时间:2011-12-15   最后修改:2011-12-15


看到了redhat在我博客上的回复,并看到redhat在这个主题中的相关回复。参与一下。

redhat的具体评论和我的具体回复,可以看这里。

《编程机制探析》第六章 面向对象
http://www.iteye.com/topic/1114088

redhat的博客地址在这里。
http://redhat.iteye.com/

看了一下,博主redhat颇有做学问的严谨精神。
另外,redhat写了一本《漫谈设计模式》一书。以后有机会拜读一下。
redhat的博文中充满了热烈的争论,令我羡慕不已。
里面有些名词和概念之争,看了之后,获益匪浅,理清了不少似是而非的概念。不过,这类讨论我是插不上嘴的。我还不到那个层次。我还在实现模型的层次上打转。我离名词定义的层次还差得太远。

以下是关于这个主题的讨论。

这里主要谈一下和本章主题相关的问题——面向对象最重要的特性。
我翻了一下,并没有在redhat的博客中发现直接综述面向对象特性的博文,倒是在redhat的一篇回复。

面向对象的三个基本特征
http://www.iteye.com/topic/1112912

redhat回复道:
redhat 写道

redhat 写道
写的还可以
对封装理解并不是很到位,请看《漫谈设计模式》第一章关于其中的讲述:http://redhat.iteye.com/blog/1007884,封装不仅是数据和方法的隐藏。并且可以参见《设计模式精解》相关的部分对封装的阐述,此外,记得infoq上有一个视频也讲述了oob的封装特性,老外讲的,忘记了在哪里,你可以找找。
另外
引用
继承会破坏封装,不能算是基本特征。

此言差异,有了继承,面向对象才有了很多扩展,详细还是请浏览《漫谈设计模式》第一章关于其中的讲述。
封装是最重要的一条。
引用
多态是隐藏实现细节的方式,目的也是为了programming to interface,实现封装

这句理解的不错,其实更加准确一点,就是使用统一的方式处理不同行为能力。但是特别注意:多态并未实现封装,目的也不只是programming to interface,也不只是programming to abstract/interface.只是为programming to abstract/interface提供了很大的方便,即使没有interface、abstract,撇过设计的好坏,多态照用无误。所以不能单纯的说目的就是它,这是它的非常重要的一个特性。



从中可以窥见了redhat的一些观点。
redhat似乎认为封装(和继承?)是面向对象的最重要的特点。

设计模式对于理解面向对象的重要性方面,我想,我的观点和redhat是一致的。从redhat写的《漫谈设计模式》这个书名就可以看出。
不过,在封装和继承的问题上,我的观点恰恰相反。
buaawhl 写道

在这里,我保证,本书绝不会重复那些无聊的废话。什么关于封装、继承之类的玩意儿,本书一概不涉及。本书只从实际出发,讲解面向对象最本质、最核心的概念以及实现原理。


先讲继承。我为什么觉得继承不重要呢?在《关于方法表的那些事儿》一章中,我讲述了继承带来的问题——方法表中同名方法的替换(override)和并存(overload)问题。而且,继承能够实现的功能,完全可以用包含和组装的方式来实现。而且,继承是面向对象特有的现象,而包含和组装则通用于函数式编程。
在方法表的功能方面,继承并不是必要的,还可能带来问题,还不能通用于其他编程模型。那么,在对于继承的态度上,我从众,认为能避免则避免。
下面说封装。封装,其实就是一个作用域的问题。函数体内的局部变量对外是不可见的。这是最通用的作用域,见于任何编程模型中。
在各种语言常见的模块定义中,一般的函数是不会暴露于模块之外的,只能通过export等关键字暴露给模块之外。
在面向对象语言的class定义中,则有public、protected、private等修饰符。
从语法的审美观方面来讲,我不太欣赏这些修饰符。因为这些修饰符可以任意添加,而不是集中在一处(ruby似乎意识到了方面的问题,提供了用attr系关键字集中修饰属性的语法)。
我更希望一种能够集中定义可见方法表的方式。比如,
construct_person(name) = (getName, getFullName)
  where
getName = name
makeFullName = “full ” + name
getFullName = makeFullName

用javascript写就是这样。
function construct_person(name) {
  makeFullName = function() {return “full” + name }

  return{
getName = function() {return name}
getFullName = makeFullName
  }
}

这两段代码是一样的意思,使用了闭包(closure)来直接构造方法列表。函数体中的最后一条语句都是返回一个(属性)方法表。不需要公开的内部函数,就不出现在列表中。这样构造出来的(属性)方法表,在语义上显得更加清晰。
在我看来,封装既非面向对象特有,而且面向对象做得也并非最好,所以,也就没有在封装身上花费更多的笔墨。

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics