1、概述
异常:就是程序出现了不正常的情况
用来封装错误信息的对象
组成结构:类型、提示、行号
2、异常的传承结构
查看API文档可知,Java中所有错误和异常的顶级父类是Throwable类
Throwable类下有两个子类,分别是Error和Exception,两者的区别是:
① Error:是指不需要捕获的严重问题,通常是Java程序外的问题,不如说硬件问题或者内存不足导致的问题等。因此Java中出现了Error,我们无须处理
② Exception:称之为异常类,他表示的是程序可以处理的问题
Exception下有很多异常子类,其中有一个异常子类是RuntimeException类,这里可以将异常类分为两大类:
① 编译时异常:
其他编译类以及不是RuntimeException子类的异常类都是检查异常(也叫编译时异常)
在编写完程序后,Java编译器会对其进行检查,如果检查出此类异常,就必须要显式处理,否则程序将无法进行编译。
例如:ClassNotFoundException、FileNotFoundException、SQLException等都是编译时异常
② 运行时异常:
RuntimeException以及子类被称为神经检查的异常(也叫作运行异常)
这类异常通常在编写完程序后没有问题,但是运行程序才出现异常,需要我们回来修改代码进行解决的异常,这类异常无须显式处理,当然也可以像编译时异常一样处理
例如:IndexOutOfBoundsException、ArithmeticException、NullPointException、ClassCastException等都是运行时异常。
判断一个异常是不是运行时异常,可以通过检查这个异常类是不是RuntimeException的子类,或者建厂这个异常是否只在程序运行时才会出现。
3、虚拟机默认处理方式
如果长须在运行时出现了问题,而我们有没有处理该问题,最终虚拟机会做默认的处理,而这种默认处理的方式为:
① 将异常的名称(类型)、异常的原因以及异常出现的位置等信息输出在了控制台(Console窗口)
② 将程序停止运行(这意味着,出现异常的代码后面代码将不再执行)
异常示例-1:
package Part03Exception;
/**
* 异常实例
*/
public class ExceptionDemo {
public static void main(String[] args) {
int i = 1/0;
System.out.println("执行完毕");
}
}
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Part03Exception.ExceptionDemo.main(ExceptionDemo.java:8)
异常示例-2:
package Part03Exception;
/**
*
*
* 异常实例
*/
public class ExceptionDemo2 {
public static void main(String[] args) {
//定义一个数组
int[] arr = { 88, 23, 45 };
System.out.println(arr[0]);
System.out.println(arr[3]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
运行结果:
88
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at Part03Exception.ExceptionDemo2.main(ExceptionDemo2.java:13)
如果程序出现了异常,需要我们自己处理,有两种方案:
① 使用 try catch 进行处理(捕获异常)
② 使用 throw 进行处理(抛出异常)
4、异常处理之try..catch.. 处理
try..catch.. 处理已偿还的格式为:
try{
可能出现异常的代码;
}catch(异常类型 变量名){
异常处理代码;
//当try中的代码出现了异常并且这个异常能和catch中的异常类型匹配上, 才会执行catch
//反之, 如果出现的异常和catch中的异常类型不匹配, 就不会执行catch
}
执行流程为:
① 程序执行到try{}中的代码时,如果出现的异常没将会自动产生一个异常对象,该异常对象会被提交给Java运行时系统;
② 当Java运行时系统接受到异常队形时,回到catch()中寻找匹配的异常类型,找到后就进出catch{}中进行异常处理;
③ 执行完毕后,程序还可以继续执行try..catch 之后的代码
代码案例-1:
package Part03Exception;
/**
* JAVA异常处理机制中的try-catch
* 语法:
* try{
* 可能会出现异常的代码片段
* }catch(XXXException e){
* 当try中执行的代码片段出现了对应的catch捕获的异常的处理方法
* }
* try语句块不能独立存在,后面必须要跟着catch或者finally
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{//可能会出现异常的代码片段
String str = null;
//这里会出现空指针异常,虚拟机会实例化空指针异常的实例,并在这里抛出
System.out.println(str.length());
}catch (NullPointerException e){//捕获是否会出现的异常类型
System.out.println("出现了空指针异常!并在这里得到了解决");
}
System.out.println("程序结束了");
}
}
代码案例-2:
package Part03Exception;
/**
* JAVA异常处理机制中的try-catch
* 语法:
* try{
* 可能会出现异常的代码片段
* }catch(XXXException e){
* 当try中执行的代码片段出现了对应的catch捕获的异常的处理方法
* }
* try语句块不能独立存在,后面必须要跟着catch或者finally
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {//可能会出现异常的代码片段
//String str = null;
String str = "";
//这里会出现空指针异常,虚拟机会实例化空指针异常的实例,并在这里抛出
System.out.println(str.length());
System.out.println(str.charAt(0));
//批量注释: 选中要注释的代码片段,crtl+/
// }catch (NullPointerException e){//捕获是否会出现的异常类型
// System.out.println("出现了空指针异常!并在这里得到了解决");
// }catch (StringIndexOutOfBoundsException e){
// System.out.println("出现了字符串下标越界异常!并在这里得到了解决");
// }
//可以合并捕获异常,当不同异常处理手段相同时,建议使用此种方案
}catch (NullPointerException|StringIndexOutOfBoundsException e){
System.out.println("出现了空指针或者下标越界异常的处理");
}
System.out.println("程序结束了");
}
}
代码案例-3:
package Part03Exception;
/**
* JAVA异常处理机制中的try-catch
* 语法:
* try{
* 可能会出现异常的代码片段
* }catch(XXXException e){
* 当try中执行的代码片段出现了对应的catch捕获的异常的处理方法
* }
* try语句块不能独立存在,后面必须要跟着catch或者finally
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {//可能会出现异常的代码片段
//String str = null;
//String str = "";
String str = "a";
//这里会出现空指针异常,虚拟机会实例化空指针异常的实例,并在这里抛出
System.out.println(str.length());
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
//批量注释: 选中要注释的代码片段,crtl+/
// }catch (NullPointerException e){//捕获是否会出现的异常类型
// System.out.println("出现了空指针异常!并在这里得到了解决");
// }catch (StringIndexOutOfBoundsException e){
// System.out.println("出现了字符串下标越界异常!并在这里得到了解决");
// }
//可以合并捕获异常,当不同异常处理手段相同时,建议使用此种方案
// }catch (NullPointerException|StringIndexOutOfBoundsException e){
// System.out.println("出现了空指针或者下标越界异常的处理");
//当异常的种类较多时,或者防止出现自己不知道会发生的异常,可以使用异常的超类,
//来兜底,一定要先捕获子类异常,然后再捕获超类异常
}catch (Exception e){
System.out.println("反正是一个错!");
}
System.out.println("程序结束了");
}
}
5、finally块
作用就是确保一定要执行的代码
代码案例-1:
package Part03Exception;
/*
* finally块
* finally是异常处理机制的最后一块,它可以直接跟在try语句块后面,或者最后一个catch之后
* finally语句块的特点:
* 只要程序可以执行到try代码块中,无论是否出现异常,都会执行finally中的代码
* 因此我们会将资源的释放操作放在finally中确保执行代码
* */
public class FinallyDemo1 {
public static void main(String[] args) {
System.out.println("程序开始了");
//选中可能会出现异常的代码片段,然后按快捷键ctrl+alt+T,选择try
try{
String str = "a";
System.out.println(str.length());
System.out.println("try中的代码块执行完毕");
return;//方法实际返回前,也必须要执行完finally
}catch (Exception e){
System.out.println("出错了!");
}finally {
System.out.println("finally中的代码执行了!");
}
System.out.println("程序结束了");
}
}
代码案例-2:
package Part03Exception;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileOutputStream;
/*
*异常处理机制在io操作中的应用
*/
public class FinallyDemo2 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.dat");
fos.write(1);
} catch (IOException e) {
System.out.println("出错了,在这里解决");
} finally {
try {
if (fos!= null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代码案例-3:
package Part03Exception;
import java.io.FileOutputStream;
import java.io.IOException;
public class AutoCloseableDemo {
public static void main(String[] args) {
try (
/*
*只有实现了AutoCloseable接口的类才可以在这里定义并初始化
* */
FileOutputStream fos = new FileOutputStream("fos.dat");
) {
fos.write(1);
} catch (IOException e) {
System.out.println("出错了!");
}
}
}
6、throw 关键字
当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时刻,您可能想要自行抛出异常,例如:字异常处理结束后,再将异常抛出,让下一层异常出库块来捕捉,若想要自行抛出异常,您可有使用"throw"关键字,并生成执行的异常对象后抛出:
例如: throw new ArithmeticException();
代码案例:
Persion类
package Part03Exception;
/**
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>100){
//对外抛出一个运行时期异常,异常信息是年龄不合法
throw new RuntimeException("年龄不合法");
}
this.age = age;
}
}
ThrowDemo类
package Part03Exception;
/**
* 异常的抛出
* throw关键字可以主动对外抛出一个异常
* 通常下列情况我们会主动抛出异常:
* 1:当前代码片段出现了异常,但是该异常不应当在当前的代码片段被解决,
* 可以将其抛出
* 2:程序可以运行,但是不满足业务要求时,我们也可以对外抛出异常告知
*/
public class ThrowDemo {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(10000);//语法没有错误,但是业务是有错误的
}catch (RuntimeException e){
System.out.println("出错了!");
p.setAge(66);
}
System.out.println("此人的年龄:"+p.getAge());
}
}
7、异常处理之throws处理
程序中会声明许多方法,这些方法中肯那个会因某些错误而引发异常,但是不希望直接在这个方法中处理这些异常,而希望调用这个他的方法来统一处理,这时候可以使用throws关键字声明这个方法抛出的异常
throws 处理异常的格式为:
...方法名() throws 异常类名 {
方法体
}
代码案例:
Person类
package Part03Exception;
/**
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception{
if (age<0||age>100){
//对外抛出一个运行时期异常,异常信息是年龄不合法
//throw new RuntimeException("年龄不合法");
/*
*java中除了RuntimeException之外,
* 其他异常throw抛出是编译器要求必须在方法上使用throws关键字声明该异常的抛出
* */
throw new Exception("年龄不合法");
}
this.age = age;
}
}
ThrowDemo
package Part03Exception;
/**
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception{
if (age<0||age>100){
//对外抛出一个运行时期异常,异常信息是年龄不合法
//throw new RuntimeException("年龄不合法");
/*
*java中除了RuntimeException之外,
* 其他异常throw抛出是编译器要求必须在方法上使用throws关键字声明该异常的抛出
* */
throw new Exception("年龄不合法");
}
this.age = age;
}
}
Throws 的重写规则
代码案例
package Part03Exception;
import java.awt.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
public class ThrowsDemo {
public void doSome() throws IOException, AWTException { }
}
class SubClass extends ThrowsDemo{
//1.父类抛什么异常,子类就抛什么异常
//public void doSome() throws IOException, AWTException { }
//2.允许仅抛出部分异常
//public void doSome() throws IOException { }
//3.允许不抛出任何异常
//public void doSome(){ }
//4.允许抛出超类方法抛出的异常的子类异常类型
//FileNotFoundException 是 IOException的子类
//public void doSome() throws FileNotFoundException { }
//5.不允许排除额外的异常(超类方法没有声明抛出的,或者和超类声明抛出的异常没有继承关系)
//public void doSome() throws SQLException { }
//6.不允许抛出超类方法抛出的异常的超类类型
//public void doSome() throws Exception { }
}
异常常用方法
代码案例
package Part03Exception;
/**
* 异常常用方法
*/
public class ExceptionApiDemo {
public static void main(String[] args) {
try {
String str = "abc";
System.out.println(Integer.parseInt(str));
}catch (Exception e){
System.out.println("PlanB");
//该方法用来在控制台输出异常的堆栈信息,便于程序员解决bug
e.printStackTrace();
//一般给用户看的
String message = e.getMessage();
System.out.println("异常的原因是:"+message);
}
}
}
自定义异常
代码案例
package Part03Exception;
/**
* 自定义异常
* 通常使用自定义异常来表达业务错误:
* 自定义异常一般要做到以下几点:
* 1.类名要见明知义
* 2.需要继承Exception(直接或者间接继承皆可以)
* 3.提供超类中所有的构造器
* Illegal: 非法的
*/
public class IllegalAgeException extends Exception{
//提供超类中的全部的构造器
//alt+insert,选择Constructor,按crtl+a全选,回车
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
public IllegalAgeException(String message, Throwable cause) {
super(message, cause);
}
public IllegalAgeException(Throwable cause) {
super(cause);
}
public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
Person
package Part03Exception;
/**
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws IllegalAgeException{
if (age<0||age>100){
//对外抛出一个运行时期异常,异常信息是年龄不合法
//throw new RuntimeException("年龄不合法");
/*
*java中除了RuntimeException之外,
* 其他异常throw抛出是编译器要求必须在方法上使用throws关键字声明该异常的抛出
* */
throw new IllegalAgeException("年龄超过了范围:"+age);
}
this.age = age;
}
}
ThrowDemo
package Part03Exception;
/**
* 异常的抛出
* throw关键字可以主动对外抛出一个异常
* 通常下列情况我们会主动抛出异常:
* 1:当前代码片段出现了异常,但是该异常不应当在当前的代码片段被解决,
* 可以将其抛出
* 2:程序可以运行,但是不满足业务要求时,我们也可以对外抛出异常告知
*/
public class ThrowDemo {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(10000);//语法没有错误,但是业务是有错误的
} catch (IllegalAgeException e) {
e.printStackTrace();
}
System.out.println("此人的年龄:" + p.getAge());
}
}
8、总结
什么时候需要try..catch..异常,什么时候需要throws异常
① 如果这个宜昌市方法内部的代码造成的异常,而不是因为调用者的传参导致的异常(也就是说这个异常和调用者没有关系),通常需要我们用try catch异常
② 如果这个异常是调用者的传参导致的异常,则将异常throws抛出(也就是将异常抛给调用者)
③ 不要在main方法上throws抛出异常,因为这样会将异常抛给虚拟机,而虚拟机不会帮我们处理异常的(虚拟机会按默认的方式处理:输出异常信息以及终止程序执行)