Java深入理解之异常

Java的基本理念是“结构不佳的代码不能运行”


为什么要使用异常?

首先我们可以明确一点就是异常的处理机制可以确保我们程序的健壮性,提高系统可用率

。异常不是程序语法错误,异常,就是在正常语法的代码运行过程中出现如 一楼所说的情况,如果不进行异常处理,那程序直接结束了,之所以捕获异常,是让你可以有发生错误补救的机会。

异常定义:异常情形是指阻止当前方法或者作用域继续执行的问题。在这里一定要明确一点:异常代码某种程度的错误,尽管Java有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的情况,可能会导致程序失败!

那么什么时候才会出现异常呢?只有在你当前的环境下程序无法正常运行下去,也就是说程序已经无法来正确解决问题了,这时它所就会从当前环境中跳出,并抛出异常。抛出异常后,它首先会做几件事。首先,它会使用new创建一个异常对象,然后在产生异常的位置终止程序,并且从当前环境中弹出对异常对象的引用,这时。异常处理机制就会接管程序,并开始寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态恢复,以使程序要么换一种方法执行,要么继续执行下去。

开发异常处理的初衷是为了方便程序员处理错误,异常处理的重要原则是:只有在你知道如何处理的情况下才捕获异常。重要目标是:把错误处理的代码通错误发生的地点相分离,使你能专注于要完成的事情,至于错误处理,在另一段代码中完成。

1、Java中抛出异常只有两种方式:throw显示抛出,自动抛出(运行时异常)。

  • 显示抛出异常:Java中的异常,一般需要通过throw关键字进行显示抛出,但是它不能单独使用,需要与try...catch/finally或throws联合使用。(try也不能单独使用,必须与catch或finally联合)
class SimpleException extends Exception{}//自定义异常类

 try
 {
  throw new SimpleException();
 }
 catch (Exception e)
 {
  e.printStackTrace();
 }

或者

public static void main(String[] args) throws SimpleException
 {throw new SimpleException();  }

这两种方法的不同点是:try(检测异常代码块)... catch(处理异常)  是程序(catch)处理过异常,程序正常运行,不退出。

而throws仅仅是异常说明,仅仅把方法可能会抛出的异常告知使用此方法的客户端程序员,它使得调用者能确切知道写什么样的代码可以捕获所有潜在的异常。Java提供相应的语法(并强制使用用这个语法),是你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。它仅仅是异常说明,说明可能会抛出的异常类型,并不抛出异常。希望调用者处理此异常。

方法里的代码产生异常却没有进行处理,编译器会发现这个问题并提醒你:要么处理(try catch)要么在异常说明中表明此方法将产生异常(throws)。

  • 自动抛出异常:

    但是运行时异常如{NullPointerException,ArrayIndexOutOfBoundsException}是不需要程序进行显示抛出,它是自动被Java虚拟机抛出。不必在异常说明中把它列出来。这种异常属于错误,将被自动捕获。

如: int[] a=new int[4];

System.out.println(a[4]);

虽然不用捕获异常,但还是可以在代码中抛出运行时异常。

如果程序中抛出了运行时异常,可以处理,可以不处理。

处理:

int[] a=new int[4];
  try{
  System.out.println(a[4]);//此语句抛出异常,不会执行此后的try语句
  System.out.print("hello"); //即不会执行
  }catch (Exception e) { //找到相应的catch语句执行
   System.out.print("越界");
  }
 System.out.print("hello");//处理异常后会执行,如果不处理异常,则不执行

如果处理则程序跳出try语句去执行相应的catch语句,并顺序执行之后的语句即hello会打印出来,如果不处理则直接跳出程序,不打印hello,并打印e.printStackTrace()方法如下:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4

at jin.feng2.Exception01.main(Exception01.java:18)

2、finally关键字(任何情况下都会执行)不论是return;break;continue;

对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行。着通常适用于内存与回收之外的情况。(内存回收由垃圾回收器自动回收)

当要把出内存之外的资源恢复到它们的初始状态时,就需要用到finally子句。如:已经打开的文件或网络连接,在屏幕上画图形,开关等等。

