29、Java 编程:GUI 设计、事件处理与数据库连接

Java 编程:GUI 设计、事件处理与数据库连接

一、Java GUI 框架概述

Java 有两个重要的 GUI 框架,Swing 和 JavaFX。自 1998 年以来,Swing 一直是 Java 开发 GUI 应用程序的主要框架。但在 2011 年末,Oracle 向 Java 核心添加了一个较新的框架 JavaFX。JavaFX 提供了比 Swing 更丰富的组件集,但对于简单应用程序,JavaFX 使用起来更困难。如果你想了解更多关于 JavaFX 的信息,可以访问 Oracle 的 Getting Started with JavaFX 页面

简单 GUI 程序示例

可以创建一个程序,显示一个包含三个组件的框架:文本字段(JTextField)、按钮(JButton)和标签(JLabel)。用户在文本字段中输入文本,点击按钮后,程序将文本字段中的任何文本复制到标签上。以下是实现该功能的代码示例:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class CopyTextFrame extends JFrame {
    private static final long serialVersionUID = 1L;
    JTextField textField = new JTextField(10);
    JButton button = new JButton("Copy");
    JLabel label = new JLabel("");

    public CopyTextFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        add(textField);
        add(button);
        add(label);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                label.setText(textField.getText());
            }
        });
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new CopyTextFrame();
    }
}

事件处理

当你知道如何响应一种类型的事件时,响应其他类型的事件就很容易了。下面是一个能响应多种事件的程序示例,该程序可以实现美元和英镑之间的货币转换:

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.NumberFormat;
import java.util.Locale;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class MoneyFrame extends JFrame implements KeyListener, ItemListener, MouseListener {
    private static final long serialVersionUID = 1L;
    JLabel fromCurrencyLabel = new JLabel(" ");
    JTextField textField = new JTextField(5);
    JLabel label = new JLabel("           ");
    JComboBox<String> combo = new JComboBox<>();
    NumberFormat currencyUS = NumberFormat.getCurrencyInstance();
    NumberFormat currencyUK = NumberFormat.getCurrencyInstance(Locale.UK);

    public MoneyFrame() {
        setLayout(new FlowLayout());
        add(fromCurrencyLabel);
        add(textField);
        combo.addItem("US to UK");
        combo.addItem("UK to US");
        add(label);
        add(combo);
        textField.addKeyListener(this);
        combo.addItemListener(this);
        label.addMouseListener(this);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300, 100);
        setVisible(true);
    }

    void setTextOnLabel() {
        String amountString = "";
        String fromCurrency = "";
        try {
            double amount = Double.parseDouble(textField.getText());
            if (combo.getSelectedItem().equals("US to UK")) {
                amountString = " = " + currencyUK.format(amount * 0.61214);
                fromCurrency = "$";
            }
            if (combo.getSelectedItem().equals("UK to US")) {
                amountString = " = " + currencyUS.format(amount * 1.63361);
                fromCurrency = "\u00A3";
            }
        } catch (NumberFormatException e) {
        }
        label.setText(amountString);
        fromCurrencyLabel.setText(fromCurrency);
    }

    @Override
    public void keyReleased(KeyEvent k) {
        setTextOnLabel();
    }

    @Override
    public void keyPressed(KeyEvent k) {
    }

    @Override
    public void keyTyped(KeyEvent k) {
    }

    @Override
    public void itemStateChanged(ItemEvent i) {
        setTextOnLabel();
    }

    @Override
    public void mouseEntered(MouseEvent m) {
        label.setForeground(Color.red);
    }

    @Override
    public void mouseExited(MouseEvent m) {
        label.setForeground(Color.black);
    }

    @Override
    public void mouseClicked(MouseEvent m) {
    }

    @Override
    public void mousePressed(MouseEvent m) {
    }

    @Override
    public void mouseReleased(MouseEvent m) {
    }
}

public class ShowMoneyFrame {
    public static void main(String args[]) {
        new MoneyFrame();
    }
}

代码分析

  1. 组件添加 MoneyFrame 类的构造函数向新窗口添加了四个组件:一个显示货币符号的标签、一个用户输入金额的文本字段、一个显示转换结果的标签和一个用于选择转换方向的组合框。
  2. 事件监听 MoneyFrame 类实现了三个接口: KeyListener ItemListener MouseListener ,因此可以监听三种类型的事件。
    • KeyListener :当用户松开按键时,事件处理线程调用 keyReleased 方法,该方法调用 setTextOnLabel 方法进行货币转换。
    • ItemListener :当用户在组合框中选择一个选项时,事件处理线程调用 itemStateChanged 方法,该方法也调用 setTextOnLabel 方法。
    • MouseListener :当鼠标进入或离开标签时,分别调用 mouseEntered mouseExited 方法,改变标签文本的颜色。

JComboBox 的使用

在 Java 中,JComboBox(通常称为下拉列表)可以显示任何类型的项目。在上述示例中,声明 JComboBox<String> combo = new JComboBox<>(); 构造了一个条目类型为 String 的 JComboBox。如果应用程序有一个 Person 类,也可以声明 JComboBox<Person> peopleBox ,Java 会通过查找 Person 类中的 toString() 方法来确定如何在下拉列表中显示每个 Person 对象。

