异常处理机制
1.异常处理
1.1 概念:
异常就是程序在运行中出现不正常的情况并终止了程序的运行。
JAVA中通过异常处理机制解决异常问题,从而保持程序继续运行;因此JAVA是一门比较稳定的语言。
异常处理机制的关键字:try...catch...finally/try...catch
1.2 try...catch
解释:将有可能出现异常的语句放到try中,当出现异常时catch会捕获到
【1】printStackTrace:打印异常执行的堆栈信息
1 java.util.InputMismatchException 2 at java.util.Scanner.throwFor(Scanner.java:864) 3 at java.util.Scanner.next(Scanner.java:1485) 4 at java.util.Scanner.nextInt(Scanner.java:2117) 5 at java.util.Scanner.nextInt(Scanner.java:2076) 6 at cn.sxt02.exception02.Test01.main(Test01.java:14)
一般而言,异常堆栈信息很多,开发者只需要看懂
第一行:异常简单信息(异常类型,异常的描述等)
最后一行:异常出现的位置(类->方法->方法具体的行)
在控制台中异常堆栈信息输出位置不固定
【2】getMessage:返回异常的描述信息
1 package cn.sxt02.exception02; 2 import java.util.Scanner; 3 public class Test01 { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请输入第一个数:"); 7 8 int num1 = 0; 9 int num2 = 0; 10 11 try { 12 num1 = sc.nextInt(); 13 14 System.out.println("请输入第二个数:"); 15 num2 = sc.nextInt(); 16 17 int r = num1 / num2; 18 System.out.println("num1/num2 = " + r); 19 }catch (Exception e) { 20 System.out.println("程序出现异常"); 21 // 打印异常的信息 22 // System.out.println(e.toString()); 23 24 25 // 打印异常堆栈信息 26 e.printStackTrace(); 27 28 // 返回异常的描述信息,如果没有信息,返回null(InputMismatchException 没有描述信息) 29 System.out.println(e.getMessage()); 30 } 31 32 System.out.println("程序正常结束"); 33 } 34 }
【3】异常类型不匹配
1 public class Test03 { 2 public static void main(String[] args) { 3 Scanner sc = new Scanner(System.in); 4 System.out.println("请输入第一个数:"); 5 6 int num1 = 0; 7 int num2 = 0; 8 9 try { 10 num1 = sc.nextInt(); 11 12 System.out.println("请输入第二个数:"); 13 num2 = sc.nextInt(); 14 15 int r = num1 / num2; 16 System.out.println("num1/num2 = " + r); 17 }catch (ArithmeticException e) { 18 System.out.println("数学计算异常:"+e.getMessage()); 19 }catch(InputMismatchException e) { 20 System.out.println("输入不匹配异常:"+e.getMessage()); 21 }catch (Exception e) { 22 System.out.println("发送异常:"+e.getMessage()); 23 } 24 25 System.out.println("程序正常结束"); 26 } 27 }
Exception是所有异常直接或间接的父类,因此当出现异常类型不匹配的时候可以这样写catch (Exception e)或者
catch省略,变成try…finally块;
1.3 try...catch...finally
finally块用于进行收尾工作(关闭数据库、关闭文件、释放内存等资源)
无论出不出现异常finally模块都会执行,当然也有特殊情况那就是添加一句system.exit();正常退出jvm,finally就不会执行
1 public static void main(String[] args) { 2 Scanner sc = new Scanner(System.in); 3 System.out.println("请输入第一个数:"); 4 5 int num1 = 0; 6 int num2 = 0; 7 8 try { 9 num1 = sc.nextInt(); 10 11 System.out.println("请输入第二个数:"); 12 num2 = sc.nextInt(); 13 14 int r = num1 / num2; 15 System.out.println("num1/num2 = " + r); 16 } catch (Exception e) { 17 System.out.println("程序出现异常"); 18 } finally { 19 System.out.println("不管是否出现异常,finally都执行"); 20 } 21 22 System.out.println("程序正常结束"); 23 }
1.4 return
return 会出现try/catch/finally执行顺序
1 package cn.sxt02.exception03; 2 3 /** 4 * 存在return的情况 5 */ 6 public class Test02 { 7 8 public static int div(int a, int b) { 9 10 try { 11 int r = a / b; 12 return r; 13 14 } catch (Exception e) { 15 System.out.println("出现异常"); 16 17 return 0; 18 19 } finally { 20 System.out.println("我是finally"); 21 } 22 23 } 24 25 public static void main(String[] args) { 26 27 int r = Test02.div(10, 0); 28 System.out.println("r=" + r); 29 System.out.println("程序正常结束"); 30 } 31 }
解释:先执行try模块然后执行cacth,在执行finally 最后才执行return 0;以上例子为例
2.异常分类
从上图可知异常和错误的最终父类都是object,异常分为两类运行时异常(RuntimeException)和检查时异常(CheckException)
2.1 RuntimeExcption
运行时异常可处理可不处理
2.2 CheckException
检查时运行必须得处理,不然将无法编译
1 public class Test01 { 2 public static void main(String[] args) { 3 // 运行时异常 4 Scanner sc = new Scanner(System.in); 5 // runtime exception 6 int r = sc.nextInt(); 7 System.out.println("r = "+ r); 8 9 // 检查时异常 10 SimpleDateFormat df = new SimpleDateFormat(); 11 try { 12 Date date = df.parse("2019"); 13 } catch (ParseException e) { 14 e.printStackTrace(); 15 } 16 } 17 }
2.3 常见的运行时异常(面试题写出5-8个)
ArithmeticException:数学计算异常。比如除数为0
InputMismatchException:输入不匹配异常
ArrayIndexOutofBoundsException:数组下标越界异常。
NullPointException:空指针异常,对象没有初始化就使用时,jvm会抛出该异常
IllegalArgumentException:非法参数异常。
ClassCastException:强制类型转换异常。
NumberFormatException:数字格式化异常。比如把“abc”格式化成数字。
2.4 常见的检查时异常:
ClassNotFoundException:类没有被发现异常。
SQLException:数据库相关异常
IOException:IO操作异常
ParseException:解析错误异常
FileNotFoundException:文件未发现异常。
2.5 运行时异常和检查时异常的区别
运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法
3.异常声明
3.1 throws关键字
当一个方法可能存在异常,而此时自身又无法更好的处理,可以交给外界处理。此时用throws声明并抛出异常。
1 public class Test01 { 2 3 public static int div(int a, int b) throws ArithmeticException{ 4 int r = 0; 5 r = a / b; 6 return r; 7 } 8 9 public static void main(String[] args) { 10 try { 11 Test01.div(10, 0); 12 } catch (ArithmeticException e) { 13 System.out.println("除数不能为0"); 14 } 15 } 16 }
开发者可以根据需要声明检查时异常(Exception或者非运行时异常)和运行时异常(RuntimeException或其子类)
如果调用处也不知道如何处理异常,可选择继续声明异常,我们把这个过程称为异常上抛。
1 public class Test01 { 2 3 public static int div(int a, int b) throws Exception{ 4 int r = 0; 5 r = a / b; 6 return r; 7 } 8 9 public static void main(String[] args) throws Exception{ 10 11 //【1】 调用处知道如何处理! 12 /* 13 try { 14 Test01.div(10, 0); 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 */ 19 20 // 【2】调用处也不知道如何处理 21 Test01.div(10, 0); 22 23 } 24 }
3.2 声明异常与重载关系
声明异常和重载没有任何关系
1 public class Test01 { 2 3 public static int div(int a, int b) throws Exception{ 4 int r = 0; 5 r = a / b; 6 return r; 7 } 8 9 public static int div(int a, int b) { 10 int r = 0; 11 r = a / b; 12 return r; 13 } 14 }
方法重载
【1】方法名相同
【2】参数列表不同(个数、类型、顺序)
【3】和返回值、修饰符、声明异常无关。
3.3 声明异常和重写关系
【1】声明一个和方法重写有关系
1 public class Father { 2 3 public void showInfo() throws Exception{ 4 5 } 6 } 7 8 public class Son extends Father{ 9 10 @Override 11 public void showInfo(){ 12 13 } 14 15 }
可以认为:父类方法抛出异常,子类在重写过程中把该异常处理掉了,所以子类方法不用声明异常。
【2】 父类方法声明没有声明任何异常(检测时或运行时),子类也不声明异常或者声明运行时异常。
1 public class Father { 2 3 public void showInfo(){ 4 5 } 6 } 7 public class Son extends Father{ 8 9 @Override 10 public void showInfo() throws Exception{ 11 12 } 13 14 }
【3】父类声明了异常(检测时或运行时),子类声明完全一样的异常。
1 public class Father { 2 3 public void showInfo() throws Exception{ 4 5 } 6 } 7 public class Son extends Father{ 8 9 @Override 10 public void showInfo() throws Exception { 11 12 } 13 14 }
4.手动抛出异常
4.1 关键字throw
除了系统自动抛出异常外,有些问题需要开发者手动抛出异常。使用关键字throw
1 package cn.sxt02.exception06; 2 3 public class Student { 4 private String name; 5 private String gender; 6 7 public String getName() { 8 return name; 9 } 10 11 public void setName(String name) { 12 this.name = name; 13 } 14 15 public String getGender() { 16 return gender; 17 } 18 19 public void setGender(String gender) throws Exception{ 20 if(gender.equals("男") || gender.equals("女")) { 21 this.gender = gender; 22 }else { 23 throw new Exception("性别不合法!"); 24 } 25 } 26 27 public Student(String name, String gender) { 28 super(); 29 this.name = name; 30 this.gender = gender; 31 } 32 33 public Student() { 34 super(); 35 } 36 37 } 38 39 public class Test01 { 40 public static void main(String[] args){ 41 Student stu = new Student(); 42 stu.setName("二狗"); 43 try { 44 stu.setGender("xxx"); 45 } catch (Exception e) { 46 System.out.println(e.getMessage()); 47 } 48 } 49 }
4.2 自定义异常
如果开发者需要手动抛出的异常在系统不存在,可以自定义异常。
如果要自定义异常,首先要确定异常类型,如果异常是运行时异常,必须继承RuntimeException或其子类;如果异常是检查时异常,必须继承Exception或其子类。
异常的命名方式,参考系统命名方式,以Exception结尾。
1 public class AgeException extends Exception{ 2 3 public AgeException() { 4 super(); 5 } 6 7 public AgeException(String message) { 8 super(message); 9 } 10 11 }