java学习笔记:java异常处理机制

java异常概述
在运行过程中,应用程序可能遇到各种严重程度不同的错误,比如访问不存在的文件、数组访问越界等。如果程序对可能发生的错误一点不做检查,那么程序将变得容易崩溃而且很难定位到问题出现的位置;而如果在执行下一语句之前,对每个方法调用都检查所有可能的错误,那么,程序就会变得难以理解。

java的异常处理机制很和谐的解决了这一矛盾命题。

==========================
java异常相关的5个关键字
try 捕获异常
catch 处理异常
finally 有没有异常都执行
throw 抛出一个异常对象
throws 声明一个异常可能被抛出

其中:
throw用来抛出异常:

throw expression;

而throws则用来声明方法可以抛出的异常,需要注意如下两点:

(1)方法能够抛出的检查型异常用throws子句声明,它后面可以是带用逗号隔开的一系列异常类型。

(2)throws子句的约定是严格强制性的,只能抛出throws子句声明的异常类型。抛出其他类型的异常是非法的,不管是直接利用throw,还是调用别的方法间接的抛出。如果方法没有throws子句,这意味着它不能抛出任何检查型异常,而不是说它它可以抛出任何类型的异常。

==========================
java异常常用的两个结构
java异常的捕获(Try/Catch/Finally)

try{
somethingDangerous();
}catch ( IOException e ){
System.out.println( “oh oh” );
throw new BadDataException();
}
finally{
file.close(); // always executed
}

java异常的上抛(Try/Catch/Throw)

public class Test extends StandardTest{
public static void main (String [] args){
try{
dangerMethod();
}
catch ( StrangeException e ){
System.out.println( “oops” + e.getMessage() );
}
} // end main

void dangerMethod() throws StrangeException{
if ( unexpected() ) throw new StrangeException ( “oh oh” );
} // end dangerMethod
} // end class Test

以上两个示例的引用地址:http://mindprod.com/jgloss/jcheat.html#TRY
==========================

一篇不错的java异常文章。

1:finally 区域内的代码块在 return 之前被执行
由于 Java 程序中,所有的对象都是在堆上( Heap )分配存储空间的,这些空间完全由垃圾回收机制来对它们进行管理。因此,从这一点可以分析得出一个推论: Java 中的异常处理模型的实现,其实要比 C++ 异常处理模型简单得多。例如,它首先不需要像 C++ 异常处理模型中那样,必须要跟踪栈上的每一个“对象”的构造和析构过程(只有跟踪并掌握了这些信息,发生异常时, C++ 系统它才会知道当前应该析构销毁哪些对象呀!),这是因为 Java 程序中,栈上是绝对没有“对象”的(实际只是对堆上对象的引用)。另外,还有 Java 语言中的异常对象的传递也更为简单和容易了,它只需传递一个引用指针而已,而完全不用考虑异常对象的构造、复制和销毁过程。

当然, Java 异常处理模型较 C++ 异常处理模型复杂的地方是,它引入了 finally 机制(主要用于数据库连接的关闭、 Socket 关闭、文件流的关闭等)。其实,我们也知道 finally 语法最早是在微软的 SEH 所设计出的一种机制,虽然它功能很强大,但是实现起来却并不是很难,从表象上来理解:当代码在执行过程中,遭遇到 return 和 goto 等类似的语句所引发作用域(代码执行流)转移时,便会产生一个局部展开( Local Unwinding );而由于异常而导致的 finally 块被执行的过程,往往被称为全局展开( Global Unwinding )。由于展开( Unwinding )而导致的 finally 块被执行的过程,非常类似于一个子函数(或子过程)被调用的过程。例如,当在 try 块中最后一条语句 return 被执行到的时候,一个展开操作便发生了,可以把展开操作想象成,是编译器在 return 语句之前插入了一些代码(这些代码完成对 finally 块的调用),因此可以得出结论: finally 区域内的代码块,肯定是在 return 之前被执行。

但是,请特别注意, finally 块区域中的代码虽然在 return 语句之前被执行,但是 finally 块区域中的代码是不能够通过重新赋值的方式来改变 return 语句的返回值。请看如下的示例代码:


public class FinallyTest {
public static void main(String[] args){
// 你认为 test 函数返回的值是多少呢?
System.out.println(“test 的返回值为: ” + test());
}

public static int test(){
int ret = 1;
try{
System.out.println(“in try block”);
return (ret);
}catch (Exception e){
System.out.println(“in catch block”);
e.printStackTrace();
}finally{
// 注意,这里重新改变了 ret 的值。
ret = 2;
System.out.println(“in finally block!”);
}
return ret;
}
}

输出如下:


in try block
in finally block!
test 的返回值为: 1

上面刚刚说了, finally 块区域中的代码不会轻易影响 try 区域中 return 语句的返回值,但是有一种情况例外,那就是在 finally 内部使用 return 语句。示例程序如下:

