Java 第六周總結
第六周的作業。
目錄
1.本章學習總結
2.Java Q&A
3.碼雲上代碼提交記錄及PTA實驗總結
1.本章學習總結
1.1 面向對象學習暫告一段落,請使用思維導圖,以封裝、繼承、多態為核心概念畫一張思維導圖,對面向對象思想進行一個總結。
注1:關鍵詞與內容不求多,但概念之間的聯系要清晰,內容覆蓋面向對象的核心內容即可。
注2:顏色要少、連線不要復雜,必要的時候要在連線上進行說明。

1.2 可選:使用常規方法總結其他上課內容。
其他上課內容,那就是GUI與Swing咯
抽象窗口工具包(Abstract Windows Toolkit AWT)是Java1.0提供的用來編寫圖形界面的類庫。Java1.1的AWT引入了事件模型,從Java 2開始使用Swing,Swing就是基於AWT的架構,它的基礎也還是AWT的事件模型。Swing因為也是用Java來寫的,所以它的可移植性也是得到了保證。
簡單來說,我膚淺地理解用Java來編寫圖形界面就是拖控件、寫監聽器。
拖控件:雖然可以手敲代碼來進行界面的布局,但很顯然有工具幫助我們去這樣做,我們可以通過可視化的IDE來省略很多的代碼量。所以這步可以不用多花心思,對於一些簡單的布局,當個demo練練手就好了。(畢竟,Java是為了提高程序猿的生產效率的→_→)
寫監聽器:組件可以發起一個事件,然后被一個甚至是多個監聽器接受,然后處理事件。事件處理的細節放在監聽器的內部。監聽器一般都是用內部類去實現,這不僅是因為邏輯上的包含與被包含的關系,也是因為內部類含有對外部類對象的引用,這樣比較方便。
就簡單介紹這么多,下期講集合有的說了……
2.Java Q&A
1.clone方法
1.1 Object對象中的clone方法是被protected修飾,在自定義的類中覆蓋clone方法時需要注意什么?
clone()方法在Object類當中的聲明:protected native Object clone() throws CloneNotSupportedException;,clone()方法是用protected修飾的,按理說對於子類是可見的,但是我們想直接調用的時候,就會編譯出錯,"The method clone() from the type Object is not visible"。protected的保護原則比較微妙,子類只有和父類在同一個包內才能訪問到使用父類的protected方法。所以如果我們想直接調用,就把類放在java.lang包里?這顯然是不可能的。為了能在其他包里面也能用上clone()方法,我們必須要重寫clone()方法,並且聲明它為public[真有意思啊,這個鏈接的網站叫做棧溢出]。然而這個時候,還是不能正常的復制,我們嘗試運行,會拋出 CloneNotSupportedException的錯誤,這是因為Java說了,一個不想操作Cloneable接口的類不是一個好類,如果要使用clone()方法,就要拋異常,所以我們應該讓要實現復制的類去操作Cloneable接口。還有一件事情,Cloneable接口里面是沒有clone()方法的,這是一個空的接口。
還有就是關於淺復制和深復制的問題,如果簡單的復制,對於基本類型就是復制值,對於對象就是復制引用。那么如果被復制的對象中有一個域(不是基本類型)發生了改變,那么復制出來的對象的相應域也要改變。
一個淺復制大概是這樣的:

