一、认识异常
1.1概念
程序中出现的错误被称为异常(Exception)。
1.2异常分类
JDK中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。
异常分为Error和Exception。
Error:
Error类层次描述了Java运行时系统内部错误或资源耗尽错误。这类错误时我们无法控制的,同时也是非常罕见的错误。所以在编译中,不去处理这类错误。Error表名系统JVM以及处于不可恢复的崩溃状态中。我们不需要管他。打开JDK的:java.lang.error,查看他的所有子类。
Exception:
所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
Runtime Exception,出现这个异常时,就一定是你的问题,可以不捕获,因为小心点这些异常时可以避免的。派生于RuntimeException的异常,是一类特殊的异常,如被0除、数组下表超范围等,其产生比较繁琐,处理麻烦,如果显示的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序。这类异常通常是由编程错误导致的,因为只有小心点,这些异常都是可以避免的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,所有这类异常都继承自java.lang.RuntimeException。
注意:RuntimeException这个名字很容易让人产生错误影响。事实上,我们这里讨论的所有Error、Exception都是运行时发生的。
二、常见异常
2.1 ArithmeticException
除数为0的异常 public static void main(String[] args) { int b = 0; int i = 0; i = b / i ;//报错行 } |
Exception in thread "main" java.lang.ArithmeticException: / by zero at com.sxt.Test0823.A.main(A.java:7) |
2.2 NullPointerException
当程序访问一个空对象的成员变量或方法,访问一个空数组的成员时发生。怎么处理? public static void main(String[] args) { A a = null; a.getClass();//报错行 } |
Exception in thread "main" java.lang.NullPointerException at com.sxt.Test0823.A.main(A.java:6) |
2.3 ClassCastException
强制转换时类型错误,不能强转时报错,比如以下把猫转成狗 public class Animal { public static void main(String[] args) { Animal d1 = new Dog(); Animal c2 = new Cat(); Dog d = (Dog)d1; Dog c = (Dog)c2;//报错行 } } class Dog extends Animal } class Cat extends Animal{ } |
Exception in thread "main" java.lang.ClassCastException: com.sxt.Test0823.Cat cannot be cast to com.sxt.Test0823.Dog at com.sxt.Test0823.Animal.main(Animal.java:8) |
2.4 ArrayIndexOutOfBoundsException
访问的元素下标超过数组的长度-下标越界 int[] a = {1,2,3}; System.out.println(a[4]);//报错行 |
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4 at com.sxt.Test0823.Animal.main(Animal.java:6)
|
2.5 NumberFormatException
数字格式异常 String str = "1234abcf"; Integer i = new Integer(str); |
Exception in thread "main" java.lang.NumberFormatException: For input string: "1234abcf" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:456) at java.lang.Integer.<init>(Integer.java:620) at com.sxt.Test0823.Animal.main(Animal.java:6) |
所有不是Runtime Exception的异常,统称为Checked Exception,又被称为“已检查异常”。这类异常的产生不是程序本身的问题,通常由外界因素造成的。为了预防这些异常产生时,造成程序的中断或得到不正确的结果,Java要求编写可能产生这类异常的程序代码时,一定要去做异常的处理。
三、异常的处理办法一:捕获异常
3.1 try
try语句指定了一段代码,该段代码就是一次捕获处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该段中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做对应的处理。一个try语句必须有至少一个catxh语句或一个finally语句块。
注:当异常处理的代码执行结束以后,是不会回到try语句去执行尚未执行的代码。
3.2 catch
每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
常用方法:toString()方法,显示异常的类名和产生异常的原因。getMessage()方法,只显示产生异常的原因,但不显示类名。printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容。这些方法均继承自Throwable类。
catch捕获异常时的补货顺序:如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面,再补然就直接把多余的catch省略掉。
3.3 finally
有些语句,不管是否发生了异常,都必须要执行,那么久可以把这样的语句放到finally语句块中。通常finally中关闭程序块已打开的资源。比如文件流、释放数据库链接等。
3.4经典代码示例
public static void main(String[] args) {
FileReader reader = null;
try{
reader = new FileReader("d:/a.txt");
char temp = (char) reader.read();
System.out.println("读出的内容:" + temp);
}catch(FileNotFoundException e) {
System.out.println("文件没有找到!!");
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
System.out.println("文件读取错误!");
}finally {
System.out.println(" 不管有没有异常,我肯定会被执行!");
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、异常的处理办法二:声明异常throws
当Checked Exception产生时,不一定立刻处理它,可以再把异常Throws出去。
在方法中使用try-catch-finally由这个方法处理异常。在一些情况下,当前方法并不需要处理发生的异常,而是向上传递给调用它的方法处理。如果一个方法中可能产生某种异常,但是并不能确定如何处理这种异常,则应根据异常规范在方法的首部声明该方法可能抛出的异常。如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。
经典代码:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) {
try {
readFile("d:/a.txt");
} catch (FileNotFoundException e) {
System.out.println("所需要的文件不存在!");
e.printStackTrace();
} catch (IOException e) {
System.out.println("文件读写出错误!");
e.printStackTrace();
}
}
public static void readFile(String fileName) throws IOException {
FileReader in = new FileReader(fileName);
try {
int tem = 0;
tem = in.read();
while (tem != -1) {
System.out.println((char) tem);
tem = in.read();
}
} finally {
in.close();
}
}
}
方法重写中声明异常原则:子类声明的异常范围不能超过父类声明的范围。包含如下意思:父类没有声明异常,子类也不能。不可抛出原有方法抛出异常类的父类或上层类。抛出的异常类型的数目不可以比原有的方法抛出的还多(不是指个数)。
五、异常的处理办法三:手动抛出异常throw
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要手工创建并抛出。在捕获一个异常前,必须有一段代码先生成异常对象并把它抛出。这个过程我们可以手工做,也可以由JRE来实现,但是他们调用的都是throw子句。
对于一个已经存在的异常类,抛出该类异常对象过程如下:找到一个合适的异常类,创建一个该类的对象,将对象抛出。
public class FileTest {
public static void main(String[] args) {
File f = new File("c:/tt.txt");
if (!f.exists()) {
try {
throw new FileNotFoundException("File can't be found!");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
六、自定义异常
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类。从Exception类或者它的子类派生一个子类即可。
习惯上,定义的类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
package com.sxt.Test0823;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
class IllegalAgeException extends Exception {
public IllegalAgeException() {
}
public IllegalAgeException(String msg) {
super(msg);
}
}
class Person {
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public void setAge(int age){
if (age < 0)
throw new IllegalAgeException("人的年龄不应该为负数");
this.age = age;
}
public String toString(){
return "name is " + name + " and age is " + age;
}
}
public class MyExceptionTest{
public static void main(String[] args){
Person p = new Person();
try {
p.setName("Lincoln");
p.setAge(-1);
} catch (IllegalAgeException e) {
e.printStackTrace();
System.exit(-1);
}
System.out.println(p);
}
}