[学习笔记]Java异常机制

概述

异常

程序在运行时出现的不正常情况,是对问题的描述,将问题进行对象的封装。Java中的异常,就是对不正常情况进行描述后的对象体现。

异常体系

Throwable
    |--Error
    |--Exception
          |--RuntimeException
  • 其中Error类处理严重异常,一般不编写针对性的代码对其进行处理。
  • Exception类处理非严重异常,可以使用针对性的处理方式进行处理。
无论Error或者Excption都具有一些共同的属性和方法,比如不正常情况的信息和引发原因等。

异常类方法


  • getMessage()
    获取异常信息,返回字符串。
  • toString()
    获取异常类名和异常信息,返回字符串。
  • printStackTrace()
    获取异常类名和异常信息,以及异常出现在程序中的位置,直接打印,返回值void。
  • printStackTrace(PrintStream s)
    通常用该方法将异常内容保存在日志文件中,以便查阅。

特点

异常体系中所有的类以及建立的对象都具有可抛性,也就是说可以被throw和throws关键字操作,只有异常体系具备这个特点。

throw和throws的用法

  • throw定义在方法内,用于产生异常对象。
  • throws定义在方法上,用于抛出方法内产生的异常类,抛出多个异常用逗号隔开。

示例

  
  
class Div {
int div(int a, int b) throws Exception // 必须对其的调用进行捕获或声明以便抛出
{
return a / b;
}
 
int MultiEx(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException {
int c = a / b;
int[] arr = new int[a];
System.out.println(arr[a]);
return c;
}
}
 
public class ExceptionDemo {
public static void main(String[] args) // throws Exception
{
Div d = new Div();
try {
int x = d.div(2, 0);
System.out.println("x = " + x);
} catch (Exception e) {
System.out.println("异常!");
System.out.println(e.getMessage()); // 异常信息
System.out.println(e.toString()); // 异常名称:异常信息
e.printStackTrace(); // 异常名称:异常信息
// 异常出现的位置
}
System.out.println("----------------");
try {
int x = d.MultiEx(4, 1);
System.out.println("x = " + x);
} catch (ArithmeticException e) {
System.out.println("除数不能为0!");
System.out.println(e.toString());
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界!");
System.out.println(e.toString());
} catch (Exception e) {
System.out.println(e.toString());
}
System.out.println("----------------");
}
}

运行结果

异常!
/ by zero
java.lang.ArithmeticException : / by zero
java.lang.ArithmeticException : / by zero
     at Div.div(ExceptionDemo.java:4)
     at ExceptionDemo.main(ExceptionDemo.java:21)
----------------
数组下标越界!
java.lang.ArrayIndexOutOfBoundsException : 4
----------------

分层思想

当捕获到异常,本功能处理不了时,可以继续在catch中抛出。
  
  
try
{
throw new AException();
}
catch(AException e)
{
throw e;
}
如果该异常处理不了,但并不属于该功能出现的异常,为了进行模块式开发,降低耦合性,可以将异常转换后,再抛出和该功能相关的异常。或者异常可以处理,但需要将异常产生后和本功能相关的问题提供出去,抛出让调用者可以处理的异常和信息,也可以将捕获异常处理后转换新的异常抛出。所以异常发生不一定抛出原来的异常,也可以在本层处理后抛出上一层可以接受的异常。
   
   
try
{
throw new AException();
}
catch(AException e)
{
//对AException处理。
throw new BException;
}

异常处理方式

1. 捕捉

格式

try{
     需要被检测的代码
}
catch(异常类 变量){
     对捕获到的异常进行处理,异常对象的常见操作:String getMessage()获取异常信息等。
     一定要定义具体的处理方法,不要简单一句e.printStackTrace(),也不要简单一句输出语句。一般使用错误日志进行保存。
}
finally{
     定义一定执行的代码,通常用于关闭资源。
}

有三种结合方式

