201521123063 《JAVA程序设计》 第5周学习总结
1. 本周学习总结
1.1 尝试使用思维导图总结有关多态与接口的知识点。
1.2 可选:使用常规方法总结其他上课内容。
-
上周学习了继承和多态,满足is-a条件,但是发现会导致继承的滥用,如果只是但以的想拥有某种行为或变量,不需要一定要继承才能实现,可以使用接口
-
Comparable和Comparator区别:
Comparable接口只提供了int compareTo(T o)方法,而Comparator接口只提供了int compare(T o1,T o2)方法
Comparator位于包java.util下,需要导入java.util.Comparator类,而Comparable位于包java.lang下,不需要导包
排序方法:
Collections.sort((Listlist, Comparator<? super T> c)
Arrays.sort((Listlist, Comparable<? super T> c) -
接口与抽象类
抽象类表示这个类是什么,接口类表示这个类能做什么
抽象类和接口都不能直接实例化
抽象类可以有具体的方法 和属性,接口只能有抽象方法和不可变常量
一个类只能继承一个抽象类,而一个类却可以实现多个接口
接口和抽象类区别
2. 书面作业
1. 代码阅读:Child压缩包内源代码
1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误。并分析输出结果。
在Child类中
public void getParenti(){
System.out.println(i);//此处报错:i不可见
}
分析:在Child的类中没有i这个属性,则morning会调用父类的i,但是i被private修饰,只允许本类访问,可改为protected,这样就可以允许子类访问了
输出结果:
1
2
2
1
1
2
1
分析结果:
由于子类Child类中没有i,j属性,那么就会使用父类的属性,所以那些输出结果都是用父类的i和j
1.2 另外一个包中的OutOfParentPackage.java,能否编译通过?提示什么错误?分析原因。如何更改才能使之正常编译?
public class OutOfParentPackage{
public static void showParentj(Parent p){
System.out.println(p.j); //报错
System.out.println(p.geti());//报错
p.getj(); //报错
}
}
答:不能通过编译,根据eclipse提示可以在Parent类中用getter和setter方法,或者将j的属性改为public修饰的,但是不大安全就是了,同理,也将geti和getj方法改为public的
2. abstract进阶:阅读GuessGame抽象类的设计与使用源代码
2.1 Guess改造前代码很简单,而改造后的代码使用了抽象类、抽象方法看起来很复杂,那这样的改造到底有什么好处呢?
改造前:
import java.util.Scanner;
public class Guess {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int number = (int) (Math.random() * 10);
int guess;
do {
System.out.print("猜数字(0 ~ 9):");
guess = scanner.nextInt();
} while(guess != number);
System.out.println("猜中了...XD");
}
}
改造之后:
public abstract class GuessGame {
public void go() {
int number = (int) (Math.random() * 10);
int guess;
do {
print("输入数字");
guess = nextInt();
} while(guess != number);
println("猜对了");
}
public abstract void print(String text);
public abstract void println(String text);
public abstract int nextInt();
}
分析:改造前的代码比较中规中矩,但只能在控制台输入输出,该造后,用abstract修饰输出输入的方法,使其抽象化,可以在控制台,图形界面或者以其他方式进行
2.2 如果想将该游戏改造成图形界面,应该进行一些什么操作?
答:首先肯定是要导入工具包,因为输入输出,还有获取输入都是用抽象方法,具体实现的内容是由继承这些类的子类来重写这些方法来实现
2.3 结合该例子,你觉得什么时候应该使用abstract?
答: 当一个类中没有充足的信息来描述一个具体的对象时,可以用abstract,以不变应万变
2.4 重要:在这个例子中,变化的是什么,不变的是什么?尝试结合abstract、继承等概念进行说明。
答:可以变化的是继承GuessGame的子类中复写父类的方法,即输入输出的方式;不变的是父类抽象类方法的定义,这些类不需要有具体的内容,毕竟不知道要干啥,可以随机应变,因为任何继承这个抽象类的子类都必须复写父类的抽象方法,这样想做什么就让子类来做就好了
3. Comparable与Comparator
3.1 描述Comparable接口的用途。为什么某个类实现了Comparable接口就可以直接使用Arrays.sort对其进行排序?
在API中查找的结果:
public static void sort(Object[] a)根据元素的自然顺序对指定对象数组按升序进行排序。数组中的所有元素都必须实现 Comparable 接口。此外,数组中的所有元素都必须是可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,e1.compareTo(e2) 不得抛出 ClassCastException)。
保证此排序是稳定的:不会因调用 sort 方法而对相等的元素进行重新排序。
该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n*log(n) 性能。
参数:
a - 要排序的数组
抛出:
ClassCastException - 如果数组包含不可相互比较的 的元素(例如,字符串和整数)。
简要分析:comparable接口当中含有compareTo比较具体方法,但是只能按一种方式比较
3.2 有了Comparable接口为什么还需要Comparator接口呢?
Arrays.sort(T[] a, Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组进行排序。
API文档中查找的结果:
sort
public static <T> void sort(T[] a,
Comparator<? super T> c)根据指定比较器产生的顺序对指定对象数组进行排序。数组中的所有元素都必须是通过指定比较器可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,c.compare(e1, e2) 不得抛出 ClassCastException)。
保证此排序是稳定的:不会因调用 sort 方法而对相等的元素进行重新排序。
该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n*log(n) 性能。
参数:
a - 要排序的数组
c - 确定数组顺序的比较器。null 值指示应该使用元素的自然顺序。
抛出:
ClassCastException - 如果数组包含使用指定的比较器不可相互比较的 的元素。
简要分析:有了Comparable接口,Arrays.sort实现比较算法进行排序,但是形式比较单一,有了Comparator接口,在集合外定义compare()的方法,可以重写compare方法。传入了两个参数,就可以根据不同的排序要求作出排序,比较灵活,在书上284页也有相应的例子
3.3 可选:使用匿名内部类、Lambda表达式实现PTA编程5-2。
匿名内部类:(主要代码)
Comparator<PersonSortable2> n1 = new Comparator<PersonSortable2>() { //按年龄升序,定义了匿名内部类
public int compare(PersonSortable2 o1, PersonSortable2 o2) {
return o1.getAge() - o2.getAge();
}
}; //注意此处的分号
Arrays.sort(p, n1);
System.out.println("NameComparator:sort");
for (int i = 0; i < n; i++) {
System.out.println(p[i]);
}
Comparator<PersonSortable2> n2 = new Comparator<PersonSortable2>() { //按姓名升序排序
public int compare(PersonSortable2 o1, PersonSortable2 o2) {
return o1.getName().compareTo(o2.getName());
}
};
Arrays.sort(p, n2);
System.out.println("AgeComparator:sort");
for (int i = 0; i < n; i++) {
System.out.println(p[i]);
}
System.out.println(Arrays.toString(n1.getClass().getInterfaces()));
System.out.println(Arrays.toString(n2.getClass().getInterfaces()));
Lambda表达式:
Comparator<PersonSortable2> n1 = (PersonSortable2 o1, PersonSortable2 o2) -> (o1.getName().compareTo(o2.getName()));
Comparator<PersonSortable2> n2 = (PersonSortable2 o1, PersonSortable2 o2) -> (o1.getAge() - o2.getAge());
补充格式:
参数 -> 表达式或程序块{ }
如果是表达式,则return该表达式的值(无需写return语句)
如果是程序块{ },可以包含多条语句
截图:
4. 面向接口案例分析
阅读Case-StudentDao.zip案例
4.1 画出类关系图,描述每个类与接口的作用。
此图由eclipse的UML自动生成
Student类当中只有name这个属性,有有参的构造函数,还有其对应的setter和getter方法
StudentDao接口定义了读写学生信息和显示全部学生信息的抽象方法,
StudenDaoListImpl类实现了StudentDao接口,用列表来存储Student类的对象,readStudent方法是通过遍历列表找到目标,writeStudent是直接通过add添加新的对象
StudentDaoArrayImpl类也实现了StudentDao接口,不过是默认存储Student对象数组大小为80,与StudenDaoListImpl类有点不同的是读入信息是判断数组元素是否为空,才存入数组,其他大同小异
Test类就是含有入口函数,测试用的
4.2 StudenDaoListImpl与StudentDaoArrayImpl有何区别?
答:顾名思义,前者是用List列表来存储对象,后者是用数组来存储对象,只是实现方式不一样,结果是一样的
5. 什么是面向接口编程?面向接口编程的好处是什么?
结合题目3与4中的Test.java的代码讨论分析。不要百度原封不动照搬!
答;面向接口编程就是在面向对象的前提下,由于各个对象之间有可能存在协作关系,所以可以采用接口来实现交互,题目3中的Comparable和Comparator接口同样都是用来比较的,但是后者是可以比较一个类生成的两个对象之间属性的比较,同为比较,但是实现的方式不同,题目4的StudenDaoListImpl与StudentDaoArrayImpl同样实现的结果是一样的,面向接口编程,就是在实现的方式不清楚的情况下,定义的一个接口,让其他类实现这个接口,再根据情况来写具体的内容,有了接口可变性比较强,能够解决需求变化。在书上202页第一段就有提到:写程序要有弹性,要有可维护性。
6. 结对编程:面向对象设计(大作业2-非常重要)
6.1内容:使用Java代码完成上周做的面向对象设计大作业,需要有初步界面。实现的功能尽量简单,少而精,只包含必要的功能,不要追求高大全。
写出:类图(尽量精简,不用太多子类,两个即可)、系统常用功能描述、关键代码与界面
形式: 两人依托码云合作完成。请在这里贴出你们的学号、姓名与任务分工。
注意: 再过几次课要讲Java图形界面编程,到时候要将该系统升级为图形界面。系统的业务逻辑部分应该变化不大,变化大的是输入与输出部分。所以编码的时候,输入(Scanner)与输出(System.out)的代码,请不要将其与某个业务处理函数绑死。
选做加分: 给出两人在码云上同一项目的提交记录截图,额外加分。注:两个人在码云上新建一个项目。
参考资料:
结对编程参考资料
可以使用Processon画图
参考书面作业第3、4题的做法,使输入输出抽象化
类图:
学生L | 学生null | 项目地址 |
---|---|---|
https://q.cnblogs.com/u/lyq063/ | https://git.oschina.net/jmulyq/ShoppingCart.git |
6.2 常用功能描述框架图
6.3 关键代码
输入输出接口:
public interface shop {
//输入输出方式抽象化
public void print(String text);
public abstract void println(String text);
public abstract int nextInt();
public abstract Double nextDouble();
public abstract String next();
}
菜单类:
public class Menu implements shop{
private Scanner scan = new Scanner(System.in);
int k=0;
public void showmenu(Cart cart){
println("**********欢迎光临**********");
println(" 1.浏览商品");
println(" 2.查找商品");
println(" 3.加入购物车");
println(" 4.我的购物车");
println(" 0.退出");
println("请选择:");
int choice=nextInt();
Iterm[] booklist=Iterm.allproduct();
switch(choice){
case 1:{
for(Iterm i:booklist){
println(i.toString2());
}
break;
}
case 2:{
boolean flag=false;
String prudoct=next();
for(k=0;k<booklist.length;k++){
if(booklist[k].getName().equals(prudoct)){
println(booklist[k].toString2());
flag=true;
break;
}
}
if(!flag){
println("您所查找的商品未找到,请重新输入");
k=-1;
}else
break;
}
case 3:{
if(k>=0){
println("请输入数量:");
int num=nextInt();
booklist[k].setNumber(num);
cart.addproduct(booklist[k]);
cart.cartmenu(booklist[k]);
}
else{
println("错误操作,还未选择商品!");
}
break;
}
case 4:{
cart.cartmenu(booklist[k]);
break;
}
case 0:return;
}
showmenu(cart);
}
@Override
public void print(String text) {
System.out.print(text);
}
@Override
public void println(String text) {
System.out.println(text);
}
@Override
public int nextInt() {
return scan.nextInt();
}
@Override
public Double nextDouble() {
return scan.nextDouble();
}
@Override
public String next() {
return scan.next();
}
}
商品类:
public class Iterm{
private String name;
private double price;
public int number;
...一堆setter、getter、toString方法(含有两种格式,一种是不需要输出数量的)
}
购物车类:
public class Cart implements shop {
ArrayList<Iterm> iterm = new ArrayList<Iterm>();
private Scanner scan = new Scanner(System.in);
public void cartmenu(Iterm i) {
println("1.删除商品");
println("2.结算全部商品");
println("3.清空购物车");
println("4.显示购物车中所有商品");
println("0.返回菜单");
println("请选择:");
int c = nextInt();
switch (c) {
case 1: {
deleteproduct(i);
break;
}
case 2: {
System.out.println(allmoney());
break;
}
case 3: {
iterm.clear();
break;
}
case 4:{
for(Iterm i1:iterm){
System.out.println(i1.toString());
break;
}
}
case 0: {
return;
}
}
}
public void addproduct(Iterm i) {
if(iterm.contains(i)){
iterm.get(iterm.indexOf(i)).number+=i.number;
}
else iterm.add(i);
}
public void deleteproduct(Iterm i) {
iterm.remove(i);
}
public double allmoney() {
double sum = 0;
for (Iterm i : iterm) {
sum += i.getPrice() * i.getNumber();
}
return sum;
}
public void showcartproduct() {
for (Iterm i : iterm) {
println(i.toString());
}
}
...还有复写接口输入输出的方法
}
测试类:
public class PersonTest {
public static void main(String[] args) {
Menu menu=new Menu();
Cart cart=new Cart();
menu.showmenu();
}
}
6.4 运行界面
主菜单及所有商品:
在书列表中找不到的情况:
加入购物车并结算:
删除当前商品:
3. 码云上代码提交记录及PTA实验总结
题目集:jmu-Java-04-面向对象2-进阶-多态接口内部类
3.1. 码云代码提交记录
- 在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 然后搜索并截图
3.2. PTA实验
- 继续完成前面未完成的实验面向对象1-基础-封装继承
- 函数(选做:4-1)、编程(5-1, 5-2)
- 一定要有实验总结
实验总结:
-
4-1
待续 -
5-1
这题编写了PersonSortable类,实现了Comparable接口,根据题意来重写compareTo和toString方法
比较简单,注意Comparable,“< >”内要写比较的对象 -
5-2
这题用的是比较器,需要import java.util.Comparator;,应为这题要求是分别对姓名和年龄排序,所以可以重写compare方法,传入两个对象进行属性之间的比较
注意最后排序时候要Arrays.sort(p,new一个对象)才行,根据要求来创建对象,实现不同的排序方法