1.处理错误的要求
如果由于出现错误而使得某些操作没有完成,程序应该:
- 返回到一种安全状态,并能够让用户执行一些其他的命令。
- 允许用户保存所有操作的结果,并以妥善的方式终止程序。
2.程序中可能出现的错误和问题
- 用户输入错误
- 设备错误
- 物理限制
- 代码错误
3.异常分类
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。
RuntimeException类是由程序错误导致的异常,包括错误的类型访问、数组访问越界和访问空指针。
IOException类是由I/O错误导致的异常。
将派生于Error类或RuntimeException类的所有异常称为非受查异常(unchecked),所有其他异常称为受查(checked)异常。
4.抛出异常
一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。例如,读取文件的代码应该考虑到文件不存在或者内容为空,因此需要抛出IOException异常。方法应该在其首部声明所有可能抛出的异常。
(1)需要抛出异常的4种情况:
- 调用一个抛出受查异常的方法,例如,FileInputStream构造器
- 程序运行中发现错误,并且利用throw语句抛出一个受查异常
- 程序出现错误。例如,a[-1]会抛出一个ArrayIndexOutOfBoundsException非受查异常
- Java虚拟机和运行库时出现的内部错误。
(2)对于一个已经存在的异常类,将其抛出的步骤为
- 找到一个合适的异常类
- 创建这个类的一个对象
- 将对象抛出
String readData(Scanner in) throws EOFException{ ... while(...){ if(!in.hasNext()) if(n<len) throw new EOFException(); } } return s; }
5.创建异常类
如果任何标准异常类都没有能够充分地描述清楚的问题,那么就应该创建自己的异常类。
只需要创建一个extends于Exception类或者Exception类的子类即可,这个定义的类应该包含两个构造器,一个是默认的构造器,一个是带有详细描述信息的构造器。然后将在需要抛出异常时创建这个定义的类的对象并抛出即可。
6.捕获异常
若某个方法抛出异常,既可以在当前方法中进行捕获,然后处理该异常,也可以将异常向上抛出,由方法调用者来处理。
捕获异常和传递异常的选择:应该捕获那些知道如何处理的异常,而将那些不知道如何处理的异常继续进行传递。
将异常直接交给能够胜任的处理器进行处理要比压制对它的处理更好。
如果在try语句块中的任何代码抛出了一个在catch字句中说明的异常类,那么
- 程序将跳过try语句块的其余代码
- 程序将执行catch字句中的处理器代码
如果在try语句块中的代码没有抛出任何异常,那么程序将跳过catch子句。
在有多个catch语句块时,用来捕获子类的catch语句要放在捕获父类的catch语句的前面。
7.代码示例
(1)try...catch语句块捕获异常
public static int parseInt(String s) throws NumberFormatException{…} try{ int age=Integer.parseInt("24L"); //抛出NumberFormatException异常 System.out.println("打印1"); }catch(NumberFormatException e){ //捕获NumberFormatException异常 System.out.println("年龄请输入整数!"); System.out.println("错误:"+e.getMessage()); } System.out.println("打印2");
(2)使用throws关键字抛出异常
dofile方法声明抛出了一个IOException异常,那么该方法的调用者main()方法中需要捕获该异常并进行处理。
(举个栗子)如果main()方法不想使用try...catch捕获异常,可以同样使用throws向上抛,然后main()方法的调用者捕获异常并处理,...可以一直向上抛,但最终要有能够处理该异常的代码。
如果异常类型是Error、RuntimeException或它们的子类,可以不使用throws来声明要抛出的异常,因为Java虚拟机会捕获此类异常。
import java.io.File; import java.io.FileWriter; import java.io.IOException; public class Demo { public static void main(String[] args){ try{ dofile("C:/mytxt.txt"); }catch(IOException e){ System.out.println("调用dofile()方法出错!"); System.out.println("错误:"+e.getMessage()); } } public static void dofile(String name) throws IOException{ File file=new File(name); //创建文件 FileWriter fileOut=new FileWriter(file); fileOut.write("Hello!world!"); //向文件中写入数据 fileOut.close(); //关闭输出流 fileOut.write("爱护地球!"); //运行出错,抛出异常 } }
(3)使用throw关键字抛出异常
throw用于方法内,并且抛出一个异常类的对象,而throws用在方法声明中,来指明方法可能抛出的多个异常。
通过throw抛出异常后,如果想由上一级代码捕获并处理,则同样需要使用throws关键字在方法的声明中指明要抛出的异常类。其他和throws抛出异常一致。
public class People { public static int check(String strage) throws Exception{ int age=Integer.parseInt(strage); //转换字符串为int型 if(age<0) //如果age小于0 throw new Exception("年龄不能为负数!"); //抛出一个Exception异常对象 return age; } public static void main(String[] args) { try{ int myage=check("-101"); //调用check()方法 System.out.println(myage); }catch(Exception e){ //捕获Exception异常 System.out.println("数据逻辑错误!"); System.out.println("原因:"+e.getMessage()); } } }
(4)自定义异常类
创建自定义异常,必须继承Exception类,同时包含两个方法
public class MyException extends Exception { //继承Exception类 private String content; public MyException(String content){ //构造方法 this.content=content; } public String getContent() { //获取描述信息 return this.content; } }
使用throw抛出自定义异常类对象
public class Example { public static void check(String str) throws MyException{ //指明要抛出的异常 char a[]=str.toCharArray(); //将字符串转换为字符数组 int i=a.length; for(int k=0;k<i-1;k++){ //检查字符数组中的每个元素 //如果当前元素是英文字母以外的字符 if(!((a[k]>=65&&a[k]<=90)||(a[k]>=97&&a[k]<=122))){ //抛出MyException异常类对象 throw new MyException("字符串\""+str+"\"中含有非法字符!"); } } } public static void main(String[] args) { String str1="HellWorld"; String str2="Hell!MR!"; try{ check(str1); //调用check()方法 check(str2); //执行该行代码时,抛出异常 }catch(MyException e){ //捕获MyException异常 System.out.println(e.getContent()); //输出异常描述信息 } } }
5.不需要通过“try...catch”、“throws”捕获或者抛出的不受查异常(Unchecked Exception)RuntimeException异常
(6)必须通过“try...catch”、“throws”捕获或者抛出,否则就会编译出错的受查异常(Checked Exception)