异常
异常指的是在运行期出现的错误;在编译阶段出现的语法错误等,不能称之为异常。
Error:
虚拟机无法解决的严重问题,如jvm系统内部错误,系统崩溃,动态链接失败等等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常程序无法处理这些错误,也不能试图通过catch块来捕获,一般不进行处理。
Exception:
其他因程序设计错误或偶然外在因素导致的一般性问题,可以使用针对性的代码进行处理,如算法异常、空指针访问、数组下标越界、所读文件不存在等。主要分两类:
1、编译异常,必须处理后才能正常编译;java程序必须捕获或声明所有编译时异常;
2、运行时异常RuntimeException,这种异常可以处理,也可以不处理。解决办法:
1、遇到错误就终止程序的运行,即不对异常进行处理;
2、在程序编写时,就考虑错误的检测、错误消息的提示,在抛给java运行时环境的时候,将这个异常对象捕获,再进行处理。
1 class TestError{ 2 public static void main(String[] args){ 3 byte[] arr = {1,2,3}; 4 try{ 5 System.out.println(arr[3]);//加上try块 6 }catch(Exception e){ 7 System.out.println("发生数组索引越界异常"); 8 } 9 } 10 }
try尝试:将可能发生异常的语句放入其中; catch捕获:当try块中的异常发生时,拿该异常区匹配catch块中的异常类,如果匹配,就执行这个catch块中 的语句,可以有多个catch块。当然只能有一个catch块被执行。 注意:catch语句块排放顺序,一定要先捕获小异常(子),再捕获大异常(父).
访问异常信息
所有异常对象包含以下常用方法:
1、getMessage():返回该异常的详细描述信息;
2、printStackTrace():将异常的跟踪栈信息打印出来;
3、printStackTrace(PrintStream s):将异常的跟踪栈信息输出到指定的输出流;
4、getStackTrace():返回该异常的跟踪栈信息。
几种常见异常:
类型转换异常(ClassCastException)、空指针异常(NullPointerException);
编译时异常(必须捕获)
1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 public class ErrorDemo { 4 public static void main(String[] args) { 5 FileInputStream in = null; 6 // in = new FileInputStream("a.txt");//error,没有捕获 7 try{ 8 in = new FileInputStream("a.txt");//有可能会抛出异常,必须捕获 9 }catch(FileNotFoundException fe){ 10 fe.printStackTrace(); 11 } 12 } 13 }
finally回收资源
在try语句中打开的物理资源,比如数据库连接、网络连接、磁盘文件等,都必须显示回收。 Java中的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收内存中对象所占用的内存资源。可使用finally进行。
除非在try块,catch块中调用了退出虚拟机的方法(System.exit(-1)),否则不管在try块,catch块中执行怎样的代码,出现怎样的情况,finally块中的语句中会被执行。 通常情况下,不要在finally块中使用return或者thorw等导致方法终止的语句,一旦在finally块中使用了return或者throw语句,将会导致try块,catch块中的return,throw语句失效。
1 class FinallyTest1{ 2 public static void main(String[] args){ 3 int res = test(); 4 System.out.println(res); 5 } 6 public static int test(){ 7 try{ 8 int x = 2/0; 9 System.out.println("a"); 10 return 1; 11 } 12 catch(Exception e){ 13 return 2; 14 } 15 finally{ 16 return 3; 17 } 18 } 19 }
当程序在try/catch中遇到了return或者是throw语句,程序停止执行,但是并不会立即结束该方法,而是去寻找是否包含了finally块,如果没有finally块,则立即执行return或者throw语句,方法终止;如果有finally块,程序立即开始执行finally块,只有当finally块中语句执行完了,系统才会返回到try块中执行return或者是throw语句;如果finally块中也包含了return或者是throw等导致方法结束的语句,则在finally块中已经终止了方法,程序就不会再跳回去执行try块,catch块中的任何代码。换句话说,在finally中的return语句提前终止了方法。
Checked 异常和Runtime异常体现
Checked异常:编译时异常
Runtime异常:运行时异常
使用throws声明抛出异常
使用throws抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由上一级的调用者处理;如果main方法也不知道如何处理这种类型的异常,也可以使用throws继续声明抛出异常,该异常将交给JVM处理。JVM对异常的处理方法是,打印异常的跟踪栈信息,并终止程序运行。
throws声明抛出异常只能在方法签名中使用,throws可以声明抛出多个异常类,彼此之间用逗号隔开;
一旦一个方法使用throws声明抛出异常,程序就无法使用try,catch块来捕获该异常了。
1 import java.io.*; 2 class ThrowsDemo2{ 3 public static void main(String[] args) throws Exception{ 4 //此处也可以使用try,catch块来捕获异常并做处理,或者再次抛出 5 test(); 6 } 7 public static void test() throws Exception{ 8 FileInputStream fis = new FileInputStream("a.txt"); 9 } 10 }
注意:方法重写时,子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或者相同,子类声明抛出的异常不允许比父类方法声明抛出的异常大。
两种异常的区别
1、如果throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里,显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把该异常交给方法的调用者处理;
2、如果throw语句抛出的是Runtime异常,则该语句无须放在try块中,也无须放在带throws声明的方法:这种运行时异常可以捕获,也可以完全不理会。
1 import java.io.IOException; 2 class ThrowsDemo{ 3 public static void main(String[] args) { 4 //被调用的checkedEx方法会抛出异常,并且是编译异常,必须用try,catch捕获,或者是在调用的方法上继续声明抛出 5 try{ 6 checkedEx(); 7 }catch(Exception e){ 8 e.printStackTrace(); 9 } 10 //被调用的方法会抛出运行时异常,可以完全不理会,由调用者处理,这里的调用者就是main方法 11 runtimeEx(); 12 } 13 //调用此方法肯定会抛出IOException 14 public static void checkedEx() throws IOException{ 15 //方法中抛出的异常是编译异常,必须要进行处理:要么放在try块中,要么在方法声明上加上抛出声明 16 throw new IOException("checked Exception"); 17 } 18 //调用此方法肯定会抛出异常,运行时异常,可以不处理 19 public static void runtimeEx(){ 20 //方法中抛出的异常是运行时异常,可以处理,也可以不理会 21 throw new NullPointerException("runtime Exception"); 22 } 23 }
自定义异常
定义异常类时通常需要提供两个构造方法,一个是无参的,一个是带有一个字符串的构造方法,这个字符串将作为该异常对象的描述信息,也就是异常对象的getMessage方法的返回值。
1 public class MyException extends Exception{ 2 //无参构造方法 3 public MyException(){} 4 //带一个字符串的构造方法,用来传递异常信息 5 public MyException(String msg){ 6 super(msg); 7 } 8 }
1 /** 2 编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。 3 对数据类型不一致(NumberFormatException) 4 缺少命令行参数(ArrayIndexOutOfBoundsException 5 除0(ArithmeticException) 6 及输入负数(EcDef 自定义的异常)进行异常处理。 7 */ 8 9 class EcDef{ 10 public static void main(String[] args){ 11 try{ 12 int a=Integer.parseInt(args[0]); 13 int b=Integer.parseInt(args[1]); 14 // int res = div(a,b); 15 int res = div(a,b); 16 System.out.println(res); 17 18 }catch(NumberFormatException e){ 19 System.out.println("输入格式错误"); 20 }catch(ArrayIndexOutOfBoundsException e){ 21 System.out.println("输入个数异常"); 22 }catch(ArithmeticException e){ 23 System.out.println("除数不能为0异常"); 24 }catch(EcDefExce e){ 25 System.out.println(e.getMessage()); 26 } 27 28 29 } 30 public static int div(int a,int b) throws EcDefExce{//自定义异常未明显说明是runtime异常,所以必须抛出 31 if(a<0||b<0){ 32 throw new EcDefExce("输入不能为负数"); 33 } 34 35 return a/b; 36 } 37 } 38 //自定义异常 39 class EcDefExce extends Exception{ 40 public EcDefExce(){} 41 public EcDefExce(String msg){ 42 super(msg); 43 } 44 }
子类重写父类方法时异常处理
注意四点:
1、可以和父类方法抛出一样异常
2、抛出的异常可以是父类异常的子类,不能是父类方法抛出异常的父类
3、抛出的异常可以比父类多
4、可以不抛出任何异常
异常处理图例
异常处理总结:
java 提供了两种方式用来处理一个异常类的对象。
处理的方式一:
1 try{ 2 //可能出现异常的代码 3 }catch(Exception1 e1){ 4 //处理的方式1 5 }catch(Exception2 e2){ 6 //处理的方式2 7 }finally{ 8 //一定要执行的代码 9 }
注:
1.try内声明的变数,类似于局部变量,出了try{}语句,就不能被调用
2.finally是可选的。
3.catch语句内部是对异常对象的处理: 常用的是:e.printStackTrace();
4.可以有多个catch语句,try中抛出的异常类对象从上往下去匹配catch中的异常类的类型,一旦满足就执行catch中的代码。执行完,就跳出其后的多条catch语句
5.如果异常处理了,那么其后的代码继续执行。
6.若catch中多个异常类型是"并列"关系,孰上孰下都可以。 若catch中多个异常类型是"包含"关系,须将子类放在父类的上面,进行处理。否则报错!
7.finally中存放的是一定会被执行的代码,不管try中、catch中是否仍有异常未被处理,以及是否有return语句。
8.try-catch是可以嵌套的。
处理的方式二:在声明中进行throws抛出
1、对于运行时异常来说,可以不显式的进行处理。
2、对于编译时异常来说,必须要显式的进行处理。