.异常处理
1、异常处理与捕获
a、try catch finally
b、throws
2.Java异常API
RuntimeException
3.Exception常用API
printStackTrace
getMessage
getCause
4.自定义Exception
-------------------------------淫荡分隔线--------------------------------------------
异常处理
1. 异常处理概述
1. 使用返回值状态标识异常
在JAVA语言出现以前,传统的异常处理方式多采用返回值来标识程序出现的异常情况,这种方式虽然为程序员所熟悉,但却有多个坏处。
首先,一个API可以返回任意的返回值,而这些返回值本身并不能解释该返回值是否代表一个异常情况发生了和该异常的具体情况,需要调用API的程序自己判断并解释返回值的含义。
其次,并没有一种机制来保证异常情况一定会得到处理,调用程序可以简单的忽略该返回值,需要调用API的程序员记住去检测返回值并处理异常情况。这种方式还让程序代码变得冗长,尤其是当进行IO操作等容易出现异常情况的处理时,代码的很大部分用于处理异常情况的switch分支,程序代码的可读性变得很差。
2. 异常处理机制
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finally块代码被执行和当前线程的所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被中止。
异常的处理和捕获
1. Throwable 类
Error和Exception
Java异常结构中定义有Throwable类,Exception和Error是其派生的两个子类。其中Exception表示由于网络故障、文件损坏、设备错误、用户输入非法等情况导致的异常,这类异常是可以通过Java异常捕获机制处理的。而Error表示Java运行时环境出现的错误,例如:JVM内存溢出等。
2. try-catch
try {...} 语句指定了一段代码,该段代码就是一次捕获并处理例外的范围。在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句分别对这些异常做相应的处理。
如果没有列外产生,所有的catch代码段都被略过不执行
在catch语句块中是对异常进行处理的代码。catch中声明的异常对( catch(SomeException e) )封装了异常事件发生的信息,在catch语句块中可以使用这个对象的一些方法获取这些信息
常见格式:
...
try{
//可能出现异常的代码片段
}catch(Exception e){
//处理该异常的代码片段
}
...
3. 多个catch
每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常 。catch捕获的异常类型由上至下的捕获异常类型的顺序应是子类到父类的
例如
1 try{ 2 … 3 }catch(NullPointerException e){ //子类异常应在上面捕获 4 … 5 }catch(RuntimeException e){ //父类异常在下面捕获 6 … 7 }catch(Exception e){ //应养成最终捕获Exception的习惯 8 9 … 10 }
1 ** 2 * 使用try-catch 捕获并解决异常 3 * @author Administrator 4 * 5 */ 6 class TestExcept{ 7 public static void main(String[] args) { 8 System.out.println("程序开始了"); 9 /* 10 * jvm 11 * 1、当运行到str.length()时发现str是null 12 * 2、jvm实例化了一个NullPointerException 13 * 3、将程序执行过程的详细信息设置到该异常实例中 14 * 4、将其在str.length()这个位置抛出该异常实例 15 * 5、常看出错的方法str.length()是否被try包含 16 * 6、若没有被try语句包含,jvm会将该异常向str.length所在的方法之外抛出 若抛到main方法之外 那该程序直接kill 17 * 18 * 7、若被try语句包围,顺序调用try下面的的catch 查看哪个catch可以捕获该异常。若有则进入catch内部来执行代码的片段,否则和第6步相同 19 * 20 */ 21 try{ 22 String str =""; 23 System.out.println(str.length()); 24 // System.out.println(str.charAt(0)); 25 System.out.println(Integer.parseInt(str)); 26 }catch(NullPointerException e){ 27 System.out.println("出现了空指针"); 28 }catch(StringIndexOutOfBoundsException e ){ 29 System.out.println("字符串下标越界了"); 30 }catch(Exception e ){ 31 System.out.println("反正是出错了"); 32 } 33 /* 34 * 当上面的代码出现异常后,程序跳入catch 35 * try语句中剩下的给代码均不会在被执行 36 * 37 * 最后一个catch要捕获最大的异常 38 */ 39 System.out.println("程序结束了"); 40 } 41 }
通常在书写代码的时候,我们应当在最后一个catch中捕获Exception,这样可以保证代码不会因为出现一个未在catch中声明的异常而导致捕获失败使得程序终止。
4. finally的作用
finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序其它部分以前,能够对程序的状态作统一管理。
无论try所指定的程序块中是否抛出例外,finally所指定的代码都要被执行,通常在finally语句中可以进行资源的消除工作,如关闭打开的文件、删除临时文件等。
finally语句块只能定义在try语句块之后,或者最后一个catch语句块之后,且只能定义一次。
无条件执行
1 /** 2 * finally 3 * @author Administrator 4 * 5 */ 6 7 class TestException{ 8 9 public static void main(String[] args) { 10 11 PrintWriter pw =null; 12 try{ 13 pw =new PrintWriter("pw.txt"); 14 pw.println("你好!"); 15 pw.println(""); 16 pw.println("再见!"); 17 }catch(FileNotFoundException e){ 18 System.out.println("创建文件不成功"); 19 }catch(Exception e){ 20 System.out.println("出了个错"); 21 }finally{ 22 System.out.println("我执行了"); 23 } 24 } 25 26 }
1 /** 2 * finally面试 3 * final finally finalize 作业 4 * final 类 属性 方法 5 * finally 异常捕获的收尾工作 通常finaly 不要写Return返回值 6 * finalize Object定义的一个方法 该方法会在gc回收其对象之前调用 调用该方法就意味着 方法使用完毕 7 * 8 * 9 */ 10 class Test{ 11 public static void main(String[] args) { 12 /* 13 * return: 14 * 1、设置当前返回值为return后面的内容 15 * 2、结束方法return之后的语句不再执行 16 */ 17 18 try { 19 //若出错,异常抛出到方法外 20 }finally{ 21 //无论是否出错,这里都要执行 22 } 23 System.out.println(test(null)+","+test("".charAt(0))+","+test(0)); 24 } 25 26 public static int test(){ 27 try{ 28 System.out.println("正常"); 29 }catch(NullPointerException e){ 30 System.out.println("空指针了"); 31 return 1 ; 32 }catch(Exception e){ 33 System.out.println("出错了"); 34 return 2; 35 }finally{ 36 return 3; 37 } 38 39 40 } 41 42 }
1 /** 2 * finally面试 3 * final finally finalize 作业 4 * final 类 属性 方法 5 * finally 异常捕获的收尾工作 通常finaly 不要写Return返回值 6 * finalize Object定义的一个方法 该方法会在gc回收其对象之前调用 调用该方法就意味着 方法使用完毕 7 * 8 * 9 */ 10 class Test{ 11 public static void main(String[] args) { 12 /* 13 * return: 14 * 1、设置当前返回值为return后面的内容 15 * 2、结束方法return之后的语句不再执行 16 */ 17 18 try { 19 //若出错,异常抛出到方法外 20 }finally{ 21 //无论是否出错,这里都要执行 22 } 23 System.out.println(test(null)+","+test("".charAt(0))+","+test(0)); 24 } 25 26 public static int test(){ 27 try{ 28 System.out.println("正常"); 29 }catch(NullPointerException e){ 30 System.out.println("空指针了"); 31 return 1 ; 32 }catch(Exception e){ 33 System.out.println("出错了"); 34 return 2; 35 }finally{ 36 return 3; 37 } 38 39 40 } 41 42 }
5. throw关键字
当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时刻,您可能会想要自行抛出异常,例如在异常处理结束后,再将异常抛出,让下一层异常处理区块来捕捉,若想要自行抛出异常,您可以使用“throw”关键词,并生成指定的异常对象。
例如:
throw new ArithmeticException();
两种情况:
1、异常不应该在当前方法里处理
2、程序出现了一个不是语法的错误,但不满足业务逻辑
/** * 测试异常抛出的第二种情况 * 当不满足业务逻辑时 * @author Administrator ** @param age * @throws Exception 当设置的年龄超出人类范畴抛出异常 * */ public class Testtow { private String name; private int age; public String toString(){ return name+"今年"+age+"岁"; } public String getName(){ return name; } public void setName(String name){ this.name=name; } public int getAge(){ return age; } public void setAge(int age){ if(age<0||age>100){ throw new RuntimeException( "超出人类年龄异常"); } this.age=age; } public static void main(String[] args) { Testtow p=new Testtow(); p.setName("李绪春"); p.setAge(1000); System.out.println(p); } }
6. throws关键字
就是丑话说前面
程序中会声明许多方法(Method),这些方法中可能会因某些错误而引发异常,但您不希望直接在这个方法中处理这些异常,
而希望调用这个它的方法来统一处理,这时候您可以使用“throws”关键词来声明这个方法将会抛出异常
例如:
public static void stringToDate(String str) throws ParseException{
……
}
1 public void setAge(int age) throws Exception{ 2 if(age<0||age>100){ 3 throw new RuntimeException( "超出人类年龄异常"); 4 } 5 this.age=age; 6 } 7 8 public static void main(String[] args) throws Exception { 9 Testtow p=new Testtow(); 10 p.setName("李绪春"); 11 /* 12 * 调用一个一有throws声明抛出异常的方法时 13 * 这段代码必须要处理方法声明抛出的异常 14 * 处理的方法有两种: 15 * 1、自行try-catch 16 * 1、继续向外抛出异常 17 */ 18 19 p.setAge(1000); 20 System.out.println(p); 21 22 或者第二中方法 23 24 try { 25 p.setAge(1000); 26 } catch (Exception e) { 27 //e方法用于输出错误堆栈,追查代码出现错误的过程 28 e.printStackTrace(); 29 30 31 不要在main方法上写throws
7. 重写方法时的throws
当使用继承时,在父类的某个方法上声明了throws抛出某些异常,而在子类中重些该方法时,我们可以做以下的操作:
不处理异常(重写方法时不声明throws)
可仅在throws中声明父类中声明的部分异常
可在throws中声明父类方法中抛出的异常的子类异常
但是不能做以下操作:
重写方法时在throws中出声明抛出额外的异常
重写方法是在throws中声明父类方法中声明的抛出异常的父类异常
1 /** 2 * 用于测试子类重写父类方法时的throws 3 * @author Administrator 4 * 5 */ 6 class Father{ 7 public void dosome() throws IOException,AWTException{ 8 9 } 10 } 11 /*允许子类抛出的: 12 * 1.重写方法是可以不throws抛出任何异常的 13 * 2.仅抛出父类抛出的部分异常 14 * 3.FileNotFoundException是IOException的子类 15 * 可以抛出父类抛出的异常的子类异常 16 *不允许子类抛出的: 17 * 1.不允许抛出额外异常 18 * 2、不允许抛出父类抛出的异常的父类异常 19 */ 20 21 class Son extends Father{ 22 public void dosome() throws IOException, AWTException{ 23 super.dosome(); 24 } 25 }
/** * 使用异常捕获机制来完成复制文件的异常处理 * */ class FIleUtils{ public static void main(String[] args) { FileInputStream fis= null; FileOutputStream fos =null; try{ fis=new FileInputStream("fos.dat"); fos=new FileOutputStream("fos_copy.dat"); int d= -1; while((d=fis.read())!=-1){ fos.write(d); } }catch(FileNotFoundException e){ System.out.println("文件没有发现"); }catch(IOException e){ System.out.println("读写出错了"); }catch(Exception e){ System.out.println("出了个错"); }finally{ //关闭两个流 if(fis!=null){ try{ fis.close(); }catch(Exception e){ } } if(fos!=null){ try{ fos.close(); }catch(Exception e){ } } } } }
3. Java异常API
1. RuntimeException
Java异常可以分为可检测异常,非检测异常
可检测异常:可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器就通不过,不允许编译
非检测异常:非检测异常不遵循处理或者声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已经解决了这样一个异常
RuntimeException 类属于非检测异常,因为普通JVM操作引起的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在java应用程序中会频繁出现。因此它们不受编译器检查与处理或声明规则的限制 。
2. 常见RuntimeException
IllegalArgumentException
抛出的异常表明向方法传递了一个不合法或不正确的参数
NullPointerException
当应用程序试图在需要对象的地方使用 null 时,抛出该异常
ArrayIndexOutOfBoundsException
当使用的数组下标超出数组允许范围时,抛出该异常
ClassCastException
当试图将对象强制转换为不是实例的子类时,抛出该异常
NumberFormatException
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
1 /** 2 * java的异常分为两类: 3 * 检查异常: 程序抛出一个检查异常 4 * 编译器会检查是否有处理异常的代码片段 5 * 没有则编译不通过 6 * 非检查异常:当程序抛出该类异常时,编译器会忽略检查 7 * @author Administrator 8 * 9 */ 10 11 class TestRuntimeException{ 12 public static void main(String[] args) { 13 String str ="aa"; 14 /* 15 * parseInt 方法抛出NumberFormatException 16 * 但是该异常是继承自RuntimeException的 17 * 所以是非检查异常,编译时忽略见擦是否有处理该异常 18 */ 19 int i=Integer.parseInt(str); 20 /* 21 * 这里编译时不通过的 ,因为FileInputStream的构造方法抛出FileNotFoundException,而 22 * 该异常是一个检查异常, 那么编译器在编译时会检查该异常是否得到了处理,没有处理则编译不通过 23 */ 24 try { 25 FileInputStream fis=new FileInputStream("fos.dat"); 26 } catch (FileNotFoundException e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } 30 31 }
Exception常用API
1. printStackTrace
Throwable中定义了一个方法可以输出错误信息,用来跟踪异常事件发生时执行堆栈的内容。该方法定义为: Throwable是所有错误的父类
void printStackTrace()
例如:
try{ … }catch(Exception e){ e.printStackTrace();//输出执行堆栈信息 }
2. getMessage
Throwable中定义了一个方法可以得到有关异常事件的信息。该方法定义为:
String getMessage()
例如:
try{ … }catch(Exception e){ System.out.println(e.getMessage()); }
class TestExceptionAPI{ public static void main(String[] args) { try { Integer.parseInt("a"); } catch (Exception e) { e.printStackTrace(); //出错的相关信息 一个字符串 System.out.println(e.getMessage()); } }
3. getCause
很多时候,当一个异常由另一个异常导致异常而被抛出的时候,Java库和开放源代码会将一种异常包装成另一种异常。这时,日志记录和打印根异常就变得非常重要。Java异常类提供了 getCause()方法来检索导致异常的原因,这些可以对异常根层次的原因提供更多的信息。该Java实践对代码的调试或故障排除有很大的帮助。另外,如果要把一个异常包装成另一种异常,构造一个新异常就要传递源异常。
Throwable getCause()
获取该异常出现的原因
class TestException2{ public static void main(String[] args) { try { dosome("aaa"); } catch (Exception e) { e.getCause().printStackTrace(); } } public static void dosome (String str){ try{ System.out.println(Integer.parseInt(str)); }catch(NumberFormatException e){ throw new RuntimeException(e); } } }
自定义Exception
1. 自定义异常的意义
Java异常机制可以保证程序更安全和更健壮。虽然Java类库已经提供很多可以直接处理异常的类,但是有时候为了更加精准地捕获和处理异常以呈现更好的用户体验,需要开发者自定义异常。
2. 继承Exception自定义异常
创建自定义异常类,语法格式:
public class [自定义异常类名] extends Exception{
…
}
3. 如何编写构造方法
当定义好自定义异常后,我们可以通过Eclipse来自动生成相应的构造方法。
具体步骤如下:
声明一个类并继承自Exception
右键点击点击Source
选择Generate Constructors from Superclass
选中父类中所有构造方法后确认生成
例如:
public class MyException extends Exception{ public MyException() { super(); // TODO Auto-generated constructor stub } public MyException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public MyException(String message) { super(message); // TODO Auto-generated constructor stub } public MyException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } }