目录




题目及要求

  1. 代码运行在命令行中,路径要体现学号信息,IDEA中,伪代码要体现个人学号信息;
  2. 参见Bag的UML图,用Java继承BagInterface实现泛型类Bag,并对方法进行单元测试(JUnit),测试要涵盖正常、异常情况、边界情况;
  3. 课上提交测试代码和测试运行的结果截图,截图要求全屏截图,包含自己的学号信息,否则无效;测试Bag类的代码中至少包含一个自定义类如Student;
  4. 课下完成码云上代码的上传。
  • 【附】Bag的UML图:

【返回目录】


思路分析

  • 首先自定义一个Bag类,使用 public class Bag<T> implements BagInterface<T> 实现给出接口的所有方法框架,之后在里面通过泛型类型 T 自定义一个数组,通过填充方法来完善Bag类。考虑到给出的接口含有addremoveisEmpty等方法,如果使用 List 创建对象实现有些简单,所以我选择了使用 Object 类定义数组实现这些方法。

    在 Bag 类的 UML类图 中,我们可以清楚地知道每个方法的返回类型、参数及需要实现的功能,大部分功能的实现比较框架化,比如:getCurrentSize()isEmptyadd(T newEntry)remove()等,其方法填充内容基本符合以下框架:
public <返回类型> <函数名>(<参数>){
//初始化返回值参数对象
//循环(遍历、修改元素)
//条件(何时修改、赋值)
//返回值(参数)
}

之所以能用 循环 + 条件 的框架是因为这些方法基本都涉及遍历环节,我也使用了几种不同的遍历方式。

需要注意的是最后一个方法 toArray(),这个方法相对陌生,我查找了API,关键句如下:

适当顺序 返回包含此列表中所有元素的数组;

如果指定的数组能容纳队列,并有剩余的空间,那么会将数组中紧接 collection 尾部的元素设置为 null。

  • 所谓的“适当顺序”,可以理解为不同与原来数组排列元素的顺序,我又继续看了UML类图中的这一方法的注释:

A new array of entries currently in the bag.

  • 我的思路是将原来数组中的空值元素都填补为同一类型的相同的值,然后再重新返回这个数组,这样就可以通过一个含空值的数组调用此方法之后的元素容量进行单元测试。实现代码如下:
    /*
Shows all objects in a new array of food bag entries.
*/
public T[] toArray() {
int j = food.length - 1;
for (int i = 0;i<food.length;i++) {
food[j] = "apple";
if (food[i] == null) { //将空值全部填补
food[i] = food[j];
j--;
}
}
return (T[]) food;
}

【返回目录】


遇到的问题和解决过程

  • 【问题】在使用Junit测试 add(T newEntry) 方法时,Junit测试异常:

  • 【解决方法】我仔细看了一下自己写的 add 方法:

    public boolean add(T newEntry) {
boolean boo = false;
for (Object i : food) {
if (i == null) {
i = newEntry; //添加到第一个空值位置
boo = true;
break;
}
}
return boo;
}

由于使用foreach遍历比较简单,我就没有考虑其他问题。IDEA的提示,给 i 重新赋值的那条语句中的 i 是多余的:

![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007184208240-2031952014.png)

我就想不通为什么是多余的,于是又开始检查 foreach 的结构框架:
for(<元素类型> <元素变量> : <遍历对象>){
引用元素变量的相关语句;
}

修改了几次数组类型之后还是不对,于是我认为是foreach方法出现问题,就做了一个foreach的测试类,发现果然是foreach方法的问题,同样的修改赋值语句,使用for循环遍历就正常,使用foreach就不能进行相应赋值:

![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185209661-1263550744.png)

接着,我开始使用debug单步调试:(单击图片可放大)

[![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185542708-250193647.png)](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185542708-250193647.png)

刚开始设断点,我并没有注意观察细节,只是跟着步骤走了一遍,又跟踪了一遍才发现给 i 赋值的语句好像并没有效果,这一句出现了问题。我又仔细地跟踪了一遍,发现add元素的地址和原来空值的地址不一样,而最后数组添加的是空值的地址:

[![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185832974-1747717902.png)](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185832974-1747717902.png)

[![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185845349-1292468436.png)](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007185845349-1292468436.png)

所以总结起来就是,i 原来对应的地址就不是指向数组元素的,所以只要用其他循环(for循环)替代即可正常修改原数组的元素:

![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007190315911-1971919464.png)

如果一定要使用foreach修改数组元素的话,那只能再另外加一层循环:
public boolean add(T newEntry) {
boolean boo = false;
for (Object i : food) {
if (i == null) { //foreach不能修改数组元素
for (int j = 0; j < food.length; j++) { //重新使用for循环赋值
if (food[j] == i) {
food[j] = newEntry; //添加到第一个空值位置
break;
}
}
boo = true;
break;
}
}
return boo;
}

至于我的验证是否正确,我又查找了相关资料,一种说法是:

foreach结构中的元素变量是个基本数据类型,在遍历时不指向数组元素的地址,它只代表数字它自己。

  • 还有一个实例可以更好地证明我的验证:
for (Integer temp : list)
{
if (temp == 1)
{
temp = temp * 2;
}
}

根据oracle的官方文档,正式翻译应该如下:

for (Iterator i = list.iterator(); i.hasNext(); )
{
float i0 = (Integer)i.next();
if(i0 == 1)
i0 = i0*2;
}

所以foreach中的 temp变量只是一个局部变量(i0),而且还是集合中元素的一个副本,并不是元素本身。这样才导致输出“修改过的数组”时,仍然输出原数组。

综上所述,我的验证正确,foreach只适合遍历数组,在实现涉及到修改数组元素的功能时,不宜使用,会造成赋值失败。

【返回目录】


代码实现及托管链接

    /**
* A finite number of objects,not necessarily distinct,in no particular order,
* and having the same data type(collection).
*
* @author 20162330
*/
public class Bag<T> implements BagInterface<T> {
private Object food[] = new Object[5]; /*
Returns the current number of objects in the bag(except null).
*/
public int getCurrentSize() {
int foodSize = 0;
for (Object i : food) { //foreach遍历
if (i != null)
foodSize++;
}
return foodSize;
} /*
Demonstrates if the food bag is empty(null).
*/
public boolean isEmpty() {
boolean boo = true; //默认为空
for (Object i : food) {
if (i != null) {
boo = false;
break;
}
}
return boo;
} /*
Adds a given object to the food bag,according to whether the addition succeeds,
return true or false.
*/
public boolean add(T newEntry) {
boolean boo = false;
for (Object i : food) {
if (i == null) { //foreach不能修改数组元素
for (int j = 0; j < food.length; j++) { //重新使用for循环赋值
if (food[j] == i) {
food[j] = newEntry; //添加到第一个空值位置
break;
}
}
boo = true;
break;
}
}
return boo;
} /*
Removes an unspecified object from the food bag,if possible.
*/
public T remove() {
Object n = null;
for (int i = 0; i < food.length; i++) {
if (food[i] != null) {
n = food[i];
food[i] = null; //移除第一个不为空的元素
break;
}
}
return (T) n;
} /*
Removes an occurrence of a particular object from the food bag,if possible.
*/
public boolean remove(T anEntry) {
boolean boo = false;
int i = 0;
while (i < food.length) {
if (food[i] == anEntry) {
food[i] = null;
boo = true;
break;
}
i++;
}
return boo;
} /*
Removes all objects from the food bag.
*/
public void clear() {
int i = 0;
do { //do-while方式遍历
food[i] = null;
i++;
}
while (i < food.length);
} /*
Counts the number of times an object occurs in the food bag.
*/
public int getFrequencyOf(T anEntry) {
int t = 0;
for (Object i : food) {
if (i == anEntry)
t++;
}
return t;
} /*
Tests whether the food bag contains a particular object.
*/
public boolean contains(T anEntry) {
boolean boo = false;
for (Object i : food) {
if (i == anEntry) {
boo = true;
break;
}
}
return boo;
} /*
Shows all objects in a new array of food bag entries.
*/
public T[] toArray() {
int j = food.length - 1;
for (int i = 0;i<food.length;i++) {
food[j] = "apple";
if (food[i] == null) { //将空值全部填补
food[i] = food[j];
j--;
}
}
return (T[]) food;
}
}

Junit单元测试截图:(单击图片可放大)