try
  {
   System.out.println("Point 1");
   if(i==1) return;
   System.out.println("Point 1");
   if(i==2) return;
  }catch (Exception e) {
   e.printStackTrace();
  }
  finally{
   System.out.println("00000");
  }
//main()
f(1);
  f(2);

//output

Point 1

00000

Point 1

Point 1

00000

返回之前会执行finally,即先执行finally再执行return.

finally关键字的误用导致异常丢失:

第一种是在异常处理之前调用finally,并在finially里抛出另一种异常。前面的异常则会丢失!

{
   try
 {
   throw new RuntimeException();
 }
 finally
 {
  System.out.println("e");
  return;
 }

//output

jin.feng2.SimpleException2

另一种异常丢失是异常还未处理,在finally中执行return

3、异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的异常。不能基于异常说明来重载方法,一个出现在基类方法的异常说明中的异常,不一定会出现在派生类中

{
   try
 {
   throw new RuntimeException();
 }
 finally
 {
  System.out.println("e");
  return;
 }

方法的异常说明里,这与继承的规则相反。在继承中,基类的方法必须出现在派生类里。

异常的限制:对于继承类,它如果所覆盖的方法有异常说明,则所列出的异常类,必须是基类该方法所列出的异常类的子集

class MyException1 extends Exception {
}
class A {
    public void proc(){
    }
}
class B extends A {
    // 编译错误:因为A.proc没有异常说明,所以子类也不能有异常说明
    // 解决的方法是为A.proc加上异常说明:throws MyException1
    // 或者在throw new MyException1();加上try块并去掉异常说明
    public void proc() throws MyException1 {
        throw new MyException1();
    }
}

class MyException1 extends Exception {
}
class MyException2 extends Exception {
}
class A {
    public void proc() throws MyException1 {
    }
}
class B extends A {
    // 错误:因为A.proc只声明了MyException1异常
    public void proc() throws MyException2 {
    }
}

构造器是一个例外,继承类可以声明更多的异常类,但必须加上基类所声明的异常类:

class MyException1 extends Exception {
}
class MyException2 extends Exception {
}
class A {
    A() throws MyException1 {

    }
    public void proc() throws MyException1 {
    }
}
class B extends A {
    B() throws MyException1, MyException2 {
    }
}

当一个类实现继承了一个抽象类和一个接口,这个抽象类和接口中有一个方法是一样的,但是异常说明不一样,这时要兼顾抽象类和接口的异常说明,这个方法的实现一定是这两个异常说明的交集。

//abstract class
public abstract void event() throws BaseballException;
//interface
public void event()throws rain;
//implement class
public void event(){}

必须是交集,所以为空

4、栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,元素0表示栈顶,即最后调用的方法。先调用,后弹出。

void printStackTrace();

//output

java.lang.RuntimeException

at jin.feng2.Exception01.main(Exception01.java:26)

public class Exception03
{
 static void f(){
  try
  {
   throw new Exception();
  }
  catch (Exception e)
  {
   for(StackTraceElement ste:e.getStackTrace())
   System.out.println(ste.getMethodName());
  }
 }
 static void g(){f();}
 static void h(){g();}
public static void main(String args[]){
 f();
 System.out.println("____________");
 g();
 System.out.println("____________");
 h();
}
}

//output

f

main

____________

f

g

main

____________

f

g

h

main

重新抛出异常:即在catch中又抛出了同一种异常。如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来的异常抛出点的调用栈,而不是重新抛出点的信息。如果想要跟新这个信息,可以调用fillInStackTrace()方法。即

throw e.fillInStackTrace

如果重新抛出另一种异常,则之前的异常栈都无法保存,但是希望把原始异常的信息保存下来——异常链

则需要使用initCause()方法。

如e.initCause(new NullPointerException);

throw e;

时间: 2024-10-25 05:58:16

Java深入理解之异常的相关文章

Java解惑四:异常之谜

谜题36 finally语句中的return语句会覆盖掉try语句中的. 谜题37 该部分还需要进一步理解 一个方法可以抛出的被检查异常集合是它所适用的所有类型声明要抛出的被检查集合的交集. Java解惑四:异常之谜,布布扣,bubuko.com

Java thread中对异常的处理策略

转载:http://shmilyaw-hotmail-com.iteye.com/blog/1881302 前言 想讨论这个话题有一段时间了.记得几年前的时候去面试,有人就问过我一个类似的问题.就是java thread中对于异常的处理情况.由于java thread本身牵涉到并发.锁等相关的问题已经够复杂了.再加上异常处理这些东西,使得它更加特殊. 概括起来,不外乎是三个主要的问题.1. 在java启动的线程里可以抛出异常吗? 2. 在启动的线程里可以捕捉异常吗? 3. 如果可以捕捉异常,对于

java的两种异常runtimeException和checkedException

java异常处理机制主要依赖于try,catch,finally,throw,throws五个关键字. try 关键字后紧跟一个花括号括起来的代码块,简称try块.同理:下面的也被称为相应的块. 它里面可置引发异常的代码.catch后对应异常类型和一个代码块,用于表明catch块用于处理这种类型的代码块.后还可以跟一个finally块,finally块用于回收在try块里打开的物理资源,异常机制会保证finally块总被执行.throws关键字主要在方法签名中使用,用于声明该方法可能抛出的异常,

JAVA个人理解

为了找到别人写的好文章,先分享下自己的知识,找找感觉路线. 学java前接触的c,后来转向java.第一个照面理解的就是面向对象,没想到让我想了好多年.当时有个负责任的老师说面向对象这个词具体释义众说纷纭,有自己的理解就好.于是java从开始就和高深神秘的"面向对象"挂钩. 里边的逻辑和其它编程逻辑差不多,表达同一些意思.至于java的核心,异常.继承.接口,没怎么用过,知道是用来做什么的,看上去还是让人觉得很麻烦,不是很懂. 很多语言也可以做到这些,只是在java里是不得不这样做,它

Java虚拟机理解总结

??Java虚拟机(Java Virtual Machine 简称JVM)是运行所有Java程序的虚拟计算机,Java虚拟机屏蔽了与具体操作系统的相关性,使得Java程序只需生成在Java虚拟机上运行的字节码,就可以在多种平台上运行.正是Java虚拟机使得Java程序能够做到"编译一次,到处运行",成就了Java.作为Java程序员,必须对Java虚拟机有学习理解,才能做好Java程序开发.当然这里的学习理解只是基本的虚拟机规范的理解,具体细节的理解掌握就太难了,对于Java开发来说也

Java.lang.NoSuchFieldError: INSTANCE异常

解决方案: java.lang.NoSuchFieldError: INSTANCE异常. 1.jar包重复了. 2.版本还不相同,如果包的版本不同也会报相应的错,不过一般情况自己导入的jar包主要看导入有没有共同范围下有重复的jar. 仅供参考,大神高论,评论下方. Java.lang.NoSuchFieldError: INSTANCE异常

在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法

今天遇到了一个在servlet的service方法中获取ServletContext对象出现java.lang.NullPointerException(空指针)异常,代码如下: 1 //获取ServletContext对象 2 ServletContext servletContext = this.getServletContext(); 这个问题很奇怪,也是第一次遇到,因为以前在servlet的doGet/doPost方法中要获取ServletContext对象时都是这样写的,也没有出现过

Java中出现的异常类型

Java中出现的异常类型     失踪的格式参数异常 java.util.MissingFormatArgumentException异常 错误提示信息: java.util.MissingFormatArgumentException:Format specifier 's' 原因:字符串格式化提供的值的数量少于字符串格式符(%s)的数量 参数:  format - 在格式字符串的语法中描述的格式字符串  args - 格式字符串中的格式说明符引用的参数.如果参数多于格式说明符,则忽略额外的参

java中常见的异常类

1. java.lang.nullpointerexception   这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等.对数组操作中出现空指针,很多情况下是一些刚开始学习编程的朋友常犯的错误,即把数组的初始化和数组元素的初始化混淆起来了.数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空