201521123091 《Java程序设计》第5周学习总结

Java 第五周总结

第五周的作业。

目录
1.本章学习总结
2.Java Q&A
3.使用码云管理Java代码
4.PTA实验


1.本章学习总结

1.1 尝试使用思维导图总结有关多态与接口的知识点。


1.2 可选:使用常规方法总结其他上课内容。

  关于多态和接口我只是在上图列举了一些理念类的东西,具体的知识点我觉得都是其次的。面向对象编程的思想更加重要吧。
  上机课上老师讲了匿名内部类。首先内部类,就是把一个类的定义放在另一个类的内部。主要就是逻辑上包含的类使用内部类。
  要在外部类当中使用内部类,必须把外部类的类名写出来。
  内部类能够访问外部类的所有成员。而且不需要任何条件。这和继承就有差了,比如子类如果想使用父类的成员,至少相关成员在父类中需要用protected来修饰,而内部类不需要,它非常了解外部类,而且还能和外部类通信。
  加了static修饰的内部类通常被称为嵌套类。我们可以用之前学到的static的相关知识来理解嵌套类
  1.首先创建嵌套类的对象,并不需要外部类的对象。
  2.不能从嵌套类的对象访问非静态的外围类对象。
  还有在内部类产生.class文件的时候,会有一个美元符号的标识符。
  最后说说匿名内部类,这个类没有名字。一般是new 父类(){};或者是new 接口(){};。就是在创建对象的时候,插入了一个类的定义。总的说来就是,创建一个继承父类或者实现及接口的对象,然后通过向上转型为父类或者是接口的引用。在匿名内部类末尾的分号只是new表达式的结束而已。还有注意如果要在内部匿名类别中使用某个方法中的变量,它必须宣告为“final”。
  就讲这么多。


2.Java Q&A

1.代码阅读:Child压缩包内源代码

1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误。并分析输出结果。

  先贴上parent包内的三个代码。

//Parent.java
package com.parent;

class Parent{
    private int i=1;
    protected int j=2;
    protected int geti(){
        return i;
    }
    public void getj(){
        System.out.println(j);
    }
}

//Other.java
package com.parent;

class Other{
    public static void showParentj(Parent p){
        System.out.println(p.j);
        System.out.println(p.geti());
    }
    
}

//Child.java
package com.parent;

public class Child extends Parent{
    public static void main(String[] args){
        Parent p = new Parent();
        Child c = new Child();
        c.getParenti();
        c.getParentj();
        Other.showParentj(p);
        
    }
    public void getParenti(){
        System.out.println(i);
    }
    public void getParentj(){
        System.out.println(super.j);
        System.out.println(j);
        System.out.println(geti());
        System.out.println(super.geti());
    }
}

  然后来分析一下哪里出错了。
  首先,出错语句是System.out.println(i);,原因是父类的i域对于子类来说是不可见的,正如从上面代码我们可以看到的那样,父类的i是private类型的,所以这边只要把访问指定词改大一点就好了,比如这边改成protected。
  接着,就运行我们的程序,程序运行结果如下:

1
2
2
1
1
2
1

  c.getParenti();输出父类的i,虽然这边没有指定是super,但是子类没有重新定义i,所以使用的还是父类的i,而父类的i在实例化的时候就初始化为1;c.getParentj();输出父类的j,父类的j,父类的i和父类的i。父类的j同理初始化为2,这边加不加super结果都是一样的,因为都是在继承父类的方法。Other.showParentj(p);输出父类的j和父类的i,因为是static修饰的,所以直接用类名调用即可。

1.2 另外一个包中的OutOfParentPackage.java,能否编译通过?提示什么错误?分析原因。如何更改才能使之正常编译?(写不出来正确答案不要紧,但请一定写出思考过程)

  先贴代码:

import com.parent.*;
public class OutOfParentPackage{
	public static void showParentj(Parent p){
		System.out.println(p.j);
		System.out.println(p.geti());
		p.getj();
	}
}

  同样不能编译通过,提示错误The type Parent is not visible。Parent类前面没有任何访问指定词,所以默认是包访问权限,而这个OutOfParentPackage又在Parent包之外,所以就访问不到了。要改的话,就只要在类前面加上指定词public即可。然而这个时候还是出错,首先第一句不能直接访问j,因为它是用protected修饰的,不能通过包外部去访问。我们可以将指定词改为public,但是更好的办法是写上getJ()方法,通过我们指定的方法来访问内部数据,符合封装对于数据隐藏的需要。虽然有geti()方法,但是它是用protected来修饰的,所以也是访问不到的,这个时候只有将其改为public才可以。


