JavaSE基础:异常处理

异常处理

1.为何要处理异常?

在学习过程或者将来工做当中,咱们重来不但愿写的代码有错误,不会出现问题,用户操做永远逻辑清晰而正确,一切都按照咱们祈祷的那样运行,然而这是不可能的。必然会有错误必然会要咱们去处理,可是错误的处理并非咱们代码的核心。html

Java 异常机制可使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提升程序健壮性。(这段话须要咱们经历了才能去理解,慢慢来)java

在有效使用异常的状况下,异常能清晰的回答 what, where, why 这 3 个问题:异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为何“会抛出。(熟练使用以后就行了)程序员

Java的异常机制依靠于try,catch,finally,throw,throws关键字,其中try块中一般放置可能引起异常的代码,catch后对应异常类型和响应的异常处理代码块,finally块在java异常机制中老是会被执行,一般用于回收try块中打开的物理资源,throw用于抛出一个实际的异常(异常的实例),throws主要在方法签名中使用,用于声明该方法可能会抛出的异常,方便或者提醒方法的使用者来捕获并处理异常。数组

2.概念

Java 异常是 Java 提供的用于处理程序中错误的一种机制。网络

所谓错误是指在程序运行的过程当中发生的一些异常事件(如:除 0 溢出,数组下标越界,所要读取的文件不存在等)。函数

设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会应为异常的发生而阻断或产生不可预见的结果。性能

  • Java 程序的执行过程当中如出现异常事件,能够生成一个异常类对象,该异常对象封装了异常事件的信息并提交给 Java 运行时系统,这个过程称为抛出(throw)异常。
  • 当 Java 运行时系统接受到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。

3.分类

请必定要记住这张图

###(1) Throwable(老祖宗)学习

有两个重要的子类:Exception 和 Error,两者都是 Java 异常处理的重要子类,各自都包含大量子类。spa

(2) Error(无能为力)

是程序没法处理的错误,一般发生于虚拟机自身,表示运行应用程序中较严重问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 再也不有继续执行操做所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)通常会选择线程终止。线程

不须要咱们进行捕获处理

(3) Exception(是非分明)

  • Checked异常(检查异常或者编译异常)

    必须处理的异常:Checked异常是Java特有的,在java设计哲学中,Checked异常被认为是能够被处理或者修复的异常,因此Java程序必须显式处理Checked异常,当咱们使用或者出现Checked异常类的时候,程序中要么显式try- catch捕获该异常并修复,要么显式声明抛出该异常,不然程序没法经过编译。(Checked异常某种程度上下降了开发生产率和代码执行率,在java领域是一个备受争论的问题,我我的坚持使用异常处理业务逻辑,那么点效率能够忽略)

    演示代码:会有错误报出来,咱们须要对程序代码进行处理

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) {
            InputStream in = new FileInputStream("C:/temp/ddd");
        }
    }
    复制代码

    处理方式一:try...catch

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) {
            try {
                InputStream in = new FileInputStream("C:/temp/ddd");
            } catch (FileNotFoundException e) {
                e.printStackTrace();//在控制台输出错误信息,给开发人员使用
                //异常出现后,须要如何处理的代码
            }
        }
    }
    
    复制代码

    处理方式二:throws

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) throws FileNotFoundException {//没有对异常进行处理,只是返回给调用者
            InputStream in = new FileInputStream("C:/temp/ddd");
        }
    }
    复制代码
  • Runtime异常(运行时异常)

    可处理可不处理的异常(RuntimeException):运行时异常的特色是 Java 编译器不会检查它,也就是说,当程序中可能出现这类异常,即便没有用 try-catch 语句捕获它,也没有用 throws 子句声明抛出它,也会编译经过。

    package com.shxt.demo01;
    
    public class Demo02 {
        public static void main(String[] args) {
           String[] array = {"1","0","abc"};
            try{
                int a = Integer.parseInt(array[0]);
                //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
                //int b = Integer.parseInt(array[2]);//NumberFormatException
                int b = Integer.parseInt(array[1]);//NumberFormatException
                int c = a/b;//当b是0的时候,ArithmeticException
                System.out.println("您输出的结果是"+c);
            }catch(IndexOutOfBoundsException ie){
                System.out.println("数组越界,输入的参数不够");
            }
            catch(NumberFormatException ne){
                System.out.println("数字格式异常:程序只能接收整数参数");
            }
            catch(ArithmeticException ae){
                System.out.println("算术法异常");
            }
            catch(Exception e){
                System.out.println("出现异常");
            }
    
        }
    }
    复制代码

    程序说明:

    程序中通常将Exception放在最后,先捕获小异常(子类异常),再捕获大异常。若是顺序颠倒,还会出现编译错误

4.捕获异常 try、catch 和 finally

(1) try...catch

语法结构:

