《Java黑皮书基础篇第10版》 第13章【笔记】

文章详细介绍了Java编程中的抽象类和接口的概念及用法,包括抽象方法的作用、抽象类的特点(如不能实例化,可以有字段和构造器)以及子类对抽象方法的实现要求。同时,讨论了Comparable接口用于对象比较,Cloneable接口标记类支持克隆,以及类设计的原则,如内聚性、一致性、封装性和清晰性等。

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

第十三章 抽象类和接口

13.1 引言
13.2 抽象类
13.2.1 为什么要使用抽象方法

在编译(compilation)期,一个声明为Person p = new Student()的Person类变量p可以通过多态,使用Person类的方法p.getName()来获取子类Student的名字

使用多态时,首先要保证p的Person类中有getName()方法,子类Student中有正确覆写getName()的方法,然后才会根据p的实际类型去调用自己的Student复写方法

可以看出,Person类的getName()方法没有被执行,但这个方法也是需要显示的编写的。因此,我们可以仅仅把Person类的getName()方法定义成一种规范,而方法内部没有必要执行

13.2.2 关于抽象类的知识点

抽象类不可以通过new操作符创建实例,但是可以声明为一种数据类型,例如

// 抽象类GeometricObject
GeometricObject[] objects = new GeometricObject[10];
objects[0] = new Circle();

抽象类可以有字段、(建议protected)构造方法和(必须写{}括号)普通方法

一个包含抽象方法的类必须声明为抽象类,但一个抽象类可以没有抽象方法

抽象类中,抽象方法不能有方法体,普通方法必须有方法体

所有的抽象方法必须是非静态的

如果一个非抽象子类继承了抽象类,那么这个子类必须执行所有的抽象方法,相当于抽象类定义了规范,强迫子类去执行

子抽象类可以继承父抽象类,子抽象类也可以继承一个常规父类

子类可以覆写父类的方法并将它定义为abstract

13.3示例学习

Number类是数值包装类、Biglnteger以及BigDecimal的抽象父类

13.4 示例学习

Calendar类是GregorianCalendar类的抽象父类

13.5 接口
理解:Java的接口其实是单继承逻辑下,实现多继承的解决方案

在接口中声明的方法必须是public abstract的,声明的常量必须是public static final的,因此在实现接口方法时,必须使用public访问修饰符进行修饰

13.6 Comparable接口
public int compareTo(T o);

Java提供了一种接口Comparable,内部实现了compareTo方法,用来比较两个相同类型的对象中的较大者

13.7 Cloneable接口
protected native Object clone() throws CloneNotSupportedException;

Cloneable接口是标记接口,没有常量也没有方法,实现Cloneable接口仅仅是为了告诉开发人员该类可以进行克隆操作,并不会强制要求实现克隆

clone()方法是定义在Object类中的方法,返回一个Object类型的引用,用于复制一个对象。

Object中的clone方法声明了一个CloneNotSupportedException异常,异常的抛出会通过native本地方法进行处理,包括调用该方法的对象有没有实现Cloneable接口,或者在子类中有没有正确覆写clone()方法,如果没有,会导致抛出异常。因此,如果一个对象想要执行clone方法,那么这个对象所在的类必须同时实现Cloneable接口并正确覆写 clone()

由于 CloneNotSupportedException 异常是从本地方法native抛出的,并不是在Java代码中真正的抛出,所以在实际调用 clone() 方法时,并不需要显式捕获该异常,因为它不会在Java代码中实际抛出。这种设计是为了保持与 Object 类的规范一致。

在浅拷贝中,如果对象的字段是基本类型,复制这个字段的值到一个新的地址,如果对象的字段是引用类型,新旧字段共享一个内存地址

在深拷贝中,所有字段彼此独立,分配在不同的内存地址

13.8 接口与抽象类

抽象类和接口都不可以通过new操作符创建实例,但是都可以声明为一种数据类型

在抽象类和接口的抽象方法中,都不能有方法体,而且必须是非静态的

抽象类实现接口不会强制覆写方法,普通类继承了抽象类继承了接口,那么就要实现接口中的抽象方法。

抽象类接口
字段可以不可以
构造方法可以不可以
普通方法可以不可以
常量可以可以
抽象方法可以可以
父抽象类父接口父普通类
子普通类->只能继承1个父抽象类实现多个父接口只能继承1个父普通类
子抽象类->只能继承1个父抽象类实现多个父接口只能继承1个父普通类
子接口->不能继承父抽象类实现多个父接口不能继承父普通类
13.9 示例学习:Rational 类
13.10 类的设计原则
13.10.1 内聚性

类应该只描述一个单一的实体。例如,学生和教职员工应该分成2个类

如果一个实体担负太多的职责,就应该按各自的职责分成几个类。例如,String类处理不可变字符串,StringBuilder类创建可变字符串

13.10.2 一致性

在类中,通常的风格是将数据声明置于构造方法之前,并且将构造方法置于方法之前

给类似的操作选择相同的名字是一个良好的实践,例如length()方法可以应用于String、StringBuilder和StringBuffer,如果每个类中length()都有不同的叫法,就会让人混乱

一般来说,应该具有一致性地提供一个公共无参构造方法,用于构建默认实例

13.10.3 封装性

类应该使用 private 修饰符隐藏其数据

只在希望数据域可读的情况下,才提供 get 方法;也只在希望数据域可更新的情况下, 才提供 set 方法

13.10.4 清晰性

用户可以以各种不同组合 、顺序,以及在各种环境中结合使用多个类。例如: Loan 类包含属性 loanAmount 、numberOfYears 和 annualInterestRate, 这些属性的值可以按任何顺序来设置

方法应在不产生混淆的情况下进行直观定义。例如: String 类中的 substring(int beginlndex, int endlndex)方法就有一点混乱。这个方法返回从 beginlndex到endlndex-1 而不是 endlndex 的子串

不应该声明一个来自其他数据域的数据域。例如,Person 类有两个数据域: birthDate 和 age。由于 age 可以从 birthDate 导出,所以 age 不应该声明为数据域

13.10.5 完整性

类是为许多不同用户的使用而设计的。为了能在一个广泛的应用中使用,一个类应该通过属性和方法提供多种方案以适应用户的不同需求。例如: 为满足不同的应用需求,String 类包含了 40 多种很实用的方法

13.10.6 实例和静态

要正确的区分实例和静态数据域/方法,不要将本应该声明为静态方法的方法声明为实例方法

不要从构造方法中传入参数来初始化静态数据域。最好使用 set 方法改变静态数据域

13.10.7 继承与聚合

苹果是一种水果;因此,可以使用继承来对 Apple 类和 Fruit 类之间的关系进行建模。人具有名字;因此,可以使用聚合来对 Person 类和 Name 类之间的关系建模

13.10.8 接口和抽象类

通常,比较强的is-a(是一种)关系清晰地描述了父子关系,应该采用类来建模。例如,因为桔子是一种水果,它们的关系就应该采用类的继承关系来建模。

弱的is-a关系,也称为is-kind-of(是一类)关系,表明一个对象拥有某种属性。弱的is-a 关系可以使用接口建模。例如,所有的字符串都是可以比较的,因此 String 类实现了 Comparable 接口。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值