corejava11(4.1 介绍面向对象编程)

本文介绍了面向对象编程(OOP)的基本概念,包括类、对象、封装、继承和多态性。探讨了OOP如何通过将数据和操作数据的方法捆绑在一起,提供更好的代码结构和重用性。同时,文章还讲解了如何识别类和对象,以及类之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4.1 介绍面向对象编程

面向对象编程,或者简写为OOP,是这些年领先的编程范式,它已经取代了1970年开发的“结构化”或者过程编程技术。因为Java是面向对象的,你不得不熟悉OOP以便更好使用Java。

一个面向对象程序是由对象构成。每个对象有一个特定的功能,暴露给它的用户,以及一个隐藏的实现。在你程序中许多对象将从库中伸手可得;其它的也能够通过自定义设计。是否你构造一个对象,或者购买它取决于你的预算或者时间。但是,一般来说,只要对象满足你的规格,你不用关心功能如何实现。

传统结构化编程,由一些过程(或者算法)构成,来解决一个问题。一旦过程决定,传统的下一步是找到合适的方式来存储数据。这也是为什么Pascal语言的设计者,Niklaus Wirth,把他著名的编程书叫做算法+数据结构=程序(Prentice Hall, 1975)。注意在Wirth的标题中,算法是第一位,并且数据第二位。这反应了当时程序员工作的方式。首先,他们决定操作数据的步骤;然后他们决定如何组织数据,以便操作能够更简单。OOP颠倒了顺序:把数据放在第一位,然后看操作数据的算法。

对于小问题,面向过程也能工作的很好。但是对象更适合解决大问题。考虑一个简单的web浏览器。它可能需要2000个过来来实现功能,它们都操作大量的数据。在面向对象风格中,可能有100个类,每个类平均有20个方法(见图4.1)。这个结构对于一个程序员更容易掌握。它也更容易找到bug。假设操作对象的数据在不正确的状态。它更容易在20个方法中找到罪犯,相比于在2000个方法中找。

图4.1 面向过程 vs 面向对象

4.1.1 类

类是用来创建对象的模板或蓝图。把类看作是饼干切割器;对象本身就是饼干。当您从一个类构造一个对象时,您就已经创建了该类的一个实例。

正如您所看到的,在Java中编写的所有代码都在一个类中。标准Java库提供了几千个类,用于用户界面设计、日期和日历以及网络编程等多种用途。然而,在Java中,仍然需要创建自己的类来描述应用程序的问题域的对象。

封装(有时称为信息隐藏)是处理对象的关键概念。形式上,封装只是在一个包中组合数据和行为,并从对象的用户那里隐藏实现细节。对象中的数据位称为其实例字段,对数据进行操作的过程称为其方法。作为类实例的特定对象将具有其实例字段的特定值。这些值的集合是对象的当前状态。每当对对象调用方法时,其状态可能会更改。

封装工作的关键是让方法永远不能直接访问类中的实例字段,而不是访问它们自己的实例字段。程序应该只通过对象的方法与对象数据交互。封装是赋予对象“黑盒”行为的一种方式,它是重用和可靠性的关键。这意味着类可能会完全改变它存储数据的方式,但是只要它继续使用相同的方法来操作数据,就没有其他对象知道或关心。

当您开始用Java编写自己的类时,OOP的另一个原则将使这更容易:类可以通过扩展其他类来构建。事实上,Java带有一个称为Object的“宇宙超类”。所有其他类都扩展这个类。在下一章中,您将进一步了解Object类。

扩展现有类时,新类具有所扩展类的所有属性和方法。然后,您将提供仅应用于新类的新方法和数据字段。扩展一个类以获得另一个类的概念称为继承。有关继承的更多信息,请参阅下一章。

4.1.2 对象

要使用OOP,您应该能够识别对象的三个关键特征:

  • 对象的行为——可以对该对象做什么,或者可以对其应用什么方法?
  • 对象的状态——在调用这些方法时,对象如何反应?
  • 对象的标识——对象与具有相同行为和状态的其他对象有何区别?

