美文网首页bugstac...
java编程中遇到的异常以及异常的一些处理

java编程中遇到的异常以及异常的一些处理

作者: 84e94ada13e1 | 来源:发表于2018-07-05 16:56 被阅读86次

    异常的概念

    程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?

    比如除法运算、读写文件操作,都可能发生异常。(当除数为0时;文件路径不存在时)。该如何处理?

    C语言的风格:用函数返回值作为执行状态。比如返回一个为0的值表示文件不存在这个状态。缺点是代码比较散乱。

    而Java语言提供了一种优秀的解决办法:异常处理机制。java将处理异常的代码放到一个统一的 try-catch-finally结构中去处理,且代码易读。请看下面的例子

    Ø 示例一

    数学运算之除0异常

    import java.util.Scanner;

    public class AllDemo {

    public static void main(String[] args) {

    System.out.println("----欢迎使用命令行除法计算器----");

    Scanner scan = new Scanner(System.in);

    int num1 = scan.nextInt();

    int num2 = scan.nextInt();

    int result = a(num1,num2);

    System.out.println("result:" + result);

    scan.close();

    }

    private static int a(int num1, int num2) {

    return b(num1,num2);

    }

    private static int b(int num1, int num2) {

    return devide(num1,num2);

    }

    public static int devide(int num1, int num2) {

    return num1 / num2;

    }

    }

    执行时输入7和0,会看到控制台结果

    ----欢迎使用命令行除法计算器----

    7

    0

    Exception in thread "main" java.lang.ArithmeticException: / by zero

    at AllDemo.devide(AllDemo.java:24)

    at AllDemo.b(AllDemo.java:20)

    at AllDemo.a(AllDemo.java:16)

    at AllDemo.main(AllDemo.java:9)

    分析:当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,于是调用它的函数b也发生了异常,于是调用b的函数a也发生异常,于是调用a的函数main也发生了异常,这样一直向调用栈的栈底回溯,这叫做异常的冒泡。这个例子没有使用异常处理机制,异常最终由main函数抛给java虚拟机,导致程序终止。

    Ø 示例二

    读写文件异常

    import java.io.FileInputStream;

    import java.io.IOException;

    class ReadFile {

    public static void testException() throws IOException

    {

    //FileInputStream的构造函数会抛出FileNotFoundException

    FileInputStream fileIn = new FileInputStream("E:\\a.txt");

    int word;

    while((word = fileIn.read())!=-1) //read方法会抛出IOException

    {

    System.out.print((char)word);

    }

    fileIn.close(); //close方法会抛出IOException

    }

    public static void main(String arg[]) throws IOException{

    ReadFile.testException();

    }

    }

    说明:

    1、如果E盘下没有文件a.txt,发生FileNotFoundException。

    2、如果a.txt存在,但是被其它进程锁住,有可能发生IOException

    3、鉴于1、2,此处为了编译正确,所以只得在testException()加上“throws IOException”,调用它的main()也得加上“throws IOException”。(注:FileNotFoundException是的IOException的子类)

    n java异常处理的try-catch-finally结构

    我们可以使用异常处理结构改进上面读文件的例子

    import java.io.FileInputStream;

    import java.io.FileNotFoundException;

    import java.io.IOException;

    class ReadFile {

    public static void testException() {

    FileInputStream fileIn = null;

    try {

    // FileInputStream的构造函数会抛出FileNotFoundException

    fileIn = new FileInputStream("E:\\a.txt");

    } catch (FileNotFoundException e) {

    e.printStackTrace();

    System.out.println(e.getMessage());

    }

    int word;

    try {

    while ((word = fileIn.read()) != -1) // read方法可抛出IOException

    {

    System.out.print((char) word);

    }

    fileIn.close(); // close方法可抛出IOException

    } catch (IOException e) {

    e.printStackTrace();

    } finally {

    System.out.println("finally块无论如何都要执行");

    }

    }

    public static void main(String arg[]) {

    ReadFile.testException();

    }

    }

    说明

    1、如果E盘下没有文件a.txt,发生FileNotFoundException。进入catch结构,打印出一些信息。

    2、程序仍然往下能继续运行,在fileIn.read()时发生异常NullPointerException,这是因为第1步进了catch块,这样fileIn变量仍是最初的null值,一旦调函数便发生NullPointerException。

    3、问:我们针对NullPointerException为什么不需要写try-catch-finally结构?具体原因请阅读后面的RuntimeException内容

    4、这个例子中故意为了演示出NullPointerException,而把try-catch块写成了两个,实际上可以合并。

    n try-catch-finally结构说明

    1、一个try可以对应多个catch块。

    2、如果发生异常,异常被抛给第一个catch 块,如果异常的类型与 catch匹配,它在这里就会被捕获。如果不匹配,它会被传递给第二个 catch 块。如此,直到异常被捕获或者通过所有的 catch 块。

    3、finally块始终会被执行。

    4、如果try或catch块中存在return语句,那么catch、finally块中的语句也会被执行完了后,才真正return。除非遇到下面的几种情况(1)System.exit(n)可导致立即终止(2)finally块中发生异常(3)程序所在线程死亡(4)关闭CPU。

    练习

    class Snippet {

    public static String t() {

    String s = "1";

    try {

    s = "2";

    return s;

    //throw new Exception("some");

    } catch (Exception e) {

    s = "3";

    System.out.println("in catch block");

    return s;

    }finally {

    s = "4";

    System.out.println("in finally block");

    return s;

    }

    }

    public static void main(String[] args) {

    String s= t();

    System.out.println(s);

    }

    }

    //无论如何,都要进到finally块执行,如果finally{}里有return 那么返回的肯定是finally里的

    Java异常类图

    从上面的示例代码可以看出,java把异常也看成是对象。进而设计了下面所示的异常方面的类结构体系。最上层类叫做Throwable。可以把这些分成两大种类

    Ø 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求程序员必须处理这些异常。在运行阶段,倘若发生Error则虚拟机几乎崩溃,倘若发生RuntimeException若程序员没处理它则一直回溯向上抛给java虚拟机处理。当然,如果程序员愿意的话,也可以编写代码处理(使用try…catch…finally)这样的异常(但是通常情况下不会这样做。需要这样做的情况是比如搞数学运算的这个专业领域要处理ArithmeticException)。

    对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。这种异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

    Ø 检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

    常见异常类型说明

    异常类型说明

    Exception 异常层次结构的父类

    ArithmeticException算术错误情形,如以零作除数

    ArrayIndexOutOfBoundsException数组下标越界

    NullPointerException尝试访问 null 对象成员

    ClassNotFoundException不能加载所需的类

    IllegalArgumentException方法接收到非法参数

    ClassCastException对象强制类型转换出错

    NumberFormatException数字格式转换异常,如把"abc"转换成数字

    n throws关键字

    声明本方法不处理异常,让调用者处理。

    在函数签名中使用throws 声明交给函数调用者caller去解决。

    import java.util.Scanner;

    public class HelloWorld {

    public static void main(String[] args) {

    try {

    divide();//调用的此方法抛出了异常

    }

    catch (Exception e) {

    System.out.println("错误:被除数和除数必须是整数,且除数不能为零。");

    e.printStackTrace();

    e.getMessage();//暂时打印不出任何内容

    }

    finally{

    System.out.println("感谢使用本程序");

    }

    }

    public static void divide() throws Exception{//声明异常

    Scanner scanner = new Scanner(System.in);

    System.out.println("请输入被除数");

    int num1 = scanner.nextInt();//有可能异常InputMismatchException

    System.out.println("请输入除数");

    int num2 = scanner.nextInt();//有可能异常InputMismatchException

    System.out.println(num1 / num2);//有可能异常除数等于0:ArithmeticException

    }

    }

    throw关键字

    主动抛出异常

    如果对于具体的一些处理逻辑,程序员也可以主动的抛出异常让 外层处理。(此异常可能是程序员自己定义的)。

    class Person {

    private String name;

    private String sex = "男";

    public void setSex(String sex) throws Exception{

    if("男".equals(sex) || "女".equals(sex)){

    this.sex = sex;

    }

    else{

    //主动抛出异常。也可做成自定义异常并抛出

    throw new Exception("性别输入错误,必须是男或女");

    }

    }

    public void print() {

    System.out.println(this.name + this.sex);

    }

    }

    class Test {

    public static void main(String[] args) {

    Person p = new Person();

    try {

    p.setSex("male");

    p.print();

    } catch (Exception e) {

    e.printStackTrace();

    }

    }

    }

    异常类的两个打印异常信息的好办法

    一般,我们需要打印出异常的相关信息。在Exception类中,定义了下面两个方法,

    e.printStackTrace();//打印调用堆栈信息 并把e.getMessage()的信息也打出来了

    e.getMessage();//打印异常的相关信息

    与IOException类相似,我们自己定义的异常类往往也是Exception的子类,我们可以(1)覆盖e.getMessage()方法; 也可以(2)构造的时候传入具体的字符串信息,因为e.getMessage()就是获取这个信息

    示例

    public class Test {

    public static void main(String[] args) {

    try {

    Exception e = new Exception("哈哈,我是异常");

    throw e;

    } catch (Exception e) {

    e.printStackTrace();

    System.out.println("message="+ e.getMessage());

    }

    }

    }

    建议

    (1)多重catch块:Catch块的排列顺序必须是从子类到父类。最后一个一般是Exception。

    (2)不要在fianlly中使用return。

    不要在finally中抛出异常。

    减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。

    将尽量将所有的return写在函数的最后面,而不是try … catch … finally中。

    练习

    import java.util.InputMismatchException;

    import java.util.Scanner;

    public class HelloWorld {

    public static void main(String[] args) {

    Scanner scanner = new Scanner(System.in);

    int num1=1 , num2=1;

    try {

    System.out.println("请输入被除数");

    num1 = scanner.nextInt();//有可能异常InputMismatchException

    System.out.println("请输入除数");

    num2 = scanner.nextInt();//有可能异常InputMismatchException

    System.out.println(String.format("%d / %d = %d", num1, num2, num1 / num2));//有可能异常除数等于0:ArithmeticException

    }

    catch(InputMismatchException e)

    {

    System.err.println("被除数和除数必须是整数");

    return;

    }

    catch(ArithmeticException e)

    {

    System.err.println("除数不能为零");

    //return;

    System.exit(0);//这个是立即终止

    }

    catch (Exception e) {

    System.out.println("错误:被除数和除数必须是整数,且除数不能为零。");

    e.printStackTrace();

    e.getMessage();//暂时打印不出任何内容

    }

    finally{

    System.out.println("感谢使用本程序");

    }

    }

    }

    相关文章

      网友评论

        本文标题:java编程中遇到的异常以及异常的一些处理

        本文链接:https://www.haomeiwen.com/subject/tjwpuftx.html