Java 之异常
异常概述
异常: 是在运行时期发生的不正常情况.
在 java 中用类的形式对不正常情况进行了描述和封装.
描述不正常情况的类, 就称为异常类.
异常体系
父类: Throwable (JDK 文档)
子类: 1. 一般不可处理: Error
2. 可以处理: Exception
该体系的特点:
- 在用 Throwable 及其所有子类都具有可抛性.可抛性通过 throws 和 throw
两个关键字体现, 凡是可以被这两个关键字所操作的类和对象都具备可抛性. - 子类的后缀都是用其父类名作为后缀, 例如 xxxException, xxError.
- Error 使用 JVM 抛出的, 属于严重性问题,这种问题的发生一般不针对性处理, 直接修改程序.
自定义异常
如果让一个类成为异常类, 必须要继承异常体系, 因为只有成为异常体系的子类才有资格具备可抛性,
才可以被两个关键字所操作: throws, throw
// 数组负数角标异常
class FuShuIndexException extends Exception
{
// 空参数构造函数
FuShuIndexException()
{}
// 带参数的构造函数
FuShuIndexException(String msg)
{
super(msg); // 直接调用父类处理 msg 的方法
}
}
class Demo
{
pulic int method(int[] arr, int index) throws FuShuIndexException
{
if(arr==null)
throw new NullPointerException("数组的引用不能为空!");
if(index<0)
throw new FuShuIndexException("角标变成负数啦!");
return arr[index];
}
}
class ExceptionDemo
{
public static void main(String[] args) throws FuShuIndexException
{
int[] arr = new int[3];
Demo d = new Demo();
int num = d.method(arr,-30);
System.out.println("num=" + num);
}
}
Exception 下的异常分为两种:
- 编译时被检测异常
- 除了特殊子类 RuntimeException, 其他都是.
- 特点: 这种问题一旦出现, 是希望在编译时就进行检测, 让这种问题有对应的处理方式.
- 编译时不检测异常(运行时异常)
- Exception 中的 RuntimeException 和其子类
- 这种问题的发生, 无法让功能继续, 更多是因为调用者的原因导致的或者引发了内部状态的改变导致的.
那么这种问题一般不处理, 直接编译通过, 在运行时, 让调用者调用时的程序强制停止, 让调用者对
代码进行修正.
throw 和 throws 的区别
- throws 使用在函数上, throw 使用在函数内
- throws 抛出的是异常类, 可以抛出多个, 用逗号隔开
throw 抛出的是异常对象.
异常的捕捉
具体格式:
try
{
// 需要被检测已成的代码
}
catch(异常类 变量) // 该变量用于接收发生的异常对象
{
// 处理异常的代码
}
finally // 通常用于关闭(释放)资源
{
// 一定会被执行的代码
}
异常处理的原则
- 函数内部如果抛出需要检测的异常, 那么函数上必须要声明.
否则必须在函数内用 trycatch 捕捉, 否则编译失败. - 如果调用到了声明异常的函数, 要么 trycatch 要么 throws, 否则编译失败.
- 函数内部可以解决的异常, 用 catch
解决不了的异常, 用 throws 告诉调用者, 由调用者解决 - 一个功能如果抛出了多个异常, 那么调用时, 必须要对应多个 catch 进行针对性处理.
内部有几个需要检测的异常, 就抛出几个异常, 抛出几个, 就 catch 几个.
多 catch 异常, 父类的 Exception 放在 catch 的最下面.
// 示例1:写出程序结果
class Exc0 extends Exception{}
class Exc1 extends Exc0{}
class Demo
{
public static void main(String[] args)
{
try
{
throw new Exc1();
}
catch(Exception e)
{
System.out.println("Exception"); // 编译失败. 多 catch 时, 父类的 catch 放在最下面
}
catch(Exc0 e)
{
System.out.println("Exc0");
}
}
}
// 示例2: 写出程序结果
class Test
{
public static String output="";
public static void foo(int i)
{
try
{
if(i==1)
throw new Exception();
output += "1";
}
catch(Exception e)
{
output += "2";
return;
}
finally
{
output += "3";
}
output += "4";
}
public static void main(String[] args)
{
foo(0);
System.out.println(output); // 134 , 注意是字符串
foo(1);
System.out.println(output); // 13423, output 是静态全局变量
}
}
异常转换(具体看视频)
异常注意事项
- 子类在覆盖父类方法时, 父类的方法如果抛出了异常,
那么子类的方法只能抛出父类的异常或者该异常的子类或者不抛出异常. - 如果父类抛出多个异常, 那么子类只能抛出父类异常的子集.
- 如果父类的方法没有抛出异常, 那么子类覆盖时绝对不能抛出异常, 只能 try.
// A 异常
class A extends Exception
{
}
class B extends A
{
}
class C extends Exception
{
}
class Fu
{
void show throws A
{}
}
class Zi extends Fu
{
// 子类覆盖父类 show()方法, 并抛出 C 异常
void show() throws C
}
class Test
{
void method(Fu f) // Fu f = new Zi(); 此时, 子类中有 C 异常,
// 而 C 异常既没有在函数上声明, 也没有捕捉.
{
try
{
f.show();
}
catch(A a)
{
}
}
}
示例1: 写出程序结果
class Demo
{
public static void main(String[] args)
{
try
{
showExe();
System.out.println("A");
}
catch(Exception e)
{
System.out.println("B");
}
finally
{
System.out.println("C");
}
System.out.println("D"); // 异常已经被解决, 最后肯定会输出 D
}
public static void showExe()throws Exception
{
throw new Exception();
}
}
// 结果: B C D
//示例2: 写出程序结果
class Demo
{
public static void func()
{
try
{
throw new Exception();
System.out.println("A"); // 该条语句无法被执行, 废话! 编译失败
}
catch(Exception e)
{
System.out.println("B");
}
}
public static void main(String[] args)
{
try
{
func();
}
catch(Exception e)
{
System.out.println("C");
}
System.out.println("D");
}
}
_参考资料_ - [JavaSE 基础视频](https://www.bilibili.com/video/av3100959/#page=1)