[Java] Java中的异常

Java中的异常

  • 与Java其他对象一样,我们总是用new在堆上创建异常,这也伴随着存储空间的分配和构造器的调用。所有标准异常类都有两个构造器:一个默认构造器,一个接受字符串作为参数的构造器(以便记录相关异常信息)

1.异常概述(一般在代码中如何使用,仅仅是一个概述,后面会对每个过程进行详细说明)

  • 代码中,通过throws抛出一个new的异常对象。
throw new NullPointerException();
  • 在代码块中throws一个异常对象时,必须采取两种动作之一(对于runtimeException不必如此,后面会讲到如何处理异常):在此函数中捕获处理异常;将异常抛给上层函数(即调用此函数的函数)
//利用try-catch-finally块处理
    public static void main(String[] args) {
        try {
            throw new MyException();
        } catch (MyException e) {
            e.printStackTrace();
        }finally{
            //do something
        }
    }
//抛给上层函数
    public static void main(String[] args) throws MyException {
        throw new MyException();
    }

2.通过throw语句抛出一个异常

既然要抛出一个new的异常,那么就需要有异常类供我们new。这个异常类必须继承自Throwable类。

Throwable
|-------------Error
|-------------Exception
                    |-----------------RuntimeException
                    |-----------------其他Exception
  • new JDK中存在的类
  • new 用户自定义的异常类(通常都继承自Exception类)

创建类对象时的构造器

  • 无参构造器
  • 有参构造器(可以利用参数帮助记录异常信息)

3.对throw出的异常进行处理

  • Java中并不是对所有的异常都需要用户进行处理(有部分异常JVM会帮助我们进行处理,所以我们不惜在代码中显示的书写处理逻辑)
  • RuntimeException及其子类在throw后无需用户进行处理。其余类在throw后必须进行处理

如何处理throw出的类

  • 利用try-catch-finally块进行处理
    public static void main(String[] args) {
        try {
            throw new MyException();
        } catch (MyException e) {
            e.printStackTrace();
            /*
                Exception in thread "main" java.lang.NullPointerException: / by zero  //异常类名称和描述信息
	                at my_exception.Calculate.mod(Calculate.java:6)  //异常出现时的系统栈情况
                	at my_exception.Main.main(Main.java:7)
            */
        }finally{
            //do something
        }
    }
  • 抛给上层函数
    public static void main(String[] args) throws MyException {
        throw new MyException();
    }
    public static void main(String[] args) throws FileNotFoundException {
        File file=new File("hello");
        try {
            FileInputStream fileInputStream=new FileInputStream(file);
        } catch (FileNotFoundException e) {
            //do something
            throw e;    //捕获到后重新抛出
        }finally {

        }
    }

catch与finally分别有什么作用

要了解catch与finally块的作用必须先了解这两个代码块分别在什么情况下进行。

  • catch是捕获到响应异常的时候执行的代码块。通常情况下利用printStackTrack()打印异常信息。
  • finally是无论是否捕获到异常都会执行的代码块。这里主要是用来进行资源的释放。但是Java提供了垃圾回收机制,那么finally块还有必要吗?答案是肯定的。虽然JVM会帮助开发人员回收垃圾,但这里指的是内存中的垃圾,对于内存之外的资源也需要把他们恢复到他们的初始状态(例如已经打开的文件网络连接屏幕上画的图形,甚至可以是外部世界的某个开关)。

catch打印异常

  • catch代码块中调用异常的printStackTrace打印栈轨迹
  • getStackTrace可以获得栈轨迹元素组成的数组,利用StackTraceElement遍历可以得到每个栈轨迹元素,调用getMethodName可以打印栈名称
    public static void main(String[] args)  {
        File file=new File("hello");
        try {
            FileInputStream fileInputStream=new FileInputStream(file);
        } catch (FileNotFoundException e) {
            for (StackTraceElement s:e.getStackTrace())
                System.out.println(s.getMethodName());
        }finally {

        }
    }

finally回收资源是必须的吗

  • 通常情况下,finally是必须进行资源回收的。但是涉及到构造器时,答案或许就不一定了。
  • 构造器会把对象设置成安全的初始状态,但还会有别的动作,比如打开一个文件。这样的操作(如打开一个文件)只有在对象使用完毕且用户调用了特殊的清理方法之后才能得以清理。如果在构造器内部抛出异常,这些清理行为也需就不能正常工作。
  • 如果构造器在执行的过程中半途而废,也需对该对象的某些部分还没有被成功创建,而这些部分在finally语句中确被当做要清理的。这些清理行为或许也就不能正常工作了。
  • 所以对于要执行其他的动作的行为(如打开文件,网络连接,屏幕画图等等),需要格外注意资源何时可以被释放。下面举一个打开文件的例子。
    public static void main(String[] args)  {
        File file=new File("hello");
        try {
            FileInputStream fileInputStream=new FileInputStream(file);
            /*
                打开文件后要执行的操作继续包在try-catch-finally块中
            */
            try {
                //打开文件后要执行的操作
            }catch (Exception e){
                
            }finally {
                //资源的释放,文件的关闭在此处进行
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            //不要在此处关闭文件,因为执行此处时可能会是打开文件失败,这样就无需释放资源
        }
    }

4.什么情况下该进行异常的抛出?异常该如何使用

  • 异常是用来处理程序出错时的代码,试想一下,如果所有潜在的错误都需要开发者在编写程序时进行处理,那会是非常可怕的事情。
  • 比如对于函数,在进入函数体后首先需要检查传入参数是否为null(如果参数是复杂数据类型时),在进行数组访问时首先要检查是否越界…这听起来确实很吓人。幸运的是,这不必由开发者亲自来做,它属于Java的标准运行时检测的一部分,如果对null引用进行调用,Java会自动抛出NullPointerException,所以检查是否为null时多余的。
  • RuntimeException代表的是编程错误,这类错误无需利用代码进行检查:1)无法预料的错误,比如在你控制范围之外传来的null引用。2)作为程序员应该在代码中进行检查的错误(比如数据越界)

(关于异常什么情况下该使用,笔者还没有较深的理解,欢迎大家向笔者提建议。同时笔者也会在以后继续补充)

你可能感兴趣的:(old)