1. 异常的定义与体系结构
1.1 定义
语法错误,语意错误,不是异常。
1.2结构体系
异常分为两类:
-
Error:是Java虚拟机无法解决的严重问题,比如内存溢出(OutOfMemory),StackOverflowError 等。这些都是我们无法处理的。
-
Exception:这一类就是我们要处理的。这一类包括读取不存在的文件,网络链接中断等等等等。
处理异常的时间就是在编译的时候,但是有些异常只有在运行的时候才会发生,所以我们又把异常分为运行时异常,和编译时异常。Error是属于运行时异常。
1.2.1 Throwable
在java.lang包下,提供了一个类Throwable。
The Throwable
class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw
statement. Similarly, only this class or one of its subclasses can be the argument type in a catch
clause. For the purposes of compile-time checking of exceptions, Throwable
and any subclass of Throwable
that is not also a subclass of either RuntimeException
or Error
are regarded as checked exceptions.
1.2.2 Error
-
说明:An
Error
is a subclass ofThrowable
that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. TheThreadDeath
error, though a “normal” condition, is also a subclass ofError
because most applications should not try to catch it. -
一些子类:AnnotationFormatError;AssertionError, AWTError, CoderMalfunctionError, FactoryConfigurationError, FactoryConfigurationError, IOError, LinkageError, SchemaFactoryConfigurationError, ServiceConfigurationError, ThreadDeath, TransformerFactoryConfigurationError, VirtualMachineError
1.2.3 Exception
Exception有许多的子类,其中,除了RutimeException之外,都是编译时异常。而RuntimeException下也有自己的子类。
2. 常见异常
2.1 运行时异常
1. NullPointerException
int[] arr = null;
System.out.println(arr[0]);
2. ArrayIndexOutOfBoundsException
int[] arr = new int[10];
System.out.println(arr[10]);
3.StringIndexOutOfBoundsException
String str = "asd";
System.out.println(str.charAt(4));
4. ClassCastException
类型转换异常
Object arr = new ArrayList();
String str = (String)arr;
5. InputMismatchException
输入不匹配异常
Scanner scanner = new Scanner(System.in);
int input = scanner.nextInt(); //这里输入的不是int类型
scanner.close();
6. ArithmeticException
算数异常
int a = 1/0;
2.2 编译时异常
Properties prop = new Properties();
FileInputStream files = new FileInputStream("test.properties");
prop.load(files); // 将files对应的文件流加载到Properties对象中
String name = prop.getProperty("name");
String password = prop.getProperty("password");
System.out.println(name+password);
files.close();
这里出现了多个编译时异常:
因为是编译时异常,所以在没有运行的时候就会报错。idea编译器提醒我们,我们这里没有处理这些异常,只要我们加上处理异常的代码后就可以正常运行了。
3. 处理异常机制
如果我们想要处理这些异常的话,就要用到if-else,使得整个程序臃肿,可读性差。所以我们会采用异常处理机制。
3.1 抓抛模型
java提供的是异常处理的抓抛模型
抛:程序在正常运行中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,一旦抛出对象后,其后的代码将不再被执行。
抓:其实就是异常处理的方法,有两种:1. try-catch-finally, 2. throws
3.2 异常处理方式一:try-catch-finally
try{
//可能出现异常的代码
}catch(异常类型1 e1){//这个变量名是必须要写
//处理异常类型1的方法,比如print一个提示也可以
}catch(异常类型2 e2){
System.out.println(e2.getMessage());
}catch(异常类型3 e3){
//处理异常类型3的方法,比如print一个提示也可以
e3.printStackTrace();
}
...
finally{
//finally块中的代码是一定会执行的代码
}
-
try中产生的异常对象会与catch中的异常类型进行匹配,一旦匹配到某一个catch时,就会进入到该catch中执行。
-
catch中的异常对象可以是RuntimeException,这样的话,所有的运行时异常都会被捕获。但是有的时候我们想针对不同的异常进行不同的处理,这时就要将这个异常对象设置的更加的详细。
-
多个catch时,要注意异常类型的“范围”,子类异常类型放在前面,父类异常类型放在后面。不然后面的catch就没有机会执行了,而且它不像if-else。if-else不会报错,但在catch中,父类异常类型放在子类异常类型前面的话会报错。
-
可以使用 e.getMessage() 来返回一个String类型的报错信息。返回的报错信息比较简单
-
也可以使用 e.printStackTrace() 来直接打印报错信息。报错信息就非常详细,它包含来getMessage里面的信息
-
finally 是可选的,这个代码块中的代码是一定会被执行的代码。在,但不限于以下情况,finally的价值就会被体现出来:catch中又出现异常了;try中又return语句,catch中有return语句。就算是这种情况finally块中的代码也会被执行
-
JVM的垃圾回收机制对一些物理的连接是无能为力的,例如 数据库连接,输入输出流,Socket等。所以需要我们手动关闭,为了防止异常的出现导致这一类代码被影响,所以我们把类似于这样的代码,即:我们需要手动释放的资源都要放在finally里面
3.3 异常处理方式二:Throws
-
Throws + 异常类型1, 异常类型2 … 是写在方法的声明处,指明执行该方法时会出现的异常类型,一旦方法体执行出了异常,也会在异常处生成一个异常类的对象,此对象如果满足Throws后面的异常类型时,就会被抛出。 异常代码后面的代码将不再被执行。
-
异常会被抛给该方法的调用者,我们可以在这个调用者中使用Try-catch进行处理,也可以在向上级抛出。当异常抛出至main方法,而main方法也没有使用try-catch进行处理,也只是使用Throws,则异常将被抛给JVM,如果JVM不能解决,则整个程序将挂掉。
-
重写之后的方法抛出的异常类型,不能大于父类的异常类型。
-
如果父类被重写的方法没有使用throws,则子类重写的方法不能使用throws。 反之,子类中重写的方法必须使用throws。
4. 手动抛出异常 throw
产生异常对象有两种方法:
-
是系统自动产生,上面的例子全部都是系统在异常代码处自动生成一个异常对象并抛出。
-
第二种就是我们手动生成一个异常对象,并抛出(throw)
有些时候我们希望在某些情况下抛出异常,但是系统可能并不会自动抛出,这时我们就需要自己手动抛出异常。
// 例子,希望当int name为负数时,抛出一个异常
if(name >= 0 ){
doSomething();
}
else{
throw new RuntimeExcption("请不要输入负数");
}
我们在自己手动throw异常时,我们new的异常对象一般在 RuntimeException 和 Exception 之间选。也可以是自己定义的异常类。
5. 用户自定义异常类
5.1 如何自定义异常
-
继承现有的异常类型,主要是两个 RunetimeException,Exception。
-
必须提供静态全局常量:serialVersionUID,用于唯一标示我们定义的这个异常类,在序列化的时候会有用。
static final long serialVersionUID = -7034897190745766939L;
-
根据需要,重载一些构造器。