  • try{}catch(){}
  • try{}catch(){}finally{}
  • try{}finally{}

注意

  • finally中定义的通常是关闭资源代码,因为资源必须释放。
  • 只有在catch中使用了System.exit(0)方法时,不执行finally中的代码。

2. 抛出

使用throws关键字将产生的异常抛出,交由调用者处理。

3. 示例

   
   
class Demo1 {
void func() throws Exception {
// 异常处理:抛出
throw new Exception();
}
}
 
class Demo2 {
void func() {
// 异常处理:捕捉
try {
throw new Exception();
} catch (Exception e) {}
}
}
 
class Demo3 {
void func() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
// 捕捉异常,如果无法处理,可以继续抛出e
throw e;
}
}
}
 
class Demo4 {
void func() {
try {
throw new Exception();
} catch (Exception e1) {
// 捕捉异常,如果无法处理,可以将异常e1转化成调用者可接受异常后抛出
try {
throw e1;
} catch (Exception e2) {
// 异常转换
}
 
}
}
}
 
class Demo5 {
void func() throws Exception {
try {
throw new Exception();
} finally {
// 关闭资源
}
}
}

4. 多异常的处理

1. 声明异常时,建议声明更为具体的异常,这样处理可以做到更具体。
2. 声明几个异常,就对应有几个catch块,不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放最后。

5. 异常分类

1. 编译时被检测异常(Exception类)
  • 该异常在编译时,如果没有处理(没抛出也没捕捉),编译失败。该异常被标识,表示可以被处理。
  • 该异常必须进行异常处理(抛出或者捕捉)。
2. 运行时异常(编译时不检测,RuntimeException类)
  • 编译时,不需要处理,编译器不检查。
  • 如果在函数内抛出该异常或其子类异常,函数上不需要声明。
    如果在函数上抛出该异常或其子类异常,调用者也不需要处理。
    因为这类异常一般无法正确继续程序,一旦出现,希望终止程序并由程序员修改代码以解决该类异常。
  • 自定义异常发生时,如果该异常无法继续程序运行,就让其继承RuntimeException。

6. 异常处理原则

  • 当函数内部有throw抛出异常对象,并未进行try处理,必须在函数上声明,否则编译失败。RuntimeException及其子类除外。
  • 如果函数声明了异常,调用者需要进行处理。处理方式有两种,即捕捉和抛出。
  • 函数上声明异常,能够提高安全性,强制调用者进行处理。
  • 调用到抛出异常的功能时,抛出几个,就处理几个。出现一个try对应多个catch,其中父类的catch放最下面。

自定义异常

按照Java的面向对象思想,将程序中出现的特有问题进行封装。

使用方法

定义继承Exception或者RuntimeException的异常类
1. 为了让该自定义类具有可抛性。
2. 让该类具备操作异常的共性方法。
3. 当要定义自定义异常信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。
  
  
class MyException extends Exception
{
MyException(String msg)
{
super(msg);
}
}

自定义异常的好处

1. 将问题进行封装。
2. 将正常流程代码和问题处理代码相分离,方便阅读。

自定义异常在父子类方法重写中的情况

1. 子类在重写父类时,如果父类的方法抛出异常,那么子类的重写方法,只能抛出父类的异常或者该异常的子类。
2. 如果父类方法抛出多异常,那么子类的重写方法,只能抛出父类异常的子集。
3. 如果父类或者接口方法中没有异常抛出,那么子类在重写方法时,也不可以抛出异常。如果子类方法发生异常,只能在方法内部进行捕捉处理。

示例
   
   
/*
* 自定义异常继承体系
* Exception
* |--AException
* | |--BException
* |--CException
*/
class AException extends Exception {}
 
class BException extends AException {}
 
class CException extends Exception {}
 
class Father {
void func() throws AException {}
}
 
class Son extends Father {
void func() throws BException {
// 只能抛出AException或者AException的子类BException,不能抛出CException
// 如果子类产生新异常CException,这里只能try,不能抛出。
}
}

