软件构造第三章3.42

输入变量
使用菱形运算符<>来帮助声明类型变量。 例如: - List ints = new ArrayList (); - 公共接口List - 公共类Entry <KeyType,ValueType>
3.4面向对象编程(OOP)
公共类PapersJar {
private List itemList = new ArrayList <>();
public void add(T item){itemList.add(item); }
public T get(int index){return(T)itemList.get(index); }
public static void main(String args []){PapersJar papersStr = new PapersJar <>(); papersStr.add( “狮子”); String str =(String)papersStr.get(0);的System.out.println(STR);
PapersJar papersInt = new PapersJar(); papersInt.add(new Integer(100));整数integerObj =(整数)papersInt.get(0);的System.out.println(integerObj);
}
}
3.4面向对象编程(OOP)

公共课对 {私人决赛E第一,第二;公共对(E优先,E秒){this.first = first; this.second = second; public E first(){return first; public E second(){return second; }}
客户:
Pair p = new Pair <>(“Hello”,“world”); String result = p.first();
3.4面向对象编程(OOP)
另一个例子:Java Set
Set是某些其他类型E的有限元素集的ADT。
Set是泛型类型的一个示例:一种类型,其规范是根据稍后要填充的占位符类型。 我们设计并实现了一个Set ,而不是为Set ,Set 等编写单独的规范和实现。
3.4面向对象编程(OOP)
另一个例子:Java Set
创作者
观察员
变异者
3.4面向对象编程(OOP)
通用接口
假设我们要实现通用的Set 接口。 - 方式1:通用接口,非通用实现:为特定类型E实现Set 。
3.4面向对象编程(OOP)
通用接口
方式2:通用接口,通用实现。 - 我们还可以实现通用的Set 接口,而无需选择E的类型。 - 在这种情况下,我们将代码盲目地写入客户为E选择的实际类型。 - Java的HashSet为Set执行此操作。
一些Java泛型细节
可以有多个类型参数 - 例如,Map <E,F>,Map <String,Integer>通配符 - 例如列表<?>或列表<?扩展Animal>或List <? super Animal>泛型是类型不变的 - ArrayList 是List 的子类型 - List 不是List 的子类型 - List 是List <?的子类型extends Object> - List 是List <?的子类型? super String>通用类型信息被删除(即仅编译时) - 不能使用instanceof()来检查泛型类型无法创建通用数组 - Pair [] foo = new Pair [42]; //将无法编译
软件构建
(4)亚型多态性
3.4面向对象编程(OOP)
继承和子类型
继承代码重用 - 只编写一次代码 - 子类中隐式可用的超类特性子类型用于多态 - 以相同的方式访问对象,但获得不同的行为 - 子类型可替代超类型
接口定义客户的期望/承诺一个类满足接口的期望 - 抽象类是一个方便的混合 - 一个子类专门化一个类的实现
A类延伸B
A类实现了我
3.4面向对象编程(OOP)
亚型
类型是一组值。 - Java List类型由接口定义。如果我们考虑所有可能的List值,它们都不是List对象:我们无法创建接口的实例。 - 相反,这些值都是ArrayList对象,或LinkedList对象,或实现List的另一个类的对象。 子类型只是超类型的子集 - ArrayList和LinkedList是List的子类型。
3.4面向对象编程(OOP)
亚型
“B是A的子类型”意味着“每个B都是A”.在规范方面:“每个B满足A的规范。” - 如果B的规范至少同样强,则B只是A的子类型作为A的规格。 - 当我们声明一个实现接口的类时,Java编译器会自动执行此要求的一部分:它确保A中的每个方法都出现在B中,并具有兼容的类型签名。 - 如果没有实现A中声明的所有方法,B类就无法实现接口A.
3.4面向对象编程(OOP)
静态检查子类型
但编译器无法检查我们是否以其他方式削弱了规范: - 强化方法的某些输入的前提条件 - 弱化后置条件 - 弱化接口抽象类型向客户端通告的保证。 如果在Java中声明子类型(例如,实现接口),则必须确保子类型的规范至少与超类型一样强。
3.4面向对象编程(OOP)
现实世界的变化
两种类型的“银行账户”
3.4面向对象编程(OOP)帐户类型层次结构的接口继承
3.4面向对象的编程(OOP)代码重用的实现继承抽象类缺少一个或多个方法的实现
受保护的元素在子类中可见
抽象方法留在子类中实现
无需定义getBalance(),代码继承自AbstractAccount
亚型多态性
客户端代码可以统一处理不同类型的对象每个对象的行为都根据其类型(例如,如果添加新类型的帐户,客户端代码不会更改)
3.4面向对象的编程(OOP)继承和子类型:层次结构的一瞥
JavaCollections API
继承/子类型的好处:重用代码,建模灵活性在Java中:每个类只能直接扩展一个父类;一个类可以实现多个接口。
3.4面向对象编程(OOP)
类型铸造
有时您需要的是与您不同的类型
双pi = 3.14; int indianaPi =(int)pi; 如果您知道自己有更具体的子类型,则非常有用:
帐户帐户= …; CheckingAccount checkingAcct =(CheckingAccount)acct; long fee = checkingAcct.getFee(); 但如果类型不兼容,它将获得ClassCastException建议: - 避免向下转换类型 - 从不(?)在超类中向下转换为子类
3.4面向对象编程(OOP)
的instanceof
测试对象是否属于给定类的运算符
建议:尽可能避免使用instanceof(),并且永远不要(?)在超类中使用instanceof()来检查类型是否与子类有关。
软件构建
9动态调度
3.4面向对象编程(OOP)
动态调度
动态调度是选择在运行时调用多态操作(方法或函数)的实现的过程。 - 面向对象的系统将问题建模为一组交互对象,这些交互对象执行由名称引用的操作。 - 多态性是这样一种现象,其中有些可互换的对象每个都暴露出同名但可能在行为上不同的操作。
确定在运行时调用哪个方法,即调用重写或多态方法在运行时解决
3.4面向对象编程(OOP)
动态调度
例如,File对象和Database对象都有一个StoreRecord方法,可用于将人事记录写入存储。他们的实施不同。 程序包含对象的引用,该对象可以是File对象或Database对象。它可能是由运行时设置决定的,在这个阶段,程序可能不知道或不关心哪个。 当程序在对象上调用StoreRecord时,需要确定哪些行为被执行。 程序将StoreRecordmessage发送到未知类型的对象,将其留给运行时支持系统将消息分派给正确的对象。该对象实现它实现的任何行为。
3.4面向对象编程(OOP)
动态调度
dividend.divide(除数)#dividend / divisor
这被认为是将一个名为divide的消息与参数除数一起发送给被除数。 仅根据股息类型(可能是理性,浮点,矩阵)选择实现,而不考虑除数的类型或值。
3.4面向对象编程(OOP)
动态调度
动态调度与静态调度形成对比,静态调度在编译时选择多态操作的实现。 - 动态分派的目的是支持在编译时无法确定多态操作的适当实现的情况,因为它取决于操作的一个或多个实际参数的运行时类型。 动态分派与后期绑定(也称为动态绑定)不同。 - 选择操作时,绑定会将名称与操作相关联。 - 在确定名称引用的操作后,Dispatching会为操作选择一个实现。使用动态分派时,名称可能在编译时绑定到多态操作,但是直到运行时才会选择实现。虽然动态调度并不意味着后期绑定,但后期绑定确实意味着动态调度,因为绑定是决定可用调度集的因素。
重载方法使用静态绑定绑定,而重写方法在运行时使用动态绑定绑定。
3.4面向对象编程(OOP)
动态方法调度
1.(编译时间)确定要查看的类2.(编译时)确定要执行的方法签名 - 查找所有可访问的,适用的方法 - 选择最具体的匹配方法3.(运行时)确定接收器的动态类4 。(运行时)从动态类中,找到要调用的方法 - 查找在步骤2中找到的具有相同签名的方法 - 否则在超类中搜索等。
3.4面向对象编程(OOP)

class Game {public void type(){System.out.println(“室内和室外”); }}
Class Cricket扩展了Game {public void type(){System.out.println(“户外游戏”); }
public static void main(String [] args){Game gm = new Game(); Cricket ck = new Cricket(); gm.type(); ck.type(); GM = CK; // gm指的是Cricket对象gm.type(); //调用Cricket的类型版本}}
10授权和组成
3.4面向对象编程(OOP)
排序示例
版本A:
版本B:
3.4面向对象编程(OOP)
代表团
委托只是当一个对象依赖于另一个对象的某个功能子集(一个实体将某些东西传递给另一个实体)时 - 例如在这里,分拣机将功能委托给某些比较器明智的委托使代码重用 - 分拣机可以重复使用任意排序顺序 - 比较器可以与需要比较整数的任意客户端代码一起重复使用显式委托:将发送对象传递给接收对象隐式委托:由该语言的成员查找规则
委托可以被描述为在实体之间共享代码和数据的低级机制。
3.4面向对象编程(OOP)
一个简单的委托示例
3.4面向对象编程(OOP)
一个简单的委托示例
切换到另一个转发器
3.4面向对象编程(OOP)
代表团
委托模式是用于实现委派的软件设计模式,尽管该术语也用于咨询或转发。 委托依赖于动态绑定,因为它要求给定的方法调用可以在运行时调用不同的代码段。 进程 - Receiver对象将操作委托给Delegate对象 - Receiver对象确保客户端不会滥用Delegate对象。
委托客户接收器代表呼叫
3.4面向对象的编程(OOP)使用委托来扩展功能
考虑java.util.List
假设我们需要一个将其操作记录到控制台的列表… - LoggingList由List组成,并将该(不记录)功能委托给该List。
3.4面向对象编程(OOP)
转发
转发:使用对象的成员(属性或方法)导致实际使用不同对象的相应成员:将使用转发到另一个对象。 转发对象通常称为wrapperobject,显式转发成员称为包装函数。
转发和委托之间的区别在于通过包装器调用时自包含中的self参数的绑定。 - 使用委托,self参数绑定到包装器,转发它绑定到包装。 - 转发是一种自动重发信息的形式;委托是一种继承形式,在运行时绑定父(超类),而不是在编译/链接时与’正常’继承绑定。
3.4面向对象编程(OOP)
委派与继承
继承:通过新操作扩展Base类或覆盖操作。 委派:捕获操作并将其发送到另一个对象。 许多设计模式使用继承和委派的组合。
+添加()+删除()
名单

  • Push()+ Pop()+ Top()
  • Push()+ Pop()+ Top()

    添加()删除()
    名单
    3.4面向对象编程(OOP)
    委派与继承
    调用b.foo()会导致什么?
    3.4面向对象编程(OOP)委派:关联和依赖
    关联:对象类之间的持久关系,允许一个对象实例使另一个对象实例代表它执行操作。 - 这种关系是结构性的,因为它指定一种对象连接到另一种对象而不代表行为。 依赖关系:对象需要其他对象(供应商)实现的临时关系。
    3.4面向对象编程(OOP)委派:组合和聚合
    组合是一种将简单对象或数据类型组合成更复杂对象的方法。 - 复合类型的物体(例如汽车)“具有”更简单类型的物体(例如轮子)。 - 实现组合使得对象包含另一个对象。 - 特殊组合:聚合在组合中,当拥有对象被销毁时,包含的对象也是如此。在汇总中,这不一定是真的。 - 一所大学拥有各个部门,每个部门都有一些教授。如果大学关闭,部门将不复存在,但这些部门的教授将继续存在。 - 大学可以被视为一个部门的组合,而部门则有教授的集合。教授可以在多个部门工作,但是一个部门不能成为一个以上大学的一部分。
    对象之间的五种关系类型
    授权 - 关联 - 依赖 - 组合 - 关联继承
    软件构建
    11 Java中的一些重要的Object方法
    3.4面向对象编程(OOP)
    重写Object方法
    equals() - 如果两个对象“相等”则为truehashCode() - 用于哈希映射的哈希码toString() - 可打印的字符串表示
    toString() - 丑陋且无法提供信息 - 你知道你的对象是什么让你可以做得更好 - 除非你知道将不会被调用,否则总是覆盖equals&hashCode - identity semantics - 如果你想要值语义,你必须覆盖 - 否则别
    3.4面向对象编程(OOP)
    覆盖toString()
    3.4面向对象编程(OOP)
    等于覆盖示例
    3.4面向对象编程(OOP)
    hashCode覆盖示例
    3.4面向对象编程(OOP)替代hashCode覆盖
    效率低下,但同样好!
    3.4面向对象编程(OOP)
    这打印什么?
    名称覆盖hashCode但不等于!因此,两个Name实例是不相等的。
    3.4面向对象编程(OOP)
    你是如何解决的?
    用重写的equals方法替换重载的equals方法。
    软件构建
    12召回:可变和不可变类(以及防御性复制)
    3.4面向对象编程(OOP)
    一个类的不变量
    不可变类:无法修改其实例的类 - 示例:String,Integer,BigInteger - 如何,为什么以及何时使用它们
    3.4面向对象编程(OOP)作为一种不变量的不变性
    我们如何保证这些Tweet对象是不可变的 - 一旦创建了推文,其作者,消息和日期永远不会被更改?
    这是可变的…
    不可变性的第一个威胁来自于客户可以 - 实际上必须 - 直接访问其字段。所以没有什么能阻止我们编写这样的代码:这段代码有什么影响?
    这是表示暴露的一个简单示例,这意味着类外部的代码可以直接修改表示。 像这样的代理曝光不仅威胁不变量,而且威胁代表性独立性。 我们无法在不影响直接访问这些字段的所有客户端的情况下更改Tweet的实现。
    3.4面向对象编程(OOP)
    使其不可变…
    私有和公共关键字指示哪些字段和方法只能在类中访问,哪些可以从类外部访问。 final关键字还有助于保证在构造对象后不会重新分配此不可变类型的字段。
    3.4面向对象编程(OOP)
    这个怎么样 …
    这段代码有什么影响?
    retweetLater()接受一条推文,应该返回另一条带有相同消息的推文(称为转推),但一小时后发送。 retweetLater()方法可能是系统的一部分,它自动回应Twitter名人所说的有趣的事情。
    3.4面向对象编程(OOP)
    有什么问题?
    这里有什么问题? - getTimestamp调用返回对tweet t引用的同一Date对象的引用。 t.timestamp和d是同一可变对象的别名。 - 因此,当d.setHours()突变该日期对象时,这也会影响t中的日期,如快照图所示。 推文的不变性不变量已被打破。 问题在于Tweet泄露了对其不可变性所依赖的可变对象的引用。 我们以这样的方式暴露了代表,即Tweet不再能保证其对象是不可变的。 完全合理的客户端代码创建了一个微妙的错误。
    3.4面向对象编程(OOP)如何解决? —防御性复制
    我们可以通过使用防御性复制来修补这种代表性暴露:制作可变对象的副本以避免泄露对代表的引用。
    防御性复制是防御性编程的一种方法 - 假设客户将试图破坏不变量 - 实际上可能是真的(恶意黑客),但更可能是,诚实的错误 - 确保类不变量在任何输入中存活,以最小化可变性
  • 与第8章robustness联系起来
    3.4面向对象编程(OOP)
    复制和克隆()
    可变类型通常有一个复制构造函数,允许您创建一个复制现有实例值的新实例。 - 在这种情况下,Date的复制构造函数使用自1970年1月1日以来以毫秒为单位的时间戳值。 - 另一个例子,StringBuilder的复制构造函数采用String。 复制可变对象的另一种方法是clone(),某些类型支持但不是全部支持。
    3.4面向对象编程(OOP)
    仍然代表曝光…
    此代码的副作用是什么?
    Tweet的构造函数保存传入的引用,因此所有24个Tweet对象都以相同的时间结束。
    3.4面向对象编程(OOP)如何解决? —再次,防御性复制
    通常,您应该仔细检查所有ADT操作的参数类型和返回类型。如果任何类型是可变的,请确保您的实现不返回对其表示的直接引用。 这样做可以创造代表曝光。
    3.4面向对象编程(OOP)将责任交给您的客户?
    您可能会反对这看起来很浪费。为什么要制作所有这些日期副本?为什么我们不能通过精心编写的规范来解决这个问题呢?
    是的,它有效,但是你推理程序的能力以及你避免错误的能力是巨大的。 在没有令人信服的相反论据的情况下,抽象数据类型保证其自身的不变量几乎总是值得的,并且防止代理暴露对此至关重要。
    3.4面向对象编程(OOP)关于可变Date的另一个例子
    这段代码怎么样?
    3.4面向对象编程(OOP)
    怎么解决?
    通过防御性复制
    这段代码怎么样?
    解决它?
    3.4面向对象编程(OOP)要使用不可变类型,最好!
    更好的解决方案是选择不可变类型。 如果我们使用了一个不可变的日期对象,比如java.time.ZonedDateTime,而不是可变的java.util.Date,那么这些潜在的错误就会消失,并且不会再进一步​​暴露。
    围绕可变数据类型的不可变包装
    Java集合类提供了一个有趣的折衷方案:不可变包装器。 -  Collections.unmodifiableList()接受一个(可变的)List并用一个看起来像List的对象包装它,但是它的mutator被禁用 -  set(),add(),remove()等抛出异常。因此,您可以使用mutator构建一个列表,然后将其密封在一个不可修改的包装器中(并抛弃您对原始可变列表的引用,如Mutability&Immutability中所述),并获得一个不可变列表。 这里的缺点是你在运行时获得了不变性,但在编译时却没有。如果您尝试sort()这个不可修改的列表,Java将不会在编译时警告您。您将在运行时获得异常。但这仍然比没有好,所以使用不可修改的列表,地图和集合可以是降低错误风险的一种非常好的方法。
    3.4面向对象编程(OOP)
    摘要
    不要将可变参数合并到对象中;制作防御性副本返回可变字段的防御性副本…  - 返回新实例而不是修改或返回可变字段的不可修改视图实际课程 - 使用不可变组件,以消除防御性复制的需要
    3.4面向对象编程(OOP)如何编写不可变类
    不要提供任何变更器确保不会覆盖任何方法使所有字段成为最终使所有字段保密“确保任何可变组件的安全性
    3.4面向对象编程(OOP)
    不可变类的例子
    3.4面向对象编程(OOP)
    不可变类的例子
    3.4面向对象编程(OOP)不可变类的优点
    简单性固有的线程安全可以自由共享不需要防御性副本优秀的构建块
    3.4面向对象编程(OOP)何时使类不可变
    总是,除非有充分的理由不总是让小的“价值类”不变! - 示例:颜色,电话号码,单位 - 日期和点都是错误! - 专家经常使用long而不是Date
    3.4面向对象编程(OOP)何时使类可变
    类表示状态发生变化的实体 - 真实世界 -  BankAccount,TrafficLight  - 抽象 - 迭代器,匹配器,集合 - 进程类 - 线程,计时器如果类必须是可变的,最小化可变性 - 构造函数应该完全初始化实例 - 避免重新初始化方法
    软件构建
    13 OOP的历史
    3.4面向对象编程(OOP)仿真和面向对象编程的起源
    20世纪60年代:Simula 67是由挪威计算中心的Kristin Nygaard和Ole-Johan Dahl开发的第一个面向对象的语言,用于支持离散事件模拟。 (类,对象,继承等)术语“面向对象编程(OOP)”最初由Xerox PARC用于他们的Smalltalk语言。 1980年代:OOP变得突出,其中的主要因素是C ++。 NiklausWirth用于模块化编程和数据抽象,使用Oberon和Modula-2; Eiffel和Java
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值