一.事件处理
对于采用了图形用户界面的程序员来说,事件控制是非常重要的.到目前为止,我们编写的图形用户界面程序都仅仅是完成了界面,而并没有任何实际的功能,要实现对应的功能,必须进行事件处理.
**用户与GUI组件进行交互就会发生事件.**如:按下一个按钮,用键盘输入一个字符,点击鼠标等等.
当然我们要关注的并不是事件是如何产生的,而是讨论发生事件之后,我们应该如何处理.
java中,事件处理的基本思路如下:
一个源(事件源)产生一个事件(事件对象)并把它送到监听器那里.监听器只是简单的等待.直到他收到一个事件,**一旦事件被接收监听器将处理这件事件.
一个事件源必须注册监听器以便监听器可以接受一个特定事件的通知.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Demo extends JFrame {
public Demo() throws HeadlessException {
this.setSize(400, 250);
JPanel jPanel = new JPanel();
JButton jButton = new JButton("登陆");
JButton jButton1 = new JButton("取消");
jPanel.add(jButton);
jPanel.add(jButton1);
this.add(jPanel);
this.setVisible(true);
//为两个按钮创建监听器
/*
参数需要传入一个事件对象,ActionListener是一个接口
我们可以在自建类实现这个接口,但是比较麻烦,并且参数传递起来比较苦难
因此在这次需要用到内部类
*/
jButton.addActionListener(new MyAction());
}
class MyAction implements ActionListener{
//重写类中的方法
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("被点击了");
}
}
}
这就是第一种写法.我们还有另一种方式.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Demo1 extends JFrame {
public Demo1() throws HeadlessException {
this.setSize(400, 250);
JPanel jPanel = new JPanel();
JButton jButton = new JButton("登陆");
JButton jButton1 = new JButton("取消");
jPanel.add(jButton);
jPanel.add(jButton1);
this.add(jPanel);
this.setVisible(true);
//为两个按钮创建监听器
/*
创建一个匿名内部类,这样就不用像之前那种方式繁琐
看似是new接口,但是实际上我们重写了接口中的方法,是新建一个没有名字的实现了接口的类的对象
*/
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("被点击了");
}
});
}
}
这个方式创造监听器明显比第一种要简单许多
鼠标事件
鼠标监听器是专门为鼠标事件设计的监听器
//创建鼠标监听器
//如果我们通过new接口来创建匿名内部类,需要重写接口中的所有方法
jButton.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("单击鼠标监听");
}
@Override
public void mousePressed(MouseEvent e) {
System.out.println("鼠标按下去的监听");
}
@Override
public void mouseReleased(MouseEvent e) {
System.out.println("鼠标释放的监听");
}
@Override
public void mouseEntered(MouseEvent e) {
System.out.println("鼠标移入的监听");
}
@Override
public void mouseExited(MouseEvent e) {
System.out.println("鼠标移出的监听");
}
});
当然很明显我们能发现一个问题,当我们只需要处理一种事件的时候,我们重写方法就变得非常繁琐.
处理方法首先我们想到的是新建一个类实现接口然后重写这些方法.方法里面可以什么都不写,我们只重写我们需要的方法就可以.然后在这里new出来我们自己新建的类调用一下就好了.
当然java也考虑到了这个问题.因此就有了下面这种方法
//创建鼠标监听器
/*
我们new出来鼠标适配器这个类的对象
这个类实现了MouseListener, MouseWheelListener, MouseMotionListener三个接口
因此重写了这三个接口中所有的抽象方法,只不过没有方法体
我们就可以需要什么,重写什么方法
*/
jButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("鼠标点击了");
}
});
这样我们的使用就可以方便很多
键盘监听
//创建键盘监听器
//当然一般我们一般都是输入密码完成后按下回车登录,因此一般键盘监听器监听的是文本框事件
//这里一样,我们只需要new键盘适配器对象就好了
jPasswordField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//我们可以使用这个方法来到得到键对应的值
System.out.println(e.getKeyCode());
//当然KeyEvent类中也有一些静态成员变量已经定义好了,我们也可以直接用,如回车KeyEvent.VK_ENTER.....
if (e.getKeyCode() == 10){
System.out.println("按下了回车");
}
}
});
二.对话框
当我们监听到一个事件之后,需要给用户一个提示,这个时候就不能在控制台进行输出,用户看不到控制台.这个时候就需要**对话框.**这个对话框指的是消息对话框.
JOptionPane对话框有两种类型:
消息对话框
showMessageDialog(),消息对话框
主要有五种消息类型,类型不同,图标不同:
ERROR_MESSAGE 错误消息提示
INFORMATION_MESSAGE 信息提示
WARNING_MESSAGE 警告提示
QUESTION_MESSAGE 问题提示
PLAIN_MESSAGE 简洁提示
jPasswordField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10){
//四个参数的方法,这里后面的两个参数就是默认情况下的参数
JOptionPane.showMessageDialog(null, "按下回车", "消息", JOptionPane.INFORMATION_MESSAGE);
//两个参数的方法
JOptionPane.showMessageDialog(null, "按下回车");
}
}
});
确认对话框
showConfirmDialog();确认对话框
主要有四种消息类型,类型不同,样式不同:
DEFAULT_OPTION 默认选项
YES_NO_OPTION 是/否选项
YES_NO_CANCEL_OPTION 是/否/取消选项
OK_CANCEL_OPTION 确定/取消
jPasswordField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10){
//后面两个参数依旧是默认情况下的参数
JOptionPane.showConfirmDialog(null, "按下回车", "选择一个选项", JOptionPane.YES_NO_CANCEL_OPTION);
JOptionPane.showConfirmDialog(null, "按下回车");
}
}
});
三.内部类
什么是内部类
把类定义在另一个类的内部,那么这个类就被称为内部类.
如果在类A的内部再定义一个类B,那么B就称为内部类(或者嵌套类).而类A则称为外部类(或者宿主类).
内部类的特点
内部类依然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号.
内部类不能用普通的方式访问.内部类是外部类的一个成员,因此内部类可以自由的访问外部类的成员变量,即使是用private修饰的.
其他类也可以调用这类中的内部类,但是内部类就必须是静态的.但是当内部类声明成静态的,就不能随便访问外部类的成员变量,只能访问外部类的静态成员变量.
内部类的分类
内部类一般来说包括四种
1.成员内部类
成员内部类就是位于外部类成员位置的类.
特点:可以使用外部类中所有的成员变量和成员方法.(包括被private修饰的)
当然如果我们的内部类不想轻易被任何人访问,可以选择使用private修饰内部类,这样我们就无法通过创建对象的方式来访问,想要访问只需要在外部类中定义一个public修饰的方法,间接调用.这样做的好处是可以在public方法中加入一些判断语句,起到数据安全的作用.
public class Demo {
private int num = 0;
public void test(){
//一般在本类中使用内部类
new CYl().test();
}
private class CYl{
public void test(){
//在内部类可以直接使用外部类的成员,即使是使用private修饰的
num++;
System.out.println(num);
}
}
public static void main(String[] args) {
new Demo().test();
}
}
2.静态内部类
当然内部类也属于成员.可以使用static关键字来修饰.与静态方法的使用一样.静态内部类只能访问静态的成员变量.
public class Demo1 {
private static int num = 0;
public void test(){
new JTl().test();
}
//静态内部类只能访问外部的静态成员变量
private static class JTl{
public void test(){
num++;
System.out.println(num);
}
}
public static void main(String[] args) {
new Demo1().test();
}
}
3.局部内部类
局部内部类就是定义在一个方法或者一个作用域里面的类.主要是作用域发生了变化,只能在自身所在的方法和属性中被使用.
public class Demo2 {
private int num = 0;
public void test(){
//从内部类访问方法变量,需要将方法变量设置为final型
int n = 0;
class JBl{
public void test(){
System.out.println(num++);
//即使没有被final修饰,也无法进行运算操作
System.out.println(n);
}
}
new JBl().test();
}
public static void main(String[] args) {
new Demo2().test();
}
}
4.匿名内部类
一个没有名字的类,是内部类的简化写法.
那这个其实在上面已经提到了匿名内部类,他相比较于内部类来说语法更加简单.但也是非常实用.
内部类的意义
众所周知,我们的C++程序语言是多继承制的,而多继承明显的好处就是,相对而言只需要写较少的代码即可完成一个类的定义,因为我们可以通过继承其他类来获取别人的实现.
但是,他有一个致命性的缺陷,那就是容易出现**钻石继承结构.**比如说两个父类A和B都有name这个属性,然后C类继承了A类和B类,那么C类中的name到底是哪一个呢?
简而言之,内部类的意义有以下两个主要的意义
1.封装性
作为一个类的编写者,我们很显然要对这个类的使用访问者的权限做出一定的限制,我们需要将我们不愿意被别人看到的操作隐藏起来.而内部类就可以将这些操作封装起来.
2.实现多继承
比如AB两个类分别有不同的两个属性,然后C类需要继承这两个类来对属性进行访问.显然java是不支持多继承的.那么我们就可以在C类的内部定义两个内部类分别继承AB两个类,那么在外部的C类就可以访问到AB两个类中的属性了.
四.补充sort排序的另一种方式
在之前我们的API学习中我们提到过API中Arrays类的sort方法.这个方法可以对我们的数组进行排序.当然我们自建类是也可以进行排序的.这个排序就需要我们对于我们的自建类实现一个名为Comparable的接口,此接口支持泛型.实现接口就要实现接口中的抽象方法.这个抽象方法就规定了我们的排序规则.
public class Car implements Comparable<Car>{
private int num;
private String name;
public Car(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Car o) {
//按照num升序进行排序
return (this.num - o.num);
}
}
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Car car1 = new Car(101, "car1");
Car car2 = new Car(102, "car2");
Car car3 = new Car(103, "car3");
Car car4 = new Car(104, "car4");
Car[] cars = {car3,car1,car4,car2};
Arrays.sort(cars);
System.out.println(Arrays.toString(cars));
}
}
这个就是以前学过的方法.当然,我们经过查阅API发现.sort方法的参数不止只有传入数组名这一个,还有其他的方法.
public class Car{
private int num;
private String name;
public Car(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
@Override
public String toString() {
return "Car{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
import java.util.Comparator;
//我们可能在许多地方都使用到汽车排序这种情况,因此需要这个类
public class CarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getNum()-o2.getNum();
}
}
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Car car1 = new Car(101, "car1");
Car car2 = new Car(102, "car2");
Car car3 = new Car(103, "car3");
Car car4 = new Car(104, "car4");
Car[] cars = {car3, car1, car4, car2};
Arrays.sort(cars,new CarComparator());
System.out.println(Arrays.toString(cars));
}
}
当然我们可以使用匿名内部类的写法.
Arrays.sort(cars, new Comparator<Car>() {
@Override
public int compare(Car o1, Car o2) {
return o1.getNum()-o2.getNum();
}
});