您的位置:首页 > 编程语言 > Java开发

Java异常详解

2015-08-06 16:00 676 查看

异常的介绍

Java中的异常就是那些会阻碍当前程序运行,使程序执行可能失败的一些可能情况,如程序中出现除零错误,数组下标越界等。异常在Java中被封装成了一个类,继承自
Throwable
,名为
Exception
,它有很多子类,分别描述了系统中很多常见的异常情况,这些异常机制的出现使得编写程序时对一些问题的处理变得尤为方便,下面是一些简单的使用情况。

异常捕获的一般格式

/**
* javac ExceptionDemo.javac
* java ExceptionDemo
* 输出:String index out of range: 3
*/
class ExceptionDemo {
public static void main(String[] args) {
try {
// 可能产生异常的代码放到try内
System.out.println("Hi".charAt(3));
} catch(Exception e) {
// 对异常的处理
System.out.println(e.getMessage());
}
}
}


当将
catch
中的
Exception
换成
StringIndexOutOfBoundsException
时,输出结果同样为
String index out of range: 3
,如下:这里
Exception
StringIndexOutOfBoundsException
的父类,子类的对象赋值给父类的类型,这在Java中称作多态。

/**
* 输出:String index out of range: 3
*/
class ExceptionDemo {
public static void main(String[] args) {
try {
// 可能产生异常的代码放到try内
System.out.println("Hi".charAt(3));
} catch(StringIndexOutOfBoundsException e) {
// 对异常的处理
System.out.println(e.getMessage());
}
}
}


异常的继承关系

Exception
的继承关系图可以清楚的知道
Exception
的父类及其子类的关系,超类是
Throwable
Error
Exception
的共同超类),
Throwable
的超类便是
Object
(除本身外Java中所有类的直接或者间接超类)。以
Exception
结尾的类都是继承自
Exception



try
内有多条语句时,可能会产生多种异常,下列代码虽然加上了异常捕获操作但是没有还是产生了异常,程序崩溃。

/**
* 输出:H
*       Exception in thread "main" java.lang.ArithmeticException: / by zero
*       at ExceptionDemo3.main(ExceptionDemo3.java:10)
*/
class ExceptionDemo3 {
public static void main(String[] args) {
try {
// 可能产生异常的代码放到try内
System.out.println("Hi".charAt(0));
System.out.println(123 / 0);
} catch(StringIndexOutOfBoundsException e) {
// 对异常的处理
System.out.println(e.getMessage());
}
}
}


原因是虽然有catch来捕获异常,但是仅仅捕获的是
StringIndexOutOfBoundsException
异常,这条异常只有
System.out.println("Hi".charAt(0));
才会产生。而
System.out.println(123 / 0);
这条语句会产生另一种异常,叫做
ArithmeticException
,即运算异常。而这条异常没有相应的捕获语句,所以虚拟机采用默认处理方式,即让程序崩溃。

加上
ArithmeticException
异常捕获后便可以正常处理异常,如下:

/**
* 输出:H
*       异常:/ by zero
*/
class ExceptionDemo3 {
public static void main(String[] args) {
try {
// 可能产生异常的代码放到try内
System.out.println("Hi".charAt(0));
System.out.println(123 / 0);
} catch(StringIndexOutOfBoundsException e) {
// 对异常的处理
System.out.println(e.getMessage());
} catch(ArithmeticException e) {
System.out.println("异常:" + e.getMessage());
}
}
}


难道try内有大量语句时,会产生很多异常的情况就要加很多个
catch
?当然你可以像第一个示例那样使用
Exception
来接收所有异常,但是这样又会有问题,那就是所有异常都会统一处理,那么就使用
Exception
和其他异常混合使用的情况,这种情况的时候要注意一点,
Exception
一定要放在最后面一条
catch
中,否则编译会报错。正确写法如下:

