1 异常
1.1 异常处理的作用
在编程时,如果出现文件打开失败,读写文件就会异常退出。如果出现内存溢出错误,程序也会异常退出。如果不能对这些异常进行处理。程序则无法正常运行。所以java提供了一种异常处理机制,能够捕获这些异常,并能够进行处理。避免程序异常退出,保持程序的健壮性。异常处理还可以将主逻辑代码和异常处理代码分开,提高了程序的可阅读性,使得更加清晰分明。
1.2 异常的声明thows
在定义一个方法时,方法内部可能出现什么样的异常需要在方法定义时进行声明。使用thows关键字声明。多个用逗号隔开。声明之后,调用该方法时,一定要实现处理该异常的代码,否则程序会报错。
public Scanner(File source) throws FileNotFoundException, IOException;
1.3 异常处理try{} catch{} finally{}
Try内部存放主要的逻辑代码,catch用于分类处理各种异常,如果没有匹配的catch异常类型,异常就会在调用栈中传给该方法的调用者,当前的方法会执行finally块并且退出调用栈。无论异常是否发生,finally代码块都会执行,除非JVM遇到了严重的错误或者在catch块中执行了System.exit()方法。finally代码块用来清理资源。
try
{
// Main logic here
Scanner in = new Scanner(new File("test.in"));
}
catch (FileNotFoundException ex)
{ // Exception handlers below
// Exception handler for "file not found"
} catch (IOException ex)
{
// Exception handler for "IO errors"
} finally
{
close file; // always try to close the file
}
1.4 异常处理传递
1.4.1 方法调用堆栈
在方法A中调用了方法B,异常处理的传递顺序也是按照方法调用堆栈的顺序去传递的。
public class MethodCallStackDemo {
public static void main(String[] args) {
System.out.println("Enter main()");
methodA();
System.out.println("Exit main()");
public static void methodA(){
System.out.println("Enter methodA()");
methodB();
System.out.println("Exit methodA()");
}
public static void methodB(){
System.out.println("Enter methodB()");
System.out.println("Exit methodB()");
}
}
调用方法有一个方法调用方法堆栈,main方法先入栈执行,然后是方法A入栈执行,然后是方法B入栈执行,方法B执行结束后出栈,方法A执行结束出栈,方法main执行结束出栈。调用方法堆栈符合先进后出的原则。所以调用正常打印结果是
Enter main()
Enter methodA()
Enter methodB()
Exit methodB()
Exit methodA()
Exit main()
如果在调用方法B的时候出现异常,异常之后的主逻辑代码就不会被执行,所以不会打印Exit。异常发生后,方法B、方法A和方法main就会立刻出栈,不会正常执行,没有打印Exit。打印结果如下
Enter main()
Enter methodA()
Enter methodB()
1.4.2 异常传递实现
在方法A中调用了方法B,如果希望在调用方法B时产生的异常能够传递给方法A,也就是说在调用方法A的地方去处理方法B中可能会出现的异常。这样就需要将异常传递到上一级。但是需要传递的方法都声明了异常才可以。如果没有声明,jvm找不到异常处理者,程序还是会退出。
public class MethodCallStackDemo {
public static void main(String[] args) {
System.out.println("Enter main()");
try
{
methodA();
} catch(IOException ioe)
{
}
}
System.out.println("Exit main()");
public static void methodA() throws IOException {
System.out.println("Enter methodA()");
methodB();
System.out.println("Exit methodA()");
}
public static void methodB() throws IOException {
System.out.println("Enter methodB()");
System.out.println("Exit methodB()");
}
}
当方法B中出现一个异常,这个方法会创建一个Exception对象,并且传递这个Exception对象到JVM,Exception对象包含了异常的类型以及异常发生时程序的状态,JVM的责任就是寻找处理这个Exception对象的异常处理者,它在调用栈中不断的向后搜索直到找到一个匹配的异常处理者来处理这个Exception对象,methodB()、methodA()被遍历到了,只是它们没有对异常处理,所以它们的方法会声明一个throws XxxException。main有一个XxxException处理者,并且会将这个异常对象传递给这个异常处理者者。如果JVM在方法栈的所有方法中没有找到一个匹配的异常处理者,它就会终止程序。
总之,如果出现异常,就需要从内向外搜索异常处理者, 找到后进行处理。如果没有找到,或者没有声明异常而导致异常传递终止,就会退出程序。
1.5 抛出异常
当一个Java操作遇到一个异常,出现错误的语句可以自行创建一个指定的Exception对象并且通过throw XxxException语句将它抛给JVM。
例如:
public void methodD() throws XxxException, YyyException { // method's signature
// XxxException occurs
if ( ... )
throw new XxxException(...); // construct an XxxException object and throw to JVM
1.6 常用异常类
(1)ArrayIndexOutOfBoundsException:如果访问的数组索引超出了数组的限制,就会被JVM抛出。
int[] anArray = new int[3];
System.out.println(anArray[3]);
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
(2)NullPointerException:当代码尝试使用一个对象的空引用的时候,就会被JVM抛出。
String[] strs = new String[3];
System.out.println(strs[0].length());
Exception in thread "main" java.lang.NullPointerException
(3)NumberFormatException: 当尝试将一个字符串转换为一个数字类型,但是字符串没有合适的转换方法。
Integer.parseInt("abc");
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
(4)ClassCastException: 当对象类型转换失败的时候,就会被JVM抛出。
Object o = new Object();
Integer i = (Integer)o;
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
(5)IllegalArgumentException:当一个方法接受到一个非法或者不合适的参数的时候就会抛出,我们可以在自己的代码中重用这个异常。
(6)IllegalStateException: 最典型的例子就是当可能某个方法只允许调用一次,但是被尝试多次调用,也就是当某个方法被调用,但是当前程序对于这个任务的执行并不在一个合适的状态下,就会抛出这个异常。
(7)NoClassDefFoundError: 当某个定义的类没有被找到,就会被抛出。在JDK 1.7之前,如果你尝试访问一个不存在的类,就会看到看到这个异常调用栈信息,在JDK 1.7之后,就只会打印简要的错误信息:"Error: Could not find or load main class xxx"。
1.7 自定义异常类
可以通过扩展Exception或者它的子类来创建自己的异常类。
public class MyMagicException extends Exception {
public MyMagicException(String message) { //constructor
super(message);
}
}
在异常条件满足时,通过thow抛出异常。
public class MyMagicExceptionTest {
// This method "throw MyMagicException" in its body.
// MyMagicException is checked and need to be declared in the method's signature
public static void magic(int number) throws MyMagicException {
if (number == 8) {
throw (new MyMagicException("you hit the magic number"));
}
System.out.println("hello"); // skip if exception triggered
}