目录




题目及要求

  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. hdu5977 Garden of Eden

    都不好意思写题解了 跑了4000多ms 纪念下自己A的第二题 (我还有一道freetour II wa20多发没A...呜呜呜 #include<bits/stdc++.h> using ...

  2. nodejs异步案例

    const fs = require('fs'); fs.readFile('./test.txt', 'utf-8', (err, data) => { err ? console.error ...

  3. JustMock .NET单元测试利器(一)

    1.什么是Mock? Mock一词是指模仿或者效仿,用于创建实例和静态模拟.安排和验证行为.在软件开发中提及"mock",通常理解为模拟对象.模拟对象的概念就是我们想要创建一个可以 ...

  4. 初识SSO与JWT

    以前在学校做项目的时候,登录注销,权限验证这些事情,都是交给框架来做的,每次都是把这个架子拿到项目中去,也没有真正思考过它的过程,总觉的这些都是十分简单的逻辑. 然而来公司工作之后,慢慢觉得登录和权限 ...

  5. [UVa11426]最大公约数之和——极限版II

    题意:给出n,求: \[\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}\gcd(i,j)\] 多组数据,\(n<=4*10^6\) sol 今天心血来潮再来写一写式子 首先这里 ...

  6. [HNOI2011]XOR和路径

    题面在这里 题意:给定一个无向图,从1号节点出发,每次等概率选择连接该节点的一条边走到另一个节点,到达n号节点时,将走过的路径上的所有边权异或起来,求这个异或和的期望 sol 一道期望大火题(表示看了 ...

  7. A brief introduction to weakly supervised learning(简要介绍弱监督学习)

    by 南大周志华 摘要 监督学习技术通过学习大量训练数据来构建预测模型,其中每个训练样本都有其对应的真值输出.尽管现有的技术已经取得了巨大的成功,但值得注意的是,由于数据标注过程的高成本,很多任务很难 ...

  8. Spring+Mybatis+Mysql搭建分布式数据库访问框架

    一.前言 用Java开发企业应用软件, 经常会采用Spring+MyBatis+Mysql搭建数据库框架.如果数据量很大,一个MYSQL库存储数据访问效率很低,往往会采用分库存储管理的方式.本文讲述如 ...

  9. Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

    一.概念 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture).当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实.当前流行的图形系统中,纹理绘制已经成为 ...

  10. Android RecyclerView 滚动到中间位置

    最近看到QQ音乐的歌词每次滑动后都可以滚回到中间位置.觉得甚是神奇,打开开发者模式显示布局,发现歌词部分不是采用 android 控件的写的,应该是前端写的.于是,我想,能不能用 recyclerVi ...