所以為了要實現徹徹底底的復制,藕斷絲連的復制,我們就要進行深復制,也就是將需要復制對象里面的內容全部都給復制出來。
1.2 自己設計類時,一般對什么樣的方法使用protected進行修飾?以作業Shape為例說明。
protected修飾的時候只要考慮到它的作用就行了,當我們在寫對於類用戶來說是不可見的,但是對於任何子類或者其他位於同一個包內的類來說,都是可以訪問的方法時,我們就用protected。
abstract class Shape {
private final static double PI = 3.14;
public abstract double getPerimeter();
public abstract double getArea();
}
//有兩個類Circle和Rectangle繼承Shape類
我這邊用的是public,其實用protected也是可以的,比如我這邊把這兩個方法現在就修飾為protected,那么對於繼承這個抽象類的子類來說,首先它們都是圖形,我有計算它們的周長和面積的需要。但是我覺得類用戶沒有什么必要去獲得它們的周長和面積,或者說我想對類用戶隱藏這個方法,可能僅僅只是為了讓這個子類去使用,那么我就設置成protected。如果這個比較別扭,那就加個這個例子:
public static double getPi() {
return PI;
}
對於private變量PI,我只給子類提供訪問的方法,但是對於類用戶卻是不可訪問的,就是這樣。
1.3 在test1包中編寫簡單的Employee類,在test2包中新建一個TestProtected類,並在main中嘗試調用test1包中的Employee的clone方法克隆一個新對象,能否成功?為什么?
package test1;
public class Employee implements Cloneable {
private String name;
private double salary;
public Employee(String name, double salary) {
super();
this.name = name;
this.salary = salary;
}
@Override
protected Employee clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return null;
}
}
package test2;
import test1.*;
public class TestProtected {
Employee employee = new Employee("ss", 100.0);
Employee employee2 = employee.clone();
}
並不可以,會出現clone()方法不可見的錯誤。protected對於包外的類也是不可見的。所以一般我們寫clone()方法,一般都用public來修飾。
2.使用匿名類與Lambda表達式改寫題集面向對象2-進階-多態接口內部類的題目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;
}
}
};

