异常是程序运行过程过程出现的错误,在Java中用类来描述,用对象来表示具体的异常。 Java将其区分为Error与Exception,Error是程序无法处理的错误,Exception是程序可以处理的错误。 异常处理是为了程序的健壮性。
Thorwable类是所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。 其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常, 这两种异常有很大的区别,也称之为非检测异常(Unchecked Exception)和检测异常(Checked Exception)
下面来通过代码直观地看一下这两者的差别:
检查异常:
如上图所示,如果不对红线所存在的异常进行处理,该代码是无法通过编译的。必须对其进行throws或者try-catch:
如上图所示,对两个异常进行了try-catch处理,代码可以正常编译。那么这样的异常(FileNotFoundException和IOException)就叫作检查异常,也叫作非运行时异常,意思是在编译前就应该对该异常进行处理,否则无法通过编译。
非检查异常:
如上图所示,出现了ArrayIndexOutOfBoundException,这个异常是编译成功,运行时出现的异常,顾名思义,该异常意思是数组角标越界了,由于该异常是在运行时出现的异常,所以此类异常也叫作运行时异常,或非检查异常。类似的异常还有NullPointerException等。
自定义异常:
上面出现的几种异常不管是什么类型,都是Throwable的间接子类,都已经封装完毕,我们直接使用就行,但有时候我们系统根据需求会出现各种各样的异常,这时候就要自定义异常对象了。自定义异常,也是将异常进行封装,说白了就是用一个自定义的类对异常信息进行封装,该类继承Exception,只需要复写父类的构造方法即可,通过super将信息传入到参数。先来看看父类的构造是如何实现的:
Exception:
Throwable:
这就很明显了,我们在自定义异常类时,在构造方法中用super调用了该类的父类Exception中的构造方法,并将参数传入,在Exception中用super调用父类Throwable中的构造方法,并将参数传入,并将该实参值赋值给该类的detailMessage属性,在getMessage()方法中获取该值,所以也就有了"myException.getMessage()"(下文会提到)了,可以看得出来这个方法其实是调用的Throwable类中的方法。
假设当前有个需求:对于用户输入的用户名进行检测,如果用户名不是“张三”,则抛出我们自定义的异常对象myException,并打印异常信息。
自定义的异常类:
1 public class MyException extendsException{2
3 private static final long serialVersionUID = 1041819209403514525L;4
5 publicMyException(String message) {6 super(message);7 }8 }
controller层:
1 public classUserController {2 private UserService userService = newUserService();3
4 public User queryUser(String name,String password) throwsMyException {5 returnuserService.queryUser(name,password);6 }7 }
service层:
1 public classUserService {2 private UserDao userDao = newUserDao();3 public User queryUser(String name, String password) throwsMyException {4 returnuserDao.queryUser(name,password);5 }6 }
dao层:
1 public classUserDao {2
3 public User queryUser(String name, String password) throwsMyException {4 if (!"张三".equals(name)) {5 throw new MyException("输入的密码或者用户名错误");6 }7
8 User user = newUser();9 user.setUsername(name);10 user.setPassword(password);11
12 returnuser;13 }14 }
测试:
1 public classTest {2
3 public static voidmain(String[] args) {4 UserController userController = newUserController();5 try{6 User user = userController.queryUser("张大三", "111");7 System.out.println(user);8 } catch(MyException myException) {9 System.out.println(myException.getMessage());10 }11
12 }13 }
如上图所示:在测试类中调用controller中的方法queryUser(String name,String password)来查询用户,该方法内部调用的是service层的queryUser(String name,String password)方法,service中的该方法内部调用的是dao层的queryUser(String name,String password)方法,在dao层的该方法内部,对方法参数进行了判断,如果参数满足某条件就出现异常,将该异常抛出,并在方法声明部分进行throws声明,告知调用该方法的对象,该方法可能存在异常,你要调用,就要对其处理,至于处理的方法取决于调用者,要么直接在方法声明部分throws抛出,交给更上一层,要么进行try-catch,那么当前的调用者是谁呢,调用dao层该方法的是service层,那么service层中只是进行了throws,将异常继续向上抛了,同理,调用service的是controller,controller同样也是进行了throws,那么在测试类Test中调用controller层的方法,进行了try-catch处理,对可能出现异常的代码进行try,如果监听到的异常和catch捕获的异常对象对应,那么就对其处理,此处处理只是简单打印异常信息: