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的題解我在上面都說了,這邊就不贅述了。

看的不過癮的請點下面
回到頂部


  這周沒什么廢話,很累……做的不好,后面再改。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM