1、什么是异常
指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行
2、java中的异常机制
如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下 会抛出一个封装了错误信息的对象。此时,这个方法会立刻退出同时不返回任何值。另外,调用 这个方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器。
java中的异常体系如下
在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。 Throwable 类有两 个重要的⼦类:
Exception 又有两个分支,一个是运行时异常 RuntimeException , 一个是 ckedException。 RuntimeException 如 : NullPointerException 、 ClassCastException ; 一 个 是 检 查 异 常 CheckedException,如 I/O 错误导致的 IOException、SQLException。 RuntimeException 是那些可 能在 Java 虚拟机正常运行期间抛出的异常的超类。 如果出现 RuntimeException,那么一定是程序员的 错误.
检查异常 CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序 去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一般包括几个方 面:
1. 试图在文件尾部读取数据
2. 试图打开一个错误格式的 URL
3. 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在
Error : Error 属于程序⽆法处理的错误 ,我们没办法通过 catch 来进⾏捕获不建议通过 catch 捕获 。例如 Java 虚拟机运⾏错误( Virtual MachineError )、虚拟机内存不够错误 ( OutOfMemoryError )、类定义错误( NoClassDefFoundError )等 。这些异常发⽣时,Java 虚拟 机(JVM)⼀般会选择线程终⽌
异常的父类是 java.lang.Throwable ,Throwable中的常用方法
//打印异常的详细情况 包含异常的类型,异常原因、还包括异常出现的位置,在开发和调试阶段,都得使用此方法 public void printStackTrace() //获取异常信息 提示给用户的时候,只提示错误原因 public void getMessage() //获取异常类型和异常信息 public String toString()
其下有两个子类:java.lang.Error与 java.lang.Exception
2.1、Error
严重错误Error,无法通过处理的错误,只能事先避免,好比绝症,最常见的就是VirtualMachineError,它有两个经典的子类:StackOverflowError、OutOfMemoryError。
public class Test { public void test(){ //StackOverflowError 栈溢出异常 test(); } public void test1(){ //OutOfMemoryError 堆溢出异常 int[] arr = new int[Integer.MAX_VALUE]; } }
2.1、Exception
表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的,分为运行时异常和编译时异常
运行时异常(非受检异常,RuntimeException及其子类)
public static void main(String[] args) { //这种写程序时候不报错,但是编译完运行的时候抛异常的就是运行时异常 System.out.println(10/0); }
RuntimeException类常见的异常子类有:
1. ArithmeticException(试图除数为0时)
2. NullPointerException(当程序访问一个空对象的成员变量或方法,访问一个空数组的成员时发生。)
4. ArrayIndexOutOfBoundsException访问的元素下表超过数组长度 分析jvm是如何处理异常的过程
5. NumberFormatException数字格式异常!
编译异常(受检异常)
指的是那些必须处理的异常(运行之前程序就报错)
public static void main(String[] args) { //这种写代码的时候就报错不能编译的就是编译异常 System.out.println(a); try { //这里必须使用try-catch处理的是也是编译时异常 FileInputStream fileInputStream = new FileInputStream("d:/test.txt"); }catch (Exception e){ e.printStackTrace(); } }
3、异常产生过程分析
4、异常处理
在Java应用中,异常的处理机制分为声明异常,抛出异常和捕获异常
可以根据下图来选择是捕获异常,声明异常还是抛出异常
4.1、捕获异常
try{ ...... //可能产生异常的代码 } catch( 异常类型1 e ){ ...... //当产生异常类型1型异常时的处置措施 } catch( 异常类型2 e ){ ...... //当产生异常类型2型异常时的处置措施 } finally{ ...... //无论是否发生异常,都无条件执行的语句 }
1、整体运行流程
如果在程序运行时,try块中的代码没有发生异常,那么catch所有的分支都不执行。
如果在程序运行时,try块中的代码发生了异常,根据异常对象的类型,将从上到下选择第一个匹配的catch分支执行。此时try中发生异常的语句下面的代码将不执行,而整个try…catch之后的代码可以继续运行。
如果在程序运行时,try块中的代码发生了异常,但是所有catch分支都无法匹配(捕获)这个异常,那么JVM将会终止当前方法的执行,并把异常对象“抛”给调用者。如果调用者不处理,程序就挂了
2、try
捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的业务逻辑代码放在try语句块中。
3、catch
catch分支,分为两个部分,catch()中编写异常类型和异常参数名,{}中编写如果发生了这个异常,要做什么处理的代码。
如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。
比如:可以用ArithmeticException类作为参数的地方,就可以用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)。
每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
如果有多个catch分支,并且多个异常类型有父子类关系,必须保证小的子异常类型在上,大的父异常类型在下。否则,报错。
catch中常用异常处理的方式
a、public String getMessage():获取异常的描述信息,返回字符串
b、public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。包含了异常的类型、异常的原因、还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace()。
4、finally
因为异常会引发程序跳转,从而会导致有些语句执行不到。而程序中有一些特定的代码无论异常是否发生,都需要执行。例如,数据库连接、输入流输出流、Socket连接、Lock锁的关闭等,这样的代码通常就会放到finally块中。所以,我们通常将一定要被执行的代码声明在finally中。
唯一的例外,使用 System.exit(0) 来终止当前正在运行的 Java 虚拟机。
不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
finally语句和catch语句是可选的,但finally不能单独使用。
try{ }finally{ }
案例1:关闭资源
package com.atguigu.keyword; import java.util.InputMismatchException; import java.util.Scanner; public class TestFinally { public static void main(String[] args) { Scanner input = new Scanner(System.in); try { System.out.print("请输入第一个整数:"); int a = input.nextInt(); System.out.print("请输入第二个整数:"); int b = input.nextInt(); int result = a/b; System.out.println(a + "/" + b +"=" + result); } catch (InputMismatchException e) { System.out.println("数字格式不正确,请输入两个整数"); }catch (ArithmeticException e){ System.out.println("第二个整数不能为0"); } finally { System.out.println("程序结束,释放资源"); input.close(); } } @Test public void test1(){ FileInputStream fis = null; try{ File file = new File("hello1.txt"); fis = new FileInputStream(file);//FileNotFoundException int b = fis.read();//IOException while(b != -1){ System.out.print((char)b); b = fis.read();//IOException } }catch(IOException e){ e.printStackTrace(); }finally{ try { if(fis != null) fis.close();//IOException } catch (IOException e) { e.printStackTrace(); } } } }
关于try-catch使用 推荐文章
java 异常 try catch finally中return的影响详解_zzhongcy的博客-CSDN博客
4.2、抛出异常
如果你觉得解决不了某些异常问题,且不需要调用者处理,那么你可以抛出异常。 throw关键字作用是 在方法内部抛出一个Throwable类型的异常。任何Java代码都可以通过throw语句抛出异常。
public static void main(String[] args) { test(); } public static void test() { if(1==2){ //可以throw一个运行时异常 throw new RuntimeException(); }else if (1>2){ //throw一个自定义异常 throw new MyException(); }else { //可以throw一个具体的异常 throw new IndexOutOfBoundsException(); } }
有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子 系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节
4.3、声明异常
通常 应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下 去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常
public int div(int a,int b) throws Exception { return a/b; }
throws后面可以写多个异常类,用逗号隔开。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理,如下
public static void main(String[] args) throws FileNotFoundException { test(); } public static void test() throws FileNotFoundException { if(1==2){ //可以throw一个运行时异常 throw new FileNotFoundException(); } }
6、自定义异常
1. 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception 。
2. 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException
习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详 细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用)