:Java异常概念处理和自定义异常类
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:Java异常概念处理和自定义异常类相关的知识,希望对你有一定的参考价值。
文章目录
一:什么是异常
异常:在Java中,将程序执行过程中发生的不正常行为称之为异常,此时编译器将会报出错误,也即抛出异常。在编写程序时,我们会经常遇到以下异常
①:算术异常
public static void main(String[] args)
System.out.println(1 / 0);
②:数组越界异常
public static void main(String[] args)
int[] arr = 1, 2, 3;
System.out.println(arr[3]);
③:空指针异常
public static void main(String[] args)
int[] arr = null;
System.out.println(arr.length);
二:异常体系结构和分类
(1)体系结构
异常体系结构:在Java中,异常就是一个类,名字叫做Throwable
其体系结构如下图所示(部分),其中
-
Throwable:是异常体系顶层类,派生出了
Error
和Exception
-
Error:指的是JVM无法解决的严重问题,例如JVM内部错误、资源耗尽等等。
StackOverflow
和OutOfMemoryError
是其典型代表 -
Exception:也即异常;异常产生后程序员可以通过代码进行出来使其正常运行
(2)异常分类
异常分类:根据异常发生时机的不同,可以将异常分为如下两类
- 编译时异常
- 运行时异常
A:编译时异常
编译时异常:发生于程序编译期间,也称之为受查异常(Checked Exception)
如下,在IDEA中编写代码时,如果出现了红色波浪线且提示你需要抛出或捕获异常,那么表示此时是编译时异常
B:运行时异常
运行时异常:发生于程序执行期间,也称之为非受查异常(Unchecked Exception)
- 上图所示的“RunTimeException”及其子类对应的异常都是运行时异常
- 注意编译时出现的语法错误并不能称之为异常
- 常见的运行时异常前面已举例
三:异常处理
(1)防御式编程
防御式编程:只要从写第一行代码的那一刻开始,错误就已经潜在了。所以我们需要做到的就是当程序出现问题时能及时通知程序员解决。主要有以下两种方式
①:事前防御:在编写代码时就预想到一切可能发生的错误
- 缺点:把处理错误的代码逻辑搅合到正常代码逻辑中,显得混乱;而且你也无法将所有错误情况都考虑到
boolean ret = false;
ret = 登录游戏();
if(!ret)
处理登录游戏错误;
return;
ret = 匹配对局();
if(!ret)
处理匹配对局错误;
return;
ret = 购买物品();
if(!ret)
处理购买物品错误;
return;
...
②:事后认错(异常处理的核心思想):不管怎样“先运行再说”,遇到问题了再解决
- 优点:正常流程和错误流程是分离开的,程序员更关注正常流程,代码更清晰,容易理解代码
try
登录游戏();
匹配对局();
购买物品();
...
catch(登录游戏异常)
处理登录游戏异常;
catch(匹配对局异常)
处理匹配对局异常;
catch(购买物品异常)
处理购买物品异常;
(2)Java异常处理五大关键
在Java中,关于异常的处理无外乎就是以下五点
throw
try
catch
final
throws
A:异常抛出
异常抛出:在Java中,可以借助关键字throw
,抛出一个指定的异常对象,将错误信息告知给调用者,语法如下。注意:
throw
必须写在方法体内部- 抛出对象必须为
Exception
或其子类 - 如果抛出的是
RunTimeException
或其子类,则可以不用处理,JVM会自动处理 - 如果抛出的是编译时异常,则用户必须处理,否则无法通过编译
- 异常一旦抛出成功,后面代码便不再执行
throw new XXXEXception("异常产生的原因")
例如下面是一个获取数组中任意位置元素的例子
public static int getElement(int[] array, int index)
if(null == array)
throw new NullPointerException("传递的数组为null");
if(index < 0 || index >= array.length)
throw new ArrayIndexOutOfBoundsException("数组下标越界");
return array[index];
public static void main(String[] args)
int[] arr = 61, 45, 93, 26, 88;
int b = getElement(arr, 10);
int c = getElement(null, 2);
B:异常捕获
异常捕获:也即异常的具体处理方式,有如下两种
- 异常声明
throws
try-catch
捕获处理
①:异常声明throws
异常声明throws:适用于当前方法不处理异常,题型方法调用者去处理异常;异常声明位于方法声明时的参数列表之后。语法格式如下
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...
例如下面的例子中,需求是加载指定的配置文件config.ini
,这里会有一个隐含错误就是文件名字错误,所以我们需要抛出一个“FileNotFoundException”异常,但是对于这种异常是无法处理的,只有调用者才可以检查并修改文件名字
import java.io.File;
import java.io.FileNotFoundException;
public class Config
File file;
public void OpenConfig(String filename) throws FileNotFoundException
if(filename.equals("config.ini"))
throw new FileNotFoundException("配置文件名字错误,请检查");
public void readConfig()
;
注意以下几点
throws
必须跟在方法的参数列表之后- 声明的异常必须是
Exception
或其子类 - 方法内部如果抛出了多个异常,
throws
之后必须跟多个异常类型,之间用逗号隔开。如果抛出多个异常类型 具有父子关系,直接声明父类即可 - 调用声明了抛出异常的方法时,调用者必须对该异常进行处理,或者继续用throws抛出
②:try-catch捕获并处理(常用)
try-catch捕获并处理:throws
并未对异常进行处理,如果不作处理那么就会交由JVM处理,而且结果大概率就是程序终止,所以很多时候我们需要对异常进行捕获并作出相应处理,这里就需要用到try-catch
语句。语法格式如下
try
这里放置可能出现异常的代码
catch(要捕获的异常类型 e)
如果[try中的代码抛出异常了,且和catch捕获的异常类型一致时(或其基类)]就会被捕获到
此时就会对异常进行处理,处理完成之后,try-catch跳出,继续执行代码
catch(要捕获的异常类型 e)
...
catch(要捕获的异常类型 e)
...
...
...
finally
此处的代码一定会被执行到
//后续代码
当异常被捕获到时,异常就被处理了,后续代码会被执行
如果没有捕获到或者是捕获到了但是没有其对应的类型,这里的代码不会执行(此时会交由JVM处理,一般来说程序会终止)
例如下面的例子中,需求是加载指定的配置文件config.ini
public class Config
File file;
public void OpenConfig(String filename) throws FileNotFoundException, FileSystemException
if(filename.equals("config.ini"))
throw new FileNotFoundException("配置文件名字错误,请检查");
//打开文件
public void readConfig()
;
public static void main(String[] args)
Config config = new Config();
try
//打开文件时有可能错误,然后抛出IOException异常
config.OpenConfig("config.txt");
System.out.println("文件打开成功");
catch (IOException e)
//异常处理方式
System.out.println(e.getMessage()); //可以只打印异常信息
System.out.println(e); //可以打印异常类型:异常信息
e.printStackTrace(); //可以把信息打印全
System.out.println("异常一旦被捕获且被处理了,此处代码便会执行");
注意以下几点
-
try
块内抛出异常后,该位置之后的代码不再会被执行 -
如果抛出异常类型与
catch
块内异常类型不匹配时,也即异常不会被成功捕获,也就不会处理。接着,它会继续向上抛,知道JVM收到后中断程序
-
如果异常具有父子关系,一定保证子类异常在前,父类异常在后,否则会出现语法错误
C:finally
finally:在写程序时,有些代码,无论程序是否发生异常,都必须要执行,例如网络连接资源、数据库连接资源、I/O流等等,这些资源在程序正常或异常退出时,必须予以回收。所以finally可以解决这个问题,语法格式如下
try
//可能会发生异常的代码
catch(异常类型 e)
//异常处理
finally
//无论是否发生异常或者能够捕捉到,这里一定会被执行
如下是一个数组越界异常的例子
public class TestDemo
public static void main(String[] args)
try
int[] arr = 1, 2, 3;
System.out.println(arr[100]);
catch (ArrayIndexOutOfBoundsException e)
System.out.println(e);
finally
System.out.println("finally中代码执行");
System.out.println("后续代码");
问题:如下,既然finally
和 try-catch-finally
后面的代码都会执行,那么finally
的存在是否显得多余?
如下是一个获取输入数字的例子,main
方法调用getData()
方法获取一个整数。如果正常输入,那么这个方法将会直接结束在try
块内,后面的sc.close()
也不会执行,所以资源未能回收
public class TestDemo
public static int getDate()
Scanner sc = null;
try
sc = new Scanner(System.in);
int data = sc.nextInt();
return data;
catch (InputMismatchException e)
e.printStackTrace();
finally
System.out.println("finally中的代码");
System.out.println("try-catch-finally后的代码");
if(sc != null)
System.out.println("关闭资源");
sc.close();
return 0;
public static void main(String[] args)
int data = getDate();
System.out.println(data);
但是如果把资源的回收工作写在finally
内部,那么即便方法返回,它也是会自动执行的
try
sc = new Scanner(System.in);
int data = sc.nextInt();
return data;
catch (InputMismatchException e)
e.printStackTrace();
finally
System.out.println("finally中的代码");
if(sc != null)
System.out.println("关闭资源");
sc.close();
四:异常的处理流程
异常的处理流程:总结如下
- 程序先执行
try
中的代码 - 如果
try
中的代码出现异常,就会结束try
中的代码,看和catch
中的异常类型是否匹配 - 如果找到匹配的异常类型,就会执行
catch
中的代码 - 如果没有找到匹配的异常类型,就会将异常向上传递到上层调用者
- 无论是否找到匹配的异常类型,
finally
中的代码都会被执行到(在该方法结束之前执行) - 如果上层调用者也没有处理的了异常,就继续向上传递
- 一直到
main
方法也没有合适的代码处理异常,就会交给JVM来进行处理,此时程序就会异常终止
以上是关于:Java异常概念处理和自定义异常类的主要内容,如果未能解决你的问题,请参考以下文章
异常概念和处理机制,try-catch-finally,throw和throws,自定义异常
异常概念和处理机制,try-catch-finally,throw和throws,自定义异常