问题:

1、常见的异常有哪些

2、遇到异常是如何解决的?有几种解决方式?

一、异常概述与异常体系结构

1、异常 :在Java语言中,将程序执行中发生的不正常情况称之为“异常”。
注:开发过程中的语法错误和逻辑错误不是属于异常
2、java程序在执行的过程中所发生的异常事件可分为两类:
2.1 Error
Java 虚拟机无法解决的严重问题。如: JVM 系统内部错误 、资源 耗尽等严重 情况 。比如: StackOverflowError OOM 。一般不编写针对性 的代码进行处理 。
StackOverflowError

  1. /**
  2. * StackOverflowError 栈溢出
  3. * @author dongxinxin
  4. * @create 2022-03-05 14:59
  5. */
  6. public class ErrorTest01 {
  7. public static void main(String[] args) {
  8. main(args);
  9. }
  10. }
  11. Error 异常
  12. Exception in thread "main" java.lang.StackOverflowError
  13. at com.java.test01.ErrorDemo01.main(ErrorDemo01.java:9)

OOM

  1. /**
  2. * OOM 异常
  3. * 堆内存不足
  4. * @author dongxinxin
  5. * @create 2022-03-05 15:13
  6. */
  7. public class ErrorDemo01 {
  8. public static void main(String[] args) {
  9. Integer[] arr = new Integer[1024 * 1024 * 1024];
  10. }
  11. }
  12. OOM 异常
  13. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  14. at com.java.test01.ErrorDemo01.main(ErrorDemo01.java:10)

