一.异常的分类
一.异常类的继承图
1.所有的异常对象都是派生于Throwable类
2.error类表示java内部错误,出现这样的问题一般不需要我们解决,也没法解决。
3.Exception类及其子类,Exception主要有两个子类,IOException和RuntimeException。
IOException是IO错误导致的。RuntimeException类异常表示运行时异常,可以加以避免。
二.检查类异常(checked)和非检查类异常(unchecked)
检查类异常(checked)就是指在编译过程中,编译器就会报错,程序员必须去解决
非检查类异常(unchecked)是指运行时候才会报错的异常
其中:error类和RuntimeException类属于非检查异常类(unchecked)
而IOException属于检查类异常。
二.运行时异常和解决方法
运行时异常为非检查类异常,编译过程中不会报错,但运行时会报错,一般可以使用逻辑代码来解决这样的异常。总之 运行时异常是需要程序员去检查和处理的。
例如
1.除以0异常
2.类强制转换异常
3.空指针异常
4.数组越界异常
5.数字格式化异常
三.异常的处理过程
下面先来看一段例子
此处的异常应该属于RuntimeException,是unchecked异常(除以0异常)。
这段程序中异常处理的过程应该是这样的:main方法中调用run()方法;于是进入run()方法内部,执行int a = 10/0;产生异常,但是在本方法中未找到处理,于是返回调用处,也就是main方法,但是main方法中依然没有处理方法,于是只能默认打印异常信息。
四.异常处理1:捕获try_catch_finally
捕获异常,我们使用try catch 关键字,
try中代码表示可能出现异常的代码块,
catch中的代码块表示捕捉到该异常之后需要进行的操作。
finally表示无论有无异常,都会执行的内容。
此时,当我们使用Java代码想要读取一个文件中的字符时,这里就发生了check异常,这是因为我们调用了FileReader这个类,这个类在源代码中本身就是抛出异常的,抛出异常后,本身不做处理,交给调用者取处理,这里调用者就是我们的readFile()方法。
我们可以使用try catch捕获异常 或者继续throws 抛出 ,这里我们使用try catch语句。
在try catch语句中可以有多个catch来处理不同的异常,但是顺序要从上到下,由子类到父类的顺序来编写,(子类异常在前,父类异常在后)
显然在本案例中,try语句块的第一句出现了文件没有找到异常,之后从上到下寻找对应的catch,当执行完第一个对应的catch语句后,之后的catch就不在在被执行,直接到finally语句块。不仅如此,当try语句块中发现异常之后,try语句块之后的代码也不会再执行。
当第一句话发生异常的时候,temp1已经之后的try语句就都没有在执行。对应的执行了相应的catch语句块,temp3并没有被执行。最后执行到finally的关闭指令。
五.抛出异常 throws
try catch语句可以用来处理方法中的异常,但是我们也可以暂时不处理可能会发生的异常
,选择是向上抛出异常(向调用出抛出,让调用者处理),而本身不做处理。
在run方法中,将可能会发生的数组溢出的异常直接抛出,交给调用者取处理,就是我们的main方法,main方法中可以继续抛出异常,将异常抛给JRE,然后JRE默认打印异常信息,也可以我们自己来处理异常 用trycatch
六.手动抛出异常 throw
程序执行到throw语句的时候立即停止,后面的语句都不会执行。
七.自定义异常
自定义类只需要从Exception类和他的子类中或者它的子类派生一个类
自定义异常类如果继承Exception类,则为受检查异常,必须对其进行异常处理;
如果不想处理,可以让自定义异常类继承运行时异常RuntimeException
习惯上 ,自定义异常类应该包含2个构造器:一个是默认的构造器。另一个是带有详细信息的构造器
实例1 继承Exception类 ,其类型为check异常,也就是手动抛出这个类后,必须进行抛出或者trycatch处理
public class define2 {
public static void main(String[] args) {
Student s = new Student();
s.setAge(140);
System.out.println(s.getAge());
}
}
class Student{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if(age<=0||age>=130) {
try { //必须进行处理
throw new illegalAge("年龄输入错误");
} catch (illegalAge e) {
e.printStackTrace();
}
}
this.age = age;
}
}
class illegalAge extends Exception{
public illegalAge() { //一个是默认的构造器
}
public illegalAge(String sth) { //另一个是带有详细信息的构造器
super(sth);
}
}
实例2 继承RunTimeException类 ,其类型为uncheck异常,也就是手动抛出这个类后,并不是必须进行抛出或者trycatch处理
public class define2 {
public static void main(String[] args) {
Student s = new Student();
s.setAge(140);
System.out.println(s.getAge());
}
}
class Student{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if(age<=0||age>=130) {
throw new illegalAge("错误了");
}
this.age = age;
}
}
class illegalAge extends RuntimeException{
public illegalAge() {
}
public illegalAge(String sth) {
super(sth);
}
}
运行结果
八.异常机制的实现细节
1.trycatch语句块中。可以由多个catch语句的,对多个不同的异常进行捕获
2.finally这个关键字中如果出现return等关键字程序的接下来的执行顺序问题。
public class TestReturn {
public static int test() {
int b = 0;
try {
int a = 1/0;
return b=1;
}catch (RuntimeException e) {
System.out.println("出错了");
return b=2;
}finally {
return b=3;
}
}
public static void main(String[] args) {
System.out.println(test());
}
}
运行结果
只要知道finally语句块中是最后执行的就可以理解了,当try中出现异常,被catch接住,将b赋值为2,在最后跳转到finally语句块中,最后在finally语句块中返回3。
这种
在finally语句块中有return语句的会阻止catch中的return执行。
九.异常的使用原则
1.在当前方法声明中使用trycatch语句来捕获异常
2.一个方法被重写(覆盖)时,重写的方法必须抛出相同的异常或者是该异常的子类
3.如果父类抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新异常。