当前位置:首页 > 经验

程序异常退出是什么原因 0x40000015装系统出现c报错

茫茫人海千千万万,感谢这一秒你看到这里。希望我的文章对你的有所帮助!

愿你在未来的日子,保持热爱,奔赴山海!

Java 异常处理

相信各位都会在使用Java中或多或少的出现一些异常bug,那这些异常是从何而来的呢?

异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。其实在Java中,异常是Java提供的一种识别及响应错误的一致性机制。从而可以达到程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。

1. 什么是异常?

异常,就是不正常的意思。而在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响。在程序中的意思就是:

  • 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
  • 异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
  • 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。
  • Java为异常设计了一套异常处理机制,当程序运行过程中发生一些异常情况时,程序不会返回任何值,而是抛出封装了错误信息的异常对象。这样保证程序代码更加优雅,并提高程序健壮性。为什么要设计异常呢?首先,引入异常之后,我们就可以把错误代码从正常代码中分离出来进行单独处理,这样使代码变得更加整洁;其次,当出现一些特殊情况时,我们还可以抛出一个检查异常,告知调用者让其处理。

2. 异常的体系

  • 我们先来看下异常体系结构:
  • 可以看出异常类的整体继承关系,当然上图不是所有的异常,也有很多子类没有列出,这里先列出了比较常用的异常类。当然,用户自己也可以自定义异常实现。

2.1 Throwable

所有的异常都是从Throwable继承而来的,是所有所有错误与异常的超类。Throwable包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace()等接口用于获取堆栈跟踪数据等信息。

  • 而Throwable体系下包含有两个子类,Error(错误)和Exception(异常),它们通常用于指示发生了异常情况。二者都是 Java 异常处理的重要子类,各自都包含大量子类。

2.2 Error(错误)

  • 定义:Error类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM出现的问题。
  • 特点:对于所有的编译时期的错误以及系统错误都是通过Error抛出的。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时。通常有如Virtual MachineError (虚拟机运行错误)等。当 JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError(内存不出错误),还有StackOverflowError(栈溢出错误)等。这些异常发生时,JVM一般会选择线程终止
  • 注意:这些错误是不受检异常,非代码性错误,不可查的。因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。因此,当此类错误发生时,应用程序不应该去处理此类错误。

2.3 Exception(异常)

Exception 是另外一个非常重要的异常子类。程序本身可以捕获并且可以处理的异常。这类异常一旦出现,我们就要对代码进行更正,修复程序。Exception这种异常又分为两类:运行时异常和编译时异常。

2.3.1 运行时异常

  • 定义:RuntimeException 类及其子类异常,如NullPointerException (空指针异常)、IndexOutOfBoundsException (下标越界异常)等,表示 JVM在运行期间可能出现的异常。
  • 特点:此类异常,Java 编译器不会检查它,属于不受检异常。一般是由程序逻辑错误引起的,此类程序应该从逻辑角度尽可能避免这类异常的发生。而当程序中可能出现这类异常,即使没有用try-catch 语句捕获它,也没有用throws 子句声明抛出它,也会编译通过。在程序中可以选择捕获处理,也可以不处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!
  • 注意:RuntimeException 异常会由JVM自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题,应该从逻辑上去解决并改进代码。这里我们来看下运行时异常是怎样的,这里我想说下,出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。然后看是什么异常。可以看出,我们的程序逻辑出现错误,所以出现了算术异常。我们只要修改int b = 10就行了,或者b不等于0都可以。所以遇到异常,我们不用担心。可以先从查看异常类名开始,看是什么异常,看是什么原因,找到我们程序出错的地方并进行修改就可以正常运行了。
  • 那我们什么都没有处理,那出现异常时,是谁处理了这个异常呢?是JVM的默认处理:把异常的名称,原因,位置等信息输出在控制台,但是呢程序就不能继续执行了。

2.3.2 非运行时异常(编译时异常)

  • 定义:Exception中除 RuntimeException 及其子类之外的异常。
  • 特点:此类异常, Java 编译器会检查它。如果程序中出现此类异常,从程序语法角度讲是必须进行处理的异常。例如:ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws 进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。
  • 注意:在程序中,通常我们不会自定义该类异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。通过注释可以看到,createNewFile() 方法是处理了IOException异常的,而IOException异常又继承来自Exception,是非运行时异常,所以必须处理异常。所以我们如果是编译时异常,在编译时期就报错了,必须处理这个异常,不然程序不能编译通过。

