JavaRush /Java 博客 /Random-ZH /喝咖啡休息#220。如何修复 Java 中的异常 - 详细指南

喝咖啡休息#220。如何修复 Java 中的异常 - 详细指南

已在 Random-ZH 群组中发布
来源:JavaTechOnline 本教程将帮助您学习如何修复 Java 中的常见异常。除了理论之外,您还将看到解决此类问题的代码示例。 喝咖啡休息#220。 如何修复 Java 中的异常 - 详细指南 - 1与其他编程语言一样,Java 开发人员在编写代码时可能会遇到错误或异常。应认真对待异常情况,因为异常情况的发生会占用工作时间。然而,只要掌握一点知识,您就可以加快解决大多数此类问题的速度。那么,让我们学习如何修复 Java 中的常见异常。

Java 中避免异常的一般准则

为了避免在 Java 中引发任何异常,理解并遵循编码指南非常重要:
  1. 在代码中使用用户输入之前,请始终验证用户输入。
  2. 使用try-catchthrowsthrow block ,具体取决于适合特定场景的内容。
  3. 不要忽略代码中有​​关处理和错误的消息。这将有助于识别和纠正问题。
  4. 请务必记录异常以进行调试。您可以使用记录器消息来帮助识别问题。
  5. 遵循编码最佳实践以避免异常并彻底测试您的代码。
  6. 更新您的 API 依赖项,以确保您使用最新、最稳定版本的库和框架。
  7. 使用必要的工具和测试框架在代码引发异常之前识别代码中的潜在问题。
  8. 监控应用程序的性能和日志,以快速识别和解决任何问题。
  9. 及时了解最新的安全最佳实践,以确保您的代码安全并免受任何潜在的攻击。
  10. 彻底记录您的代码及其异常,以便其他开发人员更容易理解和维护。

Java 中最常见的异常列表

Java 有一个相当长的异常列表。让我们看看一些最常见的:

空指针异常

NullPointerException是一种RuntimeException异常。当我们尝试使用具有空值的引用变量时,就会发生这种情况。这意味着它指向一个值为null的对象或变量。当我们尝试调用方法或访问尚未初始化的对象的字段时,或者当我们将null作为参数传递给不处理它的方法时,可能会发生此类错误。要防止引发NullPointerException,可以使用==运算符检查引用变量是否为 null 。如果是这种情况,则需要正确处理 null 情况。或者,我们可以将变量初始化为默认值(如果可能),以确保我们永远不会遇到空引用。避免NullPointerException 的另一种方法是使用Optional类。在 Java 中,Optional是一个容器对象,可能包含也可能不包含非空值。它用于表示值是否存在,类似于使用 null 值表示值不存在。如果程序员尝试访问Optional对象的空值,程序将不会抛出NullPointerException,而是返回一个空的Optional对象。换句话说,Optional 强制程序员处理值丢失的情况,这有助于避免NullPointerException

索引越界异常

IndexOutOfBoundsException是一个运行时异常,当用于访问数组或集合的索引为负数或者大于或等于数组或集合的大小时抛出。为了防止这种异常的发生,我们必须确保用于访问数组或集合的索引在其范围内,即它必须大于或等于零且小于数组或集合的大小。

ArrayIndexOutOfBoundsException

ArrayIndexOutOfBoundsException是IndexOutOfBoundsException的一种类型,当尝试访问无效索引处的数组时会引发该异常。在 Java 中,数组索引从0开始,到length()-1结束,其中length()是数组中元素的数量。如果尝试访问超出此范围的索引处的元素,Java 将抛出ArrayIndexOutOfBoundsException。当遇到ArrayIndexOutOfBoundsException时,您应该检查代码以确保在访问数组中的元素时使用正确的索引。

StringIndexOutOfBoundsException

