异常处理(try...catch...final 和 throw , throws)
1.传统(弱语言)处理异常方式
原理:利用判断来控制异常出现
1 publicclass Test01 { 2 publicstaticvoid main(String[] args) { 3 Scanner sc = new Scanner(System.in); 4 System.out.println("请输入第一个数:"); 5 6 intnum1 = 0; 7 if(sc.hasNextInt()) { 8 num1 = sc.nextInt(); 9 10 System.out.println("请输入第二个数:"); 11 intnum2 = 0; 12 if(sc.hasNextInt()) { 13 num2 = sc.nextInt(); 14 15 if(0 == num2) { 16 System.out.println("除数不能为0!"); 17 }else { 18 intr = num1 / num2; 19 System.out.println("num1/num2 = "+r); 20 } 21 22 }else { 23 System.out.println("第二个数输入不是数字"); 24 } 25 26 }else { 27 System.out.println("第一个数输入不是数字!"); 28 } 29 } 30 }
这样做的缺点:
【1】通过判断处理异常影响执行效率。
【2】判断逻辑和业务逻辑交织在一起,不利于后期维护。
2.异常
异常是所有异常类的直接或间接父类。
异常是指在运行过程中发生了不正常的情况,这是它就会中断程序。
异常处理机制:指java中异常处理机制为程序提供处理异常的能力,从而使程序不中断的运行。
3.异常处理
异常处理的关键字有:try...catch 和 try...catch...finally
1)try...catch
解释:try— 把有可能产生异常的代码放到try代码块中,catch—负责捕获异常并匹配异常再处理异常。
【1】正常执行,没有遇到异常
【2】出现异常,异常处理,正常结束。(红字是处理异常步骤)
异常常见方法:
prinStackTrace:打印异常的执行堆栈信息(在控制台中异常堆栈信息输出位置不固定)
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)
一般,异常的堆栈信息很多,开发者只需要看懂
——第一行:异常简单的信息(异常类型,异常描述等)
——最后一行:异常出现的位置(如上的Test01.java:14)
getMessage:返回异常的描述信息
1 packagecn.sxt02.exception02; 2 importjava.util.Scanner; 3 publicclass Test01 { 4 publicstaticvoid main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请输入第一个数:"); 7 8 intnum1 = 0; 9 intnum2 = 0; 10 11 try { 12 num1 = sc.nextInt(); 13 14 System.out.println("请输入第二个数:"); 15 num2 = sc.nextInt(); 16 17 intr = 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 }
【3】异常类型不匹配
【4】多重catch
1 publicclass Test03 { 2 publicstaticvoid main(String[] args) { 3 Scanner sc = new Scanner(System.in); 4 System.out.println("请输入第一个数:"); 5 6 intnum1 = 0; 7 intnum2 = 0; 8 9 try { 10 num1 = sc.nextInt(); 11 12 System.out.println("请输入第二个数:"); 13 num2 = sc.nextInt(); 14 15 intr = num1 / num2; 16 System.out.println("num1/num2 = " + r); 17 }catch (ArithmeticExceptione) { 18 System.out.println("数学计算异常:"+e.getMessage()); 19 }catch(InputMismatchExceptione) { 20 System.out.println("输入不匹配异常:"+e.getMessage()); 21 }catch (Exception e) { 22 System.out.println("发送异常:"+e.getMessage()); 23 } 24 25 System.out.println("程序正常结束"); 26 } 27 }
2)try...catch...finally
解读:把有可能产生异常的代码放到try的代码块中,catch捕捉异常并匹配异常再处理异常,finally块用于收尾工作
(如关闭数据库,关闭文件,释放内存等资源)
注:finally :无论是否发生异常,finally都将执行,常用于收尾工作
1 publicstaticvoid main(String[] args) { 2 Scanner sc = new Scanner(System.in); 3 System.out.println("请输入第一个数:"); 4 5 intnum1 = 0; 6 intnum2 = 0; 7 8 try { 9 num1 = sc.nextInt(); 10 11 System.out.println("请输入第二个数:"); 12 num2 = sc.nextInt(); 13 14 intr = 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】finally不执行的情况
System.exit(0)正常退出jvm,程序结束,不会执行finally
【2】catch可以省略,变成try...finally形式
4.return
当return存在于try...catch...finally时的执行顺序(return始终在最后)
1 packagecn.sxt02.exception03; 2 3 /** 4 * 存在return的情况 5 */ 6 publicclass Test02 { 7 8 publicstaticintdiv(inta, intb) { 9 10 try { 11 intr = a / b; 12 returnr; 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 publicstaticvoid main(String[] args) { 26 27 intr = Test02.div(10, 0); 28 System.out.println("r=" + r); 29 System.out.println("程序正常结束"); 30 } 31 }
5.异常的分类
Throwable类是 Java 语言中所有错误(Error)或异常(Exception)的父类,只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
Error 类表示错误类。仅靠程序本身无法恢复的严重错误。如jvm内存耗尽、jvm崩溃等。
Exception类表示异常类,可以通过java 异常处理机制处理。
Exception根据是否处理分为两种情况。
RuntimeException:运行时异常。不要求程序必须做出处理。是所有运行时异常的父类。
CheckedException:检查时异常。要求程序必须处理,不处理编译不通过。
1 publicclass Test01 { 2 publicstaticvoid main(String[] args) { 3 // 运行时异常 4 Scanner sc = new Scanner(System.in); 5 // runtime exception 6 intr = sc.nextInt(); 7 System.out.println("r = "+ r); 8 9 // 检查时异常 10 SimpleDateFormatdf = newSimpleDateFormat(); 11 try { 12 Date date = df.parse("2019"); 13 } catch (ParseExceptione) { 14 e.printStackTrace(); 15 } 16 } 17 }
常见的运行时异常
ArithmeticException:数学计算异常。如除数为0
InputMismatchException:输入不匹配异常。
ArrayIndexOutofBoundsException:数组下标越界异常。
NullPointException:空指针异常,对象没有初始化就使用时,jvm就会抛出该异常。
IllegalArgumentException:非法参数异常。
ClassCastException:强制类型转化异常。
NumberFormatException:数字格式化异常。如把“abc”格式化为数字。
常见的检查时异常:
ClassNotFoundException:类没有被发现异常。
SQLException:数据库相关异常
IOException:IO操作异常
ParseException:解析错误异常
FileNotFoundException:文件未发现异常。
运行时异常和检查时异常的区别:
运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、 ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行
编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
检查时异常:Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者
声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,
通过throws声明抛出异常从而交给上一级调用方法处理
6.声明异常
1)throws关键字
解读:当一个方法可能存在异常,而此时自身又无法更好的处理,可以交给外界处理。此时用throws声明并抛出异常。
1 publicclass Test01 { 2 3 publicstaticintdiv(inta, intb) throws ArithmeticException{ 4 intr = 0; 5 r = a / b; 6 returnr; 7 } 8 9 publicstaticvoid main(String[] args) { 10 try { 11 Test01.div(10, 0); 12 } catch (ArithmeticExceptione) { 13 System.out.println("除数不能为0"); 14 } 15 } 16 }
注:开发者可以根据需求声明检查时异常 (Exception或者非运行时异常及其子类) 和运行时异常(RuntimeException或其子类)
如果抛出异常后,调用处不知道如何处理异常,可以选择继续声明异常(抛出异常),这个过程叫做异常上抛。
1 publicclass Test01 { 2 3 publicstaticintdiv(inta, intb) throws Exception{ 4 intr = 0; 5 r = a / b; 6 returnr; 7 } 8 9 publicstaticvoid 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 }
2)声明异常和重载的关系
声明异常和重载没有任何关系
1 publicclass Test01 { 2 3 publicstaticintdiv(inta, intb) throws Exception{ 4 intr = 0; 5 r = a / b; 6 returnr; 7 } 8 9 publicstaticintdiv(inta, intb) { 10 intr = 0; 11 r = a / b; 12 returnr; 13 } 14 }
注:方法重载:
【1】方法名相同
【2】参数列表不同(个数,类型,不同类型的顺序不同)
【3】和返回值,修饰符,声明异常无关
3)声明异常和重写的关系
声明异常和方法重写有关系,关系如下
[1]父类方法声明了异常(检测时或运行时),子类可以不声明任何异常。
publicclass Father { publicvoidshowInfo() throws Exception{ } }
publicclass Son extendsFather{
@Override
publicvoidshowInfo(){
}
}
理解:父类方法抛出异常,子类在重写过程中把该异常处理掉了,所以子类方法不用声明异常。
[2] 父类方法声明没有声明任何异常(检测时或运行时),子类也不声明异常或者声明运行时异常。
publicclass Father {
publicvoidshowInfo(){
}
}
publicclass Son extendsFather{ @Override publicvoidshowInfo() throws Exception{ } }
[3]父类声明了异常(检测时或运行时),子类声明完全一样的异常。
publicclass Father {
publicvoidshowInfo() throwsException{
}
}
publicclass Son extendsFather{
@Override
publicvoidshowInfo() throwsException {
}
}
7.手动抛出异常
1)throw
除了系统自动抛出异常外,有些问题需要开发者手动抛出异常。使用关键字throw
1 packagecn.sxt02.exception06; 2 3 publicclass Student { 4 private String name; 5 private String gender; 6 7 public String getName() { 8 returnname; 9 } 10 11 publicvoidsetName(String name) { 12 this.name = name; 13 } 14 15 public String getGender() { 16 returngender; 17 } 18 19 publicvoidsetGender(String gender) throws Exception{ 20 if(gender.equals("男") || gender.equals("女")) { 21 this.gender = gender; 22 }else { 23 thrownew Exception("性别不合法!"); 24 } 25 } 26 27 publicStudent(String name, String gender) { 28 super(); 29 this.name = name; 30 this.gender = gender; 31 } 32 33 publicStudent() { 34 super(); 35 } 36 37 }
1 publicclass Test01 { 2 publicstaticvoid main(String[] args){ 3 Student stu = newStudent(); 4 stu.setName("二狗"); 5 try { 6 stu.setGender("xxx"); 7 } catch (Exception e) { 8 System.out.println(e.getMessage()); 9 } 10 } 11 }
2)自定义异常
理解:如果开发者需要手动抛出的异常在系统不存在,可以自定义异常。
注意:如果要自定义异常,首先要确定异常类型,如果异常是运行时异常,必须继承RuntimeException或其子类;如果异常是检查时异常,必须继承Exception或其子类。
——异常的命名方式,参考系统命名方式,以Exception结尾。
publicclassAgeExceptionextendsException{ publicAgeException() { super(); } publicAgeException(String message) { super(message); } }