- 「BUAA OO Pre」 Pre 2总结回顾概览
- Part 0 前言
- 写作背景
- 定位
- 您可以在这里期望获得
- 您在这里无法期望获得
- 对读者前置知识的期望
- Part 1 Pre 2 task 1
- 题目
- 描述
- 输入/输出格式
- 数据范围与操作限制
- 变量约束
- 操作约束
- 测评方法
- 输入样例
- 输出样例
- 提示
- 值得注意的点
- 题目
- Part 2 Pre 2 task 2
- 题目
- 基本要求
- 描述
- 输入/输出格式
- 数据范围与操作限制
- 变量约束
- 操作约束
- 测评方法
- 输入样例
- 输出样例
- 提示
- 值得注意的点
- 扩展知识
- BigInteger
- 主要的构造方法
- 常用方法
- BigInteger
- 题目
- Part 3 Pre 2 task 3
- 题目
- 基本要求
- 题目描述
- 输入输出
- 数据范围与操作限制
- 变量约束
- 操作约束
- 测评方法
- 输入样例
- 输出样例
- 补充材料
- 值得注意的点
- 扩展知识
ArrayList
和HashMap
基础用法对比- 向上转型、向下转型和方法重写
- 扩展知识
- 题目
- Part 4 Pre 2 task 4
- 题目
- 描述
- 输入/输出格式
- 数据范围与操作限制
- 变量约束
- 操作约束
- 测评方法
- 输入样例
- 输出样例
- 提示
- 值得注意的点
- 扩展知识
Comparable
接口和compareTo
方法HashSet
和HashMap
与hashCode
和equals
- 题目
- Part 5 Pre 2 task 5
- 题目
- 基本要求
- 题目描述
- 输入/输出格式
- 数据范围与操作限制
- 变量约束
- 操作约束
- 测评方法
- 输入样例
- 输出样例
- 提示
- 值得注意的点
- 题目
- Part 6 后记
- Part 0 前言
笔者在完成寒假预习作业Pre2系列任务时遇到了一些挑战并有一些收获和心得,在这里记录和大家分享。
定位基于本篇博客,您可以了解笔者在实现Pre2尤其是迭代开发中的心路历程及踩过的坑。
为了读者的阅读体验,本篇博客将按照task顺序
展开。
为了博客的完整性和顺利展开,对于课程组guidebook中的题目要求部分将做引用,若有侵权请联系即刻删除相关内容。
您可以在这里期望获得- 笔者从做题者的视角的收获
- 笔者踩过的坑
- 部分知识的深层原理
- 完成《程序设计基础》/《C语言程序设计》和《数据结构》课程
- 有一般学生C语言的水平
- 了解Java语言的基本语法,此部分知识可以通过各种在线资源获得
先介绍 pre2 练习的背景故事。
想象你是一个冒险者,现在正在一个新的星球上进行探险,这个过程中你需要通过努力收集各种物品来不断增强自身能力值,在第一个 task 中你需要对第一个基本物品 Bottle 进行建模。
经过之前的预习和上面的练习现在到了实战的时候了:构造一个 Bottle 类,来表示冒险者需要用到的瓶子类,要求 Bottle 类包含属性:ID,名字,价格,容量,和表达瓶子是否装满的标志量。相应的要求完成的程序可以查询瓶子的 ID,名字,价格,容量以及是否装满。
一开始给出一个瓶子。之后,有 7 种操作:(序号即为操作数)
- 查询名字
- 查询价格
- 查询容量
- 查询是否装满
- 更改价格
- 设置是否装满
- 输出瓶子描述字符串
第一行给出 整数 id
(取值范围为0-2147483647
),字符串 name
,长整数 price
、浮点数 capacity
分别表示瓶子ID,名字,价格,容量,以空格分隔。
第二行一个数 \(m\),表示待输入的操作数目。
接下来 \(m\) 行,每行一个操作,输入的操作以{type} {attribute}
的形式来描述,具体内容如下:
{price}
更改价格为 {price}
6
{filled}
更改瓶子装满的状态为 {filled}
7
无
以The bottle's id is {id}, name is {name}, capacity is {capacity}, filled is {filled}.
的形式打印状态。
建议在 Bottle
类中定义 toString
方法,返回描述字符串,在主类的 main
方法中调用 Bottle
对象的 toString
方法来打印。
- 操作数满足 \(1 \leq m \leq 2000\)。
输出数值时,你的输出数值需要和正确数值相等。
假设你的输出值 \(x_{out}\) 和正确数值 \(x_{std}\) 之间的绝对或相对误差小于等于 \(10 ^ {-5}\),则认为是相等的,即满足
\[\dfrac {|x_{std} - x_{out}|}{\max(1, |x_{std}|)} \leq 10^{-5} \]输入样例12345667 water 20 100
8
1
2
3
4
5 30
6 true
7
2
输出样例
water
20
100.0
true
The bottle's id is 12345667, name is water, capacity is 100.0, filled is true.
30
提示
下面简单介绍 pre2 系列任务的迭代形式:
从 Task1 到 Task3 ,逐步引导同学们实现一系列基础的类,并且熟悉类、属性和方法的使用,引导大家向面向对象的思维方式转变。
Task4 涉及方法的重写和复用,并引入异常处理机制,希望同学们可以感性地体会到层次化设计的好处,了解并简单应用异常处理(异常处理在之后也常会用到)。
Task5 涉及接口,需要同学们在之前 Task 的基础上完成更加复杂的操作。如果此时仍然使用原来的编码习惯,会在这个 Task 中遇到巨大困难,而严格按照我们的提示去做的同学会体会到好处。
强烈建议按照从 Task1~Task5 的顺序完成 pre2 的练习,思考如何进行增量迭代和持续重构,而不要在实现每一个 Task 时都完成一份新的代码。
值得注意的点- 读者应当先按照pre1中工具链相关介绍完整正确配置好所有相关配置,特别是IDEA的checkstyle及new file settings等,磨刀不误砍柴工。
- 建议每一个类(class)都新建一个
.java
文件,一方面保证高内聚低耦合,另一方面使得结构层次清晰。 - 站在Pre 2 task 5的视角来看,从这里开始就应当逐渐学习并掌握静态方法和静态变量的书写方式,以在未来更复杂的输入情况中依然可以遵守checkstyle要求的类行数不超过60的要求。
- 时刻记得checkstyle,以保证代码风格符合规范。
- 使用
package
管理当前作业的文件。 commit
到课程仓库的只需要所有.java
文件,不需要其他文件。
Part 2 Pre 2 task 2 题目 基本要求
- 建立冒险者类,且符合封装的要求
- 使用适当的容器管理多个冒险者实例
在这个问题中,你需要管理多个冒险者。初始时,你没有需要管理的冒险者。接下来会有 \(m\) 个操作:
- 加入一个需要管理的冒险者
- 给某个冒险者增加一个瓶子
- 删除某个冒险者的一个瓶子
- 查询某个冒险者所持有瓶子的价格之和
- 查询某个冒险者所持有瓶子价格的最大值
你需要对操作 4、5 进行回答。
输入/输出格式
第一行一个整数 \(m\),表示操作的个数。
接下来的 \(m\) 行,每行一个形如 {type} {attribute}
的操作,操作输入形式及其含义如下:
{adv_id} {name}
加入一个 ID 为 {adv_id}
、名字为 {name}
的冒险者,且未持有任何瓶子
无
2
{adv_id} {bot_id} {name} {price} {capacity}
给 ID 为 {adv_id}
的冒险者增加一个瓶子,瓶子的 ID、名字、价格、容量分别为 {bot_id}
、{name}
、{price}
、{capacity}
,且默认为已装满
无
3
{adv_id} {bot_id}
将 ID 为 {adv_id}
的冒险者的 id 为 {bot_id}
的瓶子删除
无
4
{adv_id}
查询 ID 为 {adv_id}
的冒险者所持有瓶子的价格之和
一个整数,表示瓶子价格之和
5
{adv_id}
查询 ID 为 {adv_id}
的冒险者所持有瓶子价格的最大值
一个整数,表示瓶子价格的最大值
数据范围与操作限制
变量约束
- 操作数满足 \(1 \leq m \leq 2000\)。
- 保证所有冒险者与瓶子的 ID 两两不同。
- 操作 1:不会加入与已有冒险者和瓶子 ID 相同 ID 的新冒险者。
- 操作 2:冒险者 ID 一定存在,且新瓶子的 ID 与当前所有冒险者和瓶子的 ID 均不相同。
- 操作 3:冒险者 ID 一定存在,且冒险者一定持有该 ID 的瓶子。
- 操作 4:冒险者 ID 一定存在,若冒险者不持有任何瓶子,则输出 0。
- 操作 5:冒险者 ID 一定存在,且冒险者一定持有至少一个瓶子。
输出数值时,你的输出数值需要和正确数值相等。
假设你的输出值 \(x_{out}\) 和正确数值 \(x_{std}\) 之间的绝对或相对误差小于等于 \(10 ^ {-5}\),则认为是相等的,即满足
\[\dfrac {|x_{std} - x_{out}|}{\max(1, |x_{std}|)} \leq 10^{-5} \]输入样例7
1 1 Person1
2 1 2 bottle1 10 5
2 1 3 bottle2 15 12
4 1
5 1
3 1 2
4 1
输出样例
25
15
15
提示
建立一个对象的集合,实现向集合中增加对象和访问集合中对象的操作,学习容器的使用和选择。
熟悉对容器的操作,题目中限制了所有对象(冒险者、瓶子)的 ID 不会相同,思考一下,哪种容器会更加适合本次任务?或者说哪些容器呢?
在本次作业中我们有求和操作,尽管我们将输入数据限制在 long 的范围内,但是在求和时可能会超出精度范围。请你查阅 Java 相关资料,来看看在 Java 中是如何解决超过普通数据类型数据范围的精度问题的。
Java 中有些特别的类用于处理大数运算,如 BigInteger
,BigDecimal
。
值得注意的点
-
了解
BigInteger
相关知识并应用,这将在下面的扩展知识进行部分介绍。 -
了解Java中的一种遍历方式如下:
for (Bottle bottle : bottles) { max = Math.max(bottle.getPrice(),max); }
该遍历方式可以便捷遍历如
ArrayList
等容器内的所有对象,优于C语言中按索引遍历的方式,尤其是我们不关心容器内对象的索引的时候。对于各种容器的使用,可以首先参考Java ArrayList - 菜鸟教程,了解ArrayList
基本用法后迁移知识到其他容器如HashMap
等。
扩展知识 BigInteger 主要的构造方法
BigInteger(String val)
使用举例:BigInteger eg = new BigInteger("100");
-
加减乘除:
加法:
BigInteger add(BigInteger val)
减法:
BigInteger subtract(BigInteger val)
乘法:
BigInteger multiply(BigInteger val)
除法:
BigInteger divide(BigInteger val)
-
获得两个
BigInteger
中的最大/最小值最大值:
BigInteger max(BigInteger val)
最小值:
BigInteger min(Biginteger val)
-
获得(长)整型(
long
)的BigInteger
对象static BigInteger valueOf(long value)
Part 3 Pre 2 task 3 题目 基本要求
- 建立类 Equipment,所有的装备均继承自这个类(该类因而可称为基类, base class),请将所有装备都具有的属性定义在这个类里。
- 建立 Bottle 类与 Sword 类,应当满足
- 符合某种继承关系
- 具备信息查询方法
- 实现各项装备的查询和增删指令
具体实现细节将在“题目描述”中展开
题目描述现在我们将在上一个任务的基础上对 Bottle 进行细分,并添加新的“武器类”——Sword
将有以下操作:
- 加入一个冒险者
- 给某个冒险者添加某件装备(装备包括药水和武器)
- 删除某个冒险者拥有的某个装备
- 查询某个冒险者所拥有装备的价格之和
- 查询某个冒险者所拥有装备的价格最大值
- 查询某个冒险者拥有的装备总数
- 打印一个装备的全部属性,属性的输出顺序与输入创建该装备时给定的各参数顺序一致,具体格式详见下方
属性打印方式
第一行一个整数 \(m\),表示操作的个数。
接下来的 \(m\) 行,每行一个形如 {type} {attribute}
的操作,操作输入形式及其含义如下:
{adv_id} {name}
加入一个 ID 为 {adv_id}
、名字为 {name}
的冒险者,且未持有任何装备
无
2
{adv_id} {equipment_type} {vars}(equipment_type和vars的含义见下表)
给予某个人某件装备,装备类型由 {equipment_type}
定义,属性由 {vars}
定义,所有的瓶子初始默认装满
无
3
{adv_id} {equipment_id}
删除 ID 为 {adv_id}
的冒险者的 ID 为 {equipment_id}
的装备
无
4
{adv_id}
查询 ID 为 {adv_id}
的冒险者所持有装备的价格之和
一个整数,表示该冒险者所有装备的价格总和
5
{adv_id}
查询 ID 为 {adv_id}
的冒险者所持有装备价格的最大值
一个整数,表示该冒险者所有装备价格的最大值
6
{adv_id}
查询 ID 为 {adv_id}
的冒险者的装备总数
一个整数,表示该冒险者所有装备的数量之和
7
{adv_id} {equipment_id}
打印 ID 为 {equipment_id}
的装备的全部属性
该装备的全部属性,格式见下文“属性打印方式”
- 操作数满足 \(1 \leq m \leq 2000\)。
- 保证所有冒险者与装备的 ID 两两不同。
- 操作 1:不会加入与已有冒险者和装备 ID 相同 ID 的新冒险者。
- 操作 2:冒险者 ID 一定存在,且新装备的 ID 与当前所有冒险者和装备的 ID 均不相同。
- 操作 3:冒险者 ID 一定存在,且冒险者一定持有该 ID 的装备。
- 操作 4:冒险者 ID 一定存在,若冒险者不持有任何装备,则输出 0。
- 操作 5:冒险者 ID 一定存在,且冒险者一定持有至少一个装备。
- 操作 6:冒险者 ID 一定存在,若冒险者不持有任何装备,则输出 0。
- 操作 7:冒险者 ID 一定存在,且冒险者一定持有该 ID 的装备。
输出数值时,你的输出数值需要和正确数值相等。
假设你的输出值 \(x_{out}\) 和正确数值 \(x_{std}\) 之间的绝对或相对误差小于等于 \(10 ^ {-5}\),则认为是相等的,即满足
\[\dfrac {|x_{std} - x_{out}|}{\max(1, |x_{std}|)} \leq 10^{-5} \]输入样例9
1 1 Person1
1 2 Person2
2 1 1 3 bottle1 10 5
2 1 6 4 sword1 20 7 0.6
2 2 3 5 bottle2 15 3 8
6 1
7 2 5
3 1 3
5 1
输出样例
2
The expBottle's id is 5, name is bottle2, capacity is 3.0, filled is true, expRatio is 8.0.
20
补充材料
-
请思考,本次作业中的求和操作等是否会出现超出数据限制的情况。
提示:Java 中有特别的类:
BigInteger
和BigDecimal
。 -
设计模式是软件开发人员经过相当长的实践总结出来的最佳设计方案,在面向对象设计与构造课程中,你将逐步了解和掌握几种基本的设计模式,包括工厂模式、单例模式、生产者-消费者模式等。
现在,希望大家可以了解工厂模式,这是在继承和接口实现中常用的设计模式。
大家可以参考链接中的介绍,也可以自行查阅资料。这将帮助你更轻松的完成日后的作业