StringIndexOutOfBoundsException是一种IndexOutOfBoundsException类型,当尝试访问字符串中具有无效索引的字符时,会引发该异常。与前面的ArrayIndexOutOfBoundsException一样,请记住,在 Java 中,字符串的索引从0开始,到length()-1结束,其中length()是字符串中的字符数。如果尝试访问超出此范围的索引处的字符,则可能会遇到StringIndexOutOfBoundsException。如果遇到StringIndexOutOfBoundsException,您应该检查代码以确保在访问字符串中的字符时使用正确的索引。

类转换异常

当我们尝试将对象转换为与其实际类型不兼容的类型时,就会发生此异常。为了解决这样的问题,重要的是要确保您的 Java 程序仅尝试将对象转换为其实例的类。在应用强制转换之前,您可以使用instanceof运算符检查对象的类型。

非法参数异常

IllegalArgumentException是一种RuntimeException,当使用非法或不适当的参数调用方法时抛出。换句话说,当使用超出预期范围或不具有预期格式或结构的参数调用方法时,可能会发生此错误。例如,一个方法需要一个正数作为参数,而我们提供了一个负数,这是无效的输入。如果遇到IllegalArgumentException,您应该检查您的代码以确保您使用有效且适当的参数调用方法。

非法状态异常

IllegalStateException是一种RuntimeException,当对象处于不适合执行操作的状态时抛出。如果对不处于预期状态的对象调用方法,则可能会发生这种情况。例如,如果我们创建一个数据库连接对象然后关闭它。然后,如果我们尝试在关闭的连接中创建Statement对象,它将抛出IllegalStateException 因为createStatement()方法需要打开的连接。如果遇到IllegalStateException,您应该检查您的代码以确保您正在对处于适当状态的对象调用方法。

不支持的操作异常

UnsupportedOperationException是一种RuntimeException类型,当尝试对对象执行不受支持的操作时会引发该异常。当我们调用对象未实现或对象不支持的方法时,可能会发生此错误。为了防止抛出异常,我们不应该调用对象不支持的此类操作。我们需要检查代码以确保我们正在调用支持该操作的对象上的方法。

算术异常

ArithmeticException是一种RuntimeException,当算术运算产生无效结果时抛出。当我们尝试使用无效或无效参数执行算术运算时,可能会发生此异常。例如,当尝试除以零时。为了解决这个问题,我们可以在执行算术运算之前进行输入验证并确保参数满足所需条件。

安全异常

SecurityException是RuntimeException的一种,当程序执行期间发生安全违规时抛出。当程序尝试执行安全策略不允许的操作时,可能会发生此错误。为了解决这个问题,我们必须确保我们能够访问资源并执行我们拥有特定权限的操作。

数字格式异常

NumberFormatException是RuntimeException的一种类型,当调用方法将字符串转换为数字格式,但字符串的格式不正确时,会引发该异常。为了解决这个问题,我们必须首先验证用户的输入,然后再尝试转换它。另请检查您的代码,以确保您正在尝试转换格式适合目标数字类型的字符串。

中断异常

InterruptedException是一个检查异常,如果线程正在等待、休眠或阻塞某个事件并且该事件被另一个线程中断,则会引发该异常。当线程正在等待输入、释放锁或完成某些其他操作,并且另一个线程中断等待线程时,可能会发生该错误。要解决此问题,您可以捕获InterruptedException并通过清理资源、停止线程或采取其他适当的操作进行响应。如果遇到InterruptedException,您应该检查代码以确保正确处理线程中断。

文件未找到异常

FileNotFoundException 是一个检查异常,当程序尝试访问不存在或无法在指定位置找到的文件时引发。如果文件拼写错误、移动或删除,甚至程序没有访问该文件所需的权限,则可能会出现此错误。要修复该错误,您可以执行输入验证以确保文件路径正确并且程序具有访问该文件的必要权限。

IO异常

IOException是 Java 中的检查异常,表示执行输入或输出操作(例如读取或写入文件或网络套接字)时遇到的错误。发生这种情况的原因有多种,例如指定的文件丢失或无法访问、网络错误或权限不足。要解决该问题,您需要执行多项操作,例如检查错误消息、使用try-catch处理异常、关闭资源、检查文件权限等。