2.4 受检异常与非受检异常

通常,Java的异常(Throwable)分为受检异常(checked exceptions)和非受检异常(unchecked exceptions)。

2.4.1 受检异常

编译器要求必须处理的异常

  • 正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除了Exception中的 RuntimeException 及其子类以外,其他的 Exception类及其子类异常就是非运行时期异常都属于受检异常。
  • 这种异常编译器会检查它,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。

2.4.2 非受检异常

编译器不会进行检查并且不要求必须处理的异常。

  • 此类异常,就是当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws 抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException 极其子类)和错误( Error)。RuntimeException 发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException 。

3. 异常的处理机制(重点)

在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。

3.1 Java异常处理

异常还不知道是什么?一文教会你异常是什么,如何优雅处理

  • 在Java中,一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常,把各种不同的异常进行分类,并提供了良好的接口。
  • 在 Java 中,每个异常都是一个对象,它是 Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。
  • Java 的异常处理涉及了 5 个关键词:try、catch、 finally、throw 和throws。
  • 在Java应用中,异常的处理机制分为声明异常throws,抛出异常throw 和捕获异常try、catch、 finally。接下来让我为大家详细讲述吧。

3.2 异常处理的关键词

  • throw : 用于抛出异常。
  • try : 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
  • catch :用于捕获异常。catch用来捕获try语句块中发生的异常。
  • finally : finally语句块总是会被执行。它主要用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件)。注意:只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw 语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
  • throws: 用在方法签名中,用于声明该方法可能抛出的异常。

这里先了解下关键词,具体定义格式和使用方法在下面介绍:

3.3 抛出异常throw

  • 那什么时候使用呢?作为一个合格的程序员(这不就是我吗),在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。或者当你觉得解决不了某些异常问题,且不需要调用者处理,那么你也可以抛出异常。
  • throw的作用:在方法内部抛出一个Throwable 类型的异常。任何Java代码都可以通过throw语句抛出异常。
  • 具体如何抛出一个异常呢?创建一个异常对象。封装一些提示信息(信息可以自己编写)。需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw异常对象。throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
  • 定义格式:throw new 异常类名(参数);
  • 例子:throw new NullPointerException(“要访问的arr数组不存在”);
    throw new ArrayIndexOutOfBoundsException(“该索引在数组中不存在,已超出范围”);

接下来用程序具体演示下吧:

public class ThrowDemo {
    public static void main(String[] args) {
        //创建一个数组
        int[] arr = {2,4,52,2};

        //根据索引找对应的元素
        int index = 4;
        int element = getElement(arr, index);

        System.out.println(element);
        System.out.println("over");
    }
    /*
     * 根据 索引找到数组中对应的元素
     */
    public static int getElement(int[] arr,int index){
        //判断索引是否越界
        if(index<0 || index>arr.length-1){
             /*
                判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
                这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
              */
            throw new ArrayIndexOutOfBoundsException("老弟,你的索引越界了,别这么干");
        }
        int element = arr[index];
        return element;
    }
}

运行后输出结果为:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 老弟,你的索引越界了,别这么干
    at com.it.test2.ThrowDemo.getElement(ThrowDemo.java:25)
    at com.it.test2.ThrowDemo.main(ThrowDemo.java:10)

可以看到我定义了索引为4,但是数组的长度只有4。所以会报错。

注意:所以如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。结果是
ArrayIndexOutOfBoundsException 的数组索引越界的问题。

那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。

3.4 声明异常throws