/**
* 输出:H
*       异常:/ by zero
*/
class ExceptionDemo3 {
public static void main(String[] args) {
try {
// 可能产生异常的代码放到try内
System.out.println("Hi".charAt(0));
System.out.println(123 / 0);
} catch(StringIndexOutOfBoundsException e) {
// 对异常的处理
System.out.println(e.getMessage());
} catch(ArithmeticException e) {
System.out.println("异常:" + e.getMessage());
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}


finally
关键字

Java异常捕获中的另一个关键字
finally
,同
catch
的用法类似,不过
finally
后没有类型,
finally
的功能是作为
try...catch...finally
中必然执行的一段。也就是说
try
内的代码产生或者没有产生异常,最终都会执行
finally
内的代码。这可以应用到一些网络操作中,如访问数据库时有打开数据库,当操作数据库时出现了错误,那么在
finally
中写上关闭数据库的操作便起到了很好的作用。避免系统打开很多数据库连接而无法关闭且又无法操作,这样会非常消耗系统资源。访问网络时也是同样的道理。一些
finally
的演示如下:

/**
* 输出:
* B
* C
*/
class ExceptionDemo4 {
public static void main(String[] args) {
try {
int num = 4 / 0; // 制作异常
System.out.println("A");
} catch (Exception e) {
System.out.println("B");
} finally {
System.out.println("C");
}
}
}

/**
* 输出:
* A
* B
* C
*/
class ExceptionDemo4 {
public static void main(String[] args) {
try {
System.out.println("A");
int num = 4 / 0; // 制作异常
} catch (Exception e) {
System.out.println("B");
} finally {
System.out.println("C");
}
}
}

/**
* 输出:
* 4
* 4
* 0
*/
class ExceptionDemo4 {
public static void main(String[] args) {
int num = 4;
try {
System.out.println(num);
int n = 10 / 0;  // 制造异常
num += 2;   // 异常发生后会立即进入异常处理部分
} catch (Exception e) {
System.out.println(num);
num = 0;
} finally {
System.out.println(num);
}
}
}


try
内的代码在执行时碰到了异常后便不再继续执行,而是跳到对应的异常处理代码段执行,然后再执行
finally
段的代码。

在带有返回值的函数中时,
finally
的执行如下:

/**
* 输出:
* try:4
* catch:4
* finally:5
* main:4
*/
class ExceptionDemo5 {
public static void main(String[] args) {
System.out.println("main:" + method());
}

public static int method() {
int num = 4;
try {
System.out.println("try:" + num);
int n = 10 / 0;  // 制造异常
} catch (Exception e) {
System.out.println("catch:" + num);
return num;
} finally {
num ++;
System.out.println("finally:" + num);
}
return 0;
}
}


finally
之前出现了
return
语句时,返回值的内容会被压栈,所以在
finally
中修改
num
的值是不会影响最终在
main
函数中接收到的返回值的内容的,这也体现了
finally
一定会执行的一点。

但是当遇到下面这种情况就要另当别论了:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 文件内容为:Hello
* 若将System.exit(0);注释掉,文件内容为Hi
*/
class ExceptionDemo5 {
public static void main(String[] args) {
try {
FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));
fout.write("Hello".getBytes());
fout.close();
System.exit(0); // 直接退出系统
} catch (IOException e) {

} finally {
try {
FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));
fout.write("Hi".getBytes());
fout.close();
} catch (IOException e) {

}
}
}
}


所以
finally
的内容必然执行也是要建立再程序还处于正在运行的状态,程序已退出虚拟机当然无法再执行
finally
的内容了。

try...finally
组合

除了
try...catch...finally
还有一种
try...finally
组合方式,即去掉catch段。若代码段抛出的是
RuntimeException
便将异常抛给上一层,若是非
RuntimeException
或其子类便会编译错误。(
ArithmeticException
属于
RuntimeException


/**
* 输出:
* method:finally
* main:/ by zero
*/
class ExceptionDemo6 {
public static void main(String[] args) {
try {
method();
} catch(Exception e) {
System.out.println("main:" + e.getMessage());
}
}

public static void method() {
try {
int num = 6 / 0;
} finally {
System.out.println("method:finally");
}
}
}


异常的抛出
throws

对于异常的处理可以使用
try...catch
,可以使用
try...catch...finally
,也可以使用
try..finally
,当然还有其他方式,你可以使用
throws
把异常抛给上一层,这里的上一层指的是如
main
函数调用
method
方法,对于
method
方法来说
main
函数就是上一层。

/**
* 输出:
* main:Hello
*/
class ExceptionDemo7 {
public static void main(String[] args) {
try {
method();
} catch(ClassNotFoundException e) {
System.out.println("main:" + e.getMessage());
}
}

public static void method() throws ClassNotFoundException {
Class<?> c = Class.forName("Hello");
}
}


多个异常的抛出时可以在
throws
后面使用
,
隔开,而抛出一条异常是使用
throw
来实现的,如下:

/**
* 输出:
* main:-Message-
*/
class ExceptionDemo7 {
public static void main(String[] args) {
try {
method();
} catch(ClassNotFoundException e) {
System.out.println("main:" + e.getMessage());
} catch(Exception e) {
System.out.println("main:" + e.getMessage());
}
}

public static void method() throws ClassNotFoundException, IllegalAccessException {
// Class<?> c = Class.forName("Hello");
throw(new IllegalAccessException("-Message-"));
}
}


自定义异常

自定义异常其实很简单,只需要继承
Exception
即可,或者继承
Exception
的子类,如下:

/**
* 输出:
* main:My Exception
*/

class MyException extends Exception {

public MyException(String msg) {
super(msg);
}
}
class ExceptionDemo7 {
public static void main(String[] args) {
try {
method();
} catch(MyException e) {
System.out.println("main:" + e.getMessage());
}
}

public static void method() throws MyException {
throw(new MyException("My Exception"));
}
}


RuntimeException
简介

当抛出
RuntimeException
或者
RuntimeException
的子类时无需使用
throws
在方法名后面标出,这一类异常可能无法通过捕获处理很好地处理,如除零错误发生时即使捕获到了异常,但是后面的运算结果是会受到影响的,一定会出现一个不正确的运算结果。如上面出现过的除零异常,无需在方法名后加异常类型说明:

/**
* 输出:
* method:finally
* main:/ by zero
*/
class ExceptionDemo6 {
public static void main(String[] args) {
try {
method();
} catch(Exception e) {
System.out.println("main:" + e.getMessage());
}
}

public static void method() /*这里不需要加 ArithmeticException*/{
try {
int num = 6 / 0;
} finally {
System.out.println("method:finally");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: