Java 编程技巧全解析:数组、集合与异常处理
1. 命令行参数与二维数组概念
在处理命令行参数时,若要输入包含空格的字符串值,只需将该值用双引号括起来即可。例如,运行特定代码时可使用参数 “My Big Fat File.txt” 7。
数组是一行数据的集合,但并非所有数据都能仅用一维数组表示。以旅馆房间为例,若房间编号从 0 到 9,可看作一维数组。但如果是拥有 50 层、每层 100 个房间的大宾馆,数据就呈方形,此时二维数组更合适。二维数组中每个元素有两个索引:行号和列号。
2. 数组的局限性与集合类的引入
数组虽好用,但存在严重局限性。假设用数组存储客户姓名,预设数组大小为 100:
String name[] = new String[100];
for (int i = 0; i < 100; i++) {
name[i] = new String();
}
当第 101 个客户出现时,数组无法扩展,程序会抛出
ArrayIndexOutOfBoundsException
异常。若将数组大小扩大到 1000:
String name[] = new String[1000];
for (int i = 0; i < 1000; i++) {
name[i] = new String();
}
在经济衰退时,只有 3 个客户,会造成大量空间浪费。而且当新客户姓名需插入数组靠前位置时,会涉及大量数据移动,浪费处理时间和资源。
为解决这些问题,Java API 提供了集合类。集合类有存储大量值的方法,能有效处理上述问题。例如,使用集合类的
add
方法可将元素插入指定位置,并合理处理可能产生的连锁反应。
3. 使用 ArrayList 集合类
ArrayList
是 Java 中最通用的集合类之一。以下是使用
ArrayList
的示例代码:
import static java.lang.System.out;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
class ShowNames {
public static void main(String args[]) throws IOException {
ArrayList<String> people = new ArrayList<String>();
Scanner diskScanner = new Scanner(new File("names.txt"));
while (diskScanner.hasNext()) {
people.add(diskScanner.nextLine());
}
people.remove(0);
people.add(2, "Jim Newton");
for (String name : people) {
out.println(name);
}
}
}
上述代码从
names.txt
文件中读取姓名,存储到
ArrayList
中,然后移除第一个元素,在第 2 个位置插入新姓名,最后遍历输出所有姓名。
ArrayList
有两个不同的
add
方法:
-
people.add(diskScanner.nextLine())
:将读取的姓名追加到列表末尾。
-
people.add(2, "Jim Newton")
:将姓名插入指定位置。
下面是
ArrayList
操作流程的 mermaid 流程图:
graph LR
A[创建 ArrayList] --> B[读取文件]
B --> C{是否有下一行}
C -- 是 --> D[添加到列表]
D --> C
C -- 否 --> E[移除第一个元素]
E --> F[插入新元素]
F --> G[遍历输出]
4. 泛型的使用
Java 5.0 引入了泛型,使集合类更安全和易用。在旧版 Java 中,
ArrayList
可存储任意类型的数据,这会导致难以预测取出的数据类型。例如:
ArrayList things = new ArrayList();
things.add(new Account());
Account myAccount = things.get(0); // 错误代码
使用泛型后,可明确指定集合中存储的数据类型:
ArrayList<Account> things = new ArrayList<Account>();
things.add(new Account());
Account myAccount = things.get(0); // 正确代码
Java 7 引入了钻石运算符
<>
,可简化泛型声明。例如:
ArrayList<ArrayList<String>[]> mess = new ArrayList<>();
5. 测试数据的存在性
在编写程序时,无需提前知道输入文件中的数据数量。
Scanner
类提供了
hasNextInt
、
hasNextDouble
和
hasNext
等方法,用于检查是否还有输入数据。在上述
ShowNames
示例中,使用
hasNext
方法检查文件是否还有下一行,直到文件读取完毕。
6. 异常处理的重要性与历史案例
计算机编程中,异常情况时有发生。从 1945 年哈佛 Mark II 计算机因飞蛾导致故障,到 2000 年的千年虫问题,都凸显了处理异常的重要性。以下是一些历史上的编程事故:
| 时间 | 事件 | 原因 |
| ---- | ---- | ---- |
| 1945 年 9 月 9 日 | 哈佛 Mark II 计算机故障 | 飞蛾飞入继电器 |
| 1957 年 4 月 19 日 | FORTRAN 程序编译错误 | 语句中缺少逗号 |
| 1962 年 7 月 22 日 | 水手一号航天器被毁 | 火箭速度公式中缺少横杆 |
| 2000 年 1 月 1 日 | 千年虫问题 | 日期处理不当 |
7. 异常处理机制
Java 提供了异常处理机制,当程序检测到可能出错的情况时,会创建一个新的异常对象,即抛出异常。异常对象会在代码中传递,直到被某个代码块捕获并处理。异常处理使用以下几个关键字:
-
throw
:创建新的异常对象。
-
throws
:将异常传递给调用该方法的代码。
-
try
:包含可能抛出异常的代码。
-
catch
:捕获并处理异常。
以下是一个简单的库存管理程序示例,用于说明异常处理的使用:
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryA {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("How many boxes do we have? ");
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print("The value is ");
out.println(currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println("That's not a number.");
}
}
}
上述代码中,
Integer.parseInt
方法可能会抛出
NumberFormatException
异常,使用
try-catch
块捕获并处理该异常。
8. 异常类型与自定义异常
Java 中有多种异常类型,除了系统内置的异常,还可以自定义异常。以下是自定义异常类和使用该异常的示例代码:
// 自定义异常类
@SuppressWarnings("serial")
class OutOfRangeException extends Exception {
}
// 使用自定义异常
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryC {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("How many boxes do we have? ");
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
out.print("The value is ");
out.println(currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println("That's not a number.");
} catch (OutOfRangeException e) {
out.print(numBoxesIn);
out.println("? That's impossible!");
}
}
}
在上述代码中,当用户输入的盒子数量为负数时,抛出
OutOfRangeException
异常,并在
catch
块中处理该异常。
9. 多 catch 子句与匹配规则
一个
try
块可以有多个
catch
子句,用于捕获不同类型的异常。当异常抛出时,计算机从第一个
catch
子句开始检查,找到匹配的异常类型后执行相应的处理代码,然后跳过其他
catch
子句。匹配规则如下:
- 子句的参数类型与抛出的异常类型相同。
- 子句的参数类型是异常类型的超类。
以下是一个包含多个
catch
子句的示例代码:
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryD {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("How many boxes do we have? ");
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
out.print("The value is ");
out.println(currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println("That's not a number.");
} catch (OutOfRangeException e) {
out.print(numBoxesIn);
out.println("? That's impossible!");
} catch (Exception e) {
out.print("Something went wrong, ");
out.print("but I'm clueless about what ");
out.println("it actually was.");
}
out.println("That's that.");
}
}
在上述代码中,根据用户输入的不同情况,可能会抛出
NumberFormatException
、
OutOfRangeException
或其他异常,分别由不同的
catch
子句处理。
10. Java 7 的多捕获子句
Java 7 引入了多捕获子句,允许在一个
catch
子句中捕获多种类型的异常。例如:
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
out.print("The value is ");
out.println(currency.format(numBoxes * boxPrice));
} catch (NumberFormatException | OutOfRangeException e) {
out.print(numBoxesIn);
out.println("? That's impossible!");
} catch (Exception e) {
out.print("Something went wrong, ");
out.print("but I'm clueless about what ");
out.println("it actually was.");
}
上述代码中,使用
|
符号指定可以捕获
NumberFormatException
和
OutOfRangeException
两种异常。
11. 异常捕获的限制与有用的异常处理
Java 不允许捕获不可能抛出的异常。例如,以下代码会被编译器报错:
try {
i++;
} catch (IOException e) {
e.printStackTrace();
}
因为
i++
语句不会抛出
IOException
异常。
在实际应用中,异常处理不仅可以用于处理错误输入,还可以用于正常的程序流程控制。例如,在文件复制时,可通过捕获
EOFException
来检测文件结束:
try {
while (true) {
dataOut.writeByte(dataIn.readByte());
}
} catch (EOFException e) {
numFilesCopied = 1;
}
以下是异常处理流程的 mermaid 流程图:
graph LR
A[执行可能抛出异常的代码] --> B{是否抛出异常}
B -- 是 --> C[查找匹配的 catch 子句]
C --> D{是否找到匹配}
D -- 是 --> E[执行 catch 子句代码]
E --> F[继续执行后续代码]
D -- 否 --> G[异常未处理,程序终止]
B -- 否 --> F
通过合理使用数组、集合类和异常处理机制,可编写更健壮、高效的 Java 程序。在实际开发中,应根据具体需求选择合适的数据结构和异常处理方式,以提高程序的可靠性和可维护性。
Java 编程技巧全解析:数组、集合与异常处理
12. 持续处理异常:带循环的异常处理
前面的示例大多是捕获异常后输出错误信息就结束程序,其实可以让程序在捕获异常后继续运行。例如下面的代码,使用
do-while
循环,不断让用户输入,直到输入有效数据为止:
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryLoop {
public static void main(String args[]) {
final double boxPrice = 3.25;
boolean gotGoodInput = false;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency = NumberFormat.getCurrencyInstance();
do {
out.print("How many boxes do we have? ");
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print("The value is ");
out.println(currency.format(numBoxes * boxPrice));
gotGoodInput = true;
} catch (NumberFormatException e) {
out.println();
out.println("That's not a number.");
}
} while (!gotGoodInput);
out.println("That's that.");
}
}
操作步骤如下:
1. 初始化变量
gotGoodInput
为
false
,表示还未得到有效输入。
2. 进入
do-while
循环,提示用户输入盒子数量。
3. 尝试将用户输入转换为整数,如果成功,计算并输出盒子的总价值,将
gotGoodInput
设为
true
。
4. 如果转换失败,捕获
NumberFormatException
异常,输出错误信息。
5. 只要
gotGoodInput
为
false
,就继续循环。
下面是该程序的执行流程 mermaid 流程图:
graph LR
A[初始化 gotGoodInput 为 false] --> B[提示输入盒子数量]
B --> C[读取用户输入]
C --> D{输入是否可转换为整数}
D -- 是 --> E[计算并输出总价值]
E --> F[设置 gotGoodInput 为 true]
F --> G{gotGoodInput 是否为 true}
G -- 是 --> H[结束程序]
D -- 否 --> I[捕获异常,输出错误信息]
I --> G
G -- 否 --> B
13. 异常处理的实际应用场景总结
异常处理在 Java 编程中有广泛的应用场景,以下是一些常见的场景总结:
| 应用场景 | 说明 | 示例代码 |
| ---- | ---- | ---- |
| 输入验证 | 确保用户输入的数据符合要求,如整数、日期等 | 前面的库存管理程序,验证输入是否为有效整数 |
| 资源管理 | 处理文件、网络连接等资源的打开和关闭,避免资源泄漏 |
java try (FileInputStream fis = new FileInputStream("file.txt")) { // 使用文件输入流 } catch (IOException e) { e.printStackTrace(); }
|
| 业务逻辑检查 | 检查业务规则是否满足,如账户余额是否足够 |
java if (account.getBalance() < amount) { throw new InsufficientBalanceException(); }
|
| 系统错误处理 | 处理系统级错误,如数据库连接失败、网络异常等 |
java try { Connection conn = DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); }
|
14. 集合类与异常处理的综合应用
在实际开发中,集合类和异常处理常常结合使用。例如,从文件中读取数据到集合,可能会遇到文件不存在、数据格式错误等异常。以下是一个示例代码:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class CollectionAndException {
public static void main(String[] args) {
ArrayList<String> data = new ArrayList<>();
try {
Scanner scanner = new Scanner(new File("data.txt"));
while (scanner.hasNextLine()) {
data.add(scanner.nextLine());
}
scanner.close();
} catch (FileNotFoundException e) {
System.out.println("文件未找到:" + e.getMessage());
}
// 尝试对集合中的元素进行处理
try {
for (String item : data) {
int number = Integer.parseInt(item);
System.out.println("处理数据:" + number);
}
} catch (NumberFormatException e) {
System.out.println("数据格式错误:" + e.getMessage());
}
}
}
操作步骤如下:
1. 创建一个
ArrayList
用于存储数据。
2. 尝试从文件中读取数据到集合,捕获
FileNotFoundException
异常。
3. 对集合中的元素进行处理,尝试将其转换为整数,捕获
NumberFormatException
异常。
15. 泛型集合的高级应用
泛型集合不仅可以存储简单的数据类型,还可以存储自定义对象。例如,创建一个存储
Person
对象的
ArrayList
:
import java.util.ArrayList;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class GenericCollectionAdvanced {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
for (Person person : people) {
System.out.println(person.getName() + ",年龄:" + person.getAge());
}
}
}
操作步骤如下:
1. 定义
Person
类,包含姓名和年龄属性。
2. 创建
ArrayList<Person>
集合。
3. 向集合中添加
Person
对象。
4. 使用增强
for
循环遍历集合,输出每个
Person
对象的信息。
16. 异常处理的最佳实践
为了编写更健壮、易维护的代码,在进行异常处理时,可遵循以下最佳实践:
-
精确捕获异常
:尽量捕获具体的异常类型,而不是捕获通用的
Exception
类型,这样可以更准确地处理不同的异常情况。例如:
try {
// 代码
} catch (NumberFormatException e) {
// 处理数字格式错误
} catch (IOException e) {
// 处理输入输出错误
}
-
避免空的
catch块 :空的catch块会隐藏异常,使问题难以调试。如果确实不需要处理异常,也应添加注释说明原因。 -
使用
finally块 :finally块中的代码无论是否抛出异常都会执行,常用于释放资源,如关闭文件、数据库连接等。例如:
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// 使用文件输入流
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 自定义异常信息 :在抛出异常时,提供详细的异常信息,方便调试和定位问题。例如:
if (amount < 0) {
throw new IllegalArgumentException("金额不能为负数:" + amount);
}
17. 总结与展望
通过对数组、集合类和异常处理的学习,我们了解到 Java 提供了丰富的工具和机制来处理各种编程场景。数组适用于固定大小的数据存储,集合类则更灵活,能动态扩展和管理数据。异常处理可以让程序在遇到错误时更加健壮,避免程序崩溃。
在未来的 Java 开发中,随着技术的不断发展,可能会有更多高效的数据结构和更智能的异常处理方式出现。开发者应不断学习和掌握新的知识,结合实际项目需求,灵活运用这些技术,编写出高质量的 Java 程序。同时,也要注重代码的可读性和可维护性,遵循最佳实践,提高开发效率和代码质量。
总之,掌握数组、集合类和异常处理是 Java 编程的基础,也是迈向高级开发的重要一步。希望大家在实际开发中能够熟练运用这些知识,解决各种编程难题。
超级会员免费看
170万+

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