使用lamda表達式:
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;
}
});
運行結果同上。
3.分析下列代碼,回答shapeComparator所指向的對象與Comparator接口有什么關系?
Comparator<Shape> shapeComparator = new Comparator<Shape>() {
@Override
public int compare(Shape o1, Shape o2) {
//你的代碼
}
};
shapeComparator這個對象操作了Comparator這個接口。要創建匿名內部類也要和定義類的時候操作接口一樣,重寫接口當中的抽象方法。匿名內部類既可以擴展類,也可以實現接口,但是不能像正規的繼承那樣兩者兼備,而且如果是實現接口,也只能實現一個接口。在Java8之前要求匿名內部類使用的外部定義的對象必須得是final類型的,但是在Java8之后就取消了這個限制。
4.GUI中的事件處理
就拖控件,是不能與用戶進行交互的。就假如按下按鈕的時候,什么事情都不會發生,因為我們還需要深入進去編寫一些代碼來決定到底應該發生什么事情。
4.1 寫出事件處理模型中最重要的幾個關鍵詞。
事件:就是用戶在GUI組件上進行的操作,比如敲擊鍵盤 、點擊鼠標或者是關閉窗口之類的動作,還有像上課講的什么焦點事件也是。
事件源:能夠產生事件的GUI組件對象,如按鈕、文本框這些。事件監聽器就是要注冊在事件源上。
事件監聽器:就是一組接口,每種事件類都有一個負責監聽這種事件的接口,接口中定義了處理該事件的抽象方法。要是想使用事件監聽器,就應該在前面就先注冊好。
事件適配器:是一個接口的實現類,定義事件監聽器的時候可以繼承這個適配器,並重寫部分所需要的方法,而不需要定義所有的抽象方法。
JAVA圖形界面(GUI)之事件處理機制
java心得--GUI事件處理
簡述GUI系統的事件機制
4.2 使用代碼與注釋,證明你理解了事件處理模型。
使用匿名內部類來實現接口:
public EventMainGUI1 (String title){super(title);}
...
EventMainGUI1 f=new EventMainGUI1 ("hello");//初始化窗體,並將標題取為hello
final JButton b = new JButton("1");//創建一個按鈕,這個東西就是一個事件源,初始化文本為1
b.addActionListener(new ActionListener(){
//注冊監聽器,然后每觸發一次,都將顯示的數字自增一次
public void actionPerformed(ActionEvent evt){
b.setText(new Integer(++count).toString());
}
});
f.add(b);
f.setSize(100,100);
f.setBackground(Color.blue);
f.setVisible(true);
f.add(b);//添加按鈕
f.setSize(100,100);//設置窗體大小
f.setBackground(Color.blue);//設置窗體顏色
f.setVisible(true);//設置窗體可見
使用implements來實現接口public class EventMainGUI2 extends Frame implements ActionListener
public interface ActionListener extends EventListener {
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);
}
ActionListener這個接口里面只有這么一個抽象方法,當一個時間發生的時候,就會響應。
關於適配器:
public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener {
/**
* {@inheritDoc}
*/
public void mouseClicked(MouseEvent e) {}
/**
* {@inheritDoc}
*/
public void mousePressed(MouseEvent e) {}
/**
* {@inheritDoc}
*/
public void mouseReleased(MouseEvent e) {}
/**
* {@inheritDoc}
*/
public void mouseEntered(MouseEvent e) {}
/**
* {@inheritDoc}
*/
public void mouseExited(MouseEvent e) {}
/**
* {@inheritDoc}
* @since 1.6
*/
public void mouseWheelMoved(MouseWheelEvent e){}
/**
* {@inheritDoc}
* @since 1.6
*/
public void mouseDragged(MouseEvent e){}
/**
* {@inheritDoc}
* @since 1.6
*/
public void mouseMoved(MouseEvent e){}
}
適配器里面的方法都是空的,然后我們只要按照自己的需要重寫其中任意數量個方法即可。
class MyMouseListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
super.mouseClicked(e);
}
}
//即使我里面什么東西都沒寫,還是可以編譯通過,但是操作MouseListener接口就得實現全部的抽象方法。這樣可以讓編寫監聽器類變得更加容易。
最后就是注冊多個監聽器:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
class MyMouseListener extends MouseAdapter {
public MyMouseListener(int i) {
// TODO Auto-generated constructor stub
System.out.println("注冊MyMouseListener,參數為" + i);
}
}
class MyActionListener implements ActionListener {
public MyActionListener(int i) {
// TODO Auto-generated constructor stub
System.out.println("注冊MyActionListener,參數為" + i);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
public class EventMainGUI2 extends Frame implements ActionListener{
JButton b;
public EventMainGUI2(String title) {
super(title);
setLayout(new FlowLayout());
b=new JButton("Mouse 1");
b.addMouseListener(new MyMouseListener(1)); //注冊MyMouseListener
b.addActionListener(new MyActionListener(1)); //注冊 MyActionListener
add(b);
setSize(300,300);
setBackground(Color.blue);
setVisible(true);
}
public static void main(String args[]) {
EventMainGUI2 f = new EventMainGUI2("hello");
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
//運行結果:
//注冊MyMouseListener,參數為1
//注冊MyActionListener,參數為1
5.結對編程:面向對象設計(大作業2-非常重要,未完成-2)
繼續完善上周的項目作業。考核點如下:
吐血,終於寫完了,很丑陋,請各位看官按捺住自己內心不屑的心情……或者pass過這一段
5.1 嘗試使用圖形界面改寫。
先貼運行結果,然后看着講吧……

首先要說的就是這個界面就搞了好久,我好像沒有在NetBeans上找到密碼框?於是機智的先用文本框取而代之,然后在eclipse當中換成了密碼框。當然這個修改的效果是血崩式的,因為密碼框的返回值是char[]類型的,所以有關密碼的所有類型都得改成這個char[]。不改的話,那一直都是null。我覺得圖形界面配合控制台輸入輸出對於我這種菜鳥來說是一個很破費的方法,隨時發現問題在什么地方。

本來是想在這邊寫“歡迎,隔壁老王!”這種提示語之類的,但是后面才想到,不加了,懶死了……
最簡單的就是退出按鈕,只要這樣寫上System.exit(0);就好了,就全部退出了,也不要加什么確定離開此頁面嗎?你真的不再逛逛之類的話了,太矯情了……
然后看一下瀏覽商品:

就是在滾動面板上加了jList,說起來好像是很簡單,但是其實我覺得也不容易的。這邊全部都是用默認的設置(因為比較懶)。我之前的代碼是有寫選擇是否有進入購買界面,那這邊其實就是相當於彈出一個窗口。
接下來是這次比較核心的代碼實現了:
private void surfAllGoods() {
JFrame surfFrame = new JFrame();
AllGoods allGoods = new AllGoods();
Vector<Goods> vector = new Vector<Goods>(allGoods.getGoodsList());
JList<Goods> jList = new JList<Goods>(vector);
jList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
@SuppressWarnings("rawtypes")
JList source = (JList)e.getSource();
Goods goods = (Goods)source.getSelectedValue();
if (goods.getClass() == Book.class) {
BookFrame bookFrame = new BookFrame(goods);
bookFrame.setVisible(true);
} else {
ClothesFrame clothesFrame = new ClothesFrame(goods);
clothesFrame.setVisible(true);
}
}
}
});
surfFrame.setSize(500, 200);
surfFrame.add(new JPanel().add(new JScrollPane(jList)));
surfFrame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
surfFrame.setVisible(true);
}
這邊以瀏覽商品為例,其他的都差不多,尤其是搜索,其實就是搜索窗口,再加上這個,又是很偷懶……
JList有一些初始化的方法,我這邊用的是Vector,當然下面我們還會見到用別的數據結構來初始化的例子。當然這個JList是不能直接用ArrayList直接初始化的,所以要多這么一步,早知道當初就搞成Vector了(C/C++寫多了,對Vector有莫名好感)。這樣一個列表就搞好了,然后把它放在一個滾動面板上,這樣如果列表里面有很多東西的話,我就可以拖動滾動條了。給JList加上一個監聽器,這邊首選了適配器,反正我只要寫一個函數。如果雙擊的話,通過getSelectedValue()我就知道我在選擇哪個商品,然后就能進入相應的商品詳情頁。
只要雙擊某一個商品,就能進入當商品詳情頁:

