生活中总是会存在各种突发情况......
程序猿和小爪约好了一起去爬山,美女相伴,猿生好不惬意!忽然下起了瓢泼大雨(这就是所谓的异常情况)
程序猿傻眼了,然后眼看着小爪淡定的从背包里掏出雨衣穿上(这就是对异常的处理)
1 异常介绍
1.1 什么是异常
异常是程序在运行期间发生的不正常的事件,它会中断指令的正常执行流程。
异常是很难完全避免的,设计良好的程序应该在异常发生时,提供处理这些不正常事件的方法,使程序不会因为异常的发生而中断或产生不可预见的结果。
Java语言使用异常处理机制,为程序提供异常处理的能力
1.2 异常分类
1)Java中的异常分为两大类
错误(Error):
JVM系统内部错误或资源耗尽等严重情况,属于JVM需要负担的责任。
这一类异常事件无法恢复或不可捕获,将导致应用程序中断。程序(员)对其不做处理
癌症晚期,无法进行治疗
异常(Exception):
因编程错误、不严谨或偶然的外在因素导致的问题。
所有异常类的父类,其子类对应了各种各样可能出现的异常事件
程序员通常可以处理,可以显式地声明或捕获。
这类异常如果处理合理,程序有机会恢复至正常运行状态
小感冒,吃点药就好了
2)Exception也分为两类
非受检(unchecked)异常:
编译器不要求必须处理的异常,也叫运行时异常。
指程序的逻辑错误,是程序员应该避免的,会在程序运行过程中触发,比如:
错误的类型转换:java.lang.ClassCastException
数组下标越界:java.lang.ArrayIndexOutOfBoundsException
空指针访问:java.lang.NullPointerException
算术异常(除0溢出):java.lang.ArithmeticException
等等
受检(checked)异常:
编译器要求必须处理的异常
比如:
没有找到指定名称的类:java.lang.ClassNotFoundException
访问不存在的文件:java.io.FileNotFoundException
操作文件时发生的异常:java.io.IOException
操作数据库时发生的异常:java.sql.SQLException
等等
public class App1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// java.lang.ArithmeticException
int a = 10 / 0;
// java.lang.NullPointerException
String str = null;
str.length();
//java.lang.OutOfMemoryError
// -Xms10m -Xmx10m
List<Integer[]> list = new ArrayList<>();
while(true) {
list.add(new Integer[1024]);
}
}
}
2 异常处理机制
Java提供了处理异常的关键字:try,catch,finally,throw,throws
程序在执行过程中,如果出现异常,会自动生成一个异常类对象,该异常对象将被自动提交给JVM,这个过程称为抛出(throw)异常。
当JVM接收到异常对象时,会寻找能处理这一异常的代码,并把当前异常对象交给其处理,这一过程称为捕获(catch)异常和处理异常。
如果JVM找不到可以捕获异常的代码,则运行时系统将终止,相应的Java程序也将退出。
2.1 捕获异常并处理
当发生异常时,由自身方法捕获,并提供处理代码
1)基本语法
try{
//可能产生异常的代码
}[catch( ExceptionName1 e ){
//异常的处理代码
}catch( ExceptionName2 e ){
//异常的处理代码
}][ finally{
//都会执行的语句
}]
try 代码段包含可能产生异常的代码;
当异常发生时,程序会中止当前的流程去执行相应的catch代码段;
finally段的代码无论是否发生异常都将执行;
catch和finally两者至少出现一个
2)catch
catch语句块中是对异常进行处理的代码,每个try语句块可以跟随多个catch语句,用于处理可能产生的不同类型的异常对象。
在catch中声明的异常对象封装了异常事件发生的信息,可以使用该对象的一些方法来获取这些信息。
如:getMessage() :获得有关异常事件的字符串信息。
printStackTrace() :用来跟踪异常发生时执行堆栈的内容。
使用多重 catch 语句时,异常子类一定要位于异常父类之前。
如果异常发生,则运行对应catch块的代码(从前到后匹配),如果没有异常产生,所有catch块的代码都会被忽略
public class App2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
// int a = 10 / 0;
String str = null;
str.length();
//try中,某个语句发生异常,直接跳转到相关的catch处,不会再执行异常语句后的代码
int[] arr = new int[5];
System.out.println(arr[6]);
int a = 10 / 0;
}
//try-catch可以捕获多种异常,但是不能将更高一级的异常(父类的异常)写在前面
// catch(Exception ex){
// System.out.println(ex.getMessage());
// }
catch(ArithmeticException ex){
//获取异常的信息
System.out.println(ex.getMessage());
//获取堆栈中异常详细信息
System.out.println(ex.getStackTrace());
}catch(ArrayIndexOutOfBoundsException ex){
System.out.println(ex.getMessage());
System.out.println(Arrays.toString(ex.getStackTrace()));
}catch(Exception ex){
System.out.println(ex.getMessage());
}
System.out.println("continue");
}
}
public class App3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
int a = 10 / 0;
int[] arr = new int[5];
arr[6] = 10;
}
//java7后,可以将多个异常写在一个catch里,多个异常类型之间使用"|"分隔
// 这些异常必须是同级的
catch(ArithmeticException |
ArrayIndexOutOfBoundsException ex){
System.out.println(ex.getMessage());
}
}
}
3)finally
finally语句为异常处理提供一个统一出口,能够对程序的状态作统一的管理。
无论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。
通常在finally语句块中可以进行资源的清理、释放工作,如:关闭打开的文件删除临时文件,关闭数据库的连接等
finally语句是可选的
public class App4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
int a = 10 / 0;
}catch(ArithmeticException ex){
System.out.println(ex.getMessage());
// 即使使用了return,finally里代码也会执行
// return;
System.exit(-1);
}catch(Exception ex){
return;
}finally{
System.out.println("程序咋写呢嘛,又异常");
}
System.out.println("hello");
}
}
语法上,try-catch-finally结构中,catch可以不写,仅出现try-finally,如果出现异常,finally中代码也会执行,只是无法捕获异常
try {
int a = 10 / 0;
}finally {
System.out.println("haha");
}
如果try或catch中出现了return语句,finally里中代码是否执行?
如果使用了System.exit()方法呢?
2.2 声明抛出异常
当某段程序有可能发生异常时,则在声明中抛出异常。如果运行发生异常,则自动抛给调用者处理,如果调用者仍然没有处理,则一直往上抛出,直到抛给JVM
审理案件时,县一级的法院处理不了,就提交给更高一级法院处理
1)throws
在定义方法时,可以使用throws关键字声明抛出异常,用于声明一个方法可能会抛出的异常。
表示当此方法发生异常时,本身不作任何处理,而交给方法的调用者。
throws的基本语法
[修饰符] 返回值类型 方法名([形式参数列表]) throws 异常类1, 异常类2....{
}
注意:如果一个方法声明抛出的是受检异常,则在调用这个方法的地方必须处理该异常
public class App5 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
try{
div2(100, 0);
}catch(ArithmeticException ex){
System.out.println(ex.getMessage());
}
}
//方法中可以声明多个抛出的异常,之间使用","分隔
private static void div2(int a, int b)
throws ArithmeticException, Exception{
System.out.println(a / b);
}
}
2)throw
用于在方法内部的代码中主动抛出一个指定类型的异常。
注意:如果方法代码中自行抛出的异常是受检异常,则该方法必须处理该异常。
public class App6 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
try{
div(100, 0);
}catch(ArithmeticException ex){
System.out.println(ex.getMessage());
}
}
private static int div(int a, int b){
if(b == 0){
throw new ArithmeticException("除数不能为0");
}
return a / b;
}
}
3 自定义异常
自定义异常一般可以继承Exception或者RuntimeException
public class MyException extends RuntimeException{
private int code;
private String msg;
public MyException(int code, String msg){
this.code = code;
this.msg = msg;
}
public int getCode(){
return this.code;
}
public String getMsg(){
return this.msg;
}
}
public class LoginService {
public void login(String name, String password) {
if(!(name.equals("admin") && password.equals("123"))) {
throw new MyException(100, "用户名或者密码不正确");
}
// 其他逻辑
System.out.println("登录成功");
}
}