没有这样的方法异常

NoSuchMethodException是当我们尝试调用类中不存在的方法时在运行时引发的异常。如果我们使用Class.getMethod()Class.getDeclaredMethod()调用方法,并且在类或接口中找不到指定的方法名称,通常会发生此异常。当我们尝试使用java.lang.reflect.Method类调用方法并且指定的方法名称在对象中不存在时,也可能会发生异常。为了避免此异常,请确保使用正确的方法签名和访问说明符调用有效的方法。

没有这样的字段异常

NoSuchFieldException是当我们尝试访问不在类中的字段时发生的运行时异常。当我们使用Class.getField()Class.getDeclaredField()调用方法并且在类或接口中找不到指定的字段名称时,通常会发生此异常。此外,如果我们尝试使用java.lang.reflect.Field类访问字段并且指定的字段名称在对象中不存在,也可以调用它。为了避免此异常,请确保您使用正确的名称和访问修饰符访问有效字段。如果您要访问私有字段,请务必使用getDeclaredField()方法而不是getField()方法。

非法访问异常

IllegalAccessException是当我们尝试访问类中的字段或方法但没有必要的访问权限时发生的运行时异常。当我们尝试从类外部访问私有字段或方法时,或者当我们尝试从不是原始类的子类的类访问受保护的字段或方法时,通常会出现此异常。当尝试访问已被java.lang.reflect.AccessibleObject类标记为不可用的字段或方法时,也可以调用它。为了避免此问题,请确保您对尝试访问的字段或方法拥有必要的权限。如果字段或方法是私有的,您可能需要使用反射并将AccessibleObject设置为true(才能访问它)。但是,在使用反射访问私有字段或方法时要小心,因为它可能会破坏封装并损害类的完整性。

验证错误

verifyError 是一个运行时错误,是LinkageError 的子类。当 Java 虚拟机 (JVM) 遇到违反某些验证规则的类文件时,就会发生这种情况。当编译 Java 类时,编译器会检查字节码是否遵循某些规则和限制,例如类型安全以及堆栈和局部变量的正确使用。如果类文件违反了这些规则,JVM在运行时加载和检查该类时将抛出VerifyError 。为了避免VerifyError,请确保您的代码遵循Java 语言的正确语法和语义。如果遇到VerifyError,您应该检查您的代码以确保其有效并且不违反Java字节码验证规则。

内存不足错误

OutOfMemoryError是Error 的子类,Error 是一种Throwable类型,会引入无法在运行时解决的严重问题。尽管 Java 8 在垃圾收集和内存管理方面进行了一些改进,但如果您的应用程序使用过多内存或错误地管理内存使用情况,您仍然可能会遇到OutOfMemoryError 。为了避免OutOfMemoryError,您需要正确管理 Java 应用程序中的内存使用情况。这涉及使用有效使用内存的数据结构和算法,避免不必要的对象创建,以及在不再需要时正确删除对象。此外,您可以在运行 Java 程序时 使用-Xmx标志来增加 JVM 的最大堆大小。

堆栈溢出错误

StackOverflowError是当程序所需的堆栈大小超过分配给它的内存量时发生的一种错误。当程序调用太多嵌套方法或方法递归调用自身太多次时,可能会发生这种情况,从而导致无限循环。Java 虚拟机 (JVM) 为执行堆栈分配固定数量的内存,用于跟踪方法调用和局部变量。当堆栈溢出时,JVM 会抛出StackOverflowError。为了避免StackOverflowError,确保您的 Java 程序充分利用递归和方法调用非常重要。如果遇到StackOverflowError ,您可以在运行 Java 程序时 尝试使用-Xss标志来增加堆栈大小。

调用目标异常

IncationTargetException是 Java 反射机制抛出的检查异常。它是java.lang.reflect.InitationTargetException包的一部分,用于指示在方法或构造函数调用期间发生异常。当使用Java反射机制调用方法或构造函数时,将调用java.lang.reflect.Methodjava.lang.reflect.Constructor类的invoke()方法。如果调用的方法或构造函数抛出异常,则invoke()方法会捕获该异常并将其包装在InvocableTargetException中。然后这个异常被传递给invoke()方法的调用者。要修复IncationTargetException,我们需要捕获它,使用getCause()方法获取根本原因异常,并相应地处理根本原因异常。请注意,根本原因可能是检查异常或运行时异常,因此请务必正确处理它。

修复 Java 中最常见异常的方法

如何修复空指针异常

场景:您有一个方法可以访问值为null的对象。
String title= null;
System.out.println(title.length()); // Это вызовет NullPointerException
解决方案#1:在使用前检查对象是否为空。
if(title!= null) {
   System.out.println(title.length());
} else {
   System.out.println("title is null");
}
解决方案#2:使用Optional来避免NullPointerException
Optional<String> optionalTitle = Optional.ofNullable(getTitle());
if (optionalTitle.isPresent()) {
   String title= optionalTitle.get();
   System.out.println("Title: " + title);
} else {
   System.out.println("Title is not available.");
}

如何修复 ArrayIndexOutOfBoundsException

场景:您尝试访问数组的索引超出其范围。
int[] numbers = {4, 5, 6};
System.out.println(numbers[3]);   // Это вызовет ArrayIndexOutOfBoundsException
解决方案:在访问数组之前检查数组的长度,并确保使用有效的索引。
int[] numbers = {4, 5, 6};
if (numbers.length > 3) {
   System.out.println(numbers[3]);
} else {
   System.out.println("ArrayIndexOutOfBoundsException: Please use valid indexes of the Array");
}

如何修复 ClassCastException

场景:您试图将对象转换为与其实际类型不兼容的类型。
Object obj = "Java Exception";
Integer number = (Integer) obj; // Это вызовет ClassCastException
解决方案:确保仅将对象转换为与其兼容的类型。
Object obj = "Java Exception";
if(obj instanceof Integer) {
   Integer number = (Integer) obj;
   System.out.println(number);
} else {
   System.out.println("Object cannot caste to Integer");
}

如何修复 IllegalArgumentException

场景:您向方法传递了无效参数。
public void printNumber(int number) {
   if(number <= 0) {
      throw new IllegalArgumentException("You cannot pass a negative number or zero");
   }
   System.out.println(number);
}

printNumber(-1); // Это вызовет IllegalArgumentException
解决方案:确保将有效的参数传递给方法。在这种情况下,请传递一个正数。
printNumber(1); //  Это успешно напечатает 1.

如何修复 IllegalStateException

场景:对象处于无效状态。
public class Bike {

   private Boolean isStarted;

   public void start() {
      if(isStarted) {
        throw new IllegalStateException("Bike is already started");
      }
      isStarted = true;
      System.out.println("Bike started");
   }
}

Bike bike= new Bike();
bike.start();
bike.start(); // Это вызовет IllegalStateException потому что bike is already started
解决方案:确保正确维护对象状态。
Bike bike= new Bike();
bike.start();

如何修复 UnsupportedOperationException

场景:您使用对象不支持的操作。一个流行的示例是,当您对不可变集合使用remove()操作时,您可能会看到UnsupportedOperationException异常。
List<String> list = Arrays.asList("Java", "Angular", "Spring");
list.add("Python"); // Это вызовет UnsupportedOperationException
由于Arrays.asList()方法返回一个不可变列表,因此它不支持添加或删除元素。 解决方案:确保仅对对象调用支持的操作。
List<String> list = new ArrayList<>(Arrays.asList("Java", "Angular" "Spring"));
list.add("Python");
System.out.println(list);

如何修复算术异常

