1 抛出异常
1.1 关于抛出异常
抛出异常是指在程序执行过程中遇到错误或异常情况时,由当前代码主动创建并抛出一个异常对象。抛出异常的目的是通知上层调用者或异常处理机制,表明当前代码无法正常处理某个特定情况,需要由上层调用者或异常处理机制来处理。
举例说明一下:
在程序开发中,有时我们需要通过网络或数据库等底层服务获取数据。然而,这些底层服务可能会出现故障,如网络连接断开或数据库不可用等情况。在这种情况下,我们可以使用try-catch块来尝试处理异常,以便在出现故障时采取适当的措施。
通过使用try-catch块,我们可以捕获并处理底层服务可能引发的异常。我们可以在catch块中执行一些恢复操作、记录日志或通知用户等操作,以确保程序的稳定性和可靠性。
然而,有时候尽管我们尝试了多次,底层服务仍然处于故障状态,无法恢复正常。在这种情况下,为了让程序的调用者知道出现了故障,并且进一步处理该情况,我们需要抛出异常。
通过抛出异常,我们将故障信息传递给调用者或上层代码。调用者可以根据异常类型和信息,采取适当的措施,例如提供备用方案、显示错误信息给用户或记录故障报告。抛出异常能够提供更灵活的异常处理方式,并将异常处理的责任交给上层代码。
综上所述,抛出异常的目的是让程序的调用者知晓底层服务的故障情况,并且能够进一步处理该异常。这种异常处理方式可以提高程序的可靠性和容错性,使程序在出现异常时具备更好的应对能力。
1.2 throw
当编写 Java 程序时,throw和throws关键字用于处理异常情况,让代码能够更好地应对错误和异常。
throw 关键字的用途:
- throw 关键字用于在代码中显式地抛出一个异常对象。
- 使用 throw 可以在程序中触发异常,表示出现了某种错误或异常情况,需要中断当前的执行流程并传递异常给上层代码。
- throw 关键字通常在方法体内部使用,用于抛出自定义的异常或已有的异常对象。
示例:
public void divide(int dividend, int divisor) {
if (divisor == 0) {
throw new ArithmeticException("Division by zero");
}
int result = dividend / divisor;
System.out.println("Result: " + result);
}
在上述示例中,如果除数为零,则会使用 throw 关键字抛出一个 ArithmeticException 异常对象,表示除以零的错误情况。
1.3 throws
throws 关键字的用途:
- throws 关键字用于在方法声明中指定可能会抛出的异常类型。
- 当一个方法可能抛出异常时,可以使用 throws 关键字在方法签名中声明这些异常,以告知方法的调用者可能需要处理这些异常。
- throws 关键字后面跟着的是一个或多个异常类型,用逗号分隔,表示该方法可能会抛出这些异常。
示例:
public void readFile() throws FileNotFoundException {
File file = new File("path/to/file.txt");
FileInputStream fis = new FileInputStream(file);
// 读取文件的代码
}
在上述示例中,readFile 方法可能会抛出 FileNotFoundException 异常,所以使用 throws 关键字在方法签名中声明了该异常。这样,调用者在调用该方法时就知道可能需要处理该异常。
需要注意的是,当使用 throws 声明异常时,调用该方法的代码必须对这些异常进行处理,或者继续将异常往上层抛出。处理异常的方式可以是使用 try-catch 块捕获并处理异常,或者在调用者方法中再次使用 throws 声明该异常。
通过使用 throw 和 throws 关键字,我们能够更好地处理异常情况,提高代码的可读性和可维护性,同时为程序提供更好的异常处理机制。
1.4 throw和throws示例
下面是一个使用 throw 和 throws 的示例:
public class ExceptionDemo4 {
public static void main(String[] args) {
try {
divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("捕获到异常:" + e.getMessage());
}
}
public static void divide(int dividend, int divisor) throws ArithmeticException {
if (divisor == 0) {
throw new ArithmeticException("除数不能为零");
}
int result = dividend / divisor;
System.out.println("结果:" + result);
}
}
在上述代码中,我们定义了一个 divide 方法,用于进行两数相除的操作。在方法中,我们首先检查除数是否为零,如果是,则使用 throw 关键字抛出一个 ArithmeticException 异常,并提供异常信息。如果除数不为零,那么我们进行除法计算,并打印结果。
在 main 方法中,我们调用了 divide 方法,并使用 try-catch 块捕获可能抛出的 ArithmeticException 异常。如果异常被捕获到,我们输出异常信息。
通过使用 throws 关键字在方法签名中声明 throws ArithmeticException,我们告知调用者,该方法有可能抛出 ArithmeticException 异常,需要进行相应的异常处理。
这个示例展示了如何使用 throw 关键字抛出异常,并使用 throws 关键字声明可能抛出的异常类型。通过这种方式,我们可以在方法内部检测到错误情况,并将异常传递给调用者进行处理
1.5 重写方法时的throws规则
在重写方法时,对于方法签名中声明的异常类型,有以下规则:
- 子类重写的方法可以不抛出任何异常,即子类方法可以不使用 throws 关键字声明任何异常。这是因为子类方法可以选择不抛出任何异常,即使父类方法声明了异常。
- 子类重写的方法可以抛出父类方法声明的异常,或者该异常的子类。这被称为异常的协变性。在重写方法时,可以使用与父类方法相同的异常声明,或者使用父类异常的子类声明。
- 子类重写的方法抛出的异常类型不能比父类方法声明的异常类型更广泛(即不能抛出父类方法未声明的异常)。这是因为子类对象可以作为父类对象使用,如果子类方法抛出了更广泛的异常,那么在父类对象引用的场景下,异常处理可能会出现问题。
下面是一个示例,展示了重写方法时 throws 的规则:
总结来说,子类重写方法时的 throws 规则是允许不抛出异常、抛出相同异常或其子类异常,但不能抛出其他异常。这样可以保持异常处理的一致性和可靠性。
编写代码,重写方法,并测试其 throws。代码示意如下:
class Parent {
public void method() throws IOException {
// 父类方法声明了IOException异常
}
}
class Child extends Parent {
// 子类重写父类方法,可以不抛出异常
@Override
public void method() {
// 不抛出异常
}
}
class Child2 extends Parent {
// 子类重写父类方法,可以抛出IOException或其子类异常
@Override
public void method() throws FileNotFoundException {
// 抛出FileNotFoundException异常
}
}
class Child3 extends Parent {
// 子类重写父类方法,不可抛出其他异常,只能抛出父类方法声明的IOException异常
@Override
public void method() throws IOException {
// 抛出IOException异常
}
}
在上述示例中,Parent 类中的 method() 方法声明了抛出 IOException 异常。子类 Child 可以选择不抛出任何异常,而子类 Child2 可以抛出 FileNotFoundException 异常,因为它是 IOException 的子类。子类 Child3 只能抛出 IOException 异常,不能抛出其他异常。
这个示例展示了在重写方法时的 throws 规则,子类可以选择不抛出异常、抛出相同异常或其子类异常,但不能抛出其他异常。这样可以保持异常处理的一致性和可靠性,同时给程序提供更准确的异常信息。
2 自定义异常
2.1 什么是自定义异常
自定义异常是指开发者创建的异常类,用于表示特定类型的异常情况。尽管Java提供了许多异常类,但在实际应用中可能需要更多自定义的异常来适应特定的业务需求。例如,在进行数据操作时,可能会遇到错误的数据,这些错误的数据出现时应该抛出异常,比如AddException。然而,Java并没有提供这样的异常类,所以需要开发者自己创建一个自定义的异常类来满足需求。通过自定义异常,开发者可以更精确地描述和处理特定的异常情况,提高代码的可读性和可维护性。
2.2 创建自定义异常
创建自定义异常通常涉及以下步骤:
1. 创建一个继承自Exception或RuntimeException的子类。根据异常的性质和场景选择合适的父类。如果希望自定义异常是受检异常,应继承Exception类;如果希望自定义异常是非受检异常,应继承RuntimeException类。
2. 在自定义异常类中,给类起一个明确的名称,反映该异常所代表的特定异常情况。命名应具有描述性,能够清晰地表达异常的含义,方便其他开发者理解和使用。
3. 在自定义异常类中,通常需要显式声明构造函数。构造函数可根据需要接受参数,并使用super关键字调用父类对应参数的构造函数。这样可以确保在创建自定义异常对象时,父类的属性得到正确的初始化。
通过以上步骤,开发者可以创建自定义异常类,用于表示特定的异常情况。自定义异常类可以提供更具体和明确的异常信息,使异常处理更加精确和灵活。同时,自定义异常类也可以帮助其他开发者更好地理解和处理代码中的异常情况。
下面是一个创建自定义异常的示例代码:
// 自定义异常类
class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}
// 使用自定义异常类的示例
class MyClass {
public void performOperation(int value) throws MyCustomException {
if (value < 0) {
throw new MyCustomException("值不能为负数");
}
// 执行其他操作
}
}
// 在主程序中捕获和处理自定义异常
public class CustomExceptionExample {
public static void main(String[] args) {
MyClass myObject = new MyClass();
try {
myObject.performOperation(-5);
} catch (MyCustomException e) {
System.out.println("捕获到自定义异常:" + e.getMessage());
}
}
}
在上述示例中,我们创建了一个名为UsernameCannotBeNullException的自定义异常类,它继承自Exception类。在UserRegistration类的registerUser方法中,如果传入的用户名为null或为空字符串,就会抛出UsernameCannotBeNullException异常。在主程序中,我们通过try-catch语句块捕获并处理自定义异常,打印出异常的消息。
这个示例展示了如何根据特定的业务需求创建自定义异常,并在代码中抛出和处理这个异常。这样可以提高代码的可读性,让异常信息更加明确,帮助开发人员快速定位问题