异常简介
异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。异常不是语法错误,语法错误,编译不通过,不会产生字节码文件,根本不会运行JVM。Java 中,异常本身是一个类,产生异常就是创建异常对象并且抛出一个异常对象
异常处理机制主要回答了三个问题:
What:异常类型回答了什么被抛出
Where:异常堆栈跟踪回答在哪抛出
Why:异常信息回答了为什么被抛出
异常分类
Throwable 类是 Java 语言中所有错误或异常的超类
- Error:错误,无法解决的问题(癌症)
- Exception:可被解决的问题 (感冒)
- 受检,编译时期异常:checkedException。在编译时期,就会检查(IDEA报红),如果没有处理异常,则编译失败(日期格式化异常)。需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复
- 非受检,运行时期异常:RuntimeException。程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复
数据类型 | 方法 | 说明 |
---|---|---|
String | getMessage() | 返回此 throwable 的详细消息字符串 |
String | toString() | 返回此 throwable 的简短描述 |
void | printStackTrace(PrintStream s) | 将此 throwable 及其追踪输出到指定的输出流 |
void | printStackTrace(PrintWriter s) | 将此 throwable 及其追踪输出到指定的 PrintWriter |
Error:
-OutOfMemoryError :内存不足错误,访问、修改某类的域(Field)或调用其方法,但违反域或方法的可见性声明
-IllegalAccessError:违法访问错误,当可用内存不足以让 JVM 分配给一个对象时抛出该错误
-InstantiationError:实例化错误,通过 new 操作符构造一个抽象类或者接口时抛出该异常
-StackOverflowError:堆栈溢出错误,当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误
RuntimeException:
-NullPointerException <空指针异常>,调用null对象的实例方法、访问null对象的属性、计算 null对象的长度
-ArrayIndexOutOfBoundsException <数组索引越界>,对数组的索引值为负数或大于等于数组大小 时抛出
-ClassCastException <类型转换异常>,类A和B无关系,O是A的实例,强制将O构造为类B 的实例时抛出该异常
-ClassNotFoundException <找不到类异常>,根据字符串形式的类名构造类,遍历CLASSPAH后找不 到对应的class文件,抛出异常
-ArithmeticException <算术异常>,整数除零
其他Exception:
InstantiationException <实例化异常>,new Instance()方法创建某个类的实例,而该类是 一个抽象类或接口时,抛出该异常
-IndexOutOfBoundsException <索引越界异常>,当访问某个序列的索引值小于0或大于等于序列大小 时,抛出该异常
-NumberFormatException <数字格式异常>,将String转换为数字,该字符串不满足数字 类型要求的格式时,抛出该异常
-FileNotFoundException <文件未找到异常>
-NoSuchFieldException <属性不存在异常>,当访问某个类的不存在的属性时抛出该异常
-NoSuchMethodException <方法不存在异常>,当访问某个类的不存在的方法时抛出该异常
-SQLException <操作数据库异常>
-IOException <IO异常>
JVM 是如何处理异常:
在方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列方法调用,终才进入抛出异常的方法,这一系列方法调用的有序列表叫调用栈。JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常 处理器(默认处理
器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序
Exception和RuntimeException区别:
- Exception :受检查的异常,这种异常是强制我们catch或throw的异常
- RuntimeException:运行时异常,这种异常我们不需要处理,完全由虚拟机接管, RuntimeException也是继承自Exception
Error 和 Exception 区别
- Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止
- Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行
运行时异常和一般异常(受检异常)区别:
1.运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。Java 编译器不会检查运行时异常
2.受检异常是Exception 中除 RuntimeException 及其子类之外的异常。Java 编译器会检查受检异常
3.区别:是否强制要求调用者必须处理此异常,强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,建议使RuntimeException
异常处理
throw
作用:在指定的方法中抛出指定的异常
格式:
throw new xxxException(“产生异常原因”)
注意:
1.必须写在方法内部
2.new 出的对象必须是 Exception 或它的子类对象
3.抛出的对象必须要处理(RuntimeException及其子类可以不处理)
public class ThrowDemo {
public static void main(String[] args) {
int[] arr = null;
int[] arr2 = new int[3];
//java.lang.NullPointerException: 传递数组值为null
System.out.println(getElement(arr,0));
//java.lang.ArrayIndexOutOfBoundsException: 传递的索引超出数组使用范围
System.out.println(getElement(arr2,3));
}
public static int getElement(int[] arr, int index){
//参数合法性校验
//arr 为 null ,抛出空指针异常,并且告知“传递数组值为null”
//index 不在数组索引范围,抛出数组索引越界异常,并且告知“传递的索引超出数组使用范围”
if (arr == null){
throw new NullPointerException("传递数组值为null");
//NullPointerException 运行期异常,不处理
}
if (index < 0 || index > arr.length-1){
throw new ArrayIndexOutOfBoundsException("传递的索引超出数组使用范围");
//ArrayIndexOutOfBoundsException 运行期异常,不处理
}
int ele = arr[index];
return ele;
}
}
参数合法性校验
import java.util.Objects;
public class ObjectsNullDemo {
public static void main(String[] args) {
method(null);
}
public static void method(Object obj){
//合法性校验
if (obj == null){
throw new NullPointerException("传递对象为 null");
}
Objects.requireNonNull(obj,"传递对象为 null");
}
}
throws
自己不处理,给别人处理
格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
缺陷:一旦产生异常,JVM终止运行,后续代码无法继续
import java.io.FileNotFoundException;
import java.io.IOException;
public class ThrowsDemo { //IOException 是 FileNotFoundException 的父类,可省
public static void main(String[] args) throws /*FileNotFoundException,*/ IOException {
//readFile("D:\\a.txt");
readFile("C:\\a.ttt");
}
//传递文件的路径合法性校验
private static void readFile(String filename) throws /*FileNotFoundException,*/ IOException {
if ( ! filename.equals("C:\\a.txt") ){
//FileNotFoundException 编译异常,必须处理(throws)
throw new FileNotFoundException("传递文件路径不对");
}
if ( ! filename.endsWith(".txt") ){
throw new IOException("文件后缀不对");
}
}
}
throw 和 throws 的区别:
- throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出,由方法体内的语句处理。throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常
- throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。表示如果抛出异常,由该方法的调用者来进行异常的处理。throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。throws表示出现异常的一种可能性,并不一定会发生这种异常
try...catch...finally
捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理自己处理
格式:
try{
//可能产生异常的代码;
}catch(定义异常变量,用于接收 try 中抛出的异常对象){
//异常处理(异常信息记录日志)
}
...
catch(定义异常变量,用于接收 try 中抛出的异常对象){
//异常处理(异常信息记录日志)
}finally{
//无论是否出现异常,都会执行(资源回收)
}
注意:
1.try 可能抛出多个异常,使用多个 catch 来处理这些异常对象
2.若 try 中产生异常,就会执行 catch 中的异常处理逻辑,执行完 catch 后,继续执行后续代码
若 try 中不产生异常,就不会执行 catch 中的异常处理逻辑,执行完 try 后,继续执行后续代码
import java.io.IOException;
public class TryCatchDemo {
public static void main(String[] args) {
try{
//可能产生异常的代码
readFile("C:\\a.ttt");
}catch(IOException e){
//异常处理逻辑
e.printStackTrace();
}finally{
//无论是否出现异常,都会执行
System.out.println("资源释放");
}
System.out.println("后续代码");
}
private static void readFile(String filename) throws IOException {
if ( ! filename.endsWith(".txt") ){
throw new IOException("文件后缀不对");
}
System.out.println("路径及后缀没有问题");
}
}
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗:
会执行,在 return 前执行。在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,如果在 finally 中修改了返回值,就会返回修改后的值
下面代码执行结果返回值:return 3
try {
int a = 1/0; // MathException
sout(“错误”);
return 1;
} catch (Exception e) {
sout("Exception");
return 2; // return 2
} finally {
sout("finally");
return 3;
}
catch中遇到return或者异常等能使该函数终止,若有 finally 就必须先执行完 finally 代码块里面的代码然后再返回值
以下 4 种特殊情况下, finally 块不会被执⾏
1. 在 finally 语句块第⼀⾏发⽣了异常。 因为在其他⾏, finally 块还是会得到执⾏
2. 在前⾯的代码中⽤了 System.exit(int)已退出程序。 exit 是带参函数 ;若该语句在异常语句之后, finally 会执⾏
3. 程序所在的线程死亡
4. 关闭 CPU
多异常处理
1.多异常分别处理 (一颗子弹消灭一个敌人) try...catch try...catch
2.多异常一次捕获,多次处理catch (一颗子弹消灭一队敌人) try...catch...catch
3.异常一次捕获,一次处理 (一颗子弹消灭所有敌人) try...catch (Exception e)
多个异常同时被捕获:先逮小的,再逮大的
import java.util.ArrayList;
import java.util.Collections;
public class ManyExceptionDemo {
public static void main(String[] args) {
/*
1.多异常分别处理
try...catch
try...catch
*/
try{
int[] arr = {1, 2, 3};
System.out.println(arr[3]); //ArrayIndexOutOfBoundsException
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
try{
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1, 2, 3);
System.out.println(list.get(3)); //IndexOutOfBoundsException
}catch (IndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("后续代码");
}
}
public class ManyExceptionDemo {
public static void main(String[] args) {
/*
2.多异常一次捕获,多次处理
try...catch...catch
注意:catch 里的异常变量,若有父子关系,子类的异常变量必须写上面
ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
*/
try{
int[] arr = {1, 2, 3};
System.out.println(arr[3]); //ArrayIndexOutOfBoundsException
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1, 2, 3);
System.out.println(list.get(3)); //IndexOutOfBoundsException
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}catch (IndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("后续代码");
}
}
public class ManyExceptionDemo {
public static void main(String[] args) {
/*
异常一次捕获,一次处理
try...catch (Exception e)
*/
try{
int[] arr = {1, 2, 3};
System.out.println(arr[3]); //ArrayIndexOutOfBoundsException
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1, 2, 3);
System.out.println(list.get(3)); //IndexOutOfBoundsException
}catch (Exception e){
e.printStackTrace();
}
System.out.println("后续代码");
}
}
自定义异常
/*
DIY Exception:注册类异常
格式:
public class xxxException extends Exception | RuntimeException{
空参构造
含异常信息的构造
}
注意:
1.命名以 Exception 结尾
2.必须继承 Exception 或 RuntimeException
继承 Exception:编译期异常,如果方法内部抛出异常就必须处理(throws、try...catch)
继承 RuntimeException: 运行期异常,无需处理,由 JVM 中断处理
*/
public class RegisterException extends Exception{
public RegisterException() {}
public RegisterException(String massage) { super(massage); }
}
class MyExcepiton extends Exception {
MyExcepiton(){}
MyExcepiton(String message){
super(message);
}
}
class MyException extends RuntimeException {
MyExcepiton(){}
MyExcepiton(String message){
super(message);
}
}