11_异常处理

传统的异常处理
public class Test01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");
        int num1, num2;
        if (sc.hasNextInt()) {
            num1 = sc.nextInt();
            System.out.println("请输入除数:");
            if (sc.hasNextInt()) {
                num2 = sc.nextInt();
                if (0 == num2) {
                    System.out.println("除数不能为0");
                } else {
                    int r = num1 / num2;
                    System.out.println("r=" + r);
                }
            } else {
                System.out.println("除数输入有误!");
            }
        } else {
            System.out.println("被除数输入有误!");
        }
    }
}
传统异常处理是通过if条件判断来处理的:
[1] 效率极低
[2] 业务逻辑和异常处理交织在一起,维护性差。
异常处理机制
异常的概念
异常是指在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序
通过异常处理机制,我们不能过度使用异常。
 
异常处理
try-catch
把可能产生异常的代码放入try代码块中。如果没有产生异常,程序不会执行catch代码块。如果产生异常,执行catch代码块,程序继续执行。
public class Test01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");
        int num1, num2;
        try {
            num1 = sc.nextInt();
            System.out.println("请输入除数:");
            num2 = sc.nextInt();
            
            int r = num1 / num2;
            System.out.println("r = " + r);
            
        } catch (Exception e) {
            System.out.println("oops,产生异常:"+e);
        }
        
        System.out.println("程序结束...");
    
    }
}
Exception 类是所有异常类的父类。
如果程序在try中产生异常,进入catch前需要类型匹配,所以写多个catch代码块。
try {
            num1 = sc.nextInt();
            System.out.println("请输入除数:");
            num2 = sc.nextInt();
            
            int r = num1 / num2;
            System.out.println("r = " + r);
            
        } catch (InputMismatchException e) {
            System.out.println("产生输入不匹配异常:"+e);
        } catch (ArithmeticException e) {
            System.out.println("产生数学计算异常:"+e);            
        } catch(Exception e) {
            System.out.println("通用异常:"+e);
        }
通常需要一个Exception用于匹配所有异常,一般放到最后一个catch块中。如果前方有匹配异常,Exception不在匹配。
 
Excepiotn 常用方法
[1] toString() 输出异常的类型和描述信息
e.g: java.lang.ArithmeticException: / by zero
 
[2] getMessage() 返回异常的描述信息
 
[3] printStackTrace() 打印执行堆栈
java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at cn.exception02.Test02.main(Test02.java:14)
第一行:toString()的返回值
最后一行:出现异常的方法和出现异常的具体行数
 
注意:e.printStackTrace() 在控制中输出的位置不是按照程序流程输出的。
System.err.println() 在控制台中以红色字体提示错误信息。
 
try-catch-finally
[1]try-catch和之前一样用法,finally代码块表示不管是否出现异常,都执行的代码块。
public class Test01 {
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        
        try {
            sc.nextInt();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally 代码块");
        }
        System.out.println("程序结束");
    }
}
finally 代码块中一般用于放置一些关闭数据库、关闭文件、关闭网络连接等释放资源的代码。
try {
   // 打开数据库
   // 增删改查
} catch (Exception e) {
   // 异常处理
} finally {
// 关闭数据库
}
[2] 存在return try-catch-finally代码块的执行顺序
public static int add() {
        System.out.println("请输入第一个操作数:");
        Scanner sc = new Scanner(System.in);
        int num1, num2;
        int r = 0;
        try {
            num1 = sc.nextInt();
            System.out.println("请输入第二个操作数:");
            num2 = sc.nextInt();
            r = num1 + num2;
            
            return r;
        } catch (Exception e) {
            System.out.println("出现异常.");
            return r;
        } finally {
            System.out.println("finally");
            sc.close();
        }
    }
[3]jvm 退出时finally代码块不在执行。
public class Test02 {
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        
        try {
            sc.nextInt();
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally 代码块");
        }
        
        System.out.println("程序结束");
    }
}
多重catch块
多重catch可以用到try-catch中,也可以用到try-catch-finally中。
原则:
[1] 先子类后父类
[2] 发生异常时按顺序逐个匹配
[3] 只执行第一个与异常类型匹配的catch语句
 
异常的分类
Throwable 类是 Java 语言中所有错误(Error)或异常(Exception)的父类,只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
 
Error 类表示错误类。仅靠程序本身无法恢复的严重错误。jvm内存耗尽、jvm崩溃等。
Exception 类表示异常类,可以通过java 异常处理机制处理。
 