2.abstract进阶:阅读GuessGame抽象类的设计与使用源代码

2.1 Guess改造前代码很简单,而改造后的代码使用了抽象类、抽象方法看起来很复杂,那这样的改造到底有什么好处呢?

  那就必须得先来看看改造前和改造后的区别在哪里:

//改造前的
package cc.openhome;

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");
    }
}

//改造后的

//Guess.java
package cc.openhome;

public class Guess {
    public static void main(String[] args) {
        GuessGame game = new ConsoleGame();//这边使用了抽象类GuessGame
        game.go();
    }
}

//GuessGame.java
package cc.openhome;

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();
}

  这样改造的好处是什么?那就让我们看看到底什么东西是被抽象出来的。在GuessGame类中,输出和输入整数的方法都被抽象了。Why?原因在文件名上就已经和我们说了:

改造前,未使用抽象类,只能控制台输出

改造后,使用抽象类,可以在控制台,也可以使用对话框图形界面等输入

  虽然说我们可以分别对于不同的环境分别编写不同的代码,但是可能会存在我们不能等待别人对需求确定下来,再敲代码。而抽象类和抽象方法却可以先写出来,因为我并不知道到底用什么环境,但是我可以等以后确定了之后再补上具体的代码实现,而现在,不妨当这些方法为已实现(不管是控制台,对话框orWeb)的去操作。
  

2.2 如果想将该游戏改造成图形界面,应该进行一些什么操作?

  那就要设计一个图形界面猜数字的类去继承上文提到的抽象类,然后用图形界面支持的输入输出语句来重写抽象类当中的抽象方法。
  

2.3 结合该例子,你觉得什么时候应该使用abstract?

  包含抽象方法的类叫做抽象类。
  结合上面的例子,在具体的实现方法未定义的时候,需要我们用到abstract,而抽象方法正是这样的一个方法,只有声明而没有方法体。至于为什么没有具体的定义,我的理解是这样的,上例中是具体的实现方法别人还未完成,在我不知道具体是什么,有需要用到这个方法来完成其他代码的编写时,就需要用到抽象方法。还有一种就是这种抽象方法可以被一系列的类以不同的方式去操纵,由此建立起一种通用的形式。
  还有就是从抽象类的角度去理解,假如说到乐器,我们的认识中没有一个具象的东西叫做乐器,但是当我们提到类似古筝、柳琴、二胡等等,我们的脑海里面会浮现出一个具体的事物。那我们可以说乐器就是抽象的,这个乐器类显然没有有意义的对象,它是一个通用的而且抽象的概念,通过加上abstract关键字,我们也可以在编译阶段就阻止试图创建抽象类对象的错误。
  
  

2.4 重要:在这个例子中,变化的是什么,不变的是什么?尝试结合abstract、继承等概念进行说明。

  在这个例子中,变化的是环境,我们可以用不同的环境:控制台、对话框图形界面或者是web,还有很多……,不变的是这个抽象类,它提供若干输入、输出,完成一个猜数的游戏,这个是不变的,而我们根据环境的不同来具体决定到底使用何种输入输出方法。虽然这个类是是通用的,但是脱离了具体环境它就是抽象的。没有具体的输入、输出这个游戏就没有办法开始,就比如最常见的最少需要两张嘴,一个用来输入,一个用来输出,就像电视上超级老套的猜数游戏,现在应该都没有了。就像算法,这个东西,虽然是一个思想,可以抽离具体的代码实现而存在,但是我们要去使用这样的算法,就需要用具体的语言去实现,假如C、Python等等。那具体的实现就是通过继承去完成的。一个抽象类,没有别的类去继承它,就显得很没有意义,因为抽象类的抽象方法是没有具体定义的,这也就要求如果一个非抽象类去实现抽象类的抽象方法,就必须把所有的抽象方法都实现了。这样才是一个不抽象的类。而这个实现的过程就是继承,继承抽象类的所有域和方法,然后通过复写的形式来具体实现方法的细节。


3.Comparable与Comparator

3.1 描述Comparable接口的用途。为什么某个类实现了Comparable接口就可以直接使用Arrays.sort对其进行排序?

// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
    for (int i=low; i<high; i++)
        for (int j=i; j>low &&
                 ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
            swap(dest, j, j-1);
    return;
}

// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
    if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
        dest[i] = src[p++];
    else
        dest[i] = src[q++];
}

  这边算法的具体实现不是我们的重点,只要知道对于小数组(这边是对于数组长度小于7的数组)来说,\(O(NlgN)\)的归并排序反而不比\(O(N^{2})\)的插入排序好,所以对于小数组,直接插入排序搞定。然后对于较大规模的,就分治地对数组进行二分,然后再合并成有序序列。
  Java编程思想上有提到,对于基本类型是快排,对于对象是归并排序,似乎也不是完全准确,对于基本类型来说,如果数组长度超过某一阈值,也是要使用归并排序。归并排序比快排要慢,但是它稳定,稳定就是具有相同关键字在排序过程中,不会改变其相对位置。
  还有至于为什么对象是用归并排序,某乎是这么说的(具体先不深究,不深究!):
  

那么为什么复杂对象不使用快速排序呢?因为对于一个hashcode计算复杂的对象来说,移动的成本远低于比较的成本 某乎链接

  但是这不是这次的重点,这次的重点是,为什么一定要实现Comparable接口,先看看源码注释:

Sorts the specified array of objects into ascending order, according to the natural ordering of its elements. All elements in the array must implement the Comparable interface. Furthermore, all elements in the array must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the array).

  老实说,你要用别人的东西,就得按照别人要求的做。
  这边的算法使用到了(Comparable),这是一个类型指派,也就是说要转化成Comparable才能正确使用该方法。然而如果一个类并没有去操作Comparable接口,它就不能被正确的指派,所以就会抛ClassCastException的错误。
  再回到第一个问题,论Comparable接口的用途。
  这个接口就是使类具有“天生”的比较能力,有时候我们就非常需要这样的比较能力。就这么说吧,人和人怎么能比呢,然后我就是一直要和隔壁家的孩子作比较。他学习比我好,但我长得比他帅,则怎么比较呢。Java提供了两种解决方法,一种是Comparable接口。拿人去操作这个Comparable接口,并定义上长得帅的人优先级高,从此我就是被家长夸奖的对象,而隔壁家的孩子就要向我学习怎么变得更帅。
  这个接口只有这么一个方法:public int compareTo(T o);文档中告诉我们一个正确的comparTo()方法应该这样写
 

Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.)
The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
Finally, the implementor must ensure that x.compareTo(y)0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.
It is strongly recommended, but not strictly required that **(x.compareTo(y)
0) == (x.equals(y)).**

  此方法接受另一个Object为参数,如果当前对象小于参数则返回负值,等于返回0,大于则返回正值。
  

3.2 有了Comparable接口为什么还需要Comparator接口呢?

  这是一个好问题,虽然说这是个靠脸吃饭的社会,但是也会有像我这样注重内心修养的男孩纸。对于一个人的评价,不能说只是看他(她)的外表,还有注重他(她)的内在。也就是说,每个人都有自己的评价标准,即使是同一类人,也会分出个不同的高低。但是实现了Comparable接口,这是一个内比较器,我觉得它有点像C++的操作符重载,在类内部定义了一种比较方式。而Comparator接口就和C++(毕竟经常sort(v.begin, v.end(), cmp);)的差不多了,参数是两个对象,然后对于不同的结果返回正数、0和负数。这样的话,即使要排序的对象没有实现Comparable接口或者它的排序方式我不满意,我都可以自己使用自己的比较方法。而且还可以定义很多的排序方法,降序、升序、随便什么序……

  
  

3.3 可选:使用匿名内部类、Lambda表达式实现PTA编程5-2。

//按名字排序
Comparator<PersonSortable2> nameComparator = 
		new Comparator<PersonSortable2>(){

			@Override
			public int compare(PersonSortable2 o1, PersonSortable2 o2) {
				// TODO Auto-generated method stub
				return o1.getName().compareTo(o2.getName());
			}
	
};