如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时它可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。

  • 声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
  • 定义格式: throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ } 注意:当方法抛出异常列表的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。比如汽车坏了,开车的人也不会修理,只能叫修车公司来修理了。
  • 演示一下:一般来说,throws和 throw通常是成对出现的,例如:public class ThrowsDemo { public static void main(String[] args) throws FileNotFoundException { readFile(“a.txt”); }public class ThrowsDemo {
    public static void main(String[] args) throws FileNotFoundException { readFile(“a.txt”); } // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明 public static void readFile(String path) throws FileNotFoundException { if(!path.equals(“a.txt”)) {//如果不是 a.txt这个文件 // 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new FileNotFoundException(“文件不存在”); } }}

而throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

public class ThrowsDemo2 {
    public static void main(String[] args) throws IOException {
        readFile("a.txt");
    }

    //若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开
    //若有异常a是异常b的子类,也可以直接省略,写b异常
    private static void readFile(String path) throws FileNotFoundException, IOException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
        if (!path.equals("b.txt")) {
            throw new IOException();
        }
    }
}

throws抛出异常的规则:

  1. 如果是非受检异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
  2. 如果一个方法可能出现受检异常(checked exception),要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。
  3. 只有当抛出了异常时,该方法的调用者才必须处理或者重新抛出该异常。若当方法的调用者无力处理该异常的时候,应该继续抛出。
  4. 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

3.5 捕获异常try 、finally 、catch

这三个关键字主要有下面几种组合方式try-catch 、try-finally、try-catch-finally。

注意:catch语句可以有一个或者多个或者没有,finally至多有一个,try必要有。

那这里你会问有没有单独try模块出现,那我想问下你,try是用来监听是否有异常,那如果发生了异常,谁来捕获呢?所以没有try单独出现。而且编译不能通过。

所以跟try模块一样,例如catch,finally也不能单独使用出现

异常还不知道是什么?一文教会你异常是什么,如何优雅处理

  • 程序通常在运行之前不报错,但是运行后可能会出现某些未知的错误,不想异常出现导致程序终止,或者不想直接抛出到上一级,那么就需要通过try-catch等形式进行异常捕获,之后根据不同的异常情况来进行相应的处理。
  • 捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。

捕获异常语法如下:

3.5.1 try-catch 形式:

try{
     编写可能会出现异常的代码
}catch(异常类型  e){
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

例如:

public class TryCatchDemo {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            //当产生异常时,必须有处理方式。要么捕获,要么声明。
            Date date = simpleDateFormat.parse("2020-10-06");
        } catch (ParseException e) {// 括号中需要定义什么呢?
            //try中抛出的是什么异常,在括号中就定义什么异常类型
            e.printStackTrace();//记录日志/打印异常信息/继续抛出异常
        }
        /*
             public Date parse(String source) throws ParseException{}
             //parse抛出了ParseException异常
             public class ParseException extends Exception {}
         */
    }
}

如何获取异常信息:

Throwable类中定义了一些查看方法:

  • public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
  • public String toString():获取异常的类型和异常描述信息。
  • public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。
  • 具体我们可以来看下:
public class TryCathDemo2 {
 public static void main(String[] args) {
     SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
     try {
         //当产生异常时,必须有处理方式。要么捕获,要么声明。
         //演示下获取异常信息,修改了格式。
         Date date = simpleDateFormat.parse("2020年10月06");
     } catch (ParseException e) {
         //public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
         System.out.println(e.getMessage());//Unparseable date: "2020年10月06"
         System.out.println(e.toString());//java.text.ParseException: Unparseable date: "2020年10月06"
         e.printStackTrace();//输出信息而且飘红!!!
         /*
         java.text.ParseException: Unparseable date: "2020年10月06"
             at java.text.DateFormat.parse(DateFormat.java:366)
             at com.it.test3.TryCathDemo2.main(TryCathDemo2.java:13)
          */
     }
 }
}

而如果有多个异常使用捕获我们又该如何处理呢?

  1. 多个异常分别处理。
  2. 多个异常一次捕获,多次处理。

一般我们是使用一次捕获多次处理方式,格式如下:

try{
     编写可能会出现异常的代码
}catch(异常类型A  e){  当try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){  当try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

例如:

public class TryCatchDemo3 {
    public static void main(String[] args) {
        //test();
        test2();
    }

    //多个异常一次捕获,多次处理。
    public static void test2(){
        int[] arr = {11, 22, 66, 0};
        try {
            //System.out.println(arr[5]);//一旦这个报错,下面的代码就不会执行
            System.out.println(arr[2]);
            System.out.println(arr[0] / arr[arr.length-1]);
        } catch (ArithmeticException e) {
             System.out.println("除数不为0");
        }catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组下标越界");
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    //分别处理的方式
    public static void test() {
        int a = 10;
        int b = 0;

        try {
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            System.out.println("除数不为0");//除数不为0
        }

        int[] arr = {1, 2, 3};
        try {
            System.out.println(arr[4]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组下标越界");//数组下标越界
        }
    }
}
        
声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:fendou3451@163.com
标签:

  • 关注微信

相关文章