java-异常处理
第九章 java异常处理
习题的引入
【1】代码:
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //实现一个功能:键盘录入两个数,求商: 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请录入第一个数:"); 7 int num1 = sc.nextInt(); 8 System.out.println("请录入第二个数:"); 9 int num2 = sc.nextInt(); 10 System.out.println("商:"+num1/num2); 11 } 12 }
运行结果:
测试过程发现问题:
录入的数据应为int类型,但是录入非int类型数据的时候,出异常:
除数为0的时候:
异常:Exception:在程序的运行过程中,发生了不正常的现象,阻止了程序的运行,我们称之为发生异常。
通过if-else解决异常
1 package com.llh; 2 3 import java.util.Scanner; 4 5 6 public class Test { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) { 9 //实现一个功能:键盘录入两个数,求商: 10 Scanner sc = new Scanner(System.in); 11 System.out.println("请录入第一个数:"); 12 if(sc.hasNextInt()){ 13 int num1 = sc.nextInt(); 14 System.out.println("请录入第二个数:"); 15 if(sc.hasNextInt()){ 16 int num2 = sc.nextInt(); 17 if(num2 == 0){ 18 System.out.println("对不起,除数不能为0"); 19 }else{ 20 System.out.println("商:"+num1/num2); 21 } 22 }else{ 23 System.out.println("对不起,你录入的不是int类型的数据!"); 24 } 25 }else{ 26 System.out.println("对不起,你录入的不是int类型的数据!"); 27 } 28 } 29 }
用if-else堵漏洞的缺点:
(1)代码臃肿,业务代码和处理异常的代码混在一起。
(2)可读性差
(3)程序员需要花费大量的经历来维护这个漏洞
(4)程序员很难堵住所有的漏洞。
try-catch
【1】基于if-else处理异常缺点太多,所以java中专门出了一个异常处理机制:
“异常三连” try-catch-finally
【2】异常出现了以后怎么看:
【3】捕获异常: try-catch
对应代码:
1 public class Test2 { 2 public static void main(String[] args) { 3 //实现一个功能:键盘录入两个数,求商: 4 try{ 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请录入第一个数:"); 7 int num1 = sc.nextInt(); 8 System.out.println("请录入第二个数:"); 9 int num2 = sc.nextInt(); 10 System.out.println("商:"+num1/num2); 11 }catch(Exception ex){ 12 System.out.println("对不起,程序出现异常!"); 13 } 14 System.out.println("----谢谢你使用计算器111"); 15 System.out.println("----谢谢你使用计算器222"); 16 System.out.println("----谢谢你使用计算器333"); 17 System.out.println("----谢谢你使用计算器444"); 18 System.out.println("----谢谢你使用计算器555"); 19 System.out.println("----谢谢你使用计算器666"); 20 } 21 }
原理:
把可能出现异常的代码放入try代码块中,然后将异常封装为对象,被catch后面的()中的那个异常对象接收,接收以后:执行catch后面的{}里面的代码,然后try-catch后面的代码,该怎么执行就怎么执行。
详细说一下:
(1)try中没有异常,catch中代码不执行。
(2)try中有异常,catch进行捕获:
如果catch中异常类型和你出的异常类型匹配的话:走catch中的代码--》进行捕获
如果catch中异常类型和你出的异常类型不匹配的话:不走catch中的代码--》没有捕获成功,程序相当于遇到异常了,中断了,后续代码不执行
注意:
(1)try中如果出现异常,然后用catch捕获成功的话,那么try中后续的代码是不会执行的。
(2)如果catch捕获异常成功,那么try-catch后面的代码该执行还是执行没有影响。
catch中如何处理异常
1 package com.llh; 2 3 import java.util.Scanner; 4 5 6 public class Test3 { 7 public static void main(String[] args) { 8 //实现一个功能:键盘录入两个数,求商: 9 try{ 10 Scanner sc = new Scanner(System.in); 11 System.out.println("请录入第一个数:"); 12 int num1 = sc.nextInt(); 13 System.out.println("请录入第二个数:"); 14 int num2 = sc.nextInt(); 15 System.out.println("商:"+num1/num2); 16 }catch(Exception ex){ 17 //第一种处理:什么都不写,什么都不做 18 //第二种处理:输出自定义异常信息 19 //System.out.println("对不起,你的代码有问题!"); 20 //第三种处理:打印异常信息: 21 /*(1)调用toString方法,显示异常的类名(全限定路径)*/ 22 /*System.out.println(ex); 23 System.out.println(ex.toString());*/ 24 /*(2)显示异常描述信息对应的字符串,如果没有就显示null 25 System.out.println(ex.getMessage());*/ 26 /*(3)显示异常的堆栈信息:将异常信息捕获以后,在控制台将异常的效果给我们展示出来,方便我们查看异常*/ 27 /* ex.printStackTrace();*/ 28 //第四种处理:抛出异常: 29 throw ex; 30 } 31 System.out.println("----谢谢你使用计算器111"); 32 } 33 }
try-catch-finally
【1】在什么情况下,try-catch后面的代码不执行?
(1)throw抛出异常的情况
(2)catch中没有正常的进行异常捕获
(3)在try中遇到return
【2】怎么样才可以将 try-catch后面的代码 必须执行?
只要将必须执行的代码放入finally中,那么这个代码无论如何一定执行。
【3】return和finally执行顺序?
先执行finally最后执行return
【4】什么代码会放在finally中呢?
关闭数据库资源,关闭IO流资源,关闭socket资源。
【5】有一句话代码很厉害,它可以让finally中代码不执行!
代码:
1 package com.llh; 2 3 import java.util.Scanner; 4 5 6 public class Test3 { 7 public static void main(String[] args) { 8 //实现一个功能:键盘录入两个数,求商: 9 try{ 10 Scanner sc = new Scanner(System.in); 11 System.out.println("请录入第一个数:"); 12 int num1 = sc.nextInt(); 13 System.out.println("请录入第二个数:"); 14 int num2 = sc.nextInt(); 15 System.out.println("商:"+num1/num2); 16 System.exit(0);//终止当前的虚拟机执行 17 return; 18 }catch(ArithmeticException ex){ 19 //throw ex; 20 }finally { 21 System.out.println("----谢谢你使用计算器111"); 22 } 23 } 24 }
多重catch
【1】try中出现异常以后,将异常类型跟catch后面的类型依次比较,按照代码的顺序进行比对,执行第一个与异常类型匹配的catch语句
【2】一旦执行其中一条catch语句之后,后面的catch语句就会被忽略了!
【3】在安排catch语句的顺序的时候,一般会将特殊异常放在前面(并列),一般化的异常放在后面。
先写子类异常,再写父类异常。
【4】在JDK1.7以后,异常新处理方式:可以并列用|符号连接:
1 package com.llh; 2 3 import java.util.InputMismatchException; 4 import java.util.Scanner; 5 6 7 public class Test4 { 8 public static void main(String[] args) { 9 //Integer 10 //实现一个功能:键盘录入两个数,求商: 11 try{ 12 Scanner sc = new Scanner(System.in); 13 System.out.println("请录入第一个数:"); 14 int num1 = sc.nextInt(); 15 System.out.println("请录入第二个数:"); 16 int num2 = sc.nextInt(); 17 System.out.println("商:"+num1/num2); 18 }catch(ArithmeticException ex){ 19 System.out.println("对不起,除数不可以为0"); 20 }catch(InputMismatchException ex){ 21 System.out.println("对不起,你录入的数据不是int类型的数据"); 22 }catch(Exception ex){ 23 System.out.println("对不起,你的程序出现异常"); 24 }finally { 25 System.out.println("----谢谢你使用计算器111"); 26 } 27 } 28 }
异常的分类
【1】层次结构:
注意:程序中语法错误,逻辑错误 都不属于上面的Error,Exception
【2】运行时异常:
1 public class Test5 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //运行时异常: 5 int[] arr = {1,2,3}; 6 System.out.println(arr.length); 7 /*int[] arr2 = null; 8 System.out.println(arr2.length);*/ 9 System.out.println(arr[10]); 10 } 11 }
【3】检查异常:
处理方式1:try-catch嵌套try-catch
1 public class Test6 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //检查异常: 5 try { 6 try { 7 Class.forName("com.msb.test01.Test").newInstance(); 8 } catch (InstantiationException e) { 9 e.printStackTrace(); 10 } catch (IllegalAccessException e) { 11 e.printStackTrace(); 12 } 13 } catch (ClassNotFoundException e) { 14 e.printStackTrace(); 15 } 16 } 17 }
处理方式2:多重catch
1 public class Test6 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //检查异常: 5 try { 6 Class.forName("com.msb.test01.Test").newInstance(); 7 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
处理方式3:throws
1 public class Test6 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { 4 //检查异常: 5 Class.forName("com.msb.test01.Test").newInstance(); 6 } 7 }
throw和throws的区别
1 package com.llh; 2 3 import java.util.Scanner; 4 5 6 public class Test7 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws Exception { 9 //实现一个功能:两个数相除,当除数为0的时候,程序出现异常。 10 /*try { 11 devide(); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 }*/ 15 devide(); 16 } 17 public static void devide() throws Exception { 18 Scanner sc = new Scanner(System.in); 19 System.out.println("请录入第一个数:"); 20 int num1 = sc.nextInt(); 21 System.out.println("请录入第二个数:"); 22 int num2 = sc.nextInt(); 23 if(num2 == 0 ){//除数为0 ,制造异常。 24 //制造运行时异常: 25 /*throw new RuntimeException();*/ 26 //制造检查异常: 27 /*try { 28 throw new Exception(); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 }*/ 32 throw new Exception(); 33 }else{ 34 System.out.println("商:"+num1/num2); 35 } 36 } 37 }
总结:
throw和throws的区别:
(1)位置不同:
throw:方法内部
throws: 方法的签名处,方法的声明处
(2)内容不同:
throw+异常对象(检查异常,运行时异常)
throws+异常的类型(可以多个类型,用,拼接)
(3)作用不同:
throw:异常出现的源头,制造异常。
throws:在方法的声明处,告诉方法的调用者,这个方法中可能会出现我声明的这些异常。然后调用者对这个异常进行处理:
要么自己处理要么再继续向外抛出异常
练习:
1 public class Student { 2 private String name; 3 private int age; 4 private String sex; 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 public String getSex() { 18 return sex; 19 } 20 public void setSex(String sex) throws Exception { 21 if(sex.equals("男")||sex.equals("女")){ 22 this.sex = sex; 23 }else{//非男非女 24 //解决办法1: 25 /*this.sex = "男";*/ 26 //解决办法2:给个友好型提示,但是打印结果为默认的null效果 27 /*System.out.println("对不起,你的性别错误了");*/ 28 //解决办法3: 29 //制造运行时异常: 30 /*throw new RuntimeException("性别不对!");*/ 31 //制造检查异常 32 /*try { 33 throw new Exception(); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 }*/ 37 throw new Exception(); 38 } 39 } 40 @Override 41 public String toString() { 42 return "Student{" + 43 "name='" + name + '\'' + 44 ", age=" + age + 45 ", sex='" + sex + '\'' + 46 '}'; 47 } 48 public Student() { 49 } 50 public Student(String name, int age, String sex) { 51 this.name = name; 52 this.age = age; 53 //this.sex = sex; 54 try { 55 this.setSex(sex); 56 } catch (Exception e) { 57 e.printStackTrace(); 58 } 59 } 60 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Student的对象: 5 /*Student s = new Student(); 6 s.setName("菲菲"); 7 s.setAge(19); 8 try { 9 s.setSex("asdfasdfasdf"); 10 } catch (Exception e) { 11 e.printStackTrace(); 12 } 13 System.out.println(s);*/ 14 Student s2 = new Student("娜娜",21,"asdfasdfasdf"); 15 System.out.println(s2); 16 } 17 }
重载和重写的异常
【1】重载:
1 public class Demo { 2 public void a() throws Exception{ 3 } 4 public void a(int age) throws ArithmeticException{ 5 } 6 }
【2】重写:
子类 <= 父类
自定义异常
自定义的异常可以继承:运行时异常
1 public class MyException extends RuntimeException { 2 3 static final long serialVersionUID = -70348971907L; 4 5 public MyException(){ 6 } 7 public MyException(String msg){ 8 super(msg); 9 } 10 }
也可以继承检查异常:
1 public class MyException extends Exception { 2 static final long serialVersionUID = -70348971907L; 3 public MyException(){ 4 } 5 public MyException(String msg){ 6 super(msg); 7 } 8 }
如果继承的是运行时异常,那么在使用的时候无需额外处理
如果继承的是检查异常,那么使用的时候需要try-catch捕获或者throws向上抛