场景#1:您正在尝试执行产生小数结果的整数除法运算。
int i = 10;
int j = 4;
int k = i/j; // Это вызовет исключение ArithmeticException: целочисленное деление будет дробным
这里除法运算的结果是 2.5,它是一个小数值。因为整数不能存储小数值,所以会抛出ArithmeticException解决方案:为了避免这种异常,我们可以使用支持小数值的数据类型(例如double)来存储除法运算的结果。这是一个例子:
int i = 10;
int j = 4;
double k = (double) i/j;
场景#2:您尝试除以零,但出现此异常。这是最常见的情况。
int i = 4;
int j = 0;
int k = i/j; // Это вызовет исключение ArithmeticException: нельзя делить на ноль
解决方案:正确处理除零。例如,下面的代码演示了正确的处理。
int i = 4;
int j = 0;
if(j != 0) {
int k = i/j;
System.out.println(k);
} else {
System.out.println("ArithmeticException: Cannot divide by zero");
}

如何修复 IndexOutOfBoundsException

场景:您正在尝试访问索引位于集合之外的集合。
List<String> list = Arrays.asList("Apple", "Papaya", "Mango");
System.out.println(list.get(3)); // Это вызовет IndexOutOfBoundsException
解决方案:在访问集合之前检查集合的大小,并确保使用有效的索引。
List<String> list = Arrays.asList("Apple", "Papaya", "Mango");
if (list.size() > 3) {
   System.out.println(list.get(3));
} else {
   System.out.println("You are using the Index which is out of bounds");
}

如何修复 IOException

场景:由于文件不可访问,输入/输出操作失败。
try {
   File inputFile = new FileReader("pqr.txt");
   BufferedReader reader = new BufferedReader(inputFile);
   String line = reader.readLine();
   System.out.println(line);
} catch (IOException e) {
   e.printStackTrace();
}
解决方案:处理I/O错误并确保资源正确关闭。
File inputFile = new File("pqr.txt");

if (!inputFile.exists() || !inputFile.canRead()) {
 System.out.println("The input file is missing or not readable.");
 return;
}

try {
BufferedReader reader = new BufferedReader(inputFile);
 String line = reader.readLine();
 System.out.println(line);
  reader.close();
} catch (IOException e) {
 e.printStackTrace();
}
请注意,作为替代方案,我们可以使用Java 7 中引入的try-with-resource功能来自动关闭资源,如下所示。我们可以在try语句中声明一个或多个资源,Java 会在块结束时自动关闭这些资源,无论块是否正常完成或抛出异常。
try (BufferedReader reader = new BufferedReader(new FileReader("pqr.txt"))) {
....
} catch {
....
}

如何修复 FileNotFoundException

场景:在指定位置未找到该文件。
try {
     BufferedReader reader = new BufferedReader(new FileReader("abc.txt"));
     String line = reader.readLine();
     System.out.println(line);
     reader.close();
} catch (FileNotFoundException | IOException e) {
     System.out.println("An error has occurred while reading the file: " + e.getMessage());
}
解决方案:确保该文件存在并且您具有访问该文件的适当权限。
try {
    File file = new File("abc.txt");
    if(!file.exists()) {
    throw new FileNotFoundException("File not found at the specified location");
    }
    BufferedReader reader = new BufferedReader(new FileReader(file));
    String line = reader.readLine();
    System.out.println(line);
    reader.close();
} catch (IOException e) {
    e.printStackTrace();
}

如何修复 NoSuchMethodException

场景:如果您尝试访问无法找到的方法。
public class TestClass {
   public void sayHello() {
      System.out.println("Hello");
   }
}

TestClass obj = new TestClass();
Method method = obj.getClass().getMethod("printHello"); // Это вызовет NoSuchMethodException
解决方案:验证该方法是否存在,并且方法名称和签名是否正确。
public class TestClass {
   public void sayHello() {
      System.out.println("Hello");
   }
}

TestClass  obj = new TestClass();
try {
    Method method = obj.getClass().getMethod("sayHello");
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}

如何修复并发修改异常

场景:集合在迭代时发生变化。
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
for (String str : list) {
  list.remove(str);  // Это вызовет ConcurrentModificationException
}
解决方案:使用迭代器迭代集合并使用迭代器方法修改它。
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String str = iterator.next();
    iterator.remove();
}
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION