Java异常处理
1.异常的定义
异常:Java语言中,将程序执行中发生的不正常情况称为异常
2.异常的分类
异常分为Error和Exception两类
- Error:Java虚拟机无法解决的严重问题
例如:- StackOverflowError 栈溢出
- OutOfMemoryError 堆溢出
- Exception:其他因为编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
例如- 空指针访问
- 试图读取不存在的文件
- 网络连接中断
- 数组越界
Exception的分类
- 编译时异常(checked)
- IOException
- ClassNotFoundException
- 运行时异常(unchecked)
- NullPointerException
- ArrayIndexOutOfBoundsException
- StringIndexOutOfBoundException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
3.异常的处理:抓抛模型
- “抛”:程序在执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出
一旦抛出对象后,后面的代码不再执行 - “抓”:异常的处理方式
- try-catch-finally
- throws
4. try-catch-finally
- 使用try将可能出现异常的代码包装起来。在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中匹配
- 一旦try中的异常对象匹配到某一个catch是,就进入catch中进行异常的处理。一旦处理完成,就跳出try-catch结构(在没有写finally的情况下)
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}finally{
//一定会执行的代码
}
- 常用的异常对象处理方法
- String getMessage();
- void printStackTrace();
System.out.println(e.getMessage);
e.printStackTrace();
说明:
- finally是可选的,不一定是必须的
- catch中的异常类型,如果没有子父类关系,声明的先后关系无所谓
如果存在子父类关系,则父类异常声明在下面,(否则子类的异常会变成unreachable) - try中声明的变量,除了try结构之后,就不能使用了
若想要在外面使用,就在外面声明这个变量,有可能出现的赋值、计算操作放在try结构里面 - 使用try-catch-finally处理编译时异常,程序在编译时就不会报错,但是运行时仍然可能报错。相当于将编译的异常,延迟到运行时出现。
- try-catch-finally结构也是可以嵌套的
5.finally
finally中的代码一定会被执行,即使catch中又出现异常、try中有return、catch中有return
//try-catch-finally中finally的使用
public class FinallyTest {
public static void main(String[] args) {
// try{
// int a = 1;
// int b = 0;
// System.out.println(a/b);
// }catch(ArithmeticException e){
// e.printStackTrace();
// }finally{
// System.out.println("finally中的代码一定会被执行");
// }
FinallyTest test = new FinallyTest();
test.method();
}
//即使try,catch中有return语句,finally中的代码也会被执行
public int method(){
try{
int[] arr = new int[10];
System.out.println(arr[11]);
return 1;
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
return 2;
}finally{
System.out.println("finally中的代码一定会被执行");
}
}
}
上面的finally是return之前最后执行的
- 例如数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的。我们需要自己手动的进行资源的释放。此时资源的释放,就需要声明在finally中
下面是Java文件操作的一次尝试
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) {
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
//data == -1表示读到文件末尾了
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//不管有没有catch,最后都要把文件资源关闭
//所以必须写在finally中
try{
if (fis != null) {
fis.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
注意
- 运行时异常比较常见,所以通常不针对运行时异常写try-catch
针对编译时异常,我们一定要考虑异常处理(不处理IDE不让运行代码) - 运行时异常一般时代码跑了之后知道的
6. throws + 异常类型
throws是写在方法的声明后面,指明此方法可能会抛出的异常类型,由方法的调用者负责处理
异常代码后的部分不会被执行了
public void readFile(String fileName) throws FileNotFoundException,IOException{
}
- try-catch-finally是解决问题,throws是将异常抛给调用者,并没有解决异常
6.2子父类方法重写中异常抛出
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
//子类中重写的方法抛出的异常类型不大于父类中被重写的异常类型
import java.io.FileNotFoundException;
import java.io.IOException;
public class OverrideTest {
}
class SuperClass{
void method() throws IOException{
}
}
//子类重写方法的抛出异常类型不大于父类抛出的异常类型
class SubClass extends SuperClass{
void method() throws FileNotFoundException{
}
}
7. 选择try-catch还是throws
- 如果父类中被重写方法没有抛出异常,那么子类重写方法也不能使用throws,意味着如果子类重写的方法如果出现异常,必须使用try-catch自己解决掉
- 假设方法X中,执行了有递进关系的方法A,B,C,建议这几个方法用throws的方式进行处理,而在方法X使用try-catch对异常进行处理
8.手动抛出异常对象
上面介绍的都是系统自动生成的异常,
下面介绍手动生成(new)一个异常对象,并抛出throw(没有s)
一般new的只有两种
- Exception
- RuntimeException
public class ThrowTest {
public static void main(String[] args) {
Student stu = new Student();
try {
stu.setId(-1000);
System.out.println(stu.toString());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
//手动抛出异常throw
public void setId(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
throw new Exception("输入的id不能为负数");
}
}
@Override
public String toString() {
return "id = " + id;
}
}
9.自定义异常
如何自定义异常类
- 继承现有的异常结构:RuntimeException、Exception
- 提供全局常量static final long serialVersionUID
- 提供一个重载的构造器
public class MyException extends RuntimeException{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}