哈工大软件构造课程知识点总结(四)

系列文章目录

哈工大软件构造课程知识点总结(一)
哈工大软件构造课程知识点总结(二)
哈工大软件构造课程知识点总结(三)
哈工大软件构造课程知识点总结(四)
哈工大软件构造课程知识点总结(五)
哈工大软件构造课程知识点总结(六)



简介

此文章是2021春哈工大软件构造课程Chapter 7、Chapter 8的知识点总结。

Chapter 7:Object-Oriented Programming (OOP)

OOP的特点

  • 封装与信息隐藏
  • 继承与重写
  • 多态、子类型、重载
  • 静态与动态分派(不做要求)

变量与方法

变量:静态变量(类成员变量)和实例变量
方法:静态方法(类方法)和实例方法

  • 类成员变量、类方法与类相关联,使用它们不需要创建对象,如Integer.parseInt()
  • 实例变量和实例方法在每个实例化的类中出现一次

接口(Interface)与类(Class)

接口与类用于定义和实现ADT,接口确定ADT的规约,类对ADT进行具体实现。

  • 接口之间可以继承和扩展
  • 一个类可以实现多个接口,从而具备多个接口中的方法
  • 一个接口可以有多种实现类
  • 接口中不应提供构造函数(不可被实例化),但允许静态方法和default方法实现
  • 类中必须实现所继承接口的所有方法,允许添加新的方法(实现多于接口)

注:

  1. 可以不需要接口直接使用类作为ADT,既有ADT定义也有ADT实现,但实际应用中更倾向于使用接口来定义变量
  2. 从Java 8开始允许接口中包含静态方法
  3. 关于接口中的default方法可查看此博文:java接口中的default方法

继承(Inheritance)和重写(Overriding)

重写

严格继承:子类只能添加新方法,无法重写父类中的方法(父类方法使用final修饰符)
可重写方法:父类方法未使用final修饰符,子类可重写,但重写不应改变原方法的本意

  • 如果父类型中某个函数实现体为空,意味着其所有子类型都需要这个功能,但各有差异,没有共性,在每个子类中均需要重写
  • 如果父类型中的被重写函数体不为空,意味着对其大多数子类型来说,该方法是可以被直接复用的

严格继承与可重写方法

抽象类(Abstract Class)与抽象方法(Abstract Method)

抽象方法:有规约但无具体实现的方法,使用abstract关键字定义
抽象类:含有至少一个抽象方法的类
接口:一个只有抽象方法的类
抽象类示例

多态、子类型、重载

多态的三种类型

多态类型
一个方法可处理多种不同数据类型且表现可能没有关联——特殊多态,如下例中的add()方法:

public class OverloadExample {
	public static void main(String args[]) {
		System.out.println(add("C","D"));
		System.out.println(add("C","D","E"));
		System.out.println(add(2,3));
	}
	public static String add(String c, String d) {
		return c.concat(d);
	}
	public static String add(String c, String d, String e){
		return c.concat(d).concat(e);
	}
	public static int add(int a, int b) {
		return a+b;
	}
}

重载

多个方法具有相同的名字,但有不同的参数列表或返回值类型。可以方便客户端调用,客户端可用不同的参数列表调用同样的参数。

重载的规则:

  • 参数列表必须不同
  • 返回值类型可相同,也可不同
  • 访问修饰符(publicprivateprotected)可相同也可不同
  • 可声明新的或更广范围的异常
  • 可以在同一个类内重载,也可在子类中重载

重载是一种静态多态,根据参数列表进行最佳匹配,支持静态类型检查,且在编译阶段决定具体执行哪个方法;与此相反,重写在运行时进行动态检查。

例: 子类中重载父类方法

class Animal {
	public void eat() {}
}

class Horse extends Animal {
	public void eat(String food) {}
}

子类中重载父类方法

重写与重载的对比

重载重写
参数列表必须不同必须相同
返回值类型可以修改可以改变,但必须是父类的派生
异常可以修改可以减少或消除,不能抛出新的或更广泛的异常
访问修饰符可以修改不能加强限制
调用编译时处理,静态检查实例的类型决定选中哪个方法,运行时处理

关于重写的返回值类型问题:
Chapter 7课件上写为“Must not change”应该是有问题,下附2018年试卷选择题第9题:
LSP&Override
根据LSP原则和实际编写测试,此题应选B。
由于DoubleNumber的子类型,认为返回值可以是父类的派生。

参数化多态与泛型编程

使用<>来声明类型变量,如下例:

