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