@TOC
前言
异常和break有一点点类似,都可以让程序停止运行。异常的发生和人生病是一个道理,处理异常就是看病过程的一种抽象形式,异常处理保证了程序的健壮性,在以后的开发过程中有着举足轻重的作用。当我深入了解异常,发现原来每一种异常都是一个一个进行封装过的类,他们也存在着继承关系。其实,异常也是面向对象特性的一种体现!
一.异常分类
异常(Exception):因编程错误或者偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
异常分为==运行异常==和==编译异常==
可以看到,==异常体系其实是一个继承关系==,常见的各种异常都是父类Exception的实现子类,而类所具有的特征Exception类也同样具有,所以我们可以通过实例化异常类的方式来设计异常
二.常见运行异常与编译异常
编译异常:(程序员写代码过程中提示出来的异常)
==SQLException== 操作数据库时,查询表可能发生异常
==IOException== 操作文件时,发生的异常
==FileNotFoundException== 当操作一个不存在的文件时,发生异常
==ClassNotFoundException== 加载类,而该类不存在时,异常
==EOFException== 操作文件,到文件末尾,发生异常
==IllegalArguementException== 参数异常
运行异常:(程序运行时报错显示的异常)
==NullPointerException==空指针异常
==ArithmeticException==数学运算异常
==ArraylndexOutOfBoundsException==数组下标越界异常
==ClassCastException==类型转换异常
==NumberFormatException==数字格式不正确异常[]
三.异常处理
我们都知道,当程序遇到异常时会终止,在大项目的开发中,很难做到不犯任何错误。设想:如果以后开发的大项目出了一点错误就不能运行,那程序的健壮性太差!这就好比一个男人经受一点风吹雨打就卧床不起,太不应该。以下是几种常见的处理异常的方式,使用时选择其中一种就可!
1.try-catch-finally(🚩)
所以Java设计者们设计了==异常处理机制==,这就好比在给程序打补丁,兜底。
语法:
try {
可能发生异常的代码...
}catch(异常) { //捕获到异常
//处理
}finally{
//始终要执行的代码
}
==注意事项:==
🟢当异常发生时系统将异常封装成Exception对象e,传递给catch
🟢得到异常对象后,程序员,自己处理
🟢如果没有发生异常catch代码块不执行
🟢使不管try代码块是否有异常发生,始终要执行finally(所以,通常将释放资源的代码,放在finally部分)
public static void main(String[] args) {
try {
String a = "彭于晏";
int b = Integer.parseInt(a); //这里会出现异常
} catch (Exception e) {
// e.printStackTrace();
System.out.println("异常信息:"+e.getMessage());
}finally{
System.out.println("finally代码块被执行");
}
System.out.println("程序继续~~~");
}
▶️运行结果:
我们看到,在程序出现异常的情况下依然完整执行,并且给出了异常内容,这就是机制的妙处所在
🟢可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后, NullPointerException在前),如果发生异常,只会匹配一个catch:
例如:
在对输入的参数转换为整数并进行除法运算的过程中,会出现很多异常,要根据不同的输入,输出对应的异常,这个需求就体现多个catch语句的好处
public class Play {
public static void main(String[] args) {
try{
Scanner scanner=new Scanner(System.in);
String str1=scanner.next();
String str2=scanner.next();
int n1 = Integer.parseInt(str1);
int n2 = Integer.parseInt(str2);
double res=cal(n1,n2);//该方法可能抛出ArithmeticException
System.out.println("计算结果是:"+res);
}catch (NumberFormatException e) {
System.out.println("输入参数格式不正确,重新输入");
}catch(ArithmeticException e){
System.out.println("出现了除0的异常~");
}catch(Exception e){ //这个必须写在子类异常后
System.out.println("出现了异常~");
}
}
public static double cal(int n1, int n2) {
return n1 / n2;
}
}
▶️运行结果:
==ArithmeticException==异常
==NumberFormatException==异常
==根据不同用户的输入,抛出相应的异常这就是try-catch机制的一个重要作用!==
**try-catch-finally执行顺序:
1.当未出现异常时:执行try块中的所有语句,不执行catch块中的语句,如果有finally,最后要执行finally块中的语句
2.出现异常时:try块中的异常发生后,try块中剩下的语句不再执行,将执行catch块中的语句,同理如果有finally,最后要执行finally块里的语句**
2.throws(🚩)
==throws(抛出)==:在代码中理解成一种抛出难题的行为,它的作用是通过把异常抛给别的类或者方法让当前的部分不受该异常的影响
==throws注意事项:==
🟢存在继承关系的两个类,子类重写父类方法时,所抛出的异常类型要么和父类抛出异常类型一致,要么为父类抛出的异常类型的子类型
例如:
class Father{
public void f1() throws Exception{...}
}
class Son extends Father{
@Override
public void f1() throws RuntimeException {...} //或者抛出Exception
}
🟢调用存在异常的方法时要处理他的异常,否则会报错,若是运行异常可以不用显式处理
例如:
class A{
public static void f4() throws FileNotFoundException{
try {
f3();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void f3() throws FileNotFoundException { //抛出一个编译异常
FileInputStream fis=new FileInputStream("xxx");
}
}
处理的方式可以是try-catch也可以是throws
🟢可以直接访问同类中抛出运行异常的方法,因为==运行异常会被java默认处理==
例如:
class B{
public static void f5(){
f6();
}
public static void f6() throws ArithmeticException{}
}
3.try-finally
这个用法相当于没有捕获异常,程序会崩掉
try{
可能出现异常的代码
}finally{
总是执行的代码
}
四.自定义设计异常
我们可以通过继承Exception类的方式来自定义异常,一般情况下,我们自定义异常是继承RuntimeException(运行异常)
即把自定义异常做成运行时异常,好处是我们可以使用Java默认的处理机制!!!
例如:
自定义一个异常类通过实例化它,向构造器中传入自己定义的字符来初始化,达到程序员自定义输出异常内容的目的
public class CustomException {
public static void main(String[] args) {
//要求输入一个年龄在18-120,否则抛出一个异常
Scanner scanner=new Scanner(System.in);
System.out.println("请输入年龄:");
double age= scanner.nextInt();
if(!(age>=18&&age<=120)){
throw new AgeException("您输入的年龄有误!");
}
System.out.println("你的年龄符合~");
}
}
//自定义一个异常
class AgeException extends RuntimeException{ //继承运行异常 便于处理
public AgeException(String message) { //构造器
super(message);
}
}
▶️运行结果: