异常处理
ExceptionIntro.java
ExceptionCalculate.java
异常处理是Java编程语言中管理和解决运行时错误的一种重要机制。它使得程序可以在遇到异常情况时优雅地恢复,而不是崩溃。以下是对Java异常处理机制的介绍:
-
异常类型:
- 检查型异常(Checked Exceptions): 必须显式处理的异常,否则程序无法编译。这些异常通常是在编写安全的可恢复代码时应该捕获的。
- 非检查型异常(Unchecked Exceptions): 包括运行时异常(
RuntimeException
)和错误(Error
)。这些异常通常是编程错误(如数组越界)或硬件错误(如内存溢出)。
-
关键字:
- try: 放置可能会产生异常的代码块。
- catch: 捕获并处理特定类型或多种类型的异常。
- finally: 无论是否发生异常,此块中的代码都将执行,常用于清理资源。
- throw: 用于主动抛出异常实例。
- throws: 用于在方法签名中声明一个方法可能抛出的异常类型。
-
异常处理流程:
- 当在
try
块中发生异常时,正常的控制流程会被中断。 - 程序会尝试找到匹配的
catch
块来处理这个异常。 - 如果找到匹配的
catch
块,该块中的代码将执行。 - 无论是否找到匹配的
catch
块,finally
块(如果有的话)都将执行。 - 如果没有找到匹配的
catch
块,异常会向上传播到调用栈中的前一个方法。
- 当在
-
异常处理实践:
- 捕获异常时,应尽量具体,不要捕获
Exception
类,除非有特殊需要。 - 避免使用异常处理来控制程序流程。
- 在
finally
块中释放资源,例如关闭文件或数据库连接。 - 尽量不要在
finally
块中使用return
或抛出异常。 - 使用自定义异常来提供更多错误信息,如果标准异常类不足以描述特定的错误情况。
- 捕获异常时,应尽量具体,不要捕获
-
常见异常类型:
下表列出了一些Java中常见的异常类型及其简短描述:
异常类型 | 描述 |
---|---|
NullPointerException | 尝试使用 null 对象引用访问类的成员时抛出。 |
ArrayIndexOutOfBoundsException | 尝试访问数组时使用了非法索引(索引负数或大于等于数组大小)时抛出。 |
ArithmeticException | 发生异常的算术条件时抛出,例如除以零。 |
NumberFormatException | 尝试将字符串转换成数字格式,但字符串不具有适当的格式时抛出。 |
ClassCastException | 尝试将对象强制转换为不是实例的类时抛出。 |
IOException | 输入输出操作失败或中断时抛出,如读写文件时。 |
FileNotFoundException | 尝试打开指定路径名表示的文件失败时抛出,因为文件不存在。 |
InterruptedException | 线程在活动时被另一个线程中断时抛出。 |
NoSuchMethodException | 请求的方法不存在时抛出。 |
NoSuchFieldException | 请求的字段不存在时抛出。 |
ExceptionIntro.java
public class ExceptionIntro {
public static void main(String[] args) {
// 此处用于编写测试代码
}
}
// 示例 1:捕获特定类型的异常
class DivisionExample {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("结果是: " + result);
} catch (ArithmeticException e) {
System.out.println("发生算术异常: 不能除以零.");
}
}
public static int divide(int numerator, int denominator) {
return numerator / denominator;
}
}
// 示例 2:捕获所有异常
class CatchAllExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[3]); // 这将抛出 ArrayIndexOutOfBoundsException
} catch (Exception e) {
System.out.println("发生异常: " + e.toString());
}
}
}
// 示例 3:使用 finally 关键字
class FinallyExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[1]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组索引越界");
} finally {
System.out.println("这段代码总会执行");
}
}
}
// 示例 4:使用 throws 关键字声明异常
class ThrowsExample {
public static void main(String[] args) {
try {
myMethod();
} catch (Exception e) {
System.out.println("处理异常: " + e.getMessage());
}
}
public static void myMethod() throws Exception { // 在方法定义的时候,使用throws Xxx表示该方法可能抛出的异常类型。
throw new Exception("我的异常被抛出了");
}
}
ExceptionCalculate.java
/*
* 处理了用户输入非数字的情况,捕获 `InputMismatchException`。
* 使用 `ArithmeticException` 捕获了除法中的除零操作。
* 新增的 `%` 取余和 `^` 幂运算也有可能抛出 `ArithmeticException`(例如,当对负数进行取余或求幂时)。
* 多个异常类型可以组合捕获,如 `ArithmeticException | UnsupportedOperationException`。
* 保留了通用的 `Exception` 捕获作为后备选项,以处理未能预料的异常情况。
* `finally` 块确保了无论如何 `Scanner` 都会被关闭。
*/
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionCalculate {
public static double calculate(double number1, double number2, String operation) {
switch (operation) {
case "+":
return number1 + number2;
case "-":
return number1 - number2;
case "*":
return number1 * number2;
case "/":
if (number2 == 0) {
throw new ArithmeticException("除数不能为0。");
}
return number1 / number2;
case "%":
return number1 % number2;
case "^":
return Math.pow(number1, number2);
default:
throw new UnsupportedOperationException("不支持的操作: " + operation);
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.println("请输入第一个数字:");
double number1 = scanner.nextDouble();
System.out.println("请输入第二个数字:");
double number2 = scanner.nextDouble();
System.out.println("请选择操作 (+, -, *, /, %, ^):");
String operation = scanner.next();
double result = calculate(number1, number2, operation);
System.out.println("结果: " + result);
} catch (InputMismatchException e) {
System.out.println("错误: 输入不是有效的数字。");
} catch (ArithmeticException | UnsupportedOperationException e) {
System.out.println("错误: " + e.getMessage());
} catch (Exception e) {
System.out.println("未知错误: " + e.getMessage());
} finally {
scanner.close();
System.out.println("计算器已关闭。");
}
}
}
Log4j 了解即可
Log4j是一个组件化设计的日志系统,它的架构大致如下:
log.info("User signed in.");
│
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
├──>│ Appender │───>│ Filter │───>│ Layout │───>│ Console │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
├──>│ Appender │───>│ Filter │───>│ Layout │───>│ File │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
└──>│ Appender │───>│ Filter │───>│ Layout │───>│ Socket │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
Log4j是一个流行的Java日志框架,由Apache软件基金会开发。它用于记录应用程序的运行信息。Log4j允许开发人员控制日志信息的输出目的地(如控制台、文件、GUI组件等),以及输出格式,并且可以根据运行时信息动态地改变这些配置。Log4j使用简单而强大的配置语言,能够让开发人员启用或禁用特定的日志记录语句,控制每个日志记录语句的粒度,并通过过滤器对记录信息进行细粒度的控制。
Log4j的主要组件包括:
- Logger:负责捕获日志信息。它是与日志记录调用交互的主要对象。
- Appender:定义了日志信息的输出目的地,例如控制台、文件、数据库等。
- Layout:负责格式化日志信息的输出格式。
- Level:定义了日志信息的级别,例如DEBUG、INFO、WARN、ERROR、FATAL等。
Log4j的设计基于以下三个简单的概念:日志记录器(logger)、日志级别(level)、日志输出器(appender)。通过这种方式,Log4j提供了灵活的日志管理策略,允许开发者在运行时调整日志输出和级别,而无需修改应用程序代码。
例如,如果一个开发者在开发过程中需要调试信息,他可以将日志级别设置为DEBUG。一旦问题解决,可以修改配置文件将日志级别改回为INFO或者WARN,降低日志的冗余度,并且这些调整无需重启应用程序。