Java中的抽象与封装[重复]

Posted

技术标签:

【中文标题】Java中的抽象与封装[重复]【英文标题】:Abstraction vs Encapsulation in Java [duplicate] 【发布时间】:2012-08-11 13:10:46 【问题描述】:

可能重复:Abstraction VS Information Hiding VS Encapsulation

我知道这个问题可能已经在这个论坛上被问过数千次了,甚至网络上也充斥着很多关于这些概念的定义,但听起来都一样,而且都使用相同的技术词汇。例如以下定义

封装是将数据和对数据进行操作的代码绑定或包装成单个实体的过程。这使数据免受外部接口和误用的影响。考虑封装的一种方法是将其视为保护性包装器,以防止代码和数据被包装器外部定义的其他代码任意访问。

我从上面的定义中了解到的是创建变量,将它们标记为私有并为这些变量生成 getter-setter 并使用对象来访问这些 getter 和 setter。这样,数据就隐藏在对象内部,只能通过对象访问。 希望我是对的


抽象是Java中用于隐藏某些细节并仅显示对象的基本特征的过程。换句话说,它处理对象(界面)的外部视图。

现在这是让我一直困惑的部分。每当我想到抽象时,我想到的就是抽象类(可能是因为两者都有 Abstract 关键字)。上面的定义说抽象意味着隐藏数据并只显示所需的细节,但这就是我们在封装中已经在做的事情,对吧?那么有什么区别。我也没有得到什么是对象的外部视图,它处理对象的外部视图

如果可能的话,有人可以通过一些现实生活中的示例或一些程序示例来更清楚地说明这一点。

【问题讨论】:

【参考方案1】:

OO Abstraction 出现在类级设计期间,目的是隐藏实现复杂性如何实现 API/设计/系统提供的功能,在某种意义上简化了访问底层实现的“接口”。

抽象的过程可以在类的越来越“更高”的层次(层)上重复,这样就可以在不增加代码的复杂性和每一层的理解的情况下构建大型系统。

例如,Java 开发人员可以利用 FileInputStream 的高级特性,而无需关心它是如何工作的(即文件句柄、文件系统安全检查、内存分配和缓冲将在内部进行管理,并且对消费者)。这允许更改FileInputStream 的实现,并且只要FileInputStream 的API(接口)保持一致,针对以前版本构建的代码仍然可以工作。

同样,在设计自己的类时,您会希望尽可能对其他人隐藏内部实现细节。

在Booch定义1中,OO封装是通过Information Hiding实现的,特别是围绕隐藏类实例拥有的内部数据(表示状态的字段/成员),通过强制以受控方式访问内部数据,并防止对这些字段的直接、外部更改,以及隐藏类的任何内部实现方法(例如,通过将它们设为私有)。

例如,默认情况下可以将类的字段设置为private,并且只有在需要对这些字段进行外部访问时,才会从班上。 (在现代 OO 语言中,字段可以标记为 readonly / final / immutable,这进一步限制了更改,即使在类内也是如此)。

未应用任何信息隐藏的示例(不良做法)

class Foo 
   // BAD - NOT Encapsulated - code external to the class can change this field directly
   // Class Foo has no control over the range of values which could be set.
   public int notEncapsulated;

应用字段封装的示例

class Bar 
   // Improvement - access restricted only to this class
   private int encapsulatedPercentageField;

   // The state of Bar (and its fields) can now be changed in a controlled manner
   public void setEncapsulatedField(int percentageValue) 
      if (percentageValue >= 0 && percentageValue <= 100) 
          encapsulatedPercentageField = percentageValue;
      
      // else throw ... out of range
   

字段的不可变/仅构造函数初始化示例

class Baz 
   private final int immutableField;

   public void Baz(int onlyValue) 
      // ... As above, can also check that onlyValue is valid
      immutableField = onlyValue;
   
   // Further change of `immutableField` outside of the constructor is NOT permitted, even within the same class 

Re:抽象与抽象类

Abstract classes 是促进类之间共性重用的类,但它们本身不能直接用 new() 实例化 - 抽象类必须是子类,并且只能实例化 concrete(非抽象)子类。 Abstractionabstract class 之间可能造成混淆的一个原因是,在 OO 的早期,继承更多地用于实现代码重用(例如,使用关联的抽象基类)。现在composition is generally favoured over inheritance,还有更多的工具可以实现抽象,比如通过接口、事件/委托/函数、特征/混合等。

Re:封装与信息隐藏

封装的含义似乎随着时间的推移而演变,最近,encapsulation在确定哪些方法时也可以更普遍地使用,字段、属性、事件等捆绑到一个类中。

引用***:

在面向对象编程语言的更具体设置中,该概念用于表示信息隐藏机制、捆绑机制或两者的组合。

例如在语句中

我已将数据访问代码封装到它自己的类中

.. encapsulation 的解释大致相当于Separation of Concerns 或Single Responsibility Principal(SOLID 中的“S”),可以说可以用作重构的同义词。


[1] 看过 Booch 的 encapsulation cat picture 后,您将永远无法忘记封装——面向对象分析和设计与应用程序的第 46 页, 第二版

【讨论】:

所以我们使用的Java API是一个抽象? 是的,在高层次上,API 确实为消费者提供了一种更简单的抽象来使用,完全不知道其内部设计背后的底层复杂性。 Java 显然是一种语言,具有许多相关的高级框架。但是,我更具体地认为,在 OO 设计principals terminology 被创造出来的时候,抽象更具体地指的是类级别的设计和分解,而不是指框架或企业级别的设计。【参考方案2】:

简单来说: 在决定要实现什么时进行抽象。 当你隐藏你已经实现的东西时,你会进行封装。

【讨论】:

【参考方案3】:

抽象是关于识别共性并减少您必须在不同级别的代码中使用的功能。

例如我可能有一个Vehicle 课程。 Car 将派生自 VehicleMotorbike 也是如此。我可以向每个Vehicle 询问***、乘客等的数量,这些信息已从CarsMotorbikes 中提取并确定为共同点。

在我的代码中,我通常可以通过常用方法go()stop() 等处理Vehicles。当我稍后添加新的车辆类型(例如Scooter)时,我的大部分代码仍然会忽略这个事实,以及 Scooter 的实现单独担心 Scooter 的特殊性。

【讨论】:

您能否提供代码以更好地解释您的答案? @RahulGupta 要完全理解这一点,您需要了解继承的概念。通过继承,您可以创建父类的层次结构(高级父类是抽象的,大多数子类是具体的)并将共同的特征/属性传递给子类。如果正确完成,抽象将被实施。 - 父类做共性。 (如果用户专注于孩子,则从用户角度隐藏) - 专注于成为孩子的子类。 (外行:作为一个孩子,我尽可能不关心父母的工作,但我关心的是我所做的。)

以上是关于Java中的抽象与封装[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Java中抽象和多态的优势[重复]

Java中的接口

阶段1 语言基础+高级_1-3-Java语言高级_02-继承与多态_第2节 抽象类_9_接口的私有方法定义

java接口上的抽象方法是啥[重复]

Java 8中接口和抽象类之间的根本区别[重复]

Python中的抽象方法[重复]