Exception 更加是否处理分为两种情况。
RuntimeException:运行时异常。不要求程序必须做出处理。是所有运行时异常的父类。
CheckedException:检查时异常。要求程序必须处理,不处理编译不通过。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Scanner;
public class Test01 {
    public static void main(String[] args) {
        
        // 运行时异常
        /*Scanner sc = new Scanner(System.in);
        System.out.println("请输入1-3之间的数字:");
        int num = sc.nextInt();
        System.out.println(num);*/
        
        
        // 检查时异常
        try {
            SimpleDateFormat df = new SimpleDateFormat();
            df.parse("123");
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
常见的运行时异常
ArithmeticException:数学计算异常。比如除数为0
ArrayIndexOutofBoundsException:数组下标越界异常。
NullPointException:空指针异常
IllegalArgumentException:非法参数异常。
ClassCastException:强制类型转换异常。
NumberFormatException:数字格式化异常。比如把“abc”格式化成数字。
 
常见的检查时异常:
ClassNotFoundException:类没有被发现异常。
SQLException:数据库先关异常
IOException:IO操作异常
ParseException:解析错误异常
FileNotFoundException:文件未发现异常。
 
运行时异常和检查时异常的区别
运行时异常:包括RuntimeaException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法处理
 
 
 
 
 
声明异常
声明异常的概念
当一个方法可能存在异常时,如果不想在方法内处理(不合适在方法内处理、想让调用者处理)时,可以选择在方法上声明异常。形式:
public 返回值 方法名(形参) throws 异常类1,异常类2,…{
}
public static int div(int a,int b) throws ArithmeticException{
        int r = a / b;
        return r;
    }
所谓声明异常,就是当调用者调用该方式时知道该方法可能产生异常,并处理异常。
public class Test01 {
    
    public static int div(int a) throws ArithmeticException,InputMismatchException{
        System.out.println("请输入第二个参数:");
        Scanner sc = new Scanner(System.in);
        int b = sc.nextInt();
        int r = a / b;
        return r;
    } 
    
    public static void main(String[] args) {
        
        // 处理div声明的异常
        try {
            Test01.div(10);
        } catch (InputMismatchException | ArithmeticException e) {
            e.printStackTrace();
        }
    }
}
如果一个方法抛出异常必须被外界(调用处)处理时,可以声明检查时异常。否则,声明运行时异常。
public class Test02 {
    
    //    声明检查时异常
    public static int div(int a) throws Exception{
        System.out.println("请输入第二个参数:");
        Scanner sc = new Scanner(System.in);
        int b = sc.nextInt();
        int r = a / b;
        return r;
    } 
    
    public static void main(String[] args) {
        try {
            Test02.div(10);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}
如果在方法调用处也不知道如何处理时,可以选择继续向上抛出异常。
直到在main方法上声明异常。此时如果main方法执行时产生异常,jvm负责处理。
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test03 {
    
    //    声明检查时异常
    public static int div(int a) throws Exception{
        System.out.println("请输入第二个参数:");
        Scanner sc = new Scanner(System.in);
        int b = sc.nextInt();
        int r = a / b;
        return r;
    } 
    
    
    public static void main(String[] args) throws Exception{
    
        // 调用处也不知道如何处理,继续上抛异常 => 在main上声明异常。
        Test03.div(10);
        
    }
}
总结:
声明异常实际上就是一个异常上抛的过程。
 
重载的关系
声明异常和重载没有任何关系
 
重写的关系
声明一个和重写有关系
 
[1] 如果父类声明了运行时异常,子类可以不声明任何异常或者继承声明运行时异常。
package cn.exception07;
public class Father {
    
    public void showInfo() throws RuntimeException{
        System.out.println("father:showInfo");
    }
}
package cn.exception07;
public class Son extends Father{
    @Override
    public void showInfo() {
        super.showInfo();
        System.out.println("我是son:showInfo");
    }
}
[2] 如果父类没有声明异常,子类不用声明异常或者可以声明运行时异常。
public class Father {
    
    public void showInfo() {
        System.out.println("father:showInfo");
    }
}
@Override
    public void showInfo() throws RuntimeException{
        super.showInfo();
        System.out.println("我是son:showInfo");
    }
[3] 如果父类声明了Exception,子类要么在内部处理掉,要么继续向上抛。
public class Father {
    
    public void showInfo() throws Exception{
        System.out.println("father:showInfo");
    }
}
public class Son extends Father{
    @Override
    public void showInfo() {
        
        try {
            super.showInfo();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("我是son:showInfo");
    }
}
public class Son extends Father{
    @Override
    public void showInfo() throws Exception {
        
        super.showInfo();
        System.out.println("我是son:showInfo");
    }
}
手动抛出异常
除了jdk自身抛出的异常,开发者可以根据业务需要手动抛出异常。使用关键字throw
public void setAge(int age) throws Exception{
        if(age > 0) {            
            this.age = age;
        }else {
            throw new Exception("年龄不合法");
        }
    }
如果要抛出必须处理的异常,throw new Exception;
如果要抛出可以处理可不处理的异常,throw new RuntimeException
 
自定义异常
很多时候,jdk中定义的异常根本不能满足具有业务的需要。可以考虑自定义异常。
步骤:
[1] 继承异常类(Exception/RuntimeException)
[2] 定义构造方法。一般需要调用父类的构造方法。
public class AgeException extends Exception{
    public AgeException() {
        super();
    }
    public AgeException(String message) {
        super(message);
    }
    
    public int showInfo() {
        System.out.println(this.getClass()+"=>"+super.getMessage());
        return 0;
    }
} 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/aknife/p/10890097.html