目录
编译时异常(即checked异常、受检异常)(举几个常见的)
运行时异常(即runtime异常、unchecked异常、非受检异常)(举几个常见的)
finally的使用:(在try-catch结构中可写可不写)
异常概述和常见异常举例
异常概念:
指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。
异常不是指的语法错误和逻辑错误(就比如你少写了分号,或者求和运算,你写成求差运算)
异常的抛出机制:
java会把不同的异常用不同的类表示,一旦发生某种异常,就“创建该异常类型的对象”,并且抛出(throw)。然后程序员会通过catch(捕获)该异常并做相关处理,如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止
异常的体系
java.lang.Throwable异常体系根父类:有两个子类:java.lang.Error,java.lang.Exception。但它本身也继承于Object(他有两个的子类)
Error(错误):
一般不编写针对性的代码进行处理(虚拟机都解决不了,咱怎么解决)
- java虚拟机无法解决的严重问题。(如:jvm系统内部错误、资源耗尽等严重情况)
- 典型代表:StackOverflowErroe(栈内存溢出)、OutOfMemoryError(堆内存溢出,简称OOM)
Exception(异常):
需要使用针对性代码进行处理。(本篇的所有异常处理基本上都是针对他进行的处理)
编译时异常(即checked异常、受检异常)(举几个常见的)
- ClassNotFoundException
- FileNotFoundException
- IOException
//throws处理异常
public class Exception throws Exception{
public static void main(String[] args){
//ClassNotFoundException
Class clazz = Class.forName("java.lang.String");
//相对路径
File file = new File("hello.txt");//FileNotFoundException文件可能找不到
FileInputStream fis = new FileInputStream(file);//IOException
int data = fis.read();
while(daata != -1){
System.out.println((char)data);//IOException
data = fis.read;
}
fis.close();//IOException
}
}
运行时异常(即runtime异常、unchecked异常、非受检异常)(举几个常见的)
- ArrayIndexOutOfBoundsException
- NullPointerException
- ClassCaseException
- NumberFormatException
- InputMismatchException
- ArithmeticException
public class Exception{
public static void main(String[] args){
//ArrayIndexOutOfBoundsException数组角标越界
int[] arr =new int[10];
System.out.println(arr[10]);
//NullPointerException空指针异常
String str = null;
System.out.println(str);
//ClassCaseException
Object obj = new String();
Date date = (Date)obj;
//NumberFormatException
String str1 = "abc";
int i = Integer.parseInt(str1);
System.out.println(i);
//InputMismatchException
Scanner scan = new Scanner();
int num = scan.nextInt();//这里你输入的是abc
System.out.println(num);
//ArithmeticException
int num1 = 10;
System.out.println(num1/0);
}
}
关于异常的处理方式(针对Exception)
方式一(抓抛模型):try-catch-finally
过程
过程1“抛”:程序在执行的过程当中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出。一旦抛出,此程序就不执行其后的代码了。
过程2“抓”:针对于过程1中抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓。一旦将异常进行了处理,代码就可以执行了
格式
使用细节:
- 将可能出现异常的代码声明在try语句中,一旦代码出现异常,就会自动生成一个对应异常类的对象,并将此对象抛出。
- 针对于try中抛出的异常的对象,使用之后的catch语句进行匹配,一旦匹配上,就进入catch语句块进行处理,一旦处理结束,代码就可以继续向下执行。
- 如果catch对应的情况满足子父类关系,就必须将子类放在父类上面,否则就会报错。
- 不满足子父类的话顺序放置无影响。
- catch中异常处理的方式
- ①自己编写输出的语句
- ②printStackTrace():打印异常的详细信息。 (推荐)
- getMessage():获取发生异常的原因。
- getMessage():获取发生异常的原因。
- try中声明的变量,出了try结构之后,就不可以进行调用了。
开发体会:
运行时异常:对于运行时异常就不进行显示的处理了,如果出现了,就根据信息修改代码即可
编译时异常:一定要进行处理,否则会报错,根本就不会执行,除非你处理了他们
finally的使用:(在try-catch结构中可写可不写)
有没有这种感觉:如果出现了异常并且处理了,try-catch结构后的语句就会执行,finally也会执行,两者都可以执行,那为什么还需要finally呢?
- 理解:
- 我们将一定要被执行的代码声明在此结构中
- 无论try中或catch是否存在仍未被处理的异常(或是return ),此结构中声明的语句一定会被执行 (除了System.exit(0))
- 我们在开发中,有一些资源(比如:输入流、输出流,数据库连接、Socket连接等资源),在使用完以后,必须显式的进行关闭操作,否则,GC不会自动的回收这些资源。进而导致内存的泄漏。
- 为了保证这些资源在使用完以后,不管是否出现了未被处理的异常的情况下,这些资源能被关闭。我们必须将这些操作声明在异常处理的方式中!
可能的搭配:try-catch、try-finally
方式二(throws)
格式:
在方法的声明处,使用“throws 异常类型1,异常类型2....”。
举例:
public void test() throws 异常类型1,异常类型2……{
//可能存在编译时异常
}
是否真正处理了异常:
从编译是否能通过的角度看:看似给出了出现异常的解决方案,方案就是继续向上抛出(throws)。
但是,这种方法,仅仅是将可能出现的异常抛给了此方法的调用者,调用者仍需要考虑如何处理异常;此角度这种方式并没有处理异常。
回忆方法的重写的要求
子类重写的方法抛出的异常类型可以是父类被重写方法抛出异常类型的子类或者相同(针对于编译时异常)
思考为什么会有这样的规则呢?
就比如这种情况:如果他的子类重写的方法抛的比父类的方法还大,抓的过程可抓不到那么大的,如果小的话,就类似于多态了很容易就捕获了。
开发中,怎么选择异常处理的方式?
- 资源一定要关闭的时候(比如IO流的资源关闭)try-catch-finally
- 父类的被重写的方法没有throws异常类型,子类重写的方法只能用try-catch-finally处理异常
- 递进关系的方法中:A调B,B调C…依次调用多个方法,用throws,最后在A里用try-catch
手动throw异常对象
为什么手动抛出异常对象
实际开发中,如果出现不满足具体场景的代码问题,我们就有必要手动抛出一个指定类型的异常对象
就比如学生的id不能为负数,但java语言可没这种规定。此处抛的是运行时异常,所以不必处理。
如何理解手动和自动
在“抓抛”模型中:
- 实际的抛过程实际可以分为:自动和手动(主动使用“throw+异常类的对象”抛出异常对象)
- 这里的抓过程:
- 狭义上:try-catch的方式捕获异常,并处理。
- 广义上:把“抓”理解为“处理”,则此时对应着异常处理的两种方式try-catch-finally,throws
如何实现手动抛出异常?
方法内部,满足指定条件的情况下,使用“throw+new++异常类的对象+(语句)”抛出。
这里的语句指的是:如果在被处理的时候用了getMassage得到的就是语句里面的内容。
注意点:throw后面的代码不能被执行
throw和throws的区别
- 从使用方式角度:如何理解手动和自动
- 一个是用来产生异常对象的,一个是在方法处使用,是处理异常对象的方式。“上有排污,下游治污”
自定义异常类
如何自定义异常类
- 首先要继承异常类的体系,看想要什么类型的异常(运行时异常?等)。通常继承于RunTimeException\Exception
- 通常提供几个重载的构造器(参考已存在的异常类)
- 提供一个全局常量,声明为:static final long serialVersionUID = -331232131243L;(这个数值自己造,不要重复就行)
如何使用自定义异常类
- 在具体的代码中,满足指定条件的情况下,需要手动使用“throw+自定义异常类的对象”方式,将异常对象抛出
- 如果自定义异常类是非运行时异常,则必须考虑如何处理此异常对象。(try...\throws)
为什么需要自定义异常类
我们其实更关心的是根据异常的名称就能直接判断出此异常出现的原因,既然如此,我们有必要在实际开发环境中,不满足我们指定的条件时,指名我们自己特有的异常类。通过此异常类的名称,就能判断出具体出现的问题。