//按年龄排序
Comparator<PersonSortable2> ageComparator = 
		new Comparator<PersonSortable2>(){

			@Override
			public int compare(PersonSortable2 o1, PersonSortable2 o2) {
				// TODO Auto-generated method stub
				if (o1.getAge() <  o2.getAge()) {
					return -1;
				} else if (o1.getAge() > o2.getAge()) {
					return 1;
				} else {
					return 0;
				}
			}
	
};

//实现所实现的接口
System.out.println(Arrays.toString(nameComparator.getClass().getInterfaces()));
System.out.println(Arrays.toString(ageComparator.getClass().getInterfaces()));

  一开始还是像PTA上的用.class.getInterfaces()来得到所实现的接口,后来发现匿名类并没有名字。不可以通过类名.class来调用getInterfaces()方法,对于对象,可以用.getClass()方法来调用。
  
  lamda表达式,就是函数的简写形式,之前不知道的时候被名字吓到了。

Comparator<PersonSortable2> nameComparator = 
		(PersonSortable2 o1, PersonSortable2 o2) 
		-> (o1.getName().compareTo(o2.getName()));  
Arrays.sort(personSortable2s, nameComparator);

Comparator<PersonSortable2> ageComparator = 
		(PersonSortable2 o1, PersonSortable2 o2) 
		-> {
			if (o1.getAge() <  o2.getAge()) {
				return -1;
			} else if (o1.getAge() > o2.getAge()) {
				return 1;
			} else {
				return 0;
		}};
Arrays.sort(personSortable2s, ageComparator);

  甚至可以再往下简化:

Arrays.sort(personSortable2s, 
	(PersonSortable2 o1, PersonSortable2 o2) 
	-> (o1.getName().compareTo(o2.getName())));
	
Arrays.sort(personSortable2s, 
	(PersonSortable2 o1, PersonSortable2 o2) 
	-> {
		if (o1.getAge() <  o2.getAge()) {
			return -1;
		} else if (o1.getAge() > o2.getAge()) {
			return 1;
		} else {
			return 0;
		}
	});

  lamda表达式本身是个语法糖,之前有很多次看到过这个概念,语法糖,又叫做糖衣语法。
  

这种语法对语言的功能并没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。wiki链接

  如果用上反编译工具,我们就可以看到有时候在编译的时候,就会将我们简写的语法换成更加基础的结构,这个叫做“去糖”。
  就比如我们现在有学过的,自动装箱、拆箱、foreach语法、枚举和泛型等等都是这个样子实现的。


4.面向接口案例分析,阅读Case-StudentDao.zip案例

4.1 画出类关系图,描述每个类与接口的作用。

  • Student类:一个只有名字这个属性的类,加上通常的构造器、getter/setter和toString()方法。
  • StudentDao接口:提供三个抽象方法,写入学生数据、读取学生数据和显示所有学生信息。对于写入学生数据,通过一个boolean变量来判断是否写入成功。
  • StudentDaoArrayImpl类:用数组来存放学生信息,具体实现接口的三个抽象方法,读取学生信息,如果找不到返回null;如果遍历整个数组,没找到学生信息返回false。显示所有学生信息则要求对象都是非空的。然后还有一个构造器,提前根据用户的输入开对应长度的数组。
  • StudenDaoListImpl类:用列表来存放学生信息,具体实现接口的三个抽象方法。用ArrayList的具体方法来完成相应操作。

  

4.2 StudenDaoListImpl与StudentDaoArrayImpl有何区别?

  实现的数据结构不一样,StudenDaoListImpl是用ArrayList实现的,而StudentDaoArrayImpl是用数组实现的。这样的话,两个类对于接口的抽象方法,就会有不同的实现。但是,其实究其本质,是没什么太大差别的。因为ArrayList就是用数组实现的啊。
  看一下源代码,transient Object[] elementData;是用Object数组来实现的,但是我们在创建ArrayList指定了这个ArrayList的数据类型"Constructs a list containing the elements of the specified collection.",所以就相当于开了个Student数组。只是具体实现的时候是用类型指派来完成的。这样非Student类型的对象都不能放进去,否则就会抛ClassCastException异常。


5.什么是面向接口编程?面向接口编程的好处是什么?

结合题目3与4中的Test.java的代码讨论分析。不要百度原封不动照搬!

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。——《Java编程思想》

  首先来看看题目3和题目4吧。
  题目3用到了两个接口,分别是Comparable和Comparator,通过这两个接口,我们可以很方便地对我们自己定义的类进行比较,从而调用Arrays里面已经写好的sort()方法来排序。前一个接口赋予了类可以比较的特性,后面一个接口实现了一个可以比较两个属于某一特定类的专用比较类。
  题目4,类似于一个存放学生数据的数据库。在这个Test类里面,我们只要做的就是确定数据到底是以何种数据结构来存放,而不需要管具体的底层实现。正如本例当中,我可以用列表,也可以用数组来实现,但是这两种数据结构都可以操作写入学生数据、读取学生数据和遍历学生信息的操作。
  这样的好处第一个就是类似我们前面讲继承时提到的多态,多个子类去继承一个父类(不管他是不是抽象类),然后可以由父类统一调用某些方法,而在运行时的时候根据具体对象的类型来调用不同的方法,也就是用接口来实现多态。
  第二个就是我们在一开始说的,接口将接口与实现分离,而且更加结构化。这个怎么理解?首先我们可以从一开始学C语言的时候想,我们学C语言,最核心的就是学函数,因为函数实现了模块化的设计,我们的main()函数只需要调用自己之前已经写好的函数,这样的最大好处就是便于维护,将一个程序的若干功能都分门别类的放置好,就像原来是杂乱的桌面现在都摆放齐整一样。而接口可以说是将这种程序设计的灵活性又提高了一个档次,我可以对上层调用方法只显示接口的功能,而在下层进行代码的具体实现,而这些都不是用户层需要去知道的。同时也便于修改,如果有一个类实现接口的时候根据实际情况需要修改,那我只要动下层的代码,而不需要改上层的调用。我觉得和函数调用有可以类比之处。
  还有就是接口的使用,我觉得可以提高工作效率。正如书上所说的,有时候不能等别人完成任务的时候我们再去做。就像以前一个被用到烂的例子,早上起床的时候,烧开水的同时可以去洗漱,这样会比洗漱完之后等开水烧开更节省时间。其实这就是串行和并行的区别。我们完全可以先不需要管别人的底层实现到底是怎么样,而是把调用接口的上层同时写出来,因为上层的调用不用知道下层的具体实现。这样同时工作,效率会高很多。
  还有就是这边的面向接口到底是什么,结合题目我是否可以直接认为就是用interface修饰的“接口”。作为广义的接口,我们可能要把抽象类之类的也要包括进来。抽象类是类和接口的一种混合吧。那至于是使用抽象类还是接口,还是要视具体情况而定。抽象类有一个很大的作用就是要复用代码,就假如继承这个抽象类的子类在许多方法的实现都是一样的,那么用抽象类就是适合的,这样可以减少代码量。至于接口,是一个更加抽象的概念,抽象到可以不用管操作它的类到底是什么,而且与上面相反地,如果有大多数方法的实现都是不一样的话,用接口就是合适的。还有就是语义的区别,关于继承,需要满足一个is a的关系,如果满足这个关系,可以用抽象类。但是接口主要的就是方法的声明,它允许操作这个接口的类拥有这些方法,就像Comparable接口允许所有操作它的类都具有可以比较的能力。但是接口显然不满足is a的关系。就拿上面的举例,学生是一种可比较……这样听上去很僵,所以综上所述还是要具体问题具体分析。
  说的有点多,难免会有出错,望见谅!
  贴个参考链接吧


  

6.结对编程:面向对象设计(大作业2-非常重要)

内容:使用Java代码完成上周做的面向对象设计大作业,需要有初步界面。实现的功能尽量简单,少而精,只包含必要的功能,不要追求高大全。

写出:类图(尽量精简,不用太多子类,两个即可)、系统常用功能描述、关键代码与界面

形式: 两人依托码云合作完成。请在这里贴出你们的学号、姓名与任务分工。

注意: 再过几次课要讲Java图形界面编程,到时候要将该系统升级为图形界面。系统的业务逻辑部分应该变化不大,变化大的是输入与输出部分。所以编码的时候,输入(Scanner)与输出(System.out)的代码,请不要将其与某个业务处理函数绑死。

选做加分: 给出两人在码云上同一项目的提交记录截图,额外加分。注:两个人在码云上新建一个项目。

  没有对子,只有自己(手动滑稽)。

6.1 一张表格

学生A 学生B 项目地址
http://www.cnblogs.com/ljl36/ null http://git.oschina.net/ljl36/GWC

  
6.2 常用功能描述框架图

