1.1、异常的基本概念

什么是异常,在程序运行过程中出现的错误,称为异常

  1. public class ExceptionTest01 {
  2. public static void main(String[] args) {
  3. int i1 = 100;
  4. int i2 = 0;
  5. int i3 = i1/i2;
  6. System.out.println(i3);
  7. }
  8. }

没有正确输出,抛出了被0除异常
通过以上示例,我们看到java给我们提供了这样一个体系结构,当出现问题的时候,它会告诉我们,并且把错误的详细信息也告诉我们了,这就是异常的体系结构,这样我们的程序更健壮,我们可以把这个信息,再进行处理以下告诉用户。从上面大家还可以看到,java异常都是类,在异常对象中会携带一些信息给我们,我们可以通过这个异常对象把信息取出来

  1. public class ExceptionTest01 {
  2. public static void main(String[] args) {
  3. StringBuffer sbf = new StringBuffer();
  4. for(long i=0;i<Long.MAX_VALUE;i++){
  5. sbf.append("T-"+i);
  6. }
  7. }
  8. }
  9. 堆空间溢出
  10. --------------------------------------------
  11. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  1. public class ExceptionTest02 {
  2. public static void main(String[] args) {
  3. main(args);
  4. }
  5. }
  6. 栈空间溢出
  7. --------------------------------------------
  8. Exception in thread "main" java.lang.StackOverflowError
  9. at com.test.ExceptionTest02.main(ExceptionTest02.java:5)
  1. public class ExceptionTest05 {
  2. public static void main(String[] args) throws IOException {
  3. BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
  4. String line = bf.readLine();
  5. System.out.println(line);
  6. bf.close();
  7. }
  8. }

1.2、异常的分类

1 .2.1、异常的层次结构

GMIEU}36IJH9UH~)$04G)C4.png

1.2.2、异常的分类

异常主要分为:错误、编译时异常、运行时异常

  1. 错误:如果应用程序出现了Error,那么将无法恢复,只能重新启动应用程序,最典型的Error的异常是:OutOfMemoryError、StackOverflowError
  2. 编译时异常:出现了这种异常必须显示的处理,不显示处理java程序将无法编译通过
  3. 运行时异常:此种异常可以不用显示的处理,例如被0除异常,java没有要求我们一定要处理

    1.2.3、try、catch和finally

    异常的捕获和处理需要采用try和catch来处理,具体格式如下: ```java try {

}catch(OneException e) {

}catch(TwoException e) {

}finally {

}

  1. - try中包含了可能产生异常的代码
  2. - try后面是catchcatch可以有一个或多个,catch中是需要捕获的异常
  3. - try中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的catch语句块中,如果没有异常不会跳转到catch
  4. - finally表示,不管是出现异常,还是没有出现异常,finally里的代码都执行,finallycatch可以分开使用,但finally必须和try一块使用,如下格式使用finally也是正确的
  5. ```java
  6. try {
  7. }finally {
  8. }
  1. public class ExceptionTest02 {
  2. public static void main(String[] args) {
  3. int i1 = 100;
  4. int i2 = 0;
  5. //try里是出现异常的代码
  6. //不出现异常的代码最好不要放到try作用
  7. try {
  8. //当出现被0除异常,程序流程会执行到“catch(ArithmeticException ae)”语句
  9. //被0除表达式以下的语句永远不会执行
  10. int i3 = i1/i2;
  11. //永远不会执行
  12. System.out.println(i3);
  13. //采用catch可以拦截异常
  14. //ae代表了一个ArithmeticException类型的局部变量
  15. //采用ae主要是来接收java异常体系给我们new的ArithmeticException对象
  16. //采用ae可以拿到更详细的异常信息
  17. }catch(ArithmeticException ae) {
  18. System.out.println("被0除了");
  19. }
  20. }
  21. }

1.2.4、getMessage和printStackTrace()