public class FinallyTest {
public static void main(String[] args){
// 你认为 test 函数返回的值是多少呢?
System.out.println(“test 的返回值为: ” + test());
}
@SuppressWarnings(“finally”)
public static int test(){
int ret = 1;
try{
System.out.println(“in try block”);
return (ret);
}catch (Exception e){
System.out.println(“in catch block”);
e.printStackTrace();
}finally{
// 注意,这里重新改变了 ret 的值。
ret = 2;
System.out.println(“in finally block!”);
return ret;
}
}
}

输出如下:

in try block
in finally block!
test 的返回值为: 2

也许大多数朋友都估计到,上面的 test 函数返回值是 2 。也即是说, finally 内部使用 return 语句后,它影响(覆盖了) try 区域中 return 语句的返回值。这真是一种特别糟糕的情况,虽然它表面上看起来不是那么严重,但是这种程序极易给它人造成误解(使得阅读该代码的人总得担心或考虑,是否有其它地方影响了这里的 return 的返回值)。

之所以出现这种现象的真正原因是,由于 finally 区域中的代码先于 return 语句( try 作用域中的)被执行,但是,如果此时在 finally 内部也有一个 return 语句,这将会导致该函数直接就返回了,而致使 try 作用域中的 return 语句再也得不到执行机会(实际就是无效代码,被覆盖了)。

面对上述情况,其实更合理的做法是,既不在 try block 内部中使用 return 语句,也不在 finally 内部使用 return 语句,而应该在 finally 语句之后使用 return 来表示函数的结束和返回,把上面的程序改造一下,代码如下 :


public class FinallyTest {
public static void main(String[] args){
// 你认为 test 函数返回的值是多少呢?
System.out.println(“test 的返回值为: ” + test());
}

@SuppressWarnings(“finally”)
public static int test(){
int ret = 1;
try{
System.out.println(“in try block”);
}catch (Exception e){
System.out.println(“in catch block”);
e.printStackTrace();
}finally{
// 注意,这里重新改变了 ret 的值。
ret = 2;
System.out.println(“in finally block!”);
}
return ret;
}
}

输出如下:


in try block
in finally block!
test 的返回值为: 2

Java提供了两类主要的 异常:runtime exception和checked exception。所有的checked exception是从java.lang.Exception类衍生出来的,而runtime exception则是从java.lang.RuntimeException或java.lang.Error类衍生出来的。

它们的不同之处表现在两方面:机制上和逻辑上。

一、机制上

它们在机制上的不同表现在两点:1.如何定义方法;2. 如何处理抛出的异常。请看下面CheckedException的定义:
以下是引用片段:

public class CheckedException extends Exception
{

public CheckedException() {}
public CheckedException( String message )
{
super( message );
}
}

以及一个使用exception的例子:
以下是引用片段:


public class ExceptionalClass
{
public void method1()
throws CheckedException
{
// … throw new CheckedException( “…出错了“ );
}
public void method2( String arg )
{
if( arg == null )
{
throw new NullPointerException( “method2的参数arg是null!” );
}
}
public void method3() throws CheckedException
{
method1();
}
}

你可能已经注意到了,两个方法method1()和method2()都会抛出exception,可是只有method1()做了声明。另外, method3()本身并不会抛出exception,可是它却声明会抛出CheckedException。在向你解释之前,让我们先来看看这个类的 main()方法:
以下是引用片段:


public static void main( String[] args )
{

ExceptionalClass example = new ExceptionalClass();
try
{
example.method1();
example.method3();
}
catch( CheckedException ex ) { } example.method2( null );
}

在main()方法中,如果要调用method1(),你必须把这个调用放在try/catch程序块当中,因为它会抛出Checked exception。

相比之下,当你调用method2()时,则不需要把它放在try/catch程序块当中,因为它会抛出的exception不是checked exception,而是runtime exception。会抛出runtime exception的方法在定义时不必声明它会抛出exception。

现在,让我们再来看看method3()。它调用了 method1()却没有把这个调用放在try/catch程序块当中。它是通过声明它会抛出method1()会抛出的exception来避免这样做 的。它没有捕获这个exception,而是把它传递下去。实际上main()方法也可以这样做,通过声明它会抛出Checked exception来避免使用try/catch程序块(当然我们反对这种做法)。

小结一下:

* Runtime exceptions:

在定义方法时不需要声明会抛出runtime exception;

在调用这个方法时不需要捕获这个runtime exception;

runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。

* Checked exceptions:

定义方法时必须声明所有可能会抛出的checked exception;

在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;

checked exception是从java.lang.Exception类衍生出来的。

二、逻辑上

从逻辑的角度来说,checked exceptions和runtime exception是有不同的使用目的的。checked exception用来指示一种调用方能够直接处理的异常情况。而runtime exception则用来指示一种调用方本身无法处理或恢复的程序错误。

==========================

┌─────────────────────────────────────────
java学习笔记目录
├─────────────────────────────────────────
java学习笔记:java基础知识
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:运算符及表达式
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:类和对象
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:接口(interface)
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:字符串处理
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:异常
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:线程
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:垃圾回收及存储管理
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:包
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:IO包
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:集合
├────────────────────────────────────────
├────────────────────────────────────────
java学习笔记:系统编程
├────────────────────────────────────────
│…待总结…
└─────────────────────────────────────────

此条目发表在java/j2ee分类目录,贴了, 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据