程序优化

可以修改上述复制文本的程序,使其在用户修改文本字段内容时自动更新标签文本,而无需点击按钮。可以通过为文本字段添加 KeyListener 来实现:

import java.awt.FlowLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class AutoUpdateFrame extends JFrame {
    private static final long serialVersionUID = 1L;
    JTextField textField = new JTextField(10);
    JLabel label = new JLabel("");

    public AutoUpdateFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        add(textField);
        add(label);
        textField.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                label.setText(textField.getText());
            }
        });
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new AutoUpdateFrame();
    }
}

流程图

graph TD;
    A[用户输入文本] --> B{是否按下按键};
    B -- 是 --> C[触发 keyReleased 事件];
    C --> D[更新标签文本];
    B -- 否 --> A;

表格:事件处理接口及方法

接口 必须实现的方法 事件触发条件
KeyListener keyReleased, keyPressed, keyTyped 按键释放、按下、键入
ItemListener itemStateChanged 组合框选项改变
MouseListener mouseEntered, mouseExited, mouseClicked, mousePressed, mouseReleased 鼠标进入、离开、点击、按下、释放

二、Java 数据库连接(JDBC)

JDBC 概述

Java 数据库连接(JDBC)类为大多数数据库管理系统提供了通用访问。只需获取你喜欢的供应商系统的驱动程序,定制每个示例中的一行代码,就可以开始使用。

创建数据库和表

以下是一个创建数据库和表的示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class CreateTable {
    public static void main(String args[]) {
        final String CONNECTION = "jdbc:derby:AccountDatabase;create=true";
        try (Connection conn = DriverManager.getConnection(CONNECTION);
             Statement statement = conn.createStatement()) {
            statement.executeUpdate("create table ACCOUNTS                     " +
                    "  (NAME VARCHAR(32) NOT NULL PRIMARY KEY, " +
                    "   ADDRESS VARCHAR(32),                   " +
                    "   BALANCE FLOAT                         )");
            System.out.println("ACCOUNTS table created.");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行示例的步骤

  1. 下载数据库驱动 :运行上述示例需要一个数据库驱动,示例中使用的是 Derby JDBC 驱动。当安装 Java 9 时,不会获得 org.apache.derby.jdbc 包,需要从 http://db.apache.org/derby/derby_downloads.html 下载一个名为 derby.jar 的单独文件。
  2. 配置 IDE :下载 derby.jar 后,需要告诉 IDE 在哪里找到该文件。不同 IDE 的配置方法如下:
    • Eclipse :选择 Project ➪ Properties ,在弹出的对话框中选择 Java Build Path ,然后选择 Libraries 选项卡,点击 Add External JARs 按钮,导航到计算机硬盘上的 derby.jar 文件。
    • IntelliJ IDEA :选择 File ➪ Project Structure ,在弹出的对话框中选择 Libraries ,点击加号(+)图标,在下拉框中选择 Java ,导航到计算机硬盘上的 derby.jar 文件。
    • NetBeans :选择 File ➪ Project Properties ,在弹出的对话框中选择 Libraries ,然后选择 Run 选项卡,点击 Add JAR/Folder 按钮,导航到计算机硬盘上的 derby.jar 文件。

代码分析

  1. DriverManager.getConnection :建立与特定数据库的会话。在上述示例中,调用 getConnection 方法创建一个 AccountDatabase 并打开与之的连接。如果数据库已经存在, ;create=true 文本将不起作用。
  2. conn.createStatement :创建一个语句对象。在 Java 数据库连接中,需要创建一个语句对象,然后可以多次使用该对象,使用不同的 SQL 字符串向数据库发出不同的命令。
  3. try-with-resources :确保在使用完资源后自动关闭和释放资源,并处理异常。

SQL 命令的使用

在上述示例中, executeUpdate 调用包含一个 SQL 命令字符串,该字符串创建了一个名为 ACCOUNTS 的新数据库表,包含三个列: NAME ADDRESS BALANCE 。当编写 Java 数据库程序时,需要编写普通的 SQL 命令,并将这些命令包装在 Java 方法调用中。

数据库选择

数据库有多种类型和供应商,2017 年的顶级数据库供应商包括 Oracle、Microsoft、IBM 和 SAP,一些流行的开源数据库包括 PostgreSQL 和 Oracle 的 MySQL。上述示例使用的是 Apache Software Foundation 的开源数据库 Apache Derby。如果不想使用 Apache Derby,需要替换示例中的 CONNECTION 字符串,具体使用的字符串取决于所使用的数据库软件和其他因素,需要查看数据库供应商的文档。

旧版本 JDBC 驱动的处理

如果数据库驱动不符合 JDBC 4.0 标准,需要在每个示例中添加一些额外的语句:

final public String DRIVER = "com.databasevendorname.databasebrandname.maybeotherstuff";
try {
    Class.forName(DRIVER).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
    e.printStackTrace();
}

流程图

graph TD;
    A[开始] --> B[建立数据库连接];
    B --> C[创建语句对象];
    C --> D[执行 SQL 命令];
    D --> E{是否成功};
    E -- 是 --> F[输出成功信息];
    E -- 否 --> G[输出错误信息];
    F --> H[关闭资源];
    G --> H;
    H --> I[结束];

表格:代码关键部分说明

代码部分 说明
DriverManager.getConnection 建立与数据库的连接
conn.createStatement 创建语句对象
statement.executeUpdate 执行 SQL 命令
try-with-resources 自动关闭和释放资源,处理异常

通过以上内容,我们了解了 Java GUI 设计、事件处理以及数据库连接的相关知识和技术,希望这些内容对你的 Java 编程学习有所帮助。

三、Java 中的内部类

内部类概述

在 Java 中,可以在一个类的内部定义另一个类,这就是内部类。内部类可以访问外部类的成员,对于事件处理等场景非常有用。

内部类示例

以下是一个包含内部类的示例代码,该程序实现了一个简单的猜数字游戏:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GameFrame extends JFrame {
    private static final long serialVersionUID = 1L;
    int randomNumber = new Random().nextInt(10) + 1;
    int numGuesses = 0;
    JTextField textField = new JTextField(5);
    JButton button = new JButton("Guess");
    JLabel label = new JLabel(numGuesses + " guesses");

    public GameFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        add(textField);
        add(button);
        add(label);
        button.addActionListener(new MyActionListener());
        pack();
        setVisible(true);
    }

    class MyActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String textFieldText = textField.getText();
            if (Integer.parseInt(textFieldText) == randomNumber) {
                button.setEnabled(false);
                textField.setText(textField.getText() + " Yes!");
                textField.setEnabled(false);
            } else {
                textField.setText("");
                textField.requestFocus();
            }
            numGuesses++;
            String guessWord = (numGuesses == 1) ? " guess" : " guesses";
            label.setText(numGuesses + guessWord);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new GameFrame();
    }
}

代码分析

  • 外部类 GameFrame :继承自 JFrame ,包含随机数、猜测次数、文本字段、按钮和标签等成员变量。在构造函数中设置窗口属性,并为按钮添加事件监听器。
  • 内部类 MyActionListener :实现了 ActionListener 接口,重写了 actionPerformed 方法。在该方法中,根据用户输入的数字与随机数进行比较,更新按钮和文本字段的状态,并更新猜测次数和标签文本。

匿名内部类

如果一个内部类只使用一次,可以将其定义为匿名内部类。以下是将上述示例改为使用匿名内部类的代码:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GameFrame extends JFrame {
    private static final long serialVersionUID = 1L;
    int randomNumber = new Random().nextInt(10) + 1;
    int numGuesses = 0;
    JTextField textField = new JTextField(5);
    JButton button = new JButton("Guess");
    JLabel label = new JLabel(numGuesses + " guesses");

    public GameFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        add(textField);
        add(button);
        add(label);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String textFieldText = textField.getText();
                if (Integer.parseInt(textFieldText) == randomNumber) {
                    button.setEnabled(false);
                    textField.setText(textField.getText() + " Yes!");
                    textField.setEnabled(false);
                } else {
                    textField.setText("");
                    textField.requestFocus();
                }
                numGuesses++;
                String guessWord = (numGuesses == 1) ? " guess" : " guesses";
                label.setText(numGuesses + guessWord);
            }
        });
        pack();
        setVisible(true);
    }
}