6.3 关键代码
  先上一个超级乱的UML类图:

  下面贴上关键代码(前排仰望Daiker、锥等人的高端代码):

//就是个抽象列表的接口,主要是担心以后要做个什么文本文件,甚至是数据库什么的连起来
public interface MyAbstractList<E> {
	
	boolean add(E e);
	E get(Object object);
	
}

//输入输出的接口,因为后面要用图形界面,到时只要new的时候换个别的类就好了吧
public interface InputAndOutput {
	String nextLine();
	int nextInt();
	void print(String string);
}


//Menu类 显示主菜单,除了退出每次都要能回来
public void showMainMenu() {
	inputAndOutput.print("\t\t1.浏览商品");
	inputAndOutput.print("\t\t2.搜索商品");
	inputAndOutput.print("\t\t3.我的信息");
	inputAndOutput.print("\t\t4.查看购物车");
	inputAndOutput.print("\t\t5.退出");
	inputAndOutput.print("请输入你的选择:");
	switch (inputAndOutput.nextInt()) {
	case 1:
		surfAllGoods();
		break;
		
		
	case 2:
		startSearch();
		
		break;
		
	case 3:
		showMyself(user);
		break;
		
	case 4:
		showShoppingCart(user.getShoppingCart());
		break;
	
	case 5:
		
		System.exit(0);
		break;

	default:
		break;
	}
	showMainMenu();
}


//Menu类 就是每次都要问你要不要买的烦人的方法
private void purchaseOrReturn(AllGoods allGoods) {
	// TODO Auto-generated method stub
	inputAndOutput.print("输入商品索引查看商品或者输入0返回");
	int i = inputAndOutput.nextInt();
	switch (i) {
	case 0:
		break;

	default:
		Goods goods = allGoods.getGoodsList().get(i - 1).showDetails();
		inputAndOutput.print("输入1加入购物车,输入0返回主界面");
		switch (inputAndOutput.nextInt()) {
		case 1:
			inputAndOutput.print("请输入购买商品的个数:");
			user.getShoppingCart().add(user.getShoppingCart().new Item(goods, inputAndOutput.nextInt()));
			break;

		default:
			
			break;
		}
		break;
	}
}

//Search类 核心方法,其实也没写什么东西,按照如滨的意思,后面还要再改
void search() {
	String[] keywords = inputKeyword();
	
	for (Goods goods : allGoods.getGoodsList()) {
		boolean flag = true;
		for (String string : keywords) {
			if (!goods.getName().contains(string)) {
				flag = false;
				break;
			}
		}
		if (flag) {
			searchList.add(goods);
			showGoods(goods);				
		}
	}
	
	if (searchList.isEmpty()) {
		System.out.println(keywords[0]);
		showNoSearched();
	}
}
	
//ShoppingCart类 的方法们
public boolean delete(int i) {
	Item deleteItem = (Item) get(i);
	if (deleteItem != null) {
		totalPrice -= deleteItem.getNum() * deleteItem.getGoods().getPrice();
		items.remove(deleteItem);
		return true;
	}
	return false;
	
}

public boolean clear() {
	if (items.isEmpty()) {
		return false;
	} else {
		
		displayOrder();
		
		items.clear();
		totalPrice = 0;
		
		return true;
	}
}

public void displayAll() {
	if (items.isEmpty()) {
		inputAndOutput.print("购物车还是空的哦!");
	} else {
		for (int i = 0; i < items.size(); i++) {
			inputAndOutput.print(i + 1 + ". " + items.get(i).toString());
		}
		inputAndOutput.print("总价:" + totalPrice + "元");
	}
	
}

private void displayOrder() {
	// TODO Auto-generated method stub
	for (Item item : items) {
		inputAndOutput.print
		("确认购买" + item.goods + "共, " + item.num + "件");
	}
	
	inputAndOutput.print("总价为" + totalPrice);
	
}

//User类 下面就是一些getter/setter
public class User {
	private String userName;
	private String password;
	private String address;
	private ShoppingCart shoppingCart;
	
	public User(String userName, String password, String address) {
		super();
		this.userName = userName;
		this.password = password;
		this.address = address;
		this.shoppingCart = new ShoppingCart();
	}
	...
}


