Java中的一切不正常的行为,都属于异常(Exception)或错误(Error)。不管是Exception还是Error,都是一个类,都继承于Throwable这个类。
Error类
Error是指JVM虚拟机中无法解决的严重问题,一般造成Error的原因都是设计程序的逻辑出现了问题,比如StackOverflowError(栈溢出),就是典型的逻辑设计出现了问题。这种时候只能放弃这种逻辑。
Exception类
异常一般不需要更换设计思路,可以通过代码来处理这些异常。异常一般发生在某个小范围区域,因此只需要“对症下药”。
异常分为编译时异常(受查异常)和运行时异常(非受查异常)。
编译时异常,就是在编译阶段可以发现的异常,如果发生了编译时异常,那么编译将不能通过,只有先处理好这个异常后才能编译通过。也就是说,当你写好代码后,代码的下方会直接标出红色波浪线提醒你处理异常。比较常见的异常有IOException、FileNotFoundException、ClassNotFoundException、CloneNotSupportedException等
运行时异常在编译阶段不会发现,只有真正的运行之后,才会检测出相应的异常并停止。
运行时异常(RuntimeException)中比较常见的有:ArithmeticException(算数异常)、ArrayIndexOutOfBoundsException(数组越界异常)、NullPoninterException(空指针异常)等。
异常处理
异常的处理一般有两种方式,要么每一行代码的实现都进行一次判断,满足条件后程序才能继续执行;要么利用Java中的关键字try、catch、finally、throw、throws将正常的部分和错误的部分分开,正常的部分正常执行,出现异常的部分,在程序结束后抛出异常。
显然第一种方式不太好用,一旦出现异常,整个程序都不能运行,因此在实际过程中通常采用第二种方式处理异常。
异常的抛出和捕获
要得到异常的信息,就需要抛出异常和捕获异常。抛出异常需要用到throw关键字。具体语法为:
throw new XXXExpection("异常产生的原因");//XXXException为某个异常类
一般配合if等条件语句使用,例如:
int[] array = null;
if (array == null) {
throw new NullPointerException();
}
当然,只是抛出异常是不够的,有抛出就要有捕获,捕获异常后才能告知使用者异常的信息。捕获异常需要用到try、catch、finally关键字,具体用法:
try {
//需要检查的代码
//一旦捕获到某个异常,无论try中的代码有没有执行完,都将停止执行,并跳到finally部分
}catch(异常类 对象名) {
对象名.printStackTrace();
}catch(不同的异常类 对象名) {
对象名.printStackTrace();
}finally {
//无论是否出现异常,该部分都会执行
}
如果try内部的代码存在的异常没有在捕获的范围内,可以在finally前加上Exception类以确保可以捕获到所有的异常:
catch(Exception e) {
e.printStackTrace();
}
如果没有写捕获所有的异常类,同时又存在捕获范围外的异常,那么异常处理会交给JVM处理,交给JVM处理虽然也能获得异常信息,但是程序会强行中止。
当我们要处理的异常造成的影响或后果比较严重时,这时就需要强行终止程序或者使程序崩溃;而如果这个异常造成的影响或后果较小或者只是小范围内有影响,就考虑使用try、catch等关键字来处理。
当方法中抛出编译时异常,如果不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理,用法:
修饰限定符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2……
throws的作用仅仅是声明,它不会处理异常,仅提醒方法的调用者处理异常。声明的异常如果存在父子类关系,可以只声明父类。但这种情况下使用catch捕获异常只能先捕获子类异常再捕获父类异常,否则发生语法错误。
自定义异常类
实际开发过程中,Java内置的异常类往往不能满足实际的需求,比如说要设计一个登录器,需要捕获用户名或密码错误异常,显然Java内置的异常类并不能满足需求,那么就需要定义一个用户名错误异常类和密码错误异常类:
public class UsernamesException extends RuntimeException{
//很明显UsernamesException属于受查异常,
//因此需要继承于RuntimeException类,使它变为一个异常类
public UsernamesException() {
}
public UsernamesException(String message) {
super(message);
}
}
//密码错误异常类同理
自定义异常类的抛出和捕获方法与Java自带异常类别无二致。
实现一个登录功能:
public class User {
private String username = "zhangsan";
private String password = "123456789";
public void login(String username, String password) {
if (!this.username.equals(username)) {
throw new UsernamesException("用户名错误或用户名不存在!");
}
if (!this.password.equals(password)) {
throw new PasswordsException("密码错误!");
}
}
public static void main(String[] args) throws UsernamesException, PasswordsException{
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
User user = new User();
String setUsername = scanner.nextLine();
String setPassword = scanner.nextLine();
try {
user.login(setUsername, setPassword);
System.out.println("登陆成功!");
}catch(UsernamesException | PasswordsException e) {
e.printStackTrace();
}finally {
System.out.println("关闭无用资源……");
}
}
}
}
注:如果自定义异常类继承Exception类,则默认属于编译时异常。