50、Java编程:界面完善、测试及杂项知识

Java编程:界面完善、测试及杂项知识

1. 界面完善任务

为了让简单的界面更加完整,还有许多功能需要添加,以下是一些可以考虑实现的功能:
| 功能 | 描述 |
| — | — |
| 清空文本框 | 用户按下“添加”按钮时,清空文本框。 |
| 防止重复输入 | 添加约束条件,防止用户输入重复的课程,可在 CourseCatalog 中编写检查重复项的逻辑。 |
| 删除课程 | 添加删除按钮,允许用户选择多行进行删除。 |
| 更新课程 | 添加更新按钮,用于编辑现有课程。 |
| 数据排序 | 实现每列数据的排序功能。 |
| 数字过滤 | 添加数字过滤器,限制用户在课程编号字段中只能输入数字。 |
| 列宽设置 | 根据列内容的平均或最大宽度设置每列的宽度。 |
| 字段内容选择 | 当用户通过制表键或点击进入每个字段时,自动选择字段内容,方便用户直接输入替换。 |
| 鼠标悬停帮助 | 当用户将鼠标悬停在每个字段上时,在状态栏或小弹出“工具提示”中显示摘要信息。 |
| 键盘帮助 | 响应F1键,弹出帮助信息,这需要构建一个帮助子系统。 |
| 下拉列表替换 | 用下拉列表( JComboBox )替换部门和/或课程编号字段。 |
| 窗口位置记忆 | 与偏好子系统交互,使应用程序能够“记住”每个窗口的最后位置。 |

构建一个强大、复杂的用户界面是一项艰巨的任务,每个功能的缺失都会严重影响应用程序的有效性。开发者应将这些功能视为客户需求,客户团队需要有合格的专家来设计和详细说明用户界面的规格。

2. Swing应用开发与测试驱动开发(TDD)

Swing是Java中用于创建图形用户界面(GUI)的工具包,虽然有很多关于Swing的书籍,但在短时间内我们只是触及了它的表面。不过,我们已经了解了构建Swing应用程序的基础知识,利用这些知识可以构建一个不错的界面。

在Swing开发中,测试驱动开发(TDD)是一种非常重要的方法。很多人认为对Swing代码进行单元测试很困难,因此很多团队选择放弃测试。但不测试Swing代码会导致Swing类中混入大量可测试的应用或模型代码,使类变得臃肿。

CoursesPanel 视图类为例,它非常小且简单,除了布局外几乎没有复杂性。在用户界面应用程序中,设计目标始终是将应用和/或业务逻辑与视图分离。TDD推动我们朝着这个目标前进,其基本规则是测试所有可能出错的部分,也可以理解为“测试所有能测试的内容,并重新设计其他部分,使其不会出错”。

TDD让我们创建了一个小的视图类,其中包含简单、离散且易于测试的方法,还创建了“反应式”方法,这些方法只是将工作委托给另一个可信的类,因此不容易出错。而不测试用户界面类的团队往往会后悔,因为业务逻辑会逐渐混入应用程序和视图中,视图代码会变得杂乱无章,大量代码未经测试,缺陷率会上升。

以下是使用TDD构建Swing应用程序的一些额外准则和技术:
- 确保设计将应用、视图和领域逻辑分离。
- 进一步分解设计,遵循单一职责原则。
- 消除Swing中过多的冗余,例如使用通用方法创建和提取字段。
- 使用监听器测试视图的抽象反应,例如确保点击按钮会触发相应的操作。
- 使用模拟类避免Swing渲染。
- 不测试布局。
- 仅在万不得已时使用Swing机器人。

由于Swing并非为单元级测试而设计,因此测试Swing代码是一个解决问题的过程,需要深入研究各种Swing类,有时它们会提供测试所需的钩子,有时则需要我们跳出常规思维来进行测试。

3. Java杂项知识 - JAR文件

在Java开发中,JAR(Java ARchive)文件是一种非常重要的文件格式。在学习Java类路径时,我们知道可以将类文件夹添加到类路径中,而JAR文件也可以包含在类路径中,甚至可以替代类文件夹。

JAR文件是一个单一的文件,它收集了Java类文件和运行时所需的其他资源。可以使用Java bin目录中提供的 jar 命令行实用程序或Ant来创建JAR文件。理解JAR文件对于在任何生产Java环境中工作都是至关重要的,因为JAR文件的使用属于部署范畴。

jar 实用程序将类文件和其他资源(如图像或属性文件)组合成一个扩展名为 .jar 的文件,它以ZIP文件格式创建JAR,使用标准压缩算法减少整体空间需求。使用JAR文件有以下几个优点:
- 部署速度快:传输或安装几个JAR文件比传输或安装数千个文件要快得多。
- 压缩:磁盘需求更小,小文件下载速度更快。
- 安全性:在安全环境中工作时,可以为JAR文件附加数字签名。
- 可插拔性:可以围绕重用需求组织JAR文件,它们可以是组件或完整的API集。
- 版本控制:可以为JAR文件附加供应商和版本信息。
- 简化执行:可以将JAR文件指定为“Java可执行文件”,用户无需知道入口类名。