//作为一个抽象类Goods,也要有些抽象方法
public abstract class Goods {
	private String name;
	private double price;
	
	//略去了很多getter/setter
	
	public abstract Goods show();
	public abstract Goods showDetails();
	
}


//这次是做控制台,so
package shopping;

import java.util.Scanner;

public class Console implements InputAndOutput {

	@Override
	public String nextLine() {
		// TODO Auto-generated method stub
		@SuppressWarnings("resource")
		Scanner scanner = new Scanner(System.in);
		String string = scanner.nextLine();
		return string;
	}

	@Override
	public int nextInt() {
		// TODO Auto-generated method stub
		@SuppressWarnings("resource")
		Scanner scanner = new Scanner(System.in);
		int x = scanner.nextInt();
		return x;
	}

	@Override
	public void print(String string) {
		// TODO Auto-generated method stub
		System.out.println(string);
		
	}

}

//图书类其实没啥,就是衣服类会比较复杂,首先是ClothesType类
enum Color{
	黑色, 白色, 蓝色 
}

enum Size {
	S, M, L, XL, XLL
}

public class ClothesType extends Goods {
	private Size size;
	private Color color;
	private InputAndOutput inputAndOutput = new Console();
	
	public ClothesType(String name, double price) {
		super(name, price);
	}
	
	@Override
	public String toString() {
		return "服装类    " + super.toString();
	}


	public Goods show() {
		inputAndOutput.print(toString());
		return this;
	}
	
	
	public Clothes confirm() {
		inputAndOutput.print("颜色:");
		int id = 0;
		for (Color color : Color.values()) {
			inputAndOutput.print(++id + "." + color);
		}
		inputAndOutput.print("请选择:");
		int c = inputAndOutput.nextInt();
		
		
		inputAndOutput.print("大小:");
		id = 0;
		for (Size size : Size.values()) {
			inputAndOutput.print(++id + "." + size);
		}
		inputAndOutput.print("请选择:");
		int s = inputAndOutput.nextInt();
		
		
		return new Clothes(super.getName(), super.getPrice(), Color.values()[c - 1], Size.values()[s - 1]);
		
	}

	@Override
	public Goods showDetails() {
		// TODO Auto-generated method stub
		inputAndOutput.print(toString());
		return confirm();
	}
}

//但是衣服还有具体的颜色和大小,so这边又加了个类,就是加了两个设置的方法
public class Clothes extends ClothesType {

	public Clothes(String name, double price, Color color, Size size) {
		super(name, price);
		setColor(color);
		setSize(size);
	}
	
	
	@Override
	public String toString() {
		return "Clothes [Size" + getSize() + ", Color()=" + getColor()
						+ ", " + super.toString() + "]";
	}

}


//AllGoods和AllUsersByArrayList就是用列表做的,去操作那个抽象列表的接口,然后就是一开始做了点初始化,还没想到和外部的数据交互,可以来个重定向啥的?


6.4 运行界面
  登录成功并显示主菜单:

  浏览商品,因为特别少,所以随便浏览:

  查看商品的详细信息,确定购买数量并加入购物车内:

  然后来一个买衣服的:

  根据关键字进行搜索,找到了要(只是)买(看看)的商品:

  显示个人信息,以后会在这边加个什么编辑操作:

  结算购物车:

  退出:

  做的比较简陋……后面再改


3.使用码云管理Java代码


4.PTA实验

  • 4-1,首先这道题当时寒假做的时候,还不是很懂匿名内部类和接口,所以有些参考,csdn博客园。现在能看懂自己写的代码了。就是在创建这个MyStarter的同时,在它的参数后面跟上匿名内部类的定义,完成题目所要求的的操作即可。主要就是这个n要用final修饰。
  • 5-1,当时写出来觉得自己超级了不起,其实现在看看也就那么回事,实现Comparable接口,然后重写compareTo()方法,然后就可以了,因为接口已经与sort的交互都已经写好了。
  • 5-2,与上题类似,就是在作业中用了匿名内部类和lamda表达式的方法,打开了新世界的大门。5-2的题解我在上面都说了,这边就不赘述了。

看的不过瘾的请点下面
回到顶部


  这周没什么废话,很累……做的不好,后面再改。

posted @ 2017-03-22 23:27  学Java救不了中国人  阅读(654)  评论(7编辑  收藏  举报