第七章 异常处理
概念
异常 : 在运行中出现的非正常状况, 会导致程序崩溃!!!
异常分类
异常分类 :
Error : 极其严重错误!!!
Exception : 一般问题
按照处理方式来分 :
受检异常(checked) : 在程序中必须接受检查和处理的异常,
如果不处理, 编译直接出错, 所以也称为编译时异常
Exception及其子类(RuntimeException及其子类除外) : 不容忽视的一般问题.
非受检异常(unchecked) :在程序不是必须接受检查和处理的异常, 如果 不处理, 编译不出错, 但是运行时仍然会出问题
所以也称为运行时异常
Error及其子类 : 太过严重, 处理不了
RuntimeException及其子类 : 太轻微, 太常见. 没有大的必要处理.
无论受检异常还是非受检异常都会导致程序崩溃. 所以应该对异常进行处理.
异常处理方式 (3种)
异常处理方式 (3种)
-
1)捕获, try.. catch
try {
有可能抛出异常为的语句
} catch (可能的异常类型1 引用) {
处理异常.
} catch (可能的异常类型2 引用) {
}
........
finally {
做一定要做的事情, 包括不在GC区中的资源释放操作, 这些资源通常是向OS申请的资源
如果不释放这样的资源, 就会形成资源泄露(resource leak)
}
catch列表中的异常类型必须从子类到父类写..
捕获的目的是让异常产生的破坏缩小到try中.
组合 :
try catch
try catch finally
try finally
-
2)抛出异常 : 在方法声明中加上throws 异常类型列表, 作用是警告调用者, 此方法有可能有风 险. 请风险自负.
在方法中的throw 却是真的抛出异常对象.
在方法中使用throw语句 可以抛出一个异常对象, 方法一旦执行了throw, 就相当于return, 但是和return不一样的在于, 它是带着破坏性的结束.
如果有throw, 则必须要有throws
如果有throws, 则不一定要有throw.
-
3)捕获再抛出
在普通方法中 :
try {
} catch(可能的异常类型 引用) {
throw new 自定义异常(引用);
}
方法覆盖 : Override, 子类方法重写父类继承来的方法.
条件 :
-
方法签名要一致, 返回值类型 方法名 参数列表(类型, 个数, 顺序)
如果返回值类型是类类型, 在重写时, 子类方法返回的类型可以是父类方法返回类型的子类类型.
-
子类方法的访问控制修饰符要大于等于父类方法的访问控制修饰符
-
父类和子类中的方法都是非static的. 静态方法是共存关系.
-
子类重写方法抛出的受检异常要小于等于父类类型的.
处理方式的选择
-
功能方法或被调用的方法抛出异常.
-
主方法或入口方法要捕获异常.
-
原则 : 方法出问题会不会影响栈的生存. 不会让栈死的方法要抛出异常, 会导致栈死的方法要捕获异常.
方法二 抛出异常
// 自定义异常 : 1) 写一个类继承Exception, 2) 提供构造器
class DividedByZeroException extends Exception { // 此异常是受检异常, 必须接受检查和处理的异常.
public DividedByZeroException(String message) {
super(message);
}
public DividedByZeroException(Throwable cause) {
super(cause);
}
}
public class ExceptionTest {
public static int divide(int x, int y) throws DividedByZeroException {
if (y == 0) {
throw new DividedByZeroException("除数不可以为0!!!!"); // 受检异常
}
return x / y;
}
//public static void main(String[] args) throws DividedByZeroException {
public static void main(String[] args) {
System.out.println("main begin");
try {
System.out.println(divide(10, 2));
System.out.println(divide(10, 0));
} catch (DividedByZeroException e) {
System.out.println(e.getMessage());
}
System.out.println("main end");
}
public static int divide2(int x, int y) {
if (y == 0) {
throw new RuntimeException("除数不可以为0!!!"); // 非受检异常
}
return x / y;
}
public static void main2(String[] args) {
System.out.println("main begin");
try {
System.out.println(divide2(10, 2));
System.out.println(divide2(10, 0));
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
System.out.println("main end"); // 核心
}
public static void main1(String[] args) {
System.out.println("main begin");
boolean b1 = true;
if (b1) {
//return;
}
try {
int n = Integer.parseInt(args[0]);//java.lang.ArrayIndexOutOfBoundsException: 0 数组下标越界异常
//java.lang.NumberFormatException: For input string: "xx" : 数字格式异常
System.out.println("n = " + n);
int[] arr = null;
///System.out.println(arr.length);
boolean b = true;
if (b) {
//return; // 即使是return也无法阻挡finally
}
} catch (ArrayIndexOutOfBoundsException e) { // 如果在try中真的出现了这个类型的异常, 要进入
e.printStackTrace(); // 打印异常对象出现的栈踪迹. 专业做法
} catch (NumberFormatException e) {
System.out.println(e.getMessage()); // 获取异常的消息, 普通做法
} catch (Exception e) { // 超级无敌万能夹
System.out.println(e); // toString
} finally { // 最后状态
System.out.println("finally, 无论前面try catch...中发生什么, 我都要执行");
}
System.out.println("main end"); // 核心重要代码
}
}
应用实例
public class TestException {
//编写TestException类,在main方法中接收两个命令行参数,将它们转换为整数,并用第一个数除以第二个数,打印结果。
//运行程序,给出两个参数,测试以下情况,观察运行结果:其中某个参数不是数字, 什么都不传入, 第二个参数为0
public static void main(String[] args) {
System.out.println("main begin....");
try {
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
int n3 = n1 / n2;
System.out.println(n1 + " / " + n2 + " = " + n3);
//java.lang.ArrayIndexOutOfBoundsException : 数组下标越界异常
//java.lang.NumberFormatException : 数字格式 异常
//java.lang.ArithmeticException: / by zero
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
System.out.println(e.getMessage());
} catch (ArithmeticException e) {
System.out.println(e);
} catch (Exception e) {
System.out.println(e);
}
System.out.println("核心 代码...");
}
}
方法三 捕获再抛出
class DividedByZeroException extends Exception {
public DividedByZeroException(String message) {
super(message);
}
public DividedByZeroException(Throwable cause) {
super(cause); // 对象关联
}
}
public class ExceptionTest {
public static int divide(int x, int y) throws DividedByZeroException {
try {
return x / y;
} catch (ArithmeticException e) {
// 包装
throw new DividedByZeroException(e); // 对象关联, 自定义异常对象关联了捕获到的异常
}
}
public static void main3(String[] args) {
System.out.println("main begin");
try {
System.out.println(divide(10, 2));
System.out.println(divide(10, 0));
} catch (DividedByZeroException e) {
e.printStackTrace();
}
System.out.println("main end");
}
public static int divide2(int x, int y) throws DividedByZeroException {
if (y == 0) {
throw new DividedByZeroException("除数不可以为0");
}
return x / y;
}
//主方法不要抛出异常
//public static void main(String[] args) throws DividedByZeroException {
public static void main(String[] args) {
System.out.println("main begin");
try {
System.out.println(divide2(10, 2));
System.out.println(divide2(10, 0));
} catch (DividedByZeroException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("main end"); // 核心代码
}
}
注意
异常处理适用于所有异常, 包括Error T
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?