第4章数据抽象_第1页
第4章数据抽象_第2页
第4章数据抽象_第3页
第4章数据抽象_第4页
第4章数据抽象_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、2021-10-16With JavaBlueJObject firstPLP封面封面清华大学出版社第第4章章数据抽象数据抽象4.1数据抽象的含义131 4.1.1 基本类型的实现1 4.1.2 类的接口 4.1.3 String4.2 抽象类135 4.2.1 不能够实例化的类 4.2.2子类的占位符 4.2.3 接口继承 Vs. 实现继承4.3 Java接口142 4.3.1 接口与实现类 4.3.2 多重继承问题4.4依赖于抽象类型 148 4.4.1 开放封闭原则 4.4.2 创建对象的技术 4.4.3 启发式条例You have no choice about the necessi

2、ty to integrate your observations, your experiences, your knowledge into abstract ideas, i.e., into principles.Ayn Rand, Philosophy: Who Needs It 1974章引言章引言数据抽象数据抽象(Data abstraction)是指将数据类型的抽象特征与其实现的具体细节清晰地分离。其中数据类型的“抽象特征”是指对用户代码而言可见的、如何使用该数据类型的接口。数据抽象是接口与实现分离原则在类型层面以及对象技术中的推广。本章将介绍作为数据抽象的Java基本数据类型

3、、抽象类、Java接口,重点讨论抽象类、接口的作用。下一章继续介绍数据抽象作为抽象数据类型的线性表及其实现。 4.1数据抽象的含义 对数据进行分类是为了方便程序员识别和操作它们。前面介绍了若干的概念:数据类型、Java语言的类型、基本类型的各种操作、引用类型的7种操作。上述所有的介绍均处于Java语言的层面。或者说,一直讨论的一直讨论的是Java数据类型的抽象特征接口。 4.1.1 基本类型的实现基本类型的实现 基本类型的接口/抽象特征包括该类型的取值范围和适用操作符。int是对数学整数的模拟,int与数学整数的差别在于int不是无限的。作为int的用户,说int类型(逻辑上)拥有32位内存空

4、间时,意味着用户知道了int的值域范围。事实上,JVM规范中规定了int的值域范围,但是并没有定义int类型的内存空间。用于储存int数据的内存空间,由JVM的不同实现者自由设定。boolean类型的接口和实现类型的接口和实现 计算机会做的事情不过是算术操作和逻辑操作。因而布尔表达式在if-else语句、?:操作和循环语句中被广泛使用。 当人们眉飞色舞地讨论Java的布尔表达式、boolean类型时,事实上讨论的是boolean接口。然而Java语言中如此重要和基础的boolean类型,并不实质性地存在。嗯,这个玩笑有点大,JVM规范第2版中有一节,事实上JVM定义了boolean类型,但是没

5、有提供操作boolean的指令。Java 虚拟机规范 (Java SE 7 版)的标题更加严谨一些。 简单地说,boolean类型的实现,JVM定义了boolean这种数据类型,如以Z或 Z分别表示boolean和boolean,也通过newarray 指令直接支持创建boolean。但是对boolean操作转换为int操作,1代表true、0代表false;而boolean视为byte数组操作, 4.1.2 类的接口类的接口 源代码中没有对象,只有引用变量和引用值,关于对象的实现参考7.4.3 堆上的对象。类的接口与实现在第6章封装中详述,这里仅概要地说明。类的接口指外界对象能够访问的、类所

6、定义的接口的集合。通常,类中声明的public、protected域,也作为类接口的一部分。随着客户程序与本类的关系的不同,如在或不在一个包中、是或不是本类的子类,类的接口这一集合对客户类而言会有程度上的不同。类的类的API Parnas原则/接口与实现分离指在模块或方法层面,用户程序员有意识地忽略方法的实现。Parnas原则被推广到类层面类的接口。类的接口常常称为该类的API,这是为了与Java中的接口类型相区别。一般而言private和package-private方法不是类的接口。使用访问修饰符限定类的接口,这一机制称为封装封装2. 类和类型类和类型 type是一个名称,它标识了类的接口

7、。如果一个对象能够接受X类的接口的全部操作请求(方法调用),则称对象具有具有X类型类型。正是因为Java的子类能够满足父类的接口(尽管可以改写),所以子类的对象能够同时具有类层次中的多个类型。类型是类的接口,类是类型类型是类的接口,类是类型+实现。实现。4.1.3 String 字符串(string、常简称串)是计算机上非数值计算的基本对象,是由零个或多个字符组成的有限序列。Java中,字符串被抽象为String。String文字是指向String对象的引用。字符串需要的最小接口集串赋值。使用String文字、String构造器或其他类的某些方法如Object的toString()为引用变量s

8、赋值。串 比 较 。 比 较 串 的 字 典 顺 序 , 由 于 S t r i n g 实 现 了 J a v a 接 口Comparable,因而int compareTo(String s2)以返回的int负数、0、正数表示this与s2的字典顺序。相等比较可以用Override equals(Object)方法。求串长。int length()返回本字符串的长度。串联接。String concat(String str)相当于本字符串+str。求子串。String substring(int begin, int end)返回本字符串的索引为begin, end)的子串,子字符串的长度

9、为 end-begin。如compare.substring(4,7)返回are。如果end的最大取值为length(),这时可以使用重载的substring(int begin)。基于最小接口集 public static void startsWith(String prefix) String s = yqj2065; String s2 = s.substring(0,prefix.length(); System.out.println (s+ +s2+ +s2.equals(prefix) ); String提供了前缀后缀判断、改变大小写,更换字符、去掉无用空白trim()等大量便

10、宜方法。 2. String与基本数据类型与基本数据类型 String与基本数据类型转换由构造器String(byte bytes)或相关构造器,可以将byte 数组转换成String对象。String类的byte getBytes()方法,按消息接受者获得byte数组。 4.2 抽象类抽象类 抽象方法必须属于某一个数据类型。一个类只要包含一个抽象方法,则该类也必须用abstract修饰,即必须将它声明为抽象类抽象类(abstract class)。4.2.1 不能够实例化的类不能够实例化的类抽象类主要的语法特色是不能直接创建抽象类的对象,常常被描述成“不能够实例化的类”。抽象类需要注意的语法

11、现象:有抽象方法的类,必须是abstract class。不能直接创建其对象。子类如果没有实现父类的所有抽象方法,则它也必须是抽象类。抽象类可以没有抽象方法。这种抽象类在程序设计中能够很好的起到过渡或适配器的作用。4.2.2子类的占位符子类的占位符作为数据抽象的抽象类,在客户程序中,它是其各种各样子类的占位符。抽象类提供了一种手段:把它的子类们归结成同一个概念同一个概念,在程序中以一个名词来看待它的所有子类,从而屏蔽屏蔽子类的具体类型。抽象类的作用正是通过多态而体现。4.2.3 接口继承接口继承 Vs. 实现继承实现继承 本节详述实现继承(特化继承)存在的问题,以强调接口继承(协议继承)和抽象

12、方法的重要性。实现继承具有复用父类的代码的光环给初学者一个对象技术非常强大的第一印象,以致于代码复用超过里氏替换原则,本末倒置地成为一些程序员设计继承关系的主要目的。实现继承拥有诸多的风险实现继承拥有诸多的风险 子类的改写能否遵循父类的接口脆弱的基类(the fragile base-class)问题多重继承问题。参见4.3.2多重继承问题1. 复用父类代码的形式复用父类代码的形式 直接继承,如m1()改进语意的改写,如m2()。改写方法中必须调用父类相同签名方法,super.m2()。回顾2.3.5 super与this。替换语意的改写,如m3()。完全以新的实现替换,体现了对父类方法的漠视

13、。空方法的改写,如m4()。没有代码复用的诱惑,一种特定情况下使用的设计技巧。接口继承,如m5()。对抽象方法的继承,无代码可以复用。如果设计子类时,遇到替换语意的改写,就应该自问一下:是父类的设计自作多情还是子类的设计不合理,子类真的是一个是一个(父类的)子类型吗? 2. 改写能否遵循父类的接口改写能否遵循父类的接口 除了不合理的设计外,有时候改写甚至无法做到遵循父类的接口,例如改写改写Object.equals()的困境的困境.值类改写时需要满足Object.equals()的如下契约/条件:同时改写hashCode(),以满足相等的对象有相同的哈希码。非空(nonnullity):逻辑相

14、等建立在两个非空引用上,x.equals(null)必须返回false。如果x为null,null不能够调用任何实例方法,会抛出异常。一致性(consistent):只要用于相等比较的值没有改变,多次调用x.equals(y)其返回值必须不变。自反性(reflexive):x.equals(x) 必须返回true。对称性(symmetric):y.equals(x)与x.equals(y) 必须同真或同假。传递性(transitive):x.equals(y)为true、y.equals(z) 为true,则x.equals(z) 必须为true。Point2D public class Po

15、int2D/二维点 protected int x; protected int y; public Point2D(int x, int y) this.x = x; this.y = y; public Point2D() this(0,0); Override public boolean equals(Object o) if(!(o instanceof Point2D)/隐含 o=null return false; Point2D p = (Point2D)o; return p.x=x & p.y=y; 很容易很容易 if(p1.equals(p2) & p2.equals(p

16、1) System.out.println(ok); else System.out.println(违反对称性); assert p1.equals(p2) & p2.equals(p1) :违反对称性;三维点三维点Point3D或有色点或有色点ColorPoint2D Override public boolean equals(Object o) if(!(o instanceof Point3D)/隐含 o=null return false; Point3D p = (Point3D)o; return p.x=x & p.y=y & p.z=z; 结论结论 :一个可实例化可实例化的

17、父类改写了Object.equals()后,如果子类增加增加一个需要比较的值元素,必然破坏equals(Object)的接口。 应对方案应对方案 :让Dot有一个Point2D,而非是一个Point2D :public class Dot private final Point2D p; private final String anything; public Point2D asPoint2D() return p; Override public boolean equals(Object o) if( !(o instanceof Dot) return false; Dot d =

18、(Dot)o; return d.p.equals(this.p)& d.anything.equals(this.anything);2.脆弱的基类 一个拖儿带女的父类失去了安全演化的自由,一个对于父类的简单变化可能导致整个程序不可用。 子类可能破坏父类的封装性 public class Sup protected int i; protected void setI(int i)this.i = i ; public int getI()return i; public class Sub extends Sup Override public void setI(int i)this.i

19、 = i ; 3.多继承的复杂性 多重继承模型导致额外的复杂性,其中最著名的是钻石问题(The Diamond Problem)或者叫“讨嫌的菱形派生”(Dreadful Diamond on Derivation、DDD)参见4.3.2多重继承问题 4.3 Java接口接口 如果说抽象方法是纯粹的接口、脱离了实现的接口,那么Java接口就是这些纯粹的接口组成的数据抽象。Java接口纯粹是契约的集合,是一种程序设计的表达方式。从数据抽象的角度看,能够在不定义class的同时又可以定义type,将是程序设计中强大而有用的机制。4.3.1 接口与实现类接口与实现类 Java接口(interface

20、)是定义abstract方法和静态命名常量的Java程序单元。public interface A String STR=Hello; / public static final , void foo(); / public abstract int x (int i);2. 接口的实现类接口的实现类 修饰符修饰符 class 类名类名 extends 超类名超类名 implements 接口名接口名 类体 类继承只能单继承 一个子类实现的多个接口 interface extends 4.3.2 多重继承问题多重继承问题 子类型继承多个父类型。Java对于多重继承作出如下规定:类只能继承一个父

21、类,同时一个类可以实现(使用关键字implements)多个接口。一个接口可以继承(使用关键字extends)多个接口。接口能够多继承,并不意味着Java是为了解决多继承问题而引入接口机制,也不意味着接口机制能够解决程序设计对多继承的需求。事实上,有人把Java的内部类誉为多继承问题的彻底解决方案。 1. 对多继承的需求对多继承的需求 假定“武汉的男大学生”简称X是对象x的类型,则X与许多的概念/类之间存在Is-A关系,而且这些父类彼此并没有必然联系,每一个父类仅仅描述X的一个方面 .As-A关系 2. 多继承的复杂性多继承的复杂性 假设“人”有一个方法say(),子类“武汉人”改写后说武汉话

22、,“教师”改写后说普通话。“武汉教师”继承武汉人和教师两个父类后,他们说什么话呢?他们的类体中如何override方法say()? 简单的合并简单的合并 按照WH和Teacher中say()的协议,实现类WHer应该在不同的场合说应该说的话,然而在一般情况下,单一的方法体很难、或者说不可能满足这一需求。从设计的角度看,Java接口放弃了多继承的内在/固有目标,而显得是一个权宜之计。 3. 问题仍然存在问题仍然存在 接口的多继承虽然不会引起语法问题,但是,一般而言它并不能够满足方法在协议/接口上的差别。多继承会出现这样的问题:如果(第三方库中的)两个接口,它们的某个方法的方法名、参数表相同(即方

23、法签名相同)而返回值类型不同(兼容此外),Java中子类就无法同时继承两者,否则将编译报错。在命名常量的继承与隐藏基础上,如果Me无法确定使用的是哪一个NAME,Java采用了回避策略 4.4依赖于抽象类型抽象类和Java接口,合称为抽象类型(不可实例化的类型),可实例化的类称为具体类。本节将说明抽象类和Java接口在程序设计中的重要作用。在代码中要尽可能地使用(依赖)抽象类型,而非具体类。4.4.1 开放封闭原则开放封闭原则Bertrand Meyer(伯特兰梅尔):法国工程院院士,名著作者,对象技术大师Object Oriented Software Construction, Bertr

24、and Meyer,Prentice Hall, 1988, 中文书名面向对象的软件结构面向对象软件构造(最好阅读1997第2版),按契约设计(Design by Contract)发明了Eiffel (埃菲尔铁塔)语言。1. 依赖与需求变化依赖与需求变化 所谓依赖(dependence),是指类A的正常编译、运行都取决于类B的存在,即A依赖于B。BlueJ图示了两种依赖:1、使用关系,客户程序依赖服务类,用带箭头的线连接;2、继承关系,子类依赖父类,用带三角形的线连接。不在一个包中的类,虽然不能够直观的看到但是依赖关系相同。设计设计 “坏味道坏味道” 软件开发的目标是获得有弹性(flexib

25、le)、鲁棒的(robust)、可复用(reuse)的软件。整个软件中,各个类之间必然存在相互依赖。然而,模块之间严重的相互依赖(interdependence),常常使软件的设计发出“坏味道”,如僵硬(Rigidity 刚性、无弹性)程序A的单个变化将引起依赖于A的模块产生级联变化(a cascade of changes、连锁反应),从而整个系统的任一改变都会影响到系统中太多太多其他部分。如何使程序在需求变化面前保持稳定呢 面对需求变更的必然和级联变化的可怕,如何使程序在需求变化面前保持稳定呢?(1)降低模块之间的依赖程度,或者说耦合程度,手段为减少依赖数量和减轻依赖强度(强依赖变为弱依赖

26、),例如模块应该尽可能少地依赖其他模块(迪米特法则)、应该尽可能少地使用继承关系(组合优先原则)等等;(2) 依赖于抽象类型。2. 开放封闭的含义开放封闭的含义 Software entities (classes ,modules,functions,,etc.) should be open for extension,but closed for modification.一个软件实体(类、模块、函数等等)应对扩展开放,应对扩展开放,对修改关闭对修改关闭 。dutch doorOpen vs. closedopen for extension 可扩展(对扩展是开放的)可扩展(对扩展是开放

27、的):模块的行为、系统的功能可以被扩展被扩展,在应对需求改变或需要满足新的应用需求时,我们可以让模块以不同的方式工作。语言上:继承、多态前提:LSPclosed for modification 不可修改(对修改是封闭的)不可修改(对修改是封闭的):模块的源代码是不可改动的。任何人,包括类创建者类创建者,都不许修改模块的源代码。对模块行为的扩展,不会导致模块的源代码变化。理想主义的原则理想主义的原则 一套系统提交后,其中成百上千的类不可能都遵循OCP。(1)稳定部分。大量的类并不会遇到变化。将所有类都设想成不稳定的实体,将导致“过度设计。(2)不可避免修改部分。修改发生在众所周知的地方。例如随

28、后将提到的工厂,任何修改都在工厂内部进行,从而保证客户程序不需要修改。(3) 遵循OCP的部分。对于可预见的、可能出现的扩展模块,通过抽象类型将一个系统的可变因素封装起来,同时使该抽象类型的客户程序以不变应万变。3. 案例:抽象类型案例:抽象类型Openable package OO.principle;public class Hand_bad private Door door; public Hand_bad() door = new Door(); public Hand_bad(Door door) this.door =door; public void open() if (do

29、or=null | door.isOpen(); else door.open(); 如果用手去开门,不管是带锁的房门、浴室的推拉门、店铺的卷帘门,上述三个类都能够在不修改源代码的基础上,通过添加新类的满足新的应用需求。 如果需要手去开关的东西为超出Door的范围? 接口接口Openable 为了达到开放封闭,Hand需要依赖于一个更有预见力或洞察力的(抽象)类型,如设计一个接口Openable。不管是Drawer,手机、书本或者能够开关的任何东西所有的新的应用需求都逃不出Openable类型,需求的变化不会导致系统重新设计。Openable遵循OCP还不是关键,Openable的客户类Han

30、d怎样才能够以不变应万变呢?4.4.2 创建对象的技术创建对象的技术 Hand要以不变应万变,要点在于如何初始化Hand类的成员变量Openable item!public class Hand private Openable item;/如何初始化? / Item不能是null,必须在构造器中创建必要的对象。创建并初始化的对象有创建并初始化的对象有3种技术种技术 new表达式Class. newInstance()简单工厂和创建型设计模式,如工厂模式、工厂方法、抽象工厂等模式 1. 通过通过new表达式创建类的对象表达式创建类的对象 public Hand(int ID) if(ID =

31、0) item = new TV(); if(ID = 1) item = new Drawer(); 缺点: (1)需要知道Openable的所有实现类,而且增添新的实现类必将导致Hand的代码变化,如if(ID = 2) item = new Xxx();(2)即使以接口的引用变量item来引用具体类,Hand已经依赖于TV、Drawer等具体类。不同于图4-7,它将导致Hand与TV、Drawer等具体类建立使用关系。(3)new表达式创建具体类的代码,属于硬编码或魔术数字。 2. 通过通过Class类的类的newInstance()方法方法 public Hand(String typ

32、eName) try Class c = Class.forName(OO.principle.+typeName); this.item = (Openable) c.newInstance(); catch (Exception e)e.printStackTrace();3. 对象工厂对象工厂(object factory) 可以设计一个类OpenableFactory,将Hand(int ID /*,其他参数*/)的代码封装起来,使得Hand解除对那些具体类的依赖。创建型模式(常用的有6种)把对象创建的过程提取出来,设计成一个特定的“创建者”类(例如工厂OpenableFactory),创建者类将创建对象的代码提取出来从而保证Hand类遵循OCP。如果程序员学习过相关的知识(设计模式),那么这种修改发生在“众所周知”的地方。4.4.3 启发式条例启发式条例 这些清规戒律从各种角度说明了抽象类型的重要。在代码中要尽可

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论