以下是创建和使用JAR文件的具体步骤:
1. 创建JAR文件
- 导航到类目录。
- 执行命令 jar cvf sis.jar * ,其中 cvf 选项表示创建新的JAR文件、提供详细输出(可选但有用)并指定文件名 sis.jar * 表示归档当前目录和所有子目录中的所有文件。
2. 查看JAR文件内容
- 执行命令 jar tvf sis.jar t 选项表示列出内容目录。
- 输出示例如下:

   0 Mon Aug 02 23:25:36 MDT 2004 META-INF/
  74 Mon Aug 02 23:25:36 MDT 2004 META-INF/MANIFEST.MF
 553 Sat Jul 24 10:41:28 MDT 2004 A$B.class
 541 Sat Jul 24 10:41:28 MDT 2004 A.class
1377 Sat Jul 24 10:41:28 MDT 2004 AtomicLong.class
   0 Sat Jul 24 10:41:28 MDT 2004 com/
   0 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/
   0 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/ach/
 486 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/ach/Ach.class
 377 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/ach/AchCredentials.class
 516 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/ach/AchResponse.class
1164 Sat Jul 24 10:41:28 MDT 2004 com/jimbob/ach/AchStatus.class
 ...
  1. 提取JAR文件内容
    • 执行命令 jar xvf sis.jar x 选项表示提取文件。
  2. 将JAR文件添加到类路径并执行应用程序
    • 假设将 sis.jar 部署到 /usr/sis 目录,可以使用命令 java -cp /usr/sis/sis.jar sis.ui.Sis 执行SIS应用程序。
    • 为了让Java在JAR文件中找到类,其路径信息必须与包信息匹配。
  3. 指定主类
    • 创建一个单独的清单文件,内容如下:
Main-Class: sis.ui.Sis
- 确保文件最后有一个空行,否则Java可能无法识别清单条目。
- 创建JAR文件时,使用命令`jar cvmf main.mf sis.jar *`指定清单文件。
- 此时可以使用简化命令`java -jar sis.jar`启动SIS应用程序。

此外,Java提供了 java.util.zip 包中的完整API来编程操作JAR文件,这使得对JAR操作进行单元测试就像测试文本文件一样简单。

4. Java杂项知识 - 正则表达式

正则表达式是一种包含搜索模式的字符串,它是一种用于模式匹配规范的标准化语言。其他语言如Perl和Ruby直接支持正则表达式,程序员编辑器如TextPad和UltraEdit也允许使用正则表达式搜索文件文本。Java提供了一组类库来利用正则表达式。

4.1 字符串分割

在之前的学习中,我们使用 String 类的 split 方法将全名拆分为离散的名称部分, split 方法接受一个正则表达式作为唯一参数。例如:

for (String name: fullName.split(" "))

但当字符串中存在多个连续空格时,会出现不符合预期的结果。例如:

public void testSplit() {
    String source = "Jeffrey   Hyman";
    String expectedSplit[] = {"Jeffrey", "", "", "Hyman" };
    assertTrue(
        java.util.Arrays.equals(expectedSplit, source.split(" ")));
}

为了按任意连续的空白字符分割字符串,我们可以使用正则表达式 \s+ ,其中 \s 匹配单个空白字符(制表符、换行符、换页符、空格或回车符), + 表示匹配前面的构造至少一次。

以下是改进后的代码:

// NameTest.java
public void testExtraneousSpaces() {
    final String fullName = "Jeffrey   Hyman";
    Name name = createName(fullName);
    assertEquals("Jeffrey", name.getFirstName());
    assertEquals("Hyman", name.getLastName());
}
private Name createName(String fullName) {
    Name name = new Name(fullName);
    assertEquals(fullName, name.getFullName());
    return name;
}

// Name.java
private List<String> split(String fullName) {
    List<String> results = new ArrayList<String>();
    for (String name: fullName.split("\\s+")) 
        results.add(name);
    return results;
}

由于Java字符串中反斜杠是转义字符,所以正则表达式 \s+ 在Java字符串中表示为 \\s+ 。我们还可以进一步测试包含更多空白字符的情况:

public void testExtraneousWhitespace() {
    final String fullName = "Jeffrey   \t\t\n \r\fHyman";
    Name name = createName(fullName);
    assertEquals("Jeffrey", name.getFirstName());
    assertEquals("Hyman", name.getLastName());
}
4.2 字符串替换

