零基础学习之Java异常
概述
正如“人无完人”一样,程序也不是完美的,它总会存在这样那样的问题,而有些问题并不是能够通过程序员开发更好的代码来解决的,如果我们忽视它,可能就会造成程序的终止,甚至是系统的崩溃。因此,我们需要想办法来合理的解决它,这就是Java中异常的由来。
异常是指程序在执行过程中,出现的非正常情况。比如:空的引用、数组下标越界、内存溢出错误、读取文件不存在、网络断开等
一般来说有两种方法来处理异常:
- 遇到错误就终止程序运行;
- 开发人员在编写程序的时候考虑到可能出现的非正常情况,提前做好处理(这个是最好的,不过也是不容易的);
那究竟异常有哪些?常见的处理方法是什么?我想通过下面的文章,你一定可以得到想要的答案!
基本介绍
在Java这样一个面向对象的语言中,异常被当成了一个对象来处理。其根类是java.lang.Throwable类,Throwable类有分成了两个类,即Error和Exception。这是异常的两个大类,他们分别代表了两种情况:
- Error:不能处理的错误,这是系统内部的错误,运行时报错,属于系统问题。一般发生这种异常,JVM会选择终止程序,开发人员需要提前避免。
- Exception:可以处理的异常,这是比较常见的异常,开发人员可以根据Java提供的类和问题对这类异常进行处理。
Error和Execption包括很多种情况,这才是我们需要考虑的,下面对它们进行分别介绍。
Error
对于严重错误Error,没有办法处理,只能做到提前避免。常见的Error有:StackOverflowError(内栈溢出错误)和OutOfMemoryError (内存溢出错误)。
代码示例
OutOfMemoryError
Java中堆是用来存储对象实例的,,因此如果我们不断地创建对象, 并且对象没有被垃圾回收, 那么当创建的对象过多时, 会导致 heap 内存不足, 进而引发 OutOfMemoryError 异常.
/**
* 内存溢出
*/publicclassHeap{publicstaticvoidmain(String[] args){//创建数组list,用来保存对象List<Integer> list =newArryList<>();int i=0;while(true){//不断创建对象
list.add(i++);}}}
StackOverflowError
JVM 的运行时数据区中有一个叫做栈的内存区域, 此区域的作用是: 每个方法在执行时都会创建一个栈帧, 用于存储局部变量表、操作数栈、方法出口等信息。因此当创建一个无限递归的递归调用, 当递归深度过大时, 就会耗尽栈空间, 进而导致了 StackOverflowError 异常.
/**
* 栈溢出
*/publicclassStack{publicstaticvoidmain(String[] args){//调用方法newStack().test();}//创建测试方法publicvoidtest(){//自己调用自己(递归调用)test();}}
Exception
一般来说,我们所说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序进行处理。根据在编译时期还是运行时期去检查异常,可以将Exception分为:
- 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败(开发工具会提示),如文件找不到异常等。
- 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会被编译器检测到(不开发工具不会提示),如空指针异常,类型转换异常,数字操作异常,类型不匹配异常等。
代码示例
编译时异常这里就不举例了,在开发工具里会标红提示。下面是常见的运行时异常代码示例。
packagecom.atguigu.demo;importorg.junit.Test;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.util.Scanner;publicclassDemo1{/*
编译时异常:必须进行处理,否则编译不通过
*///文件找不到@Testpublicvoidtest07(){//这里FileInputStream报红,编译异常,需要处理FileInputStream fis =newFileInputStream("Java异常学习笔记.txt");}/*
运行时异常
*/publicstaticvoidmain(String[] args){//类型不匹配异常Scanner input =newScanner(System.in);System.out.print("请输入一个整数:");//这个时获取控制台的输入,如果输入的不是整数,则报类型不匹配异常int num = input.nextInt();}//空指针异常@TestpublicvoidtestNullPointerException(){String[] names =newString[5];System.out.println(names[0].length());//字符串的长度}//类型转换异常@TestpublicvoidtestClassCastException(){Object obj =newObject();String str =(String) obj;}//数学操作异常异常@TestpublicvoidtestArithmeticException(){int i =1/0;}}
异常的处理
既然Java程序异常是非常常见的,那我们该如何处理它呢。由于Java将异常作为对象来处理,因此我们可以将Java异常的处理分成三个部分:
- Java程序出现异常时会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
- Java程序出现异常时将问题标识出来,报告给调用者,让调用者去处理。这个过程通过throws进行声明。
- Java程序出现异常时,在方法中使用try-catch的语句块来处理异常。
throw
Java程序出现异常时会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。异常对象的生成有两种方式:
- 自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出
- 手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,但是一旦throw抛出,就会对程序运行产生影响了。
使用格式:
thrownew 异常类名(参数);
代码示例
packagecom.atguigu.demo;publicclassDemo2{publicstaticvoidmain(String[] args){//定义数组和访问的角标int[] arr ={1,2,3,4};int index =4;//测试数组越界异常// int[] arr = {1,2,3,4}; //为null,测试空指针异常//调用方法测试int element =getElement(arr, index);System.out.println(element);}publicstaticintgetElement(int[] arr,int index){//判断数组为空异常if(arr ==null){thrownewNullPointerException("数组为空!");}//判断数组越界异常// 数组最大下标 arr.length-1if(index <0|| index > arr.length -1){thrownewArrayIndexOutOfBoundsException("角标越界!");}int element = arr[index];return element;}}
throws
Java程序出现异常时将问题标识出来,报告给调用者,让调用者去处理。这个过程通过throws进行声明。关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(如果调用者不处理,则接着抛出异常).(不主动处理的方式,甩锅给别人)
声明异常格式:
修饰符 返回值类型 方法名(参数)throws 异常类名1,异常类名2…{}//可以声明多个异常(也可直接一个Exception)
代码示例
packagecom.atguigu.demo;/*
声明异常
*/publicclassDemo4{//声明一个Exception publicstaticvoidmain(String[] args)throwsException{method();//不能直接调用,必须接着声明异常或者处理异常,否则编译不通过}//声明多个异常publicstaticvoidmethod()throwsNullPointerException,ArrayIndexOutOfBoundsException{System.out.println("声明异常!");}}
try…catch
Java程序出现异常时,在方法中使用try-catch的语句块来处理异常。try-catch的方式就是捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。(主动处理的方式,不甩锅给别人)
捕获异常语法如下:
try{//可能会出现异常的代码放在这里}catch(异常类型 e){//处理异常的代码//记录日志/打印异常信息/继续抛出异常}catch(异常类型 e){//处理异常的代码//记录日志/打印异常信息/继续抛出异常}...
注意: try和catch都不能单独使用,必须连用(除非有finally,后面说)。
catch可以有好几个,即可以捕获几个异常
代码示例
packagecom.atguigu.demo;/*
try catch 捕获异常
*/publicclassDemo5{publicstaticvoidmain(String[] args){//获取方法异常信息try{//正常执行代码add();}catch(Exception e){//捕获异常System.out.println("异常.......");// String message = e.getMessage();// System.out.println(message);
e.printStackTrace();}//多个异常捕获// catch(ArrayIndexOutOfBoundsException a) {//// }}//声明异常publicstaticvoidadd()throwsException{//System.out.println("add......");int i =1/0;}}
finally块
在上述的try中,如果出现了异常,则异常后面的语句就不执行,但有些特定的语句是需要必须执行的。因此就需要finally块就是解决这个问题的,这是因为在Java中finally代码块中存放的代码都是一定会被执行的。
当在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后关闭打开的资源。
finally的语法:
try{}catch(...){}finally{//无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行}
或
try{}finally{//无论try中是否发生异常,也不管try中是否有return语句,都一定会执行}
代码示例
packagecom.atguigu.demo;publicclassDemo6{publicstaticvoidmain(String[] args){try{System.out.println("程序开始执行了");int i =1/0;}catch(Exception e){
e.printStackTrace();}finally{System.out.println("这段代码总会执行");//这里没有写前面说的资源情况,主要为了方便理解}}}
finally与return(面试)
形式一:从try回来
publicclassTestReturn{publicstaticvoidmain(String[] args){int result =test("12");System.out.println(result);}publicstaticinttest(String str){try{Integer.parseInt(str);return1;}catch(NumberFormatException e){return-1;}finally{System.out.println("test结束");}}}
形式二:从catch回来
publicclassTestReturn{publicstaticvoidmain(String[] args){int result =test("a");System.out.println(result);}publicstaticinttest(String str){try{Integer.parseInt(str);return1;}catch(NumberFormatException e){return-1;}finally{System.out.println("test结束");}}}
形式三:从finally回来
publicclassTestReturn{publicstaticvoidmain(String[] args){int result =test("a");System.out.println(result);}publicstaticinttest(String str){try{Integer.parseInt(str);return1;}catch(NumberFormatException e){return-1;}finally{System.out.println("test结束");return0;}}}
自定义异常
基本介绍
异常的情况有非常多,因此Java内部并不能把这些都包含进去,这就需要开发人员根据自己的实际业务情况进行自定义异常。比如年龄负数问题,考试成绩负数问题等等。
异常类如何定义:
- 自定义一个编译期异常::自定义类 并继承于 java.lang.Exception。
- 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException。
代码示例
packagecom.atguigu.demo;publicclassDemo7{publicstaticvoidmain(String[] args){try{newInput().method();}catch(WrongInputException wie){System.out.println(wie.getMessage());}}}// 自定义的类继承ExceptionclassWrongInputExceptionextendsException{WrongInputException(String s){super(s);}}classInput{voidmethod()throwsWrongInputException{// 抛出自定义的类thrownewWrongInputException("Wrong input");}}
异常注意事项
多个异常使用捕获又该如何处理呢?
- 多个异常分别处理。
- 多个异常一次捕获,多次处理。(推荐)
- 多个异常一次捕获一次处理。
注意:多个异常一次捕获,多次处理的方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 如果finally有return语句,永远返回finally中的结果,避免该情况.
- 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
版权归原作者 Faith_xzc 所有, 如有侵权,请联系我们删除。