[![](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007193120865-790803238.png)](http://images2017.cnblogs.com/blog/1062725/201710/1062725-20171007193120865-790803238.png)

【返回目录】


感想

  • 这次实践中解决问题确实花费了不少时间,不过我一直坚持独立思考,最终顺利解决问题,同时又练习了一下几种不同的循环(遍历)方式,Junit测试与之前相比也更全面了,也算是又体验了一回“做中学”。


参考资料

【返回目录】

20162330 第三周 蓝墨云班课 泛型类-Bag 练习的更多相关文章

  1. 20162330 第十二周 蓝墨云班课 hash

    题目要求 利用除留余数法为下列关键字集合的存储设计hash函数,并画出分别用开放寻址法和拉链法解决冲突得到的空间存储状态(散列因子取0.75) 关键字集合:85,75,57,60,65,(你的8位学号 ...

  2. 疫情下的在线上课方案:QQ直播+蓝墨云班课

    目录 疫情下的在线上课方案:QQ群视频(腾讯课堂)+蓝墨云班课 使用QQ进行直播 材料 QQ直播步骤 其他问题 使用蓝墨云班课加强学习效果 教材问题 我的直播-小学生硬笔书法基础 我的直播 - C程序 ...

  3. 补交20145226蓝墨云班课 -- MyCP

    蓝墨云班课 -- MyCP.java 具体描述: 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt ...

  4. 补交20145226蓝墨云班课 -- MyOD

    蓝墨云班课 -- MyOD.java 具体描述: 编写MyOD.java 用java MyOD XXX实现Linux下od -tx -tc XXX的功能. 提交测试代码和运行结果截图,加上学号水印,提 ...

  5. 补交20145226蓝墨云班课 -- Arrays和String单元测试

    蓝墨云班课 -- Arrays和String单元测试 具体描述: 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt ...

  6. 补交 20155202 蓝墨云班课 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能

    蓝墨云班课 编写MyCP.java 要求: 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX ...

  7. MyCP.java蓝墨云班课

    题目要求: 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bin 用来把文本文件(内容 ...

  8. 20172310 蓝墨云ASL测试 2018-1938872

    20172310 蓝墨云ASL测试 2018-1938872 题目: 已知线性表具有元素{5,13,19,21,37,56,64,75,80,88,92},如果使用折半查找法,ASL是多少? 解答:( ...

  9. 20165223 week3蓝墨云测试总结

    1. 表达式0xaa | 0x55的值为 答案: 解析: 0xaa用二进制表示为10101010,0x55用二进制表示为01010101,按位或后为11111111,十进制表示为255,十六进制表示为 ...

随机推荐

  1. 基于 angular 规范的 commit

    基于 angular 规范的 commit commit格式如下: <type>: <subject> <BLANK LINE> <body> type ...

  2. 在Visual Studio 2012中使用GSL

    1. 下载GSL http://gnuwin32.sourceforge.net/packages/gsl.htm 下载 Complete package, except sources和Source ...

  3. 手机端仿ios的单级联动脚本三

    脚本 <script>var weekdayArr=['非公司企业法人','个体工商户','私营独资企业','私营合伙企业','有限责任公司','股份有限责任公司'];var mobile ...

  4. 一个完整的springmvc + ajaxfileupload实现图片异步上传的案例

    一,原理 详细原理请看这篇文章 springmvc + ajaxfileupload解决ajax不能异步上传图片的问题.java.lang.ClassCastException: org.apache ...

  5. Python Cookbook(第3版)中文版:15.19 从C语言中读取类文件对象

    15.19 从C语言中读取类文件对象¶ 问题¶ 你要写C扩展来读取来自任何Python类文件对象中的数据(比如普通文件.StringIO对象等). 解决方案¶ 要读取一个类文件对象的数据,你需要重复调 ...

  6. wget命令企业级应用参数详解

    wget -O /etc/yum.repos.d/CentOS-Base.repo --spider: 爬虫,检查网站是不是好的 -T: 指定超时时间 --tries=2  指定重试的次数 -q   ...

  7. 金三银四,2018最新iOS面试题,由它可以搞定面试官?

    序言 这些资料,你一定会用到!我相信很多人都在说,iOS行业不好了,iOS现在行情越来越难了,失业的人比找工作的人还要多.失业即相当于转行,跳槽即相当于降低自己的身价.那么做iOS开发的你,你是否在时 ...

  8. MySQL单表百万数据记录分页性能优化,转载

    背景: 自己的一个网站,由于单表的数据记录高达了一百万条,造成数据访问很慢,Google分析的后台经常报告超时,尤其是页码大的页面更是慢的不行. 测试环境: 先让我们熟悉下基本的sql语句,来查看下我 ...

  9. SAPUI5 freestyle vs SAP Fiori Elements —— 两种开发SAP UI5 Apps的方式对比

    概述 目前SAPUI5 SDK 提供了两种方式来开发一个SAPUI5 App.一种方式是传统的SAPUI5开发方式,一种是利用SAP Fiori Elements通过模板快速构建应用的方式. 本文简单 ...

  10. 学习ASP.NET Core Razor 编程系列一

    一. 概述 .NET Core 1.0发布的时候就想进行学习的,不过根据微软的以往的发布规律1.0版可以认为是大众测试版,2.0才算稳定.现在2.1都已经发布了预览版,之前对其"不稳定&qu ...