try{
  // 须要被检测的代码
}catch(异常类 变量){
 // 处理方式
}catch(异常类 变量){
 // 处理方式
}
复制代码

语句说明:

  • try ... catch 是最多见的异常捕获语句,try就是对你代码不自信,O(∩_∩)O哈哈~,也就是业务代码,若是try块中出现问题或者异常,系统自动生成一个异常对象,该异常对象提交到Java运行环境,java运行环境收到异常对象后,会寻找可以处理该异常的catch块,若是找到,就将异常对象交给该catch块处理,若是没有找到,就终止程序运行。

  • catch块中如何处理异常:参考上面的运行时异常的示例理解下面的一段话

    一个try块以后可能存在多个catch块,java运行时与catch块()内的异常类进行比较,判断该异常是否 instanceof 该类,若是属于该类,就将该异常对象传给catch块内的异常形参,catch块后能够对该异常进行处理,获取相关异常的详细信息等。注意系统生成的异常实例对象是相对具体的子类异常对象,而进入一个catch块后就不会再进入下一个catch块,因此这也是咱们尽可能将小异常放在大异常前面的缘由。

在Throwable类的主要方法:输出出现异常提示信息

编号 方法名称 类型 描述
1 public String getMessage() 普通方法 返回详细描述字符串(经常使用)
2 public void printStackTrace() 普通方法 异常名称、异常信息、异常出现位置,程序员使用
3 public void printStackTrace(PrintStream s) 普通方法 跟踪栈信息输出到指定输出流
4 public StackTraceElement[] getStackTrace() 普通方法 返回跟踪栈信息,在J2EE的时候有可能用到

好好理解下面的一段话

采用别的替选数据或方案或者提示用户从新操做或者从新抛出异常,进行异常转译,从新包装,

交给上层调用者来对该异常进行处理,我开发过程当中常用这种方式处理一些业务逻辑的判断

捕获多个异常的JDK7后的新写法

package com.shxt.demo01;

public class Demo03 {
    public static void main(String[] args) {
       String[] array = {"1","0","abc"};
        try{
            int a = Integer.parseInt(array[0]);
            //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
            //int b = Integer.parseInt(array[2]);//NumberFormatException
            int b = Integer.parseInt(array[1]);//NumberFormatException
            int c = a/b;//当b是0的时候,ArithmeticException
            System.out.println("您输出的结果是"+c);
        }catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
            e.printStackTrace();
        }catch(Exception e){
            System.out.println("出现异常");
        }

    }
}
复制代码

(2) try...catch...finally

语法结构:

try{
  // 须要被检测的代码
}catch(异常类 变量){
 // 处理方式
}catch(异常类 变量){
 // 处理方式
}finally{
  //必定会执行
}
复制代码

或者

try{
  // 须要被检测的代码
}finally{
  //必定会执行
}
复制代码
  • try 块: 用于捕获异常。其后可接零个或多个 catch 块,若是没有 catch 块,则必须跟一个 finally 块。
  • catch 块: 用于处理 try 捕获到的异常。
  • finally 块: 不管是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回以前被执行。在如下 4 种特殊状况下,finally 块不会被执行:
    • 在 finally 语句块中发生了异常。
    • 在前面的代码中用了 System.exit() 退出程序。
    • 程序所在的线程死亡。
    • 关闭 CPU。
package com.shxt.demo01;

public class Demo04 {
    public static void main(String[] args) {
       String[] array = {"1","0","abc"};
        try{
            int a = Integer.parseInt(array[0]);
            //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
            //int b = Integer.parseInt(array[2]);//NumberFormatException
            int b = Integer.parseInt(array[1]);//NumberFormatException
            int c = a/b;//当b是0的时候,ArithmeticException
            System.out.println("您输出的结果是"+c);
        }catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
            e.printStackTrace();
        }catch(Exception e){
            System.out.println("出现异常");
        }finally{
            array[0] = "999";
            System.out.println("First element value: " +array[0]);
            System.out.println("The finally statement is executed");
        }

    }
}

复制代码

5.throws:声明抛出异常

