22、Java 编程技巧全解析:数组、集合与异常处理

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 编程的基础,也是迈向高级开发的重要一步。希望大家在实际开发中能够熟练运用这些知识,解决各种编程难题。

本课题设计了一种利用Matlab平台开发的植物叶片健康状态识别方案,重点融合了色彩纹理双重特征以实现对叶片病害的自动化判别。该系统构建了直观的图形操作界面,便于用户提交叶片影像并快速获得分析结论。Matlab作为具备高效数值计算数据处理能力的工具,在图像分析模式分类领域应用广泛,本项目正是借助其功能解决农业病害监测的实际问题。 在色彩特征分析方面,叶片影像的颜色分布常其生理状态密切相关。通常,健康的叶片呈现绿色,而出现黄化、褐变等异常色彩往往指示病害或虫害的发生。Matlab提供了一系列图像处理函数,例如可通过色彩空间转换直方图统计来量化颜色属性。通过计算各颜色通道的统计参数(如均值、标准差及主成分等),能够提取具有判别力的色彩特征,从而为不同病害类别的区分提供依据。 纹理特征则用于描述叶片表面的微观结构形态变化,如病斑、皱缩或裂纹等。Matlab中的灰度共生矩阵计算函数可用于提取对比度、均匀性、相关性等纹理指标。此外,局部二值模式Gabor滤波等方法也能从多尺度刻画纹理细节,进一步增强病害识别的鲁棒性。 系统的人机交互界面基于Matlab的图形用户界面开发环境实现。用户可通过该界面上传待检图像,系统将自动执行图像预处理、特征抽取分类判断。采用的分类模型包括支持向量机、决策树等机器学习方法,通过对已标注样本的训练,模型能够依据新图像的特征向量预测其所属的病害类别。 此类课题设计有助于深化对Matlab编程、图像处理技术模式识别原理的理解。通过完整实现从特征提取到分类决策的流程,学生能够将理论知识实际应用相结合,提升解决复杂工程问题的能力。总体而言,该叶片病害检测系统涵盖了图像分析、特征融合、分类算法及界面开发等多个技术环节,为学习掌握基于Matlab的智能检测技术提供了综合性实践案例。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值