在许多应用程序中,需要从用户输入中提取电话号码,人们输入电话号码的方式多种多样。可以使用正则表达式轻松去除电话号码中的非数字字符。例如:

public void testStripPhoneNumber() {
    String input = "(719) 555-9353 (home)";
    assertEquals("7195559353", StringUtil.stripToDigits(input));
}

public static String stripToDigits(String input) {
    return input.replaceAll("\\D+", "");
}

replaceAll 方法接受两个参数:一个用于匹配的正则表达式和一个替换字符串。 \D 匹配任何非数字字符,正则表达式语言的约定是小写构造字符用于正向匹配,大写构造字符用于反向匹配,例如 \w 匹配“单词”字符(字母、数字或下划线), \W 匹配任何非单词字符。

4.3 Pattern Matcher

在基于Swing的文本编辑器应用程序中,可以使用 JTextPane 来应用文本样式,并且很多文本编辑器提供搜索和高亮显示匹配模式的功能。以下是一个使用正则表达式进行文本搜索的测试示例:

package sis.util;
import junit.framework.TestCase;
import java.util.regex.*;

public class RegexTest extends TestCase {
    public void testSearch() {
        String[] textLines =
            {"public class Test {",
                "public void testMethod() {}",
                "public void testNotReally(int x) {}",
                "public void test() {}",
                "public String testNotReally() {}",
                "} "};
        String text = join(textLines);
        String testMethodRegex = 
            "public\\s+void\\s+test\\w*\\s*\\(\\s*\\)\\s*\\{";
        Pattern pattern = Pattern.compile(testMethodRegex);
        Matcher matcher = pattern.matcher(text);
        assertTrue(matcher.find());
        assertEquals(text.indexOf(textLines[1]), matcher.start());
        assertTrue(matcher.find());
        assertEquals(text.indexOf(textLines[3]), matcher.start());
        assertFalse(matcher.find());
    }
    private String join(String[] textLines) {
        StringBuilder builder = new StringBuilder();
        for (String line: textLines) {
            if (builder.length() > 0)
                builder.append("\n");
            builder.append(line);
        }
        return builder.toString();
    }
}

