Java中异常知识点总结

什么是异常

异常,就是不正常的意思。指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。

  • 在Java等面向对象的编程语言中,异常本身是一个类产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
  • 异常指的并不是语法错误.语法错了,编译不通过,不会产生字节码文件,根本不能运行。但异常可以在运行时产生。

异常体系

在这里插入图片描述

Throwable体系:

异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error(错误)与java.lang.Exception(异常),平常所说的异常指java.lang.Exception。

  • Error:错误Error,无法处理纠正的错误,只能事先避免,好比绝症。
  • Exception:异常Exception,异常产生后程序员可以通过代码的方式纠正,使程序继续运行。好比感冒。

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置。

异常的分类

我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。

异常(Exception)的分类:根据在编译时期还是运行时期可分为编译时异常,和运行时异常。

  • 编译时期异常:(受查异常)在编译时期,就会检查,如果没有处理异常,则编译失败。(如输入输出、类型转换异常)
  • 运行时期异常:(非受查异常)在编译时期,不会报错,在运行时期会报错.。(如数学异常)

编译时期异常

常见的编译时异常

  • 1.FileNotFoundException
    当试图打开指定路径名表示的文件失败时,抛出此异常。
  • 2.ClassNotFoundException
    找不到具有指定名称的类的定义。
  • 3.SQLException
    提供有关数据库访问错误或其他错误的信息的异常。
  • 4.NoSuchFieldException
    有时候使用反射进行Field相关操作的时候会出现这样异常
  • 5.NoSuchMethodException
    找不到具体的方法
  • 6.ParseException

运行时异常

常见的运行时异常

  • 1.NullPointerException
    当应用程序试图在需要对象的地方使用 null 时,抛出该异常(空指针
  • 2.ArithmeticException
    当出现异常的运算条件时,抛出此异常。
  • 3.ClassCastException
    是类型转换错误,通常是进行强制类型转换时候出的错误。
  • 4.ArrayIndexOutOfBoundsException
    用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引(就是数组越界
  • 5.StringIndexOutOfBoundsException
    指示某排序索引(例如数组、字符串或者向量的排序)超出范围是抛出。

详情可以参考这位博主的文章https://blog.csdn.net/M_Jack/article/details/86557992

异常的用法

捕获异常

基本语法

try{
 有可能出现异常的语句 ;
}[catch (异常类型 异常对象) {
} ... ]
[finally {
 异常的出口
}]
  • try 代码块中放的是可能出现异常的代码.
  • catch 代码块中放的是出现异常后的处理行为.
  • finally 代码块中的代码用于处理善后工作, 会在最后执行.
  • 其中 catch 和 finally 都可以根据情况选择加或者不加

举例

  • 1.不处理异常
int[] arr = {1, 2, 3};
System.out.println("before");
System.out.println(arr[100]);
System.out.println("after");
// 执行结果
before
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100

  • 2.使用 try catch 后的程序执行过程
int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    System.out.println(arr[100]);
    System.out.println("after");
    //数组越界异常
} catch (ArrayIndexOutOfBoundsException e) {
    // 打印出现异常的调用栈
    e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
java.lang.ArrayIndexOutOfBoundsException: 100
 at demo02.Test.main(Test.java:10)
after try catch

我们发现, 一旦 try 中出现异常, 那么 try 代码块中的程序就不会继续执行(不会打印after), 而是交给 catch 中的代码来执行. catch 执
行完毕会继续往下执行.

  • 3.catch 只能处理对应种类的异常

我们修改了代码, 让代码抛出的是空指针异常.

int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    arr = null;
    System.out.println(arr[100]);
    System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
    e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
Exception in thread "main" java.lang.NullPointerException
 at demo02.Test.main(Test.java:11)

此时, catch 语句不能捕获到刚才的数组越界异常. 因为此时为空指针异常,异常类型不匹配.

  • 4.catch 可以有多个
int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    arr = null;
    System.out.println(arr[100]);
    System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
 System.out.println("这是个数组下标越界异常");
    e.printStackTrace();
} catch (NullPointerException e) {
    System.out.println("这是个空指针异常");
    e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
这是个空指针异常
java.lang.NullPointerException
 at demo02.Test.main(Test.java:12)
after try catch

一段代码可能会抛出多种不同的异常, 不同的异常有不同的处理方式. 因此可以搭配多个 catch 代码块.
如果多个异常的处理方式是完全相同, 也可以写成这样

catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
 ...
}
  • 5.也可以用一个 catch 捕获所有的异常(不推荐)
int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    arr = null;
    System.out.println(arr[100]);
    System.out.println("after");
} catch (Exception e) {
    e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
java.lang.NullPointerException
 at demo02.Test.main(Test.java:12)
after try catch

由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

  • 6.finally 表示最后的善后工作, 例如释放资源
Scannersc=new Scanner(System.in);
int n=sc.nextInt();
int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    arr = null;
    System.out.println(arr[100]);
    System.out.println("after");
} catch (Exception e) {
    e.printStackTrace();
} finally {
    sc.closed();
    System.out.println("finally code");
}
// 执行结果
before
java.lang.NullPointerException
 at demo02.Test.main(Test.java:12)
finally code