左邊的這個微調器做了個小小的限制,范圍[0, 1000],步長為1。
jSpinner1 = new javax.swing.JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));。
然后上面的標簽顯示商品的具體信息,據說在標簽里面用"\n"是並沒有什么用的,親測也確實如此,所以按照網上大神的指點就是改成<html>...<br>...<html>這樣的形式,來完成換行。

點擊確認加入購物車后,會有加入成功的提示,當然數量為0,也是不會加進去的,這邊長度沒設置好……

然后關閉,除了最外面的菜單窗口,其他所有的窗口都是設置為僅關閉當前窗口,就是大概要這么寫:
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
衣服的商品詳情頁只是稍稍不一樣而已:

然后就是搜索商品了,這邊就貼一下圖,因為沒什么特別要講的,大致都和上面的一樣。


顯示個人信息,如果要拓展的話,這里面應該還要加進去可以編輯個人信息什么的:

然后是購物車:

這邊也是做得比較簡單,就是加了個右鍵菜單,這邊貼一下右鍵菜單的具體實現吧:
JFrame cartFrame = new JFrame();
DefaultListModel<Item> defaultListModel = new DefaultListModel<Item>();
for (Item item : shoppingCart.getItems()) {
defaultListModel.addElement(item);
}
JList<Item> jList = new JList<Item>(defaultListModel);
cartFrame.setLayout(new BorderLayout());
cartFrame.add(new JPanel().add(new JScrollPane(jList)), BorderLayout.CENTER);
jList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
@SuppressWarnings("rawtypes")
JList source = (JList)e.getSource();
Item item = (Item)source.getSelectedValue();
JPopupMenu popupMenu = new JPopupMenu();
JMenuItem menuItem = new JMenuItem("刪除(D)");
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
defaultListModel.removeElement(item);
shoppingCart.getItems().remove(item);
shoppingCart.setTotalPrice(
shoppingCart.getTotalPrice()
- item.getGoods().getPrice() * item.getNum());
if (shoppingCart.getItems().isEmpty()) {
cartFrame.setVisible(false);
new CartFrame(shoppingCart).setVisible(true);
}
}
});
popupMenu.add(menuItem);
popupMenu.show(cartFrame, e.getX(), e.getY());
}
}
});
JButton jButton = new JButton("清空購物車");
cartFrame.add(jButton, BorderLayout.SOUTH);
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cartFrame.setVisible(false);
JFrame jFrame = new JFrame();
JLabel jLabel = new JLabel();
String string = "";
string += "<html>";
for (Item item : shoppingCart.getItems()) {
string += ("確認購買" + item.getGoods() + "共, " + item.getNum() + "件<br>");
}
string += ("總價為" + shoppingCart.getTotalPrice() + "<html>");
jLabel.setText(string);
jFrame.add(jLabel);
jFrame.setSize(400, 300);
jFrame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jFrame.setVisible(true);
shoppingCart.clear();
}
});
cartFrame.setSize(400, 300);
cartFrame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
cartFrame.setVisible(true);
}
這邊是參考了Core Java高級特性里的代碼。如果一開始初始化JList的時候用上DefaultListModel,就可以隨時把DefaultListModel里面的數據增添及時的反映到列表里面,就是只要我有插入或者刪除,那么JList就會重繪。
然后就是右鍵菜單必須得在條目之前已經被選中的才能正確彈出,然后只有一個,就是刪除……
關於右鍵菜單,其實就是要先創建一個PopUpMeunu對象,然后往里面加入MenuItem對象,就是菜單項,再對相應的菜單項注冊他們的監聽器。MouseEvent.BUTTON3這個東西就是鼠標的右鍵。