如何取得异常对象的具体信息,常用的方法主要有两种:

  • 取得异常描述信息:getMessage()
  • 取得异常的堆栈信息(比较适合于程序调试阶段):printStackTrace();

    1. public class ExceptionTest03 {
    2. public static void main(String[] args) {
    3. int i1 = 100;
    4. int i2 = 0;
    5. try {
    6. int i3 = i1/i2;
    7. System.out.println(i3);
    8. }catch(ArithmeticException ae) {
    9. //ae是一个引用,它指向了堆中的ArithmeticException
    10. //通过getMessage可以得到异常的描述信息
    11. System.out.println(ae.getMessage());
    12. }
    13. }
    14. }
    1. public class ExceptionTest04 {
    2. public static void main(String[] args) {
    3. method1();
    4. }
    5. private static void method1() {
    6. method2();
    7. }
    8. private static void method2() {
    9. int i1 = 100;
    10. int i2 = 0;
    11. try {
    12. int i3 = i1/i2;
    13. System.out.println(i3);
    14. }catch(ArithmeticException ae) {
    15. //ae是一个引用,它指向了堆中的ArithmeticException
    16. //通过printStackTrace可以打印栈结构
    17. ae.printStackTrace();
    18. }
    19. }
    20. }

    1.2.5、编译时异常

    ```java import java.io.FileInputStream;

public class ExceptionTest05 {

  1. public static void main(String[] args) {
  2. FileInputStream fis = new FileInputStream("test.txt");
  3. }

}

  1. 从上面输出可以看到,无法编译,它抛出了一个异常,这个异常叫做“编译时市场”FileNotFoundException,也就是说在调用的时候必须处理文件不能找到<br />处理FileNotFoundException
  2. ```java
  3. /*
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. */
  7. import java.io.*;
  8. public class ExceptionTest06 {
  9. public static void main(String[] args) {
  10. try {
  11. FileInputStream fis = new FileInputStream("test.txt");
  12. }catch(FileNotFoundException ffe) { //此异常为编译时异常,必须处理
  13. ffe.printStackTrace();
  14. }
  15. }
  16. }

1.2.6、finally关键字

finally在任何情况下都会执行,通常在finally里关闭资源

  1. import java.io.*;
  2. public class ExceptionTest07 {
  3. public static void main(String[] args) {
  4. try {
  5. FileInputStream fis = new FileInputStream("test.txt");
  6. System.out.println("-------before fis.close--------");
  7. //close是需要拦截IOException异常
  8. //在此位置关闭存在问题,当出现异常
  9. //那么会执行到catch语句,以下fis.close永远不会执行
  10. //这样个对象永远不会得到释放,所以必须提供一种机制
  11. //当出现任何问题,都会释放相应的资源(恢复到最初状态)
  12. //那么就要使用finally语句块
  13. fis.close();
  14. System.out.println("-------after fis.close--------");
  15. }catch(FileNotFoundException e) {
  16. e.printStackTrace();
  17. }catch(IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

采用finally来释放资源

  1. import java.io.*;
  2. public class ExceptionTest08 {
  3. public static void main(String[] args) {
  4. //因为fis的作用域问题,必须放到try语句块外,局部变量必须给初始值
  5. //因为是对象赋值为null
  6. FileInputStream fis = null;
  7. try {
  8. //FileInputStream fis = new FileInputStream("test.txt");
  9. fis = new FileInputStream("test.txt");
  10. /*
  11. System.out.println("-------before fis.close--------");
  12. fis.close();
  13. System.out.println("-------after fis.close--------");
  14. */
  15. }catch(FileNotFoundException e) {
  16. e.printStackTrace();
  17. }finally {
  18. try {
  19. System.out.println("-------before fis.close--------");
  20. //放到finally中的语句,程序出现任何问题都会被执行
  21. //所以finally中一般放置一些需要及时释放的资源
  22. fis.close();
  23. System.out.println("-------after fis.close--------");
  24. }catch(IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  29. }

深入finally

  1. public class ExceptionTest09 {
  2. public static void main(String[] args) {
  3. int i1 = 100;
  4. int i2 = 10;
  5. try {
  6. int i3 = i1/i2;
  7. System.out.println(i3);
  8. return;
  9. }catch(ArithmeticException ae) {
  10. ae.printStackTrace();
  11. }finally {
  12. //会执行finally
  13. System.out.println("----------finally---------");
  14. }
  15. }
  16. }
  1. public class ExceptionTest10 {
  2. public static void main(String[] args) {
  3. int i1 = 100;
  4. int i2 = 10;
  5. try {
  6. int i3 = i1/i2;
  7. System.out.println(i3);
  8. //return;
  9. System.exit(-1); //java虚拟机退出
  10. }catch(ArithmeticException ae) {
  11. ae.printStackTrace();
  12. }finally {
  13. //只有java虚拟机退出不会执行finally
  14. //其他任何情况下都会执行finally
  15. System.out.println("----------finally---------");
  16. }
  17. }
  18. }
  1. public class ExceptionTest11 {
  2. public static void main(String[] args) {
  3. int r = method1();
  4. //输出为:10
  5. System.out.println(r);
  6. }
  7. /*
  8. private static int method1()
  9. {
  10. byte byte0 = 10;
  11. byte byte3 = byte0; //将原始值进行了保存
  12. byte byte1 = 100;
  13. return byte3;
  14. Exception exception;
  15. exception;
  16. byte byte2 = 100;
  17. throw exception;
  18. }
  19. */
  20. private static int method1() {
  21. int a = 10;
  22. try {
  23. return a;
  24. }finally {
  25. a = 100;
  26. }
  27. }
  28. }
  1. public class ExceptionTest12 {
  2. public static void main(String[] args) {
  3. int r = method1();
  4. //输出为:100
  5. System.out.println(r);
  6. }
  7. /*
  8. private static int method1()
  9. {
  10. byte byte0 = 10;
  11. byte0 = 50;
  12. byte0 = 100;
  13. break MISSING_BLOCK_LABEL_18;
  14. Exception exception;
  15. exception;
  16. byte0 = 100;
  17. throw exception;
  18. return byte0;
  19. }
  20. */
  21. private static int method1() {
  22. int a = 10;
  23. try {
  24. a = 50;
  25. }finally {
  26. a = 100;
  27. }
  28. return a;
  29. }
  30. }

1.2.7、final、finalize和finally?

finalize()是Object里面的一个方法,当一个堆空间中的对象没有被栈空间变量指向的时候,这个对象会等待被java回收

可以通过:System.gc();//手动调用垃圾回收 去测试这个finalize

1.2.8、如何声明异常

在方法定义处采用throws声明异常,如果声明的异常为编译时异常,那么调用方法必须处理此异常

  1. import java.io.*;
  2. public class ExceptionTest13 {
  3. public static void main(String[] args)
  4. //throws FileNotFoundException, IOException { //可以在此声明异常,这样就交给java虚拟机处理了,不建议这样使用
  5. throws Exception { //可以采用此种方式声明异常,因为Exception是两个异常的父类
  6. /*
  7. //分别处理各个异常
  8. try {
  9. readFile();
  10. }catch(FileNotFoundException e) {
  11. e.printStackTrace();
  12. }catch(IOException e) {
  13. e.printStackTrace();
  14. }
  15. */
  16. //可以采用如下方式处理异常
  17. //因为Exception是FileNotFoundException和IOException的父类
  18. //但是一般不建议采用如下方案处理异常,粒度太粗了,异常信息
  19. //不明确
  20. /*
  21. try {
  22. readFile();
  23. }catch(Exception e) {
  24. e.printStackTrace();
  25. }
  26. */
  27. readFile();
  28. }
  29. private static void readFile()
  30. throws FileNotFoundException,IOException { //声明异常,声明后调用者必须处理
  31. FileInputStream fis = null;
  32. try {
  33. fis = new FileInputStream("test.txt");
  34. //}catch(FileNotFoundException e) {
  35. // e.printStackTrace();
  36. }finally {
  37. //try {
  38. fis.close();
  39. //}catch(IOException e) {
  40. // e.printStackTrace();
  41. //}
  42. }
  43. }
  44. }
  1. public class ExceptionTest14 {
  2. public static void main(String[] args) {
  3. //不需要使用try...catch..,因为声明的是运行时异常
  4. //method1();
  5. //也可以拦截运行时异常
  6. try {
  7. method1();
  8. }catch(ArithmeticException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. //可以声明运行时异常
  13. private static void method1() throws ArithmeticException {
  14. int i1 = 100;
  15. int i2 = 0;
  16. // try {
  17. int i3 = i1/i2;
  18. System.out.println(i3);
  19. /*
  20. }catch(ArithmeticException ae) {
  21. ae.printStackTrace();
  22. }
  23. */
  24. }
  25. }

1.2.10、如何手动抛出异常

  1. public class ExceptionTest15 {
  2. public static void main(String[] args) {
  3. int ret = method1(1000, 10);
  4. if (ret == -1) {
  5. System.out.println("除数为0");
  6. }
  7. if (ret == -2) {
  8. System.out.println("被除数必须为1~100之间的数据");
  9. }
  10. if (ret == 1) {
  11. System.out.println("正确");
  12. }
  13. //此种方式的异常处理,完全依赖于程序的返回
  14. //另外异常处理和程序逻辑混在一起,不好管理
  15. //异常是非常,程序语句应该具有一套完成的异常处理体系
  16. }
  17. private static int method1(int value1, int value2){
  18. if (value2 == 0) {
  19. return -1;
  20. }
  21. if (!(value1 >0 && value1<=100)) {
  22. return -2;
  23. }
  24. int value3 = value1/value2;
  25. System.out.println("value3=" + value3);
  26. return 1;
  27. }
  28. }

采用异常来处理参数非法

  1. public class ExceptionTest16 {
  2. public static void main(String[] args) {
  3. //int ret = method1(10, 2);
  4. //System.out.println(ret);
  5. /*
  6. try {
  7. int ret = method1(1000, 10);
  8. System.out.println(ret);
  9. }catch(IllegalArgumentException iae) {
  10. //ide为指向堆中的IllegalArgumentException对象的地址
  11. System.out.println(iae.getMessage());
  12. }
  13. */
  14. try {
  15. int ret = method1(1000, 10);
  16. System.out.println(ret);
  17. }catch(Exception iae) { //可以采用Exception拦截所有的异常
  18. System.out.println(iae.getMessage());
  19. }
  20. }
  21. private static int method1(int value1, int value2){
  22. if (value2 == 0) {
  23. ////手动抛出异常
  24. throw new IllegalArgumentException("除数为0");
  25. }
  26. if (!(value1 >0 && value1<=100)) {
  27. //手动抛出异常
  28. throw new IllegalArgumentException("被除数必须为1~100之间的数据");
  29. }
  30. int value3 = value1/value2;
  31. return value3;
  32. }
  33. }

throws和throw的区别?thorws是声明异常,throw是抛出异常
进一步了解throw

  1. public class ExceptionTest17 {
  2. public static void main(String[] args) {
  3. try {
  4. int ret = method1(1000, 10);
  5. System.out.println(ret);
  6. }catch(Exception iae) {
  7. System.out.println(iae.getMessage());
  8. }
  9. }
  10. private static int method1(int value1, int value2){
  11. try {
  12. if (value2 == 0) {
  13. ////手动抛出异常
  14. throw new IllegalArgumentException("除数为0");
  15. //加入如下语句编译出错,throw相当于return语句
  16. //System.out.println("----------test111-----------");
  17. }
  18. if (!(value1 >0 && value1<=100)) {
  19. //手动抛出异常
  20. throw new IllegalArgumentException("被除数必须为1~100之间的数据");
  21. }
  22. int value3 = value1/value2;
  23. return value3;
  24. }finally {
  25. //throw虽然类似return语句,但finally会执行的
  26. System.out.println("-----------finally------------");
  27. }
  28. }
  29. }

1.2.11、异常的捕获顺序

异常的捕获顺序应该是:从小到大

  1. import java.io.*;
  2. public class ExceptionTest18 {
  3. public static void main(String[] args) {
  4. try {
  5. FileInputStream fis = new FileInputStream("test.txt");
  6. fis.close();
  7. }catch(IOException e) {
  8. e.printStackTrace();
  9. }catch(FileNotFoundException e) {
  10. e.printStackTrace();
  11. }
  12. //将IOException放到前面,会出现编译问题
  13. //因为IOException是FileNotFoundException的父类,
  14. //所以截获了IOException异常后,IOException的子异常
  15. //都不会执行到,所以再次截获FileNotFoundException没有任何意义
  16. //异常的截获一般按照由小到大的顺序,也就是先截获子异常,再截获父异常
  17. }
  18. }

1.3、如何自定义异常

自定义异常通常继承于Exception或RuntimeException。如果需要调用方显示处理,则定义编译时异常,如果不需要对方显示处理,可以定义运行时异常。

  1. import java.io.*;
  2. public class ExceptionTest19 {
  3. public static void main(String[] args) {
  4. try {
  5. method1(10, 0);
  6. }catch(MyException e) {
  7. //必须拦截,拦截后必须给出处理,如果不给出处理,就属于吃掉了该异常
  8. //系统将不给出任何提示,使程序的调试非常困难
  9. System.out.println(e.getMessage());
  10. }
  11. }
  12. private static void method1(int value1, int value2)
  13. throws MyException { //如果是编译时异常必须声明
  14. if (value2 == 0) {
  15. throw new MyException("除数为0");
  16. }
  17. int value3 = value1 / value2;
  18. System.out.println(value3);
  19. }
  20. }
  21. //自定义编译时异常
  22. class MyException extends Exception {
  23. public MyException() {
  24. //调用父类的默认构造函数
  25. super();
  26. }
  27. public MyException(String message) {
  28. //手动调用父类的构造方法
  29. super(message);
  30. }
  31. }
  1. import java.io.*;
  2. public class ExceptionTest20 {
  3. public static void main(String[] args) {
  4. method1(10, 0);
  5. }
  6. private static void method1(int value1, int value2)
  7. //throws MyException {
  8. if (value2 == 0) {
  9. //抛出编译时异常,方法可以不适用throws进行声明
  10. //但也也可以显示的声明
  11. throw new MyException("除数为0");
  12. }
  13. int value3 = value1 / value2;
  14. System.out.println(value3);
  15. }
  16. }
  17. //自定义运行时异常
  18. class MyException extends RuntimeException {
  19. public MyException() {
  20. //调用父类的默认构造函数
  21. super();
  22. }
  23. public MyException(String message) {
  24. //手动调用父类的构造方法
  25. super(message);
  26. }
  27. }

1.4、方法覆盖与异常

方法覆盖的条件:

  • 子类方法不能抛出比父类方法更多[大]的异常,但可以抛出父类方法异常的子异常 ```java import java.io.*;

public class ExceptionTest21 {

  1. public static void main(String[] args) {
  2. }

}

interface UserManager {

  1. public void login(String username, String password) throws UserNotFoundException;

}

class UserNotFoundException extends Exception {

}

class UserManagerImpl1 implements UserManager {

  1. //正确
  2. public void login(String username, String password) throws UserNotFoundException {
  3. }

}

//class UserManagerImpl2 implements UserManager {

  1. //不正确,因为UserManager接口没有要求抛出PasswordFailureException异常
  2. //子类异常不能超出父类的异常范围
  3. //public void login(String username, String password) throws UserNotFoundException, PasswordFailureException{
  4. //}

//}

class UserManagerImpl3 implements UserManager {

  1. //正确,因为MyException是UserNotFoundException子类
  2. //MyException异常没有超出接口的要求
  3. public void login(String username, String password) throws UserNotFoundException, MyException {
  4. }

}

class PasswordFailureException extends Exception {

}

class MyException extends UserNotFoundException {

} ```

1.5、总结

  1. 异常的分类
  2. 编译时异常和运行时异常
  3. 异常的5个关键字try、catch、finally、throws、throw
  4. 异常的捕获顺序,先捕获小的,再捕获大的
  5. 自定义异常:
  • 继承Exception,throw new的时候必须得throws抛出给调用者,相当于提示调用者必须捕获
  • 继承RuntimeExcetion,throw new的时候不是必须throws抛出给调用者
  • 建议:自定义异常尽量继承Exception,提示调用者必须处理异常,但是最终还是根据项目需求来
  1. 方法覆盖和异常的关系

.