异常

时间:2019-11-27
本文章向大家介绍异常,主要包括异常使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

异常体系

异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error与java.lang.Exception,平时所说的异常指java.lang.Exception

Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
    • 例如:StackOverflowError和OOM(OutOfMemoryError)
  • Exception表示异常,其他因编程错误或偶然的外在因素导致的一般性问题,程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的
    • 例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息
    • 包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段都得使用printStackTrace
  • public String getMessage():获取发生异常的原因
    • 提示给用户的时候,就提示错误原因

异常分类

  • 编译时期异常:checked异常。在编译时期,就会检查如果没有处理异常,则编译失败
  • 运行时期异常:runtime异常。在运行时期,检查异常,在编译时期,运行异常不会被编译器检测到(不报错)。(如数组索引越界异常,类型转换异常)。程序员应该积极避免其出现的异常,而不是使用try…catch处理,因为这类异常很普遍,若都使用try…catch或throws处理可能会对程序的可读性和运行效率产生影响

异常的处理

Java异常处理的五个关键字:try、catch、finally、throw、throws

异常throw

Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。异常对象的生成有两种方式:

  • 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出
  • 由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,但是一旦throw抛出,就会对程序运行产生影响了

手动抛出异常:

1、创建一个异常对象,封装一些提示信息

2、将这个异常对象告知给调用者,throw异常对象

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行

使用格式:

  1 throw new 异常类名(参数);

例如:

  1 throw new NullPointerException("要访问的arr数组不存在");
  2 throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
  1 public class ThrowDemo {
  2     public static void main(String[] args) {
  3         //创建一个数组 
  4         int[] arr = {2,4,52,2};
  5         //根据索引找对应的元素 
  6         int index = 4;
  7         int element = getElement(arr, index);
  8 
  9         System.out.println(element);
 10         System.out.println("over");
 11     }
 12     /*
 13      * 根据 索引找到数组中对应的元素
 14      */
 15     public static int getElement(int[] arr,int index){
 16         if(arr == null){
 17             /*
 18              判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
 19              这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
 20               */
 21             throw new NullPointerException("要访问的arr数组不存在");
 22         }
 23        	//判断  索引是否越界
 24         if(index<0 || index>arr.length-1){
 25              /*
 26              判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
 27              这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
 28               */
 29              throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
 30         }
 31         int element = arr[index];
 32         return element;
 33     }
 34 }

声明异常throws

声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)

声明异常格式:

  1 修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }
  1 import java.io.File;
  2 import java.io.FileNotFoundException;
  3 
  4 public class TestException {
  5 	public static void main(String[] args) throws FileNotFoundException {
  6 		readFile("不敲代码学会Java秘籍.txt");
  7 	}
  8 
  9 	// 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
 10 	public static void readFile(String filePath) throws FileNotFoundException{
 11 		File file = new File(filePath);
 12 		if(!file.exists()){
 13 			throw new FileNotFoundException(filePath+"文件不存在");
 14 		}
 15 	}
 16 
 17 }

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

  1 import java.io.File;
  2 import java.io.FileNotFoundException;
  3 
  4 public class TestException {
  5 	public static void main(String[] args) throws FileNotFoundException,IllegalAccessException {
  6 		readFile("不敲代码学会Java秘籍.txt");
  7 	}
  8 
  9 	// 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
 10 	public static void readFile(String filePath) throws FileNotFoundException,IllegalAccessException{
 11 		File file = new File(filePath);
 12 		if(!file.exists()){
 13 			throw new FileNotFoundException(filePath+"文件不存在");
 14 		}
 15 		if(!file.isFile()){
 16 			throw new IllegalAccessException(filePath + "不是文件,无法直接读取");
 17 		}
 18 		//...
 19 	}
 20 
 21 }

捕获异常try…catch

如果异常出现的话,会立刻终止程序,所以我们得处理异常:

1、该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)

2、在方法中使用try-catch的语句块来处理异常

try-catch的方式就是捕获异常

Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理

捕获异常语法如下:

  1 try{
  2      编写可能会出现异常的代码
  3 }catch(异常类型1  e){
  4      处理异常的代码
  5      //记录日志/打印异常信息/继续抛出异常
  6 }catch(异常类型2  e){
  7      处理异常的代码
  8      //记录日志/打印异常信息/继续抛出异常
  9 }
 10 ....

try:该代码块中编写可能产生异常的代码

catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理

  • 可以有多个catch块,按顺序匹配
  • 如果多个异常类型有包含关系,那么小上大下
  1 public class TestException {
  2 	public static void main(String[] args)  {
  3 		try {
  4 			readFile("不敲代码学会Java秘籍.txt");
  5 		} catch (FileNotFoundException e) {
  6 //			e.printStackTrace();
  7 //			System.out.println("好好敲代码,不要老是想获得什么秘籍");
  8 			System.out.println(e.getMessage());
  9 		} catch (IllegalAccessException e) {
 10 			e.printStackTrace();
 11 		}
 12 
 13 		System.out.println("继续学习吧...");
 14 	}
 15 
 16 	// 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
 17 	public static void readFile(String filePath) throws FileNotFoundException, IllegalAccessException{
 18 		File file = new File(filePath);
 19 		if(!file.exists()){
 20 			throw new FileNotFoundException(filePath+"文件不存在");
 21 		}
 22 		if(!file.isFile()){
 23 			throw new IllegalAccessException(filePath + "不是文件,无法直接读取");
 24 		}
 25 		//...
 26 	}
 27 
 28 }

如何获取异常信息:

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

  • public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因)
  • public void printStackTrace():打印异常的跟踪栈信息并输出到控制台

包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用PrintStackTrace

finally块

finally:有一些特定的代码无论一场是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源

finally的语法:

  1  try{
  2 
  3  }catch(...){
  4 
  5  }finally{
  6      无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行
  7  }
  8 
  9 10   try{
 11 
 12  }finally{
 13      无论try中是否发生异常,也不管try中是否有return语句,都一定会执行
 14  }

注意:finally不能单独使用

finally与return

形式一:从try回来

  1 public class TestReturn {
  2 	public static void main(String[] args) {
  3 		int result = test("12");
  4 		System.out.println(result);
  5 	}
  6 
  7 	public static int test(String str){
  8 		try{
  9 			Integer.parseInt(str);
 10 			return 1;
 11 		}catch(NumberFormatException e){
 12 			return -1;
 13 		}finally{
 14 			System.out.println("test结束");
 15 		}
 16 	}
 17 }

形式二:从catch回来

  1 public class TestReturn {
  2 	public static void main(String[] args) {
  3 		int result = test("a");
  4 		System.out.println(result);
  5 	}
  6 
  7 	public static int test(String str){
  8 		try{
  9 			Integer.parseInt(str);
 10 			return 1;
 11 		}catch(NumberFormatException e){
 12 			return -1;
 13 		}finally{
 14 			System.out.println("test结束");
 15 		}
 16 	}
 17 }

形式三:从finally回来

  1 public class TestReturn {
  2 	public static void main(String[] args) {
  3 		int result = test("a");
  4 		System.out.println(result);
  5 	}
  6 
  7 	public static int test(String str){
  8 		try{
  9 			Integer.parseInt(str);
 10 			return 1;
 11 		}catch(NumberFormatException e){
 12 			return -1;
 13 		}finally{
 14             System.out.println("test结束");
 15 			return 0;
 16 		}
 17 	}
 18 }

异常注意事项

多个异常使用捕获该如何处理?

1、多个异常分别处理

2、多个异常一次捕获,多次处理(推荐)

3、多个异常一次捕获一次处理

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

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

注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理

  • 运行时异常被抛出可以不处理,即不捕获也不声明抛出
  • 如果finally有return语句,永远返回finally中的结果,避免该情况
  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常
  • 父类方法没有抛出异常,子类重写父类该方法时也不能抛出异常。此时子类方法中产生了编译时异常,只能捕获处理,不能声明抛出

自定义异常

异常类如何定义:

1、自定义一个编译期异常:自定义类并继承于java.lang.Exception

2、自定义一个运行时期的异常类:自定义类并继承于java.lang.RuntimeException

模拟注册操作,如果用户名已存在,则抛出异常并提示:该用户名已经被注册

首先定义一个登录异常类RegisterException:

  1 // 业务逻辑异常
  2 public class RegisterException extends Exception {
  3     /**
  4      * 空参构造
  5      */
  6     public RegisterException() {
  7     }
  8 
  9     /**
 10      *
 11      * @param message 表示异常提示
 12      */
 13     public RegisterException(String message) {
 14         super(message);
 15     }
 16 }

模拟登录操作,使用数组模拟数据库中存储的数据,并提供当前注册账号是否存在方法用于判断

  1 public class Demo {
  2     // 模拟数据库中已存在账号
  3     private static String[] names = {"bill","hill","jill"};
  4 
  5     public static void main(String[] args) {
  6         //调用方法
  7         try{
  8               // 可能出现异常的代码
  9             checkUsername("nill");
 10             System.out.println("注册成功");//如果没有异常就是注册成功
 11         }catch(RegisterException e){
 12             //处理异常
 13             e.printStackTrace();
 14         }
 15     }
 16 
 17     //判断当前注册账号是否存在
 18     //因为是编译期异常,又想调用者去处理 所以声明该异常
 19     public static boolean checkUsername(String uname) throws LoginException{
 20         for (int i=0; i<names.length; i++) {
 21             if(names[i].equals(uname)){//如果名字在这里面 就抛出登陆异常
 22                 throw new RegisterException(""+name+"已经被注册了!");
 23             }
 24         }
 25         return true;
 26     }
 27 }
  • 从Exception类或者它的子类派生一个子类即可
  • 习惯上,自定义异常类应该包含两个构造器:一个是无参构造,另一个是带有详细信息的构造器
  • 自定义的异常只能通过throw抛出
  • 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型

原文地址:https://www.cnblogs.com/daidai66/p/11945040.html