自定义异常示例

   
   
class NegativeException extends Exception {
 
private int value;
 
NegativeException(String msg) {
super(msg);
}
 
NegativeException(String msg, int value) {
super(msg);
this.value = value;
}
 
int getValue() {
return value;
}
}
 
class Demo {
int customExc(int x) throws NegativeException // 函数内手动抛出非运行时异常,必须对其进行捕捉或声明抛出。
{
if (x < 0) throw new NegativeException("负数!", x);
return x;
}
 
int runtimeExc(int x) // 函数内手动抛出RuntimeException异常或其子类异常,不需要对其进行捕捉或声明抛出。
{
if (x == 0) throw new ArithmeticException("数值为0!");
return x;
}
 
void checkString(String s) {
if (s.equals("String"))
// 避免空指针异常,应修改为:
// if("String".equals(s))
System.out.println("PASS!");
else
System.out.println("FAIL!");
}
}
 
public class ExceptionCustom {
public static void main(String[] args) // throws Exception
{
Demo d = new Demo();
 
try {
int x = d.customExc(-3);
System.out.println("x = " + x);
} catch (NegativeException e) {
System.out.println(e.toString() + "\n该数为:" + e.getValue());
}
System.out.println("-------------------");
d.runtimeExc(0); // 算术异常(运行时),停止程序,需要程序员修改代码。
System.out.println("-------------------");
d.checkString("String");
d.checkString(null); // 空指针异常(运行时)。
System.out.println("-------------------");
}
}

自定义异常实例

  
  
// 员工使用电脑案例
/**
* 电脑死机异常
*/
class CrashException extends Exception {
CrashException(String msg) {
super(msg);
}
}
 
/**
* 电脑烧毁异常
*/
class BurnException extends Exception {
BurnException(String msg) {
super(msg);
}
}
 
/**
* 无法工作异常
*/
class WorkException extends Exception {
WorkException(String msg) {
super(msg);
}
}
 
class Computer {
// 电脑状态,0电脑正常,1电脑死机,2电脑烧毁
private int state = 0;
 
public void run() throws CrashException, BurnException {
if (state == 1) throw new CrashException("电脑崩溃了!");
if (state == 2) throw new BurnException("电脑烧毁了!");
System.out.println("电脑运行...");
}
 
public void reboot() {
System.out.println("电脑重启...");
state = 0;
}
 
public void setState(int state) {
this.state = state;
}
}
 
abstract class Employee {
private String name;
 
Employee(String name) {
this.name = name;
}
 
abstract void work() throws WorkException;}
 
class Staff extends Employee {
private Computer com;
 
Staff(String name) {
super(name);
com = new Computer();
}
 
public void work() throws WorkException {
try {
com.run();
} catch (CrashException e) {
// 如果电脑死机了,则重启电脑即可
System.out.println(e.toString());
com.reboot();
} catch (BurnException e) {
// 如果电脑烧毁了,则向上级报告,抛出无法工作异常请求放假。
throw new WorkException("无法继续工作!\n原因:" + e.toString());
}
System.out.println("工作!");
}
 
public void computerStateChange(int state) {
com.setState(state);
}
}
 
public class ExceptionCase {
public static void main(String[] args) {
Staff s = new Staff("Jacob");
// 分别针对不同的电脑状态,模拟工作情况
for (int i = 0; i < 3; i++) {
System.out.println("------------------情况" + i + ":------------------");
// 更改员工电脑的状态
s.computerStateChange(i);
// 员工工作
try {
s.work();
} catch (WorkException e) {
System.out.println(e.toString() + "\n放假!");
}
}
}
}

运行结果

------------------情况0:------------------
电脑运行...
工作!
------------------情况1:------------------
CrashException: 电脑崩溃了!
电脑重启...
工作!
------------------情况2:------------------
WorkException: 无法继续工作!
原因:BurnException: 电脑烧毁了!
放假!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值