最后清空購物車:

然后就沒什么東西了,畢竟業務函數上次都已經寫的差不多了,雖然這次改動也是相當大的,but總比一下子全部搞出來輕松不少了。
5.2 給出兩人在碼雲上同一項目的提交記錄截圖。
還是只有我一人,堅強的自己不需要抱抱0.0

5.3 與上周相比,項目的主要改動是什么?
最大的改動就是把搜索類和衣服類里面的輸入輸出去掉了,雖然整個Menu類都顯得相當臃腫,but其他的類看上去清爽了很多,我覺得……
還有就是輸入輸出改了,其實加了圖形界面之后改了很多東西。這邊貼上類圖吧,一些無關緊要的我就不放上去了。

3.碼雲上代碼提交記錄及PTA實驗總結
3.1 碼雲代碼提交記錄
- 在碼雲的項目中,依次選擇“統計-Commits歷史-設置時間段”, 然后搜索並截圖

3.2 PTA實驗
4.1 上次做過了,所以這邊就不再贅述了。
5.3 用數組來實現棧的操作,棧是一個先進先出的數據結構,只能在棧頂進行壓棧和彈棧的操作。就是要注意如果棧為空,則出棧和取棧頂都是無效操作。就是記得判空就好了。否則會報數組越界的錯誤。還有就是如果用ArrayList操作的話,就是用add()和remove()方法來實現,相對來說會簡單一些。
5.4 主要就是靜態內部類的使用。其實用法和加了static的域和方法都是大同小異的,就是這個類是屬於外部類的,而不是屬於任何一個由外部類產生的對象的。所以調用的時候可以或者說應該用類名來調用。(應該說是編程規范吧)還有,就是每個類都會產生一個.class文件,其中包含了如何創建該類型對象的全部信息。那么內部類該怎么辦呢,內部類也會產生一個.class文件,但是這個類文件是有一個命名規則的,就是外部類的名字,再去加上美元符號,再加上內部類的名字。至於匿名內部類,就是產生一個數字作為這個匿名內部類的標識符。所以這邊我們產生的類名就是這樣ArrayUtils$PairResult。
看的不過癮的請點下面
回到頂部
這周沒什么廢話,很累……做的不好,后面再改。
