201521123091 《Java程序设计》第6周学习总结
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
。
看的不过瘾的请点下面
回到顶部
这周没什么废话,很累……做的不好,后面再改。