package com.shxt.demo01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {//没有对异常进行处理,只是返回给调用者
        InputStream in = new FileInputStream("C:/temp/ddd");
    }
}
复制代码
  • throws声明抛出异常,在方法签名中使用,上面的Demo01就是其使用的例子。它能够声明抛出多个类,多个类之间用“,”隔开。

  • 咱们为何要声明抛出异常?

    • 当某个方法中程序的执行可能会出现异常,可是该方法并不知道如何处理异常
    • 咱们想把这个异常交给上层方法调用者来处理或者修复,那咱们给该方法加上关键字throws 异常,以声明该方法可能会出现的异常
    • 加了throws关键字以后,该方法咱们就无需再用try—catch来捕获异常了,由于这已经不是咱们这个方法须要操心的事情了
  • 使用throws声明异常的时候,涉及到子类对父类的方法重写时,子类声明的异常类型应该是父类方法声明的异常类型的子类或者相同类

  • (该段话能够忽略)若是throws 声明的是checked异常,根据checked异常的规定,咱们不能对该异常视而不见,由于咱们必须处理该异常,因此当拿到一个声明了可能会发生checked异常的方法时,在调用该方法时,要么放在try块中来显式捕捉该异常,要么放在另一个带throws声明异常的方法中。

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class Demo05 {
        public static void main(String[] args)throws Exception{
            test();//test 声明会产生checked 异常 所以main函数也须要声明异常
            // 或者在try - catch 中捕获该异常
        }
        public static void test() throws IOException {
            FileInputStream fis = new FileInputStream("a.text");
        }
    }
    复制代码
  • (记住这句话)推荐使用Runtime异常,将Checked异常转换为Runtime异常

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class Demo06 {
        public static void main(String[] args){
            //根据业务状况,看看是否要处理运行异常
            try {
                test();
            }catch (RuntimeException e){
                System.out.println(e.getMessage());
            }
    
        }
        public static void test() {
            try {
                FileInputStream fis = new FileInputStream("a.text");
            } catch (FileNotFoundException e) {
                e.printStackTrace();//这句话不是处理异常
                throw new RuntimeException("文件不存在");
            }
        }
    }
    复制代码

6.throw:抛出异常

java容许程序自行抛出异常,一般系统帮助咱们检查是否发生一些广泛定义的异常,可是有些异常可能不是广泛定义的,只是与咱们业务不符,因此咱们能够自行抛出异常,也能够自行抛出一些咱们自定义的异常,而抛出异常的行为与系统抛出异常的行为必定程度上是等价的,后续处理方式也是同样的,在这里咱们使用throw关键字。

throw语句能够单独使用,注意它抛出的是一个异常实例

  • 当咱们自行抛出的异常是checked异常的时候,该throw语句要么是在如上面例子中的try块中,显示捕获,要么是在一个已经用throws声明会出现异常的方法中

    package com.shxt.demo01;
    
    public class Demo07 {
        public static void main(String[] args) {
           int a = -1;
           try {
               if(a<0){
                   throw new Exception("数据小于零,能够单独使用");
               }
           }catch (Exception e){
               e.printStackTrace();
           }
        }
    }
    复制代码
  • 若是咱们抛出的是runtime异常,那么状况就很简单了,它无需在try块中,也不须要将对应的方法用throws声明,若是咱们想要处理,就捕获处理它,不论是在它自身方法体内,或者是对应方法者,也能够不去理会,固然咱们本身抛出的异常,一般状况下是要处理的,否则抛出去以后无论最后只能中断程序运行了,只不过抛出是runtime异常时,在编译时没有那么严格

    package com.shxt.demo01;
    
    public class Demo07 {
        public static void main(String[] args) {
           int a = -1;
    
           if(a<0){
               throw new RuntimeException("数据小于零,能够单独使用");
           }    
        }
    }
    复制代码

自定义异常类:系统会抛出一些广泛意义的异常,那么咱们也就不必再本身操心throw了,一般throw的是自定义的异常类。

  • 自定义异常类都应该继承Exception类或者Exception下的子类如RuntimeException异常(开发中大部分是基础RuntimeException)

  • 定义异常类的时候须要提供两个构造器,一个无参构造器,一个带一个字符串参数的构造器,这串字符串其实是getMessage() 时返回的异常对象的详细描述信息。

    package com.shxt.demo01;
    public class MyException extends RuntimeException {
        public MyException() {
            super();
        }
        public MyException(String message) {
            super(message);
        }
    }
    复制代码

    catch中throw(抛出)异常:有时候咱们在本方法中捕捉了异常,咱们只能处理异常的一部分,咱们还须要别的方法来处理或者咱们想把产生了异常的这个信息告诉调用者,这个时候咱们一般捕捉了异常后会在catch块中抛出咱们想抛出的异常 在企业级应用中,一般对异常处理分为两部分:应用后台打印或者经过日志记录异常发生时详细状况(异常跟踪栈)和向使用者传达某种提示。(这种思想我常用)

7.异常使用的注意事项

  • 异常捕获后不作任何处理,就是耍流氓,挖坑埋本身
  • 异常机制不要用来作流程或条件控制,由于处理效率较低(这句话不太赞同)
  • try-catch若不触发catch是不影响性能的,可是try块仍然不要滥用包裹大量代码
  • 方法出错该抛异常就抛异常,而不是返回一些错误码

下面的规则来源网络

http://www.importnew.com/27964.html

  • 在 Finally 清理资源或者使用 Try-With-Resource 特性
  • 优先明确异常
  • 记录指定的异常
  • 使用描述性消息抛出异常
  • 优先捕获最具体的异常
  • 不要捕获 Throwable 类
  • 不要忽略异常
相关文章
相关标签/搜索