2.2 Exception
其它因编程错误或偶然的外在素导致一般性问题 ,可以使 用针对性的代码进行处理 。例如:
空指针异常 如果创建一个对象,赋值为null,再用对象调用该类中的属性
IO异常
数组角标越界
3、如何解决这些写错误
对于这些错误 ,一般有两种 解决方法 :一是遇到错误就终止程序 的运行 。另一种方法是由程序员在编写时 ,就考虑到错误的检测 、错误消息的提示 ,以及错误的处理 。
捕获错误最理想的是在编译期间 ,但有的错误只在运行时才会发生 。 比如: 除数为 0,数组下标越界等
可分为:编译时异常和运行时异常
image.png
受检异常:编译异常

  1. /**
  2. * 编译时异常的一种情况
  3. *
  4. * 解决办法:使用try-catch-finally机制解决
  5. */
  6. public static void test01(){
  7. File file = null;
  8. FileInputStream fis = null;
  9. try {
  10. file = new File("a.txt");
  11. fis = new FileInputStream(file);
  12. //读取数据
  13. int data = fis.read();
  14. while (data != -1) {// 读取数据的一个逻辑
  15. System.out.print((char)data);
  16. data = fis.read();
  17. }
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } finally {
  21. try {
  22. if(fis != null){
  23. fis.close();
  24. }
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }

非受检异常:运行时异常

  1. /**
  2. * 异常体系结构
  3. * java.lang.throwable
  4. * Error: 一般不需要编写代码去解决
  5. * Exception:可以进行异常处理,需要进行异常处理
  6. * 编译时异常(checked):
  7. * IOException
  8. * FileException
  9. * ClassNotFoundException
  10. * 运行时异常(unchecked):
  11. * NullPointException
  12. * ArrayIndexOutOfBoundsException
  13. * ArithmeticException(算术异常)等
  14. * @author dongxinxin
  15. * @create 2022-03-05 15:25
  16. */
  17. public class ExceptionTest01 {
  18. public static void main(String[] args) {
  19. Integer[] arr = new Integer[2];
  20. // System.out.println(arr[2]);// ArrayIndexOutOfBoundsException
  21. // String[] str = null;
  22. // System.out.println(str[2]);// NullPointerException
  23. Object date = new Date();
  24. // String str = (String)date;// ClassCastException 类型转换异常
  25. String str = "123";
  26. String str1 = "abc";
  27. // int parseInt = Integer.parseInt(str1);// NumberFormatException
  28. }

二、常见异常

三、异常处理机制:try-catch-finally

使用try-catch-finally处理编译时异常

  1. /**
  2. * 异常的处理:抓抛模型
  3. *
  4. * “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,
  5. * 并将此对象抛出
  6. *
  7. * “抓”:异常的处理方式:
  8. * 方式一:try-catch-finally
  9. * try {
  10. * // 可能出现异常的代码
  11. * } catch(异常类型1 变量名) {
  12. * //处理异常
  13. * }catch(异常类型2 变量名) {
  14. * //处理异常
  15. * }···{
  16. *
  17. * } finally { // 不一定非得写
  18. * // 一定会执行的代码
  19. * }
  20. * 说明:
  21. * finally 可选
  22. * catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓
  23. * 如果有子父类关系,则子类声明一定在父类的上面,否则报错
  24. * 在try结构中定义中的变量,try外不可以用,如果外面想用,我们可以在try外声明
  25. * 注:为了避免报错,要给try外定义的变量要进行初始化
  26. * 方式二:throws
  27. * @author dongxinxin
  28. * @create 2022-03-05 16:17
  29. */
  30. public class ExceptionTest02 {
  31. public static void runExceptionTest(){
  32. String str = "123";
  33. str = "abc";
  34. try {
  35. int parseInt = Integer.parseInt(str);
  36. System.out.println("hello ------ 1"); // 不会去执行
  37. } catch (NumberFormatException e) {
  38. // e.printStackTrace();
  39. // System.out.println("出现数制转换异常");
  40. e.printStackTrace();
  41. }
  42. System.out.println("hello ------ 2");
  43. }
  44. public static void main(String[] args) {
  45. runExceptionTest();
  46. }
  47. }

finally的使用

  1. /**
  2. * try-catch-finally中的finally的使用
  3. * 1、finally是可选的
  4. * @author dongxinxin
  5. * @create 2022-03-05 20:29
  6. */
  7. public class FinallyTest {
  8. /**
  9. * finally 必须执行的
  10. */
  11. public static void finallyTest01(){
  12. try {
  13. int a = 10;
  14. int b = 0;
  15. System.out.println(a / b);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. } finally {
  19. System.out.println("你好");
  20. }
  21. }
  22. /**
  23. * finally 中的return的使用
  24. * @return
  25. */
  26. public static int finallyTest02(){
  27. try {
  28. int a = 10;
  29. return a;
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. } finally {
  33. return 0;// 作为最终的返回值结果
  34. }
  35. }
  36. public static void main(String[] args) {
  37. System.out.println(finallyTest02());
  38. }
  39. }

image.png
一种关于finally的小注意点:
如果try中出现异常,我们在catch中解决异常,但是如果catch中出现异常,后面的finally中地代码是否会被执行?

  1. /**
  2. * 如果try-catch中有异常我在catch中处理了,
  3. * 如果catch中有异常,按道理来说,程序应该结束,不应该再去执行了,
  4. * 但是这种情况下finally中的代码还回去执行,因为finally中表示的是最后一定要执行的
  5. * 如果没有finally则直接程序结束
  6. *
  7. * 该方法的执行结果是:
  8. * 我是finally中的代码
  9. * Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
  10. */
  11. public static void finallyTest03(){
  12. try{
  13. int a = 10;
  14. int b = 0;
  15. System.out.println("我是try中的代码:" + (a / b));
  16. }catch (ArithmeticException e){
  17. Integer[] arr = new Integer[10];
  18. System.out.println("我是catch中的代码:" + arr[10]);
  19. }finally {
  20. System.out.println("我是finally中的代码");
  21. }
  22. }
  23. /**
  24. * 如果try-catch中有异常我在catch中处理了,
  25. * 如果catch中有异常,按道理来说,程序应该结束,不应该再去执行了,
  26. * 但是这种情况下finally中的代码还回去执行,因为finally中表示的是最后一定要执行的 情况1
  27. * 但是如果没有finally,则程序结束,不会再去执行try-catch-finally之外的代码了 情况2
  28. * 如果有finally,则也不会执行finally之外的程序,但是finally之内的是要进行处理的 情况3
  29. *
  30. * 执行结果:
  31. * 情况2 :Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
  32. * 情况3:我是finally中的代码
  33. * Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
  34. */
  35. public static void finallyTest04(){
  36. try {
  37. int a = 10;
  38. int b = 0;
  39. System.out.println("我是try中的代码:" + (a / b));
  40. } catch (ArithmeticException e){
  41. Integer[] arr = new Integer[10];
  42. System.out.println("我是catch中的代码:" + arr[10]);// ArrayIndexOutOfBoundsException
  43. } finally {
  44. System.out.println("我是finally中的代码");
  45. }
  46. System.out.println("我是try-catch执行完之后的代码");// 不会去执行的
  47. }
  48. public static void main(String[] args) {
  49. finallyTest04();
  50. }

什么情况下才把代码写在finally中呢?
1、比如像流操作
2、数据库中的连接资源
3、网络编程中的socket等

  1. /**
  2. * finally中必须写的代码
  3. * 以下出现的错误都会在 catch 中处理
  4. * 情况一:
  5. * 如果在流操作当中try中的 1 出会出现错误,则 fis 流不会创建,直接到 5
  6. * 情况二:
  7. * 如果try中 1 没有出现错误,则 fis 创建成功,接着往下执行代码 执行到 2 ,
  8. * 如果 2 没有出现错误继续执行,执行到 3 ,有错误,则跳到 catch 当中,如果我将fis.close
  9. * 关闭操作放在 4,则是不是不会执行,因为刚才执行到 3 时,跳到 catch 当中处理了错误,
  10. * 因此岂不是造成了资源的浪费,只好将关闭资源的操作放在 finally 当中
  11. */
  12. public static void finallyTest05(){
  13. File file = null;
  14. FileInputStream fis = null;
  15. try {
  16. file = new File("hello.txt");
  17. fis = new FileInputStream(file);// 会出现编译错误 1
  18. int read = fis.read();// 会出现编译错误 2
  19. while (read != -1) {
  20. System.out.println((char)read);
  21. read = fis.read();// 会出现编译错误 3
  22. }
  23. // fis.close();// 4
  24. } catch (IOException e) {
  25. e.printStackTrace();// 5
  26. } finally {
  27. try {
  28. if(fis != null){
  29. fis.close();// 会出现编译错误 4
  30. }
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }

四、异常处理机制二:throws

throws跟在方法的括号后面,可以声明多个异常,以逗号分隔。这个声明的含义是说,我这个方法内可能抛出这些异常,我没有进行处理,至少没有处理完,调用者必须进行处理。这个声明没有说明,具体什么情况会抛出什么异常,作为一个良好的实践,应该将这些信息用注释的方式进行说明,这样调用者才能更好的处理异常。
一般对于运行时异常不要求使用throws进行声明的,但是对于编译异常则是必须进行生声明的。
对于一个方法调用了另一个声明抛出的编译异常,则此时的方法就应该处理这个编译异常,但是至于选择处理异常的方法,可以使用try-catch,也可以使用抛出的方式。
RuntimeException(unchecked)表示编程的逻辑错误,编程时应该检查以避免这些错误,比如说像空指针异常,如果真的出现了这些异常,程序退出也是正常的,程序员应该检查程序代码的bug而不是想办法处理这种异常。Checked exception表示程序本身没问题,但由于I/O、网络、数据库等其他不可预测的错误导致的异常,调用者应该进行适当处理。

五、手动抛出异常:throw

  1. /**
  2. * @Description TODO
  3. * @Author dongxinxin@e6yun.com
  4. * @Created Date: 2022/3/8 16:55
  5. * @ClassName ThrowsTest
  6. * @Remark
  7. */
  8. public class ThrowsTest {
  9. public static void main(String[] args) {
  10. Integer[] arr = {1,2,3};
  11. System.out.println(getArr(arr,4));
  12. }
  13. public static int getArr(Integer[] arr,int index){
  14. if(arr == null){
  15. throw new NullPointerException("有可能会导致空指针异常");
  16. }
  17. if(index < 1 || index > arr.length){
  18. throw new ArrayIndexOutOfBoundsException("请重新传参,角标越界");
  19. }
  20. return arr[index - 1];
  21. }
  22. }
  23. /**
  24. ArrayList 中添加的方法中判断
  25. */
  26. private void rangeCheckForAdd(int index) {
  27. if (index > size || index < 0)
  28. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  29. }

throws 和 throw 的区别:

1、throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。

用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常

2、throw:则是用来抛出一个具体的异常类型。

用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常

六、用户自定义异常类

  1. /**
  2. * 自定义异常
  3. * 如何自定义异常类?
  4. * 1、继承现有的异常结构
  5. * 2、Exception 仿照现有异常类进行构造
  6. * 3、重载的构造器
  7. * @author dongxinxin
  8. * @create 2022-03-08 21:01
  9. */
  10. public class MyExceptionTest extends Exception{
  11. static final long serialVersionUID = -3387516993124229948L;
  12. public MyExceptionTest(){}
  13. public MyExceptionTest(String msg){
  14. super(msg);
  15. }
  16. }
  17. class Demo01{
  18. public static void show(int index){
  19. try {
  20. if (index > 2){
  21. throw new MyExceptionTest("有异常了");
  22. } else {
  23. System.out.println("已解决");
  24. }
  25. } catch (Exception e){
  26. System.out.println(e.getMessage());
  27. }
  28. }
  29. }

七、练习

  1. /**
  2. * 练习
  3. * @author dongxinxin
  4. * @create 2022-03-08 21:16
  5. */
  6. public class DemoException {
  7. public static void methodA(){
  8. try {
  9. System.out.println("进入方法A");
  10. throw new RuntimeException("制造异常");
  11. } finally {
  12. System.out.println("A方法中的finally");
  13. }
  14. }
  15. public static void methodB(){
  16. try {
  17. System.out.println("进入方法B");
  18. return;
  19. } finally {
  20. System.out.println("B方法中的finally");
  21. }
  22. }
  23. public static void main(String[] args) {
  24. try {
  25. methodA();
  26. } catch (Exception e) {
  27. System.out.println(e.getMessage());
  28. }
  29. methodB();
  30. }
  31. }

输出结果:

进入方法A

A方法中的finally

制造异常

进入方法B

B方法中的finally