public class Main {
    public static void main(String[] args) {
        new GameFrame();
    }
}

表格:普通类、内部类和匿名内部类对比

类类型 定义方式 使用场景 优缺点
普通类 独立定义 通用的类定义,可被多个地方使用 结构清晰,易于维护,但对于只在特定场景使用的类,可能会增加代码冗余
内部类 在另一个类内部定义 用于处理与外部类紧密相关的事件或功能 可以直接访问外部类的成员,提高代码的内聚性,但可能会使代码结构变得复杂
匿名内部类 直接在使用的地方定义,没有类名 只使用一次的类,如事件监听器 代码简洁,减少类的定义,但可读性可能会降低,且难以复用

流程图

graph TD;
    A[用户点击按钮] --> B[获取用户输入];
    B --> C{输入是否正确};
    C -- 是 --> D[禁用按钮和文本字段,显示正确信息];
    C -- 否 --> E[清空文本字段,请求焦点];
    D --> F[增加猜测次数,更新标签];
    E --> F;
    F --> G[结束];

程序优化建议

  • 代码复用 :可以将一些通用的功能提取到独立的方法中,提高代码的复用性。
  • 异常处理 :在处理用户输入时,添加异常处理机制,避免因输入非数字而导致程序崩溃。
  • 界面美化 :可以使用 Java 的 GUI 设计工具,如 WindowBuilder、GUI Designer 等,美化程序界面。

通过以上内容,我们深入了解了 Java 中的内部类和匿名内部类的使用,以及如何在 Java 中进行 GUI 设计、事件处理和数据库连接。这些技术可以帮助我们开发出更加功能丰富、交互性强的 Java 应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值