java中的异常处理

来源:互联网 发布:java回调机制 编辑:程序博客网 时间:2024/06/07 03:57

异常

一、什么是异常(Exception)

Java程序在编译或运行时发生的错误,叫做异常。

 

二、为什么进行异常处理

程序一旦发生异常,会中断指令正常的运行。为了保证程序在发生异常时,能够继续向下执行。所以,引入了异常处理机制。

 

三、异常的类型

JVM异常

Java标准异常类:JDK提供的异常类。

自定义异常

 

四、异常的层次结构

1、Throwable类:Throwable 类是Java 语言中所有错误或异常的超类。

• Error

用于定义不指望程序能从其恢复过来的灾难性故障。Java中,对这种错误,不进行处理。

• Exception类:指一些可以被捕获且可能恢复的异常情况。

编译时异常(checked)

在编译时,必须进行处理;如果不处理,则无法通过编译。

 除了RuntimeException类及其子类,其他类都是编译时异常类。

运行时异常(unchecked)

Java编译器允许程序不对其进行处理,直接由运行时系统来处理。

RuntimeException及其子类都是运行时异常类。

2、常见的编译时异常和运行时异常

– 常见的编译时异常:

• ClassNotFoundException

• FileNotFoundException

• IOException

• ParseException

• SQLException

• InterruptedException

– 常见的运行时异常:

• ArrayIndexOutOfBoundsException

• StringIndexOutOfBoundsException

• NullPointerException

• ClassCastException

• ArithmeticException

• NumberFormatException

常见的编译时异常

package com.jcxy.demo10;public class Exception{    public static void main(String[] args)    {        //1- ArrayIndexOutOfBoundsException        int[] arrs = {1, 2, 3};        System.out.println(arrs[3]);        //2- StringIndexOutOfBoundsException        String s = "abc";        System.out.println(s.charAt(3));        //3-NullPointerException    引用类型声明的变量值为null的时候,如果调用该类型的属性或方法,抛出该异常。        String str = "abc";        str = null;        str.toString();        //4-ClassCastException  父子类转换的时候出现。        Object obj = new Object();        String str2 = (String)obj;        //rt5-ArithmeticException        int c = 10 / 0;        //rt6-NumberFormatException        String sss = "10a";        int x = Integer.parseInt(sss);    }}
常见的运行时异常
 <span style="white-space:pre"></span>//1:  ClassNotFoundException        Class.forName("java.lang.String1");        //:  FileNotFoundException        new FileInputStream("d:/hao.txt");        //3:  IOException        FileInputStream fis = new FileInputStream("d:/hao.txt");        fis.read();        //4:  ParseException        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");        sdf.parse("1990");        //5:  SQLException        Connection con = DriverManager.getConnection("jack", "xxx", "yyy");        //6:  InterruptedException        Thread.sleep(100);

3、try…catch…finally关键字的组合情况

– try{}.catch{}..

– try{}.catch{}.finally{}….

– try{}.finally{}.


五、Java异常的处理机制(两种)

第一种: try...catch...finally

try
{
    //可能抛出异常的代码。一般某行语句抛出异常,相当于创建了一个异常类对象。
}
catch(异常类型  e)//一个try块后,可以有多个catch代码块。
{
  //一旦try代码块中抛出异常,就会自动创建一个相应类型的异常对象,此时,该异常对象如果是catch代码块中,参数的类型,或是其子类,则就被该catch代码块捕获。执行该catch代码块中异常的处理内容。
}
catch(异常类型  e)//如果有多个catch,则catch后的参数类型必须由小到大(子类在前,父类在后)
{}

finally     //finally代码块可有可无
{
//无论try代码块中有无异常,都会执行该代码块。一般用于收尾工作。比如IO对象的关闭,JDBC中对象的关闭操作。
}

看下面例子
package com.jcxy.demo10;public class CatchTest{    public static void main(String[] args)    {        try        {            System.out.println("我是try之前的语句");            //可能发生ArithmeticException异常            int a = 10 / 0;                                     //1            System.out.println("我是try之后的语句");            //注意当前面有异常被捕捉到的时候,后面的语句都不会被执行            int b = 11 / 0;            System.out.println("我是第二个异常后的语句");        }        //第一个捕捉到了,则执行catch方法体后,接着执行finally中语句        catch (ArithmeticException e)        {            System.out.println("分母为0了");                   //2        }        //第一个匹配不到,执行第二个        catch(NullPointerException e)        {            System.out.println("空指针异常");        }        //上面全匹配不到,则由父类来处理        //父类必须要放在最后,因为如果放在第一个则后面的根本执行不到,eclipse也会报错        catch(Exception e)        {            System.out.println("上面处理不了的我来处理了");        }        //无论有没有捕捉到异常都会执行的语句块。        finally        {            System.out.println("finally处理");        }        System.out.println("我是外面的语句");    }}
由于1出发生了异常,所以异常之间的语句会执行,异常后面的语句不会执行,接着跳到与之相匹配的异常catch处2,执行catch中的语句,接着执行finally中的语句,最后执行异常处理完的语句。

所以打印输出为:

我是try之前的语句
分母为0了
finally处理
我是外面的语句

当然除了上面这种情况,也可能发生其他中情况,下面进行总结出共三种。

try...catch...finally异常处理结构。运行顺序的几种可能。

1、try代码块中没有抛出异常。

此种情况,try块中的语句全部执行, catch不会执行,finally代码块语句执行,异常处理外的语句执行。

2、try代码块中抛出异常,且能被catch捕获

此种情况,try抛出异常行前的语句执行,捕获到异常的catch语句执行,finally语句执行,异常处理外的语句执行。

3、try代码块中抛出异常,catch代码块无法捕获

此种情况,try抛出异常前的语句执行,finally语句执行。程序结束。

第二种异常处理方式-   throws声明异常

– 如果一个方法中有异常,且该方法不进行异常捕获,则需要将该方法中的异常声明出去,让使用该方法的地方处理异常。

– throws声明异常的方式,比异常捕获(上一种)消极。所以,将异常捕获叫做积极的异常处理方式;把throws声明异常叫做消极的异常处理方式。

1、throws注意点:

•1.1用在方法的后面,表示声明异常;该方法有异常且交给使用该方法的地方处理。

•1.2一个方法可以throws多个异常;多个异常之间由逗号分隔,且从小到大顺序排列。

•1.3如果方法中可能产生的异常是编译时异常,则必须要throws声明出去;如果方法中可能产生的异常是运行时异常,则可以不必处理,也可以声明出去。

在方法中,如果有异常,经常使用throws声明异常。

2、throw关键字

•2.1用于手动抛出异常。若某方法中需要直接抛出某异常时,可使用throw语句实现。

•2.2首先要生成异常对象,且抛出的异常必须是Throwable或其子类的实例。

•2.3如果一个方法可能抛出多个必检异常,那么必须在方法的声明部分一一列出,多个异常间使用逗号进行分隔

package com.jcxy.demo10;import java.io.FileInputStream;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.text.ParseException;import java.text.SimpleDateFormat;public class ThrowsTest{    public static void main(String[] args) throws IOException, SQLException, InterruptedException, ClassNotFoundException,Exception    {        //一个方法可以throws多个异常,多个异常之间由逗号分隔,且从小到大顺序排列。      //********************************************//        //1:  ClassNotFoundException        Class.forName("java.lang.String");        //2:  FileNotFoundException        new FileInputStream("d:/a.java");        //3:  IOException        FileInputStream fis = new FileInputStream("d:/Devi.txt");        fis.read();        //4:  ParseException        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");        try        {            sdf.parse("1990");        }        catch (ParseException e)        {            //可以通过throw 关键字new 一个新的异常对象            throw new Exception("扔出来个新异常");        }        //5:  SQLException        Connection con = DriverManager.getConnection("wang", "xxx", "mmm");        //6:  InterruptedException        Thread.sleep(3000);    }}


六、自定义异常

– Java语言中允许用户定义自己的异常类,但自定义异常类必须是Throwable的直接子类或间接子类。实际使用中,自定义异常常继承ExceptionRuntimeException

• 如果一个自定义异常类为Exception子类,则该自定义异常类为编译时异常类;如果自定义异常类为RuntimeException的子类,则该自定义异常类为运行时异常类。

– 自定义的异常类,一般只要声明两个构造方法,一个是不用参数的,另一个以字符串为参数。作为构造方法参数的字符串应当反映异常的信息。

– 用户定义的异常同样要用try--catch捕获,但必须由用户自己抛出 throw new MyException()

自定义异常类

package com.jcxy.demo10;public class MyException extends RuntimeException{    public MyException()    {        super();        // TODO Auto-generated constructor stub    }    public MyException(String message)    {        super(message);        // TODO Auto-generated constructor stub    }  }
测试类

package com.jcxy.demo10;public class Test{    public static void main(String[] args)    {        //因为是运行时异常,所以并不用先声明异常,在执行的时候会发生。        int age = 12;                Judge(age);    }        public static void Judge(int age)    {        if(age < 16)        {            throw new MyException("年龄太小啦");        }    }}

测试结果:

Exception in thread "main" com.jcxy.demo10.MyException: 年龄太小啦
at com.jcxy.demo10.Test.Judge(Test.java:16)
at com.jcxy.demo10.Test.main(Test.java:9)
直接输出的是运行时异常,打印出我们传入的构造函数的信息。


如果我们自定义异常类继承的是非运行时异常类

package com.jcxy.demo10;public class MyException extends Exception{    public MyException()    {        super();        // TODO Auto-generated constructor stub    }    public MyException(String message)    {        super(message);        // TODO Auto-generated constructor stub    }  }

测试类

package com.jcxy.demo10;public class Test{    public static void main(String[] args)    {        //编译时异常必要要声明或者捕捉。        int age = 12;                try        {            Judge(age);        }        catch (MyException e)        {//捕捉到则执行catch代码块,否则则执行我们自定义异常的信息。            System.out.println("年龄大于16啦");        }    }        public static void Judge(int age) throws MyException    {        if(age <= 16)        {            throw new MyException("年龄太小啦");        }    }}



使用异常的注意点:

– 1、对于运行时异常,如果不能预测它何时发生,程序可以不做处理,而是让Java虚拟机去处理它;如果可以预知它可能发生的地点和时间,则应该在程序中进行处理,而不应简单的把它交给运行时系统。

– 2、在自定义异常类时,如果它所对应的异常事件通常总是在运行时产生的,而且不容易预测它将在何时、何处发生,则可以把它定义为运行时异常,否则应定义为非运行时异常。

– 3、方法可以不对异常进行捕获而直接将其抛出,并在方法声明中说明,最好将它们留给方法的调用者进行处理,这样会增加程序的灵活性

– 4、异常对象的实例化和其后续处理工作是非常消耗资源的,过度的使用异常会明显影响程序的执行速度。所以,在使用异常处理时应该仔细考虑,只对有必要的异常情况使用异常,而不可以将异常泛化。


学完异常,可以来看看final, finally, finalize的区别?

– final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重写。

– finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)简单来说就是finally后的代码块无论如何都会被执行除非程序退出(System.exit(0))。

– finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。



最后看看return在异常中的运用

package com.jcxy.demo10;public class Test1{    public static void main(String[] args)    {        System.out.println(getNum(0));    }        public static int getNum(int a)    {        int i;        try        {            i = 10 / a;            return 1;        }        catch (Exception e)        {            System.out.println("catch");            return 2;        }        finally        {            System.out.println("finaly...");            return 3;        }    }}
打印结果是无论传入的参数是几,运行的方式无论是三种方法中的哪一种,最后getNum()返回的值都是3.因为无论执行前面哪个return,最后finally中的语句都会执行,当执行前面的return时,数据会先暂时缓存,当后面还有return的时候会替换掉原先的值,所以,最后执行的return会返回,打印结果是finally中结果。




1 0