定义:
程序运行时,发生不被期望的事件,它阻止了程序员的预期执行情况,这就是异常。
异常类结构图
异常的分类:
在java中所有的异常都有一个父类:Throwable,它有两个重要的子类Error(错误),Exception(异常)
- Error(错误):错误是程序无法解决的错误,表示程序中较严重的问题。它一般表示程序运行时JVM(虚拟机)出现的错误,大多数与程序员的操作无关。Error很少出现。
- Exception(异常):异常是程序可以处理的异常。关于异常在下面详细讲解。
Error与Exception的区别:Error无法被程序处理,Exception可以被程序处理。
Exception的进一步分类:
Exception通常分大类:
- 运行时异常:Exception有一个重要的子类RuntimeException,所有RuntimeException类及其子类都是运行时异常。这类异常可以通过编译,在程序你可以处理这类异常,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序员应该尽量避免之类异常发生。常见的运行时异常:NullPointerException:空指针异常;ArrayIndexOutOfBoundsException:数组越界异常;ClassCastException:类型转换异常;NumberFormatException:数值格式化异常;InputMismatchException:输入的类型不匹配;ArithmaticException:算数异常;
- 编译时异常:Exception中除了子类RuntimeException其他的子类都是编译时异常。这类异常不能通过编译,在程序中你必须处理这些异常。常见的编译时异常:IOException:IO文件操作异常;SQLException:sql异常等,以及用户自定义的异常。
处理异常机制:
在java中提供了异常处理的:抓抛模型
过程一:“抛”:在程序运行中一旦出现异常就会在相应的代码处生成相应的异常类对象,并将此对象抛出
异常出现位置后面的代码都不在运行
异常对象产生的方式:1》自动抛出 2》手动抛出(后面会讲)
过程二:“抓”:异常的处理方式:try - catch - finally ; thows
代码实现:
语法:
try {//try中的代码写可能出现异常的代码
}
catch(ArithmeticException e)
{
//catch中写怎么处理预期的异常
//可以包含多个catch
}
finally {
// finally中写无论是否出现异常都会被执行的代码
}
我们先运行一段代码
运行结果:
出现ArithmeticException(算数异常),我们要处理这个运行异常就可以使用try-catch
public class ExceptionTest {
public static void main(String[] args) {
try { //可能出现异常的代码块
int a = 5/0;
System.out.println(a);
}catch(ArithmeticException e)//出现异常则调用此方法
{
System.err.println("出现异常了");
}
finally { // 无论是否出现异常都会执行的代码块
System.out.println("程序运行完成");
}
}
}
程序运行try,运行到 int a = 5/0;的时候,就会出现异常,在此处抛出相关的异常:ArithmeticException(算数异常)
catch代码则接收到异常并进行处理。最后在执行dinally代码块。而try代码块中出现异常后的代码不执行。我们看下运行结果
注意:
- try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。
- 可以存在多个catch块,每一个catch块用于处理一个异常。异常匹配是按照catch块的顺序从上往下寻找的,只有第一个匹配的catch会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。
Throws函数处理异常
如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。
使用throw手动抛出异常:
我们也可以使用throw语句手动抛出一个异常,throw语句后面必须写一个异常对象。throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。
public void save(Diary diary)
{
if(diary == null)
throw new IllegalArgumentException("diary对象为空");
//......
}
创建自定义的异常类:
如果要自定义异常类,则扩展继承Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。
按照国际惯例,自定义的异常应该总是包含如下的构造函数:
- 一个无参构造函数
- 一个带有String参数的构造函数,并传递给父类的构造函数。
- 一个带有String参数和Throwable参数,并都传递给父类构造函数
- 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
下面是IOException的源码,可以参考:
public class IOException extends Exception
{
static final long serialVersionUID = 7818375828146090155L;
public IOException()
{
super();
}
public IOException(String message)
{
super(message);
}
public IOException(String message, Throwable cause)
{
super(message, cause);
}
public IOException(Throwable cause)
{
super(cause);
}
}
注意:必须要写一个序列码
static final long serialVersionUID = 7818375828146090155L;
java 7 的try 新特性
可以在try后面加个小括号,在其中声明变量,当try代码块执行完成时,try后面的小括号的变量一定会被关闭。这样我们在写一些文件流时,就不用去注意关闭资源了。
public static void main(String[] args) {
try (FileReader fileReader = new FileReader(new File(""))){
int read = fileReader.read();
}catch (IOException e){
log.error("");
}
}