List<Integer> ints = new ArrayList<Integer>;
public interface List<E>;
public class Entry<KeyType, ValueType>

通配符:只在使用泛型时出现,不能在定义中出现,如:

List<?> list = new ArrayList<String>();
List<? extends Animal> ...
List<? super Animal> ...

运行时泛型消失,成为具体类型。

子类型多态

子类型:“B是A的子类型”意味着每一个B都是A,从规约的角度说是“每个B都满足A的规约”。
子类型的规约不能弱化超类型的规约!

子类型多态:不同类型的对象可以统一的处理而无需区分——隔离”变化“。

使用instanceof可以检查一个对象在运行时的实际类型,但应尽量避免使用(运行时检查),使用时不应用于检查超类型是否是子类型(类型转换问题)。

建议避免向下做类型转换(父类型->子类型)!

Java中Object对象的三个重要方法

  • equals() - 假如两个对象“相等”则返回真
  • hashCode() - 用于HashMap(区分元素,散列存放)
  • toString() - 提供对象的可打印字符串表示

例: 重写这三种方法
equals()重写
hashCode()重写toString()重写
注:重写hashCode()方法时可考虑使用arrays.hashCode()方法,

如何设计好的类

尽量设计为immutable,优点为简洁、线程安全、自由分享、无需防御式拷贝、模块化。immutable类的编写原则如下:

  • 不提供mutator方法
  • 确保方法不能被重写
  • 设置所有属性为private final
  • 保证所有可变属性的安全(避免表示泄露)
  • 实现toString()hashCode()equals()clone()等方法

Chapter 8:Equality in ADT and OOP

等价关系

等价关系包括自反、对称、传递三方面,对应Java中的二元运算符==equals()方法。

不可变类型的等价性

  • 使用AF(Abstraction function)来定义,若AF将二者映射到相同的结果,则此二者等价
  • 站在外部观察者角度,对两个对象调用任何相同的操作,均得到相同结果,则二者等价,反之亦然

“==”与“equals()”对比

二元运算符 ==方法 equals()
比较引用(内存空间地址)比较对象的内容(属性)
引用等价性对象等价性
适用于基本数据类型适用于对象类型

在Object中实现的缺省equals()判断的是引用等价性,通常不是程序员所期望的。
故在自定义ADT时,需要重写Object的equals()方法实现对象等价性比较。

equals()方法重写需注意:

  • 使用@Override通知编译器检查是否能找到被重写的父类方法,避免将重写实现成重载
  • 传入的参数应为Object类型
  • 保证等价性,尽量避免出现类似于比较浮点数相等的两数之差小于特定值(违反传递性)的equals()实现

一个正确的示例如下:

@Override
public boolean equals(Object thatObject) {
	if (!thatObject instanceof Duration))
		return false;
	Duration t = (Duration) thatObject;
	return this.getLength() == thatDuration.getLength();
}

关于hashCode()方法

  • 等价的对象必须有相同的hashCode
  • 不相等的对象,也可以映射为同样的hashCode,但性能会变差
  • 在Object中实现的缺省hashCode()方法返回的是对象的内存地址
  • 自定义的ADT应该重写hashCode()方法,除非能保证此ADT不被放到Hash类型的集合类中(一般来说不能保证)

关于clone()方法

创建并返回一个当前对象的拷贝,主要的目标是:对于任意对象x,有:

  • x.clone() != x - 内存位置不同(体现拷贝)
  • x.clone().getClass() == x.getClass() - 两个对象的类信息相同
  • x.clone().equals(x) - 两个对象等价

可变类型的等价性

  • 观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
  • 行为等价性:调用对象的任何方法都展示出一致的结果

对可变类型来说,人们往往倾向于实现严格的观察等价性,但在有些时候,观察等价性可能导致bug,甚至可能破坏RI。例如:如果某个mutable的对象包含在Set集合类中,当其发生改变后,集合类的行为不确定。

可变类型等价设计原则:

  • 实现行为等价性即可
  • 无需重写equals()hashCode()(与不可变类型要求相反)
  • 如果要判断两个可变对象是否看起来一致,最好定义一个新方法

自动装箱及其等价

自动打包:Java自动将基本数据类型转换为包装器类型(int->Integerlong->Long等)

具体例子如下图:
自动打包
此例最终结果为false,但对于[-128, 127]范围内的数为true

有关自动装箱、自动拆箱以及具体原理说明可参考此博文:
java进阶–深入理解Java自动装箱拆箱机制(Autoboxing and unboxing)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值