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((List list, Comparator<? super T> c)
    Arrays.sort((List list, 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一个对象)才行,根据要求来创建对象,实现不同的排序方法

原文地址:https://www.cnblogs.com/lyq063/p/6603408.html