目录




题目及要求

  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. Flutter 初尝:从 Java 无缝过渡

    准备阶段 下载 Flutter SDK 新建 Flutter 文件夹,克隆 Flutter SDK: git clone -b beta https://github.com/flutter/flut ...

  2. Linux进行AES加密每次结果都不一致并且解密失败报错

    1. 现象 windows操作系统下进行"123456"的AES加密 encrypted message is below : QLNYZyjRnKF/zxAjzDt/lw== d ...

  3. Keras官方中文文档:序贯模型API

    Sequential模型接口 如果刚开始学习Sequential模型,请首先移步这里阅读文档,本节内容是Sequential的API和参数介绍. 常用Sequential属性 model.layers ...

  4. 洛谷P1501 [国家集训队]Tree II(LCT,Splay)

    洛谷题目传送门 关于LCT的其它问题可以参考一下我的LCT总结 一道LCT很好的练习放懒标记技巧的题目. 一开始看到又做加法又做乘法的时候我是有点mengbi的. 然后我想起了模板线段树2...... ...

  5. 【BZOJ2134】单位错选(数学期望,动态规划)

    [BZOJ2134]单位错选(数学期望,动态规划) 题面 BZOJ 题解 单独考虑相邻的两道题目的概率就好了 没了呀.. #include<iostream> #include<cs ...

  6. 深度剖析HashMap的数据存储实现原理(看完必懂篇)

    深度剖析HashMap的数据存储实现原理(看完必懂篇) 具体的原理分析可以参考一下两篇文章,有透彻的分析! 参考资料: 1. https://www.jianshu.com/p/17177c12f84 ...

  7. linux下线程的两种封装方式

    在网络编程的时候往往需要对Linux下原生的pthread库中的函数进行封装,使其使用起来更加方便,封装方法一般有两种:面向对象和基于对象,下面将分别介绍这两种方式,最后统一分析这两种方式的优缺点: ...

  8. JVM堆外内存随笔

    一 JVM堆外内存 1)java与io(file,socket)的操作都需要堆外内存与jvm内存进行互相拷贝,因为操作系统是不懂jvm的内存结构的(jvm的内存结构是自管理的),所以堆外内存存放的是操 ...

  9. Problem : 1008 ( Elevator )

    好操蛋啊,电梯竟然能原地不动,你大爷的,这逻辑,太弱智了.... Problem : 1008 ( Elevator )     Judge Status : Accepted RunId : 103 ...

  10. 自签名证书和私有CA签名的证书的区别 创建自签名证书 创建私有CA 证书类型 证书扩展名【转】

    自签名的证书无法被吊销,CA签名的证书可以被吊销 能不能吊销证书的区别在于,如果你的私钥被黑客获取,如果证书不能被吊销,则黑客可以伪装成你与用户进行通信   如果你的规划需要创建多个证书,那么使用私有 ...