所有属于同一类的实例的对象通过支持相同的行为共享一个族相似性。对象的行为由您可以调用的方法定义。

接下来,每个对象存储有关当前外观的信息。这是对象的状态。一个物体的状态可能会随着时间而改变,但不是自发的。对象状态的更改必须是方法调用的结果。(如果某个对象的状态在没有对该对象进行方法调用的情况下发生了更改,则有人破坏了封装。)

但是,对象的状态并不能完全描述它,因为每个对象都有一个不同的标识。例如,在订单处理系统中,两个订单是不同的,即使它们请求相同的项目。请注意,作为类实例的单个对象在其标识上总是不同的,并且在其状态上通常也不同。

这些关键特征可以相互影响。例如,对象的状态可以影响其行为。(如果订单是“已发货”或“已付款”,则可以拒绝要求其添加或删除项目的方法调用。相反,如果订单是“空的”,也就是说,还没有订购任何商品,则不应允许自己发货。)

4.1.3 识别类

在传统的过程程序中,您从顶部开始这个过程,主要功能是。当设计一个面向对象的系统时,没有“top”,OOP的新手常常想知道从哪里开始。答案是:确定类,然后向每个类添加方法。

识别类的一个简单经验法则是在问题分析中寻找名词。另一方面,方法对应于动词。

例如,在顺序处理系统中,有些名词是

  • 项目
  • 订单
  • 送货地址
  • 付款
  • 账户

这些名词可能引出类ItemOrder等。

接下来,查找动词。项目添加到订单中。订单已发货或取消。付款适用于订单。对于每一个动词,例如“添加”、“发货”、“取消”或“应用”,您都要确定负责执行它的对象。例如,当向订单添加新项目时,订单对象应该是负责的对象,因为它知道如何存储和排序项目。也就是说,add应该是以Item对象为参数的Order类的方法。

当然,“名词和动词”只是一个经验法则,只有经验才能帮助你决定在构建类时哪些名词和动词是最重要的。

4.1.4 类之间的关系

类之间最常见的关系是

  • 依赖(“uses-a”)
  • 聚合(“has-a”)
  • 继承(“is-a”)

依赖关系,或者说“uses-a”关系,是最明显的,也是最普遍的。例如,Order类使用Account类,因为Order对象需要访问Account对象来检查信用状态。但是Item类不依赖于Account类,因为Item对象不需要担心客户帐户。因此,如果一个类的方法使用或操作该类的对象,则该类依赖于另一个类。

尽量减少相互依赖的类的数量。重点是,如果一个类A不知道B类的存在,它也不关心对B的任何更改(这意味着对B的更改不会将错误引入A中)。在软件工程术语中,您希望最小化类之间的耦合。

聚合或“has-a”关系很容易理解,因为它是具体的;例如,Order对象包含Item对象。包含意味着A类对象包含B类对象。

注意

一些方法论者轻蔑地看待聚合的概念,并且倾向于使用一种更普遍的“关联”关系。从建模的角度来看,这是可以理解的。但是对于程序员来说,“has-a”关系很有意义。我们喜欢使用聚合还有另一个原因:关联的标准符号不太清楚。见表4.1。

表4.1 类关系的UML表示法

继承关系或“is-a”关系表示更特殊的类和更一般的类之间的关系。例如,RushOrder类继承自Order类。专门的RushOrder类具有特殊的优先级处理方法和不同的运费计算方法,但它的其他方法(如添加项目和计费)继承自Order类。通常,如果类A扩展了类B,则类A继承了类B的方法,但具有更多的功能。(请参阅下一章,我们将在其中详细讨论这个重要概念。)

许多程序员使用UML(统一建模语言)表示法来绘制描述类之间关系的类图。您可以在图4.2中看到这样一个图的示例。将类绘制为矩形,将关系绘制为带有各种装饰的箭头。表4.1显示了最常见的UML箭头样式。

图4.2 一个类图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值