这个正则表达式看起来很复杂,我们可以将其分解为各个构造来理解:
| 构造 | 描述 |
| — | — |
| public | 匹配文本“public” |
| \\s+ | 匹配一个或多个空白字符 |
| void | 匹配文本“void” |
| \\s+ | 匹配一个或多个空白字符 |
| test | 匹配文本“test” |
| \\w* | 匹配零个或多个单词字符 |
| \\s* | 匹配零个或多个空白字符 |
| \\( | 匹配左括号 |
| \\s* | 匹配零个或多个空白字符 |
| \\) | 匹配右括号 |
| \\s* | 匹配零个或多个空白字符 |
| \\{ | 匹配左花括号 |

正则表达式字符串是自由格式、未经验证的文本,需要使用 Pattern 类的 compile 方法进行编译,编译成功后返回一个 Pattern 对象。从 Pattern 对象可以获取一个 Matcher 用于给定的输入字符串。使用 Matcher 对象的 find 方法可以查找下一个匹配的子序列, start end 方法可以获取匹配子序列的索引, group 方法可以返回匹配的子序列文本。此外,还可以使用 matches 方法检查整个输入字符串是否匹配正则表达式,使用 lookingAt 方法检查输入字符串的开头是否匹配。

测试正则表达式非常简单,主要是比较 String 对象。要深入了解Java对正则表达式的支持,可以参考 java.util.regex.Pattern 类的API文档,还可以访问以下网站获取更多正则表达式教程和信息:
- Sun正则表达式教程
- http://www.regular-expressions.info/
- http://www.javaregex.com/

综上所述,在Java开发中,完善用户界面、采用TDD方法开发Swing应用程序、掌握JAR文件的使用和正则表达式的应用都是非常重要的技能,它们可以帮助我们构建更强大、更稳定的Java应用程序。

Java编程:界面完善、测试及杂项知识

5. Java杂项知识 - JDBC

JDBC(Java Database Connectivity)是Java用于与数据库进行交互的标准API。它允许Java程序连接到各种数据库,执行SQL语句并处理结果。以下是使用JDBC进行数据库操作的基本步骤:

  1. 加载数据库驱动 :不同的数据库有不同的驱动程序,需要先加载相应的驱动。例如,对于MySQL数据库,可以使用以下代码加载驱动:
try {
    Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
  1. 建立数据库连接 :使用 DriverManager.getConnection 方法建立与数据库的连接,需要提供数据库的URL、用户名和密码。示例代码如下:
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
    Connection connection = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
    e.printStackTrace();
}
  1. 创建Statement对象 Statement 对象用于执行SQL语句。可以使用 Connection 对象的 createStatement 方法创建 Statement 对象。示例代码如下:
Statement statement = connection.createStatement();
  1. 执行SQL语句 :可以使用 Statement 对象的 executeQuery 方法执行查询语句,使用 executeUpdate 方法执行更新、插入或删除语句。示例代码如下:
// 执行查询语句
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
while (resultSet.next()) {
    int id = resultSet.getInt("id");
    String name = resultSet.getString("name");
    System.out.println("ID: " + id + ", Name: " + name);
}

// 执行更新语句
int rowsAffected = statement.executeUpdate("INSERT INTO users (name) VALUES ('John')");
System.out.println("Rows affected: " + rowsAffected);
  1. 关闭资源 :使用完数据库连接、 Statement 对象和 ResultSet 对象后,需要及时关闭它们,以释放资源。示例代码如下:
resultSet.close();
statement.close();
connection.close();
6. Java杂项知识 - 国际化

国际化是指将应用程序设计成能够支持多种语言和文化的过程。Java提供了丰富的支持来实现国际化,主要涉及以下几个方面:

  1. 资源束(Resource Bundle) :资源束是一种用于存储不同语言和地区的文本信息的机制。可以创建多个属性文件,每个文件对应一种语言或地区,文件名遵循特定的命名规则。例如, messages.properties 是默认的属性文件, messages_zh_CN.properties 是中文(中国)的属性文件。示例属性文件内容如下:
# messages.properties
greeting=Hello!

# messages_zh_CN.properties
greeting=你好!
  1. 获取资源束 :使用 ResourceBundle 类来获取相应的资源束。示例代码如下:
Locale locale = Locale.getDefault();
ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
String greeting = bundle.getString("greeting");
System.out.println(greeting);
  1. 格式化日期和数字 :Java提供了 DateFormat NumberFormat 类来格式化日期和数字,以适应不同的语言和地区。示例代码如下:
// 格式化日期
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
Date date = new Date();
String formattedDate = dateFormat.format(date);
System.out.println("Formatted Date: " + formattedDate);

// 格式化数字
NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
double number = 1234.56;
String formattedNumber = numberFormat.format(number);
System.out.println("Formatted Number: " + formattedNumber);
7. Java杂项知识 - 引用调用与值调用

在Java中,参数传递有两种方式:引用调用和值调用。

  1. 值调用 :当传递基本数据类型(如 int double 等)时,是值调用。这意味着传递的是变量的副本,而不是变量本身。示例代码如下:
public class ValueCallExample {
    public static void main(String[] args) {
        int num = 10;
        changeValue(num);
        System.out.println("Value of num: " + num); // 输出10
    }

    public static void changeValue(int value) {
        value = 20;
    }
}
  1. 引用调用 :当传递对象时,是引用调用。这意味着传递的是对象的引用,而不是对象的副本。示例代码如下:
class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

public class ReferenceCallExample {
    public static void main(String[] args) {
        Person person = new Person("John");
        changeName(person);
        System.out.println("Name of person: " + person.name); // 输出Tom
    }

    public static void changeName(Person p) {
        p.name = "Tom";
    }
}
8. Java周边知识概述

除了上述介绍的内容,Java还有许多其他的API和技术,以下是一些常见的Java周边知识的简要概述:

知识领域 描述
Java NIO 提供了非阻塞I/O操作,用于提高性能和可扩展性。
Java Reflection 允许程序在运行时检查和操作类、方法、字段等。
Java Concurrency 提供了多线程编程的支持,用于实现并发和并行处理。
Java Security 提供了安全机制,如加密、认证和授权。
Java Web Services 用于构建分布式应用程序,实现不同系统之间的通信。

要深入了解这些知识领域,可以参考Java的官方文档和相关的教程。

总结

Java编程涉及到众多的知识和技术,包括界面完善、测试驱动开发、JAR文件使用、正则表达式、JDBC、国际化、引用调用与值调用以及周边知识等。通过掌握这些知识和技术,我们可以开发出更强大、更稳定、更具扩展性的Java应用程序。在实际开发中,需要不断学习和实践,结合具体的需求选择合适的技术和方法。同时,要注重代码的质量和可维护性,遵循良好的编程规范和设计原则。

下面是一个简单的mermaid流程图,展示了使用JDBC进行数据库操作的基本流程:

graph LR
    A[加载数据库驱动] --> B[建立数据库连接]
    B --> C[创建Statement对象]
    C --> D{执行SQL语句}
    D -- 查询语句 --> E[处理ResultSet结果]
    D -- 更新语句 --> F[获取受影响行数]
    E --> G[关闭ResultSet]
    F --> G
    G --> H[关闭Statement]
    H --> I[关闭数据库连接]

通过这个流程图,可以更清晰地理解JDBC数据库操作的步骤和顺序。希望这些知识能对Java开发者有所帮助,让大家在Java编程的道路上不断前进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值