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();
}
}
代码分析
- 组件添加 :
MoneyFrame类的构造函数向新窗口添加了四个组件:一个显示货币符号的标签、一个用户输入金额的文本字段、一个显示转换结果的标签和一个用于选择转换方向的组合框。 - 事件监听 :
MoneyFrame类实现了三个接口:KeyListener、ItemListener和MouseListener,因此可以监听三种类型的事件。- KeyListener :当用户松开按键时,事件处理线程调用
keyReleased方法,该方法调用setTextOnLabel方法进行货币转换。 - ItemListener :当用户在组合框中选择一个选项时,事件处理线程调用
itemStateChanged方法,该方法也调用setTextOnLabel方法。 - MouseListener :当鼠标进入或离开标签时,分别调用
mouseEntered和mouseExited方法,改变标签文本的颜色。
- KeyListener :当用户松开按键时,事件处理线程调用
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();
}
}
}
运行示例的步骤
- 下载数据库驱动 :运行上述示例需要一个数据库驱动,示例中使用的是 Derby JDBC 驱动。当安装 Java 9 时,不会获得
org.apache.derby.jdbc包,需要从 http://db.apache.org/derby/derby_downloads.html 下载一个名为derby.jar的单独文件。 - 配置 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文件。
- Eclipse :选择
代码分析
- DriverManager.getConnection :建立与特定数据库的会话。在上述示例中,调用
getConnection方法创建一个AccountDatabase并打开与之的连接。如果数据库已经存在,;create=true文本将不起作用。 - conn.createStatement :创建一个语句对象。在 Java 数据库连接中,需要创建一个语句对象,然后可以多次使用该对象,使用不同的 SQL 字符串向数据库发出不同的命令。
- 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 应用程序。
超级会员免费看
946

被折叠的 条评论
为什么被折叠?



