JavaSE--异常处理机制
一、异常exception
1、什么是异常,以及Java提供的异常处理机制
程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
java语言提供了异常的处理方式,如果程序执行过程中出现了不正常情况,
java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮
2、异常以类和对象的形式存在
异常在Java中以类的形式存在,每一个异常类都可以创建异常对象
// 通过“异常类”实例化“异常对象” NumberFormatException nfe = new NumberFormatException("数字格式化异常!"); // java.lang.NumberFormatException: 数字格式化异常! System.out.println(nfe); // 通过“异常类”创建“异常对象” NullPointerException npe = new NullPointerException("空指针异常发生了!"); //java.lang.NullPointerException: 空指针异常发生了! System.out.println(npe);
3、遇到异常,Java会自己new对象
/* 程序在这里除以0, 发生了ArithmeticException异常, 底层new了一个ArithmeticException异常对象, 然后抛出了,由于是main方法调用了100 / 0, 所以这个异常ArithmeticException抛给了main方法, main方法没有处理,将这个异常自动抛给了JVM。VM最终终止程序的执行。 ArithmeticException 继承 RuntimeException,属于运行时异常。 在编写程序阶段不需要对这种异常进行预先的处理。 */ System.out.println(100 / 0); // 这里的HelloWorld没有输出,没有执行。 System.out.println("Hello World!");
二、异常处理机制
1、异常的处理结构
Throwable继承自Object超类 // Throwable(可抛出的) 不管是错误还是异常都是可以抛出的 Error和Exception继承自Throwable // Error是错误类,只要有错误发生,Java程序只有一个结果,就是终止程序执行,退出jvm // 错误是不能处理的 // Exception是异常类 Exception分为:Exception的直接子类、RuntimeException 编译时异常 所有Exception的直接子类,都叫做编译时异常。编译时异常不是在编译阶段发生的 编译时异常是表示必须在编写程序的时候预先对这种异常进行处理,如果不处理编译器报错。 运行时异常 所有的RuntimeException及子类都属于运行时异常。运行时异常在编写程序阶段,你可以选择处理,也可以不处理。
2、编译时异常和运行时异常的区别
编译时异常发生概率高,编译时异常又被称为受检异常或者受控异常
运行时异常发生概率低,运行时异常又被称为未受检异常或者未受控异常
注意::所有异常都是发生在运行阶段的
3、异常处理的两种方式
1)第一种方式:
在方法声明位置上,使用throws关键字看,抛给上一级(异常的上抛)
谁调用我就抛给谁
注意:一般不建议抛给main方法,main方法中最好使用try catch
public class ExceptionTest04 { public static void main(String[] args) { // main方法中调用doSome()方法 // 因为doSome()方法声明位置上有:throws ClassNotFoundException // 我们在调用doSome()方法的时候必须对这种异常进行预先的处理。 // 如果不处理,编译器就报错 //编译器报错信息: Unhandled exception: java.lang.ClassNotFoundException doSome();// 编译时报错 } /** * doSome方法在方法声明的位置上使用了:throws ClassNotFoundException * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。 * 叫做类没找到异常。这个异常直接父类是:Exception,所以ClassNotFoundException属于编译时异常。 * @throws ClassNotFoundException */ public static void doSome() throws ClassNotFoundException{ System.out.println("doSome!!!!"); } }
// 第一种:使用throw关键字 public class ExceptionTest04 { // 抛给调用者 public static void main(String[] args) throws ClassNotFoundException{ doSome(); } public static void doSome() throws ClassNotFoundException{ System.out.println("doSome!!!!"); } }
注意注意:throws抛异常时可以抛出该异常,也可以抛出该异常的父类异常,也可以抛出所有异常
// 抛FileNotFoundException的父对象IOException,这样是可以的。因为IOException包括FileNotFoundException private static void m2() throws IOException { // Exception包括所有的异常 private static void m2() throws Exception{ // throws后面也可以写多个异常,可以使用逗号隔开。 private static void m2() throws ClassCastException, FileNotFoundException{
1)第二种方式:
使用try... catch语句进行异常的捕捉(异常的捕捉)
注意:Java中 异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序的执行。
// 第二种try...catch public static void main(String[] args) { try { // 尝试 doSome(); } catch (ClassNotFoundException e) {// e是为这个异常取的名字 // catch是捕捉到异常之后走的分支 System.out.println("类没有发现异常"); e.printStackTrace(); } } public static void doSome() throws ClassNotFoundException{ System.out.println("doSome!!!!"); } }
注意注意:只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行
4、try...catch的深入了解
1)catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型,也可以抛出Exception
2)catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试
3)catch写多个的时候,从上到下,必须遵守从小到大
public class ExceptionTest07 { public static void main(String[] args) { try { //创建输入流 FileInputStream fis = new FileInputStream("D:document\\风格.pdf"); //读文件 fis.read(); } catch(FileNotFoundException e) { System.out.println("文件不存在!"); } catch(IOException e){ System.out.println("读文件报错了!"); } } }
// JDK8的新特性 // 可以 或 异常,这个异常或者那个异常 try { //创建输入流 FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf"); // 进行数学运算 System.out.println(100 / 0); // 这里这些异常可以 或 } catch(FileNotFoundException | ArithmeticException | NullPointerException e) { System.out.println("文件不存在?数学异常?空指针异常?都有可能!"); }
5、上抛和捕捉怎么选择
如果希望调用者来处理,就使用上抛。其他情况使用捕捉
6、异常对象常用方法
获取异常简单的描述信息: String msg = exception.getMessage(); 打印异常追踪的堆栈信息: exception.printStackTrace();
// 这里只是为了测试getMessage()方法和printStackTrace()方法。 // 这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。 NullPointerException e = new NullPointerException("空指针异常"); // 获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。 String msg = e.getMessage(); //空指针异常 System.out.println(msg); // 打印异常堆栈信息 // java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。 e.printStackTrace();
7、try catch中finally关键字
1)
在finally自居中的代码时最后执行的,并且一定会执行的,即使try语句块中的代码出现异常
必须和try catch一起使用
2)finally语句通常使用在哪些情况下
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。即使try语句块中的代码出现异常,finally中代码也会正常执行。
3)退出jvm之后finally就不执行了
System.exit(0);
public class ExceptionTest10 { public static void main(String[] args) { FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。 try { // 创建输入流对象 fis = new FileInputStream("D:\\JavaSE进阶-01-面向对象.pdf"); // 开始读文件.... String s = null; // 这里一定会出现空指针异常! s.toString(); System.out.println("hello world!"); // 流使用完需要关闭,因为流是占用资源的。 // 即使以上程序出现异常,流也必须要关闭! // 放在这里有可能流关不了。 //fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } catch(NullPointerException e) { e.printStackTrace(); } finally { System.out.println("hello 浩克!"); // 流的关闭放在这里比较保险。 // finally中的代码是一定会执行的。 // 即使try中出现了异常! if (fis ! = null) { // 注意注意::避免空指针异常 try { // close()方法有异常,采用捕捉的方式。 fis.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("hello kitty!"); } }
三、自定义异常
1、为什么自定义异常
SUN提供的JDK内置的异常肯定是不够的用的
在实际的开发中,有很多业务,这些业务出现异常之后,JDK中都是没有的和业务挂钩的。那么异常类我们可以自己定义
2、怎么定义异常
第一步:编写一个类继承Exception或者RuntimeException
第二步:提供两个构造方法,一个无参数的,一个带有String参数的
// 就是这样的结构,这样写就行,根据sun公司的源代码来这样写 public class MyException extends Exception{ // 编译时异常 public MyException(){ } public MyException(String s){ super(s); } } /* public class MyException extends RuntimeException{ // 运行时异常 } */
public class ExceptionTest15 { public static void main(String[] args) { // 创建异常对象(只new了异常对象,并没有手动抛出) MyException e = new MyException("用户名不能为空!"); // 打印异常堆栈信息 e.printStackTrace(); // 获取异常简单描述信息 String msg = e.getMessage(); System.out.println(msg); } }
3、怎么使用自定义异常
trow手动抛出异常
// 先定义一个异常 /** * 栈操作异常:自定义异常 */ public class MyStackOperationException extends Exception{ // 编译时异常 public MyStackOperationException(){ } public MyStackOperationException(String s){ super(s); } }
// 一个模拟栈的类 // 其中的一个方法 /** * 压栈的方法 * @param obj 被压入的元素 */ public void push(Object obj) throws MyStackOperationException { if(index >= elements.length - 1){ /* 栈满了 这也算是一个异常,那我们就可以自定一个异常类来这个让我们使用 以前的写法 System.out.println("压栈失败,栈已满"); return; */ // 创建异常对象 //MyStackOperationException e = new MyStackOperationException("压栈失败,栈已满!"); // 手动将异常抛出去! //throw e; //这里捕捉没有意义,自己new一个异常,自己捉,没有意义。栈已满这个信息你需要传递出去。 // 合并 throw new MyStackOperationException("压栈失败,栈已满!"); } index++; elements[index] = obj; System.out.println("压栈" + obj + "元素成功,栈帧指向" + index); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能