无论是否存在异常,finally中的代码一定都会执行到。保证最终一定会执行到Scannner的close方法

  • 7.如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递(如果上层也没有处理的话,最终会交给jvm解决,程序就会异常终止(和我们最开始未使用 try catch 时是一样的)
public static void main(String[] args) {
    func();
      System.out.println("after try catch");
}
public static void func() {
    int[] arr = {1, 2, 3};
    System.out.println(arr[100]);
}
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
 at demo02.Test.func(Test.java:14)
 at demo02.Test.main(Test.java:8)

可以看到, 程序已经异常终止了, 没有执行到 System.out.println("after try catch)这一行。

  • 8.捕捉异常的时候从上往下,先子类再父类
public void getCustomerInfo() {

    try {

      // do something that may cause an Exception

    } catch (java.io.FileNotFoundException ex) {

      System.out.print("FileNotFoundException!");

    } catch (java.io.IOException ex) {

      System.out.print("IOException!");

    } catch (java.lang.Exception ex) {

      System.out.print("Exception!");

    }

  }

异常处理的流程

  • 程序先执行 try 中的代码
  • 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配(找匹配).
  • 如果找到匹配的异常类型, 就会执行 catch 中的代码
  • 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  • 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  • 如果上层调用者也没有处理的了异常, 就继续向上传递. 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理,此时程序就会异常终止(jvm保底

关于 finally 的注意事项

public static void main(String[] args) {
 System.out.println(func());
}
public static int func() {
 try {
 return 10;
 } finally {
 return 20;
 }
}
// 执行结果
20 

注意:

  • finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.
  • 一般我们不建议在 finally 中写 return (被编译器当做一个警告)

抛出异常

除了 Java 内置的类会抛出一些异常之外, 程序猿也可以手动抛出某个异常. 使用 throw 关键字完成这个操作*(之前说过异常是一个类,可以产生对象).

public static void main(String[] args) {
  System.out.println(divide(10, 0));
}
public static int divide(int x, int y) {
  if (y == 0) {
  throw new ArithmeticException("抛出除 0 异常");
 }
  return x / y;
}
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: 抛出除 0 异常
 at demo02.Test.divide(Test.java:14)
 at demo02.Test.main(Test.java:9)

在这个代码中, 我们可以根据实际情况来抛出需要的异常. 在构造异常对象同时可以指定一些描述性信息.。

异常说明

我们在处理异常的时候, 通常希望知道这段代码中究竟会出现哪些可能的异常.我们可以使用 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常.

public static int divide(int x, int y) throws ArithmeticException {
 if (y == 0) {
 throw new ArithmeticException("抛出除 0 异常");
 }
 return x / y;
} 

自定义异常

Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.

分类

  • 自定义的运行时异常(继承RuntimeException)
class MyException extends  RuntimeException{
    public MyException(String message) {
        super(message);
    }
}
public class Test {
    public static void main(String[] args) {
        throw new MyException("自己定义运行时异常");
    }
}

Exception in thread “main” MyException: 自己定义运行时异常
at Test.main(Test.java:19)

  • 自定义的编译时异常(继承Exception)
class MyException1 extends  Exception{
    public MyException1(String message) {
        super(message);
    }
}
public class Test {
    public static void main(String[] args) throws MyException1 {
        throw new MyException1("自己定义的编译时异常");

    }
}
Exception in thread "main" MyException1: 自己定义的编译时异常
	at Test.main(Test.java:14)

举例

我们实现一个用户登陆功能.

public class Test {
 private static String userName = "admin";
 private static String password = "123456";
 public static void main(String[] args) {
 login("admin", "123456");
 }
 public static void login(String userName, String password) {
 if (!Test.userName.equals(userName)) {
 // TODO 处理用户名错误
 }
 if (!Test.password.equals(password)) {
 // TODO 处理密码错误
 }
 System.out.println("登陆成功");
 }
} 

此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们代码相关的异常类。

class UserException extends Exception {
    public UserException(String message) {
        super(message);
    }
}
class PasswordException extends Exception {
    public PasswordException(String message) {
        super(message);
    }
}

这两种都为编译时异常,我们在使用的时候需要主要。此时我们的 login 代码有两种写法
第一种在login中抛出异常,不在login中处理异常,但在后续的Test当中需要处理异常

public class Test {
    private static String userName = "admin";
    private static String password = "123456";

    //在logn这里只是声明,需要在test当中去处理异常
    public static void login(String userName, String password) throws UserException, PasswordException {
        if (!Test.userName.equals(userName)) {
            throw new UserException("用户名错误");
        }
        if (!Test.password.equals(password)) {
            throw new PasswordException("密码错误");
        }
    }
    public static void main(String[] args) {
        try {
            login("admin", "123456");
        } catch (UserException userException) {
            userException.printStackTrace();
        } catch (PasswordException passwordError) {
            passwordError.printStackTrace();
        }
    }
}

第二种在login当中直接就处理了,在后续的Test便不需要在处理了(我们建议用第二种写法)

public class Test {
    private static String userName = "admin";
    private static String password = "123456";
    //在login这里处理了异常,在Test1当中就不需要处理了
    public static void login(String userName, String password) {
        if (!Test.userName.equals(userName)) {
            try {
                throw new UserException("用户名错误");
            } catch (UserException e) {
                e.printStackTrace();
            }
        }
        if (!Test.password.equals(password)) {
            try {
                throw new PasswordException("密码错误");
            } catch (PasswordException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        login("admin", "123456");
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值