Java-异常处理结构

知识点1-掌握异常的概念

1.异常(Exception)

  异常指的是程序运行时发生的不正常事件;异常能够被程序处 理,保证程序继续运行下去;例如除数为0、文件没有找到、输入的数字格式不对……

  下面程序因为除数为0,所以发生了异常

  •       
    复制代码
    public class Test1 {
        public static void main(String[] args) {
            System.out.println("程序出现异常案例:");
            //出现异常java.lang.ArithmeticException算数错误情形
            System.out.println(100 / 0);
            System.out.println("程序继续运行");
        }
    }
    复制代码

2.错误(Error)

  错误(Error):错误程序没法处理,例如内存泄漏。发生错误后,一般虚拟机会选择 终止程序运行,程序员需要修改代码才能解决相关错误;

  

 

  下面程序因为数组长度过长,内存溢出

  •   
    public class Test1 {
        public static void main(String[] args) {
            //出现错误java.lang.OutOfMemoryError堆内存溢出
            int [] a =new int[1024*1204*1024];
        }
    }

知识点2-熟悉异常结构及常见运行时异常

1.异常层次关系:

2.Throwable类

  API中标准异常的顶级父类是Throwable类;

  Throwable类有两个子类:Exception和Error;所有异常都是Exception类的直接或 间接子类;所有错误都是Error的直接或间接子类;

3.Exception

  Exception有很多子类;这些子类又可以分为两大类;

3.1.运行时异常

  RuntimeException的子类都是运行时异常

  • 运行时异常:也称为非检测异常(unchecked Exception), 这些异常在编译 期不检测,程序中可以选择处理,也可以不处理,编译通过,如果不处理运行时 会中断,但是编译没问题;

    • NullPointerException-空指针异常

      • 发生前提:当对一个空对象,即没有初始化,依然为null的对象调用属性或方法时;

      复制代码
      public class RuntimeExceptionTest {
          public static void main(String[] args) {
      //        test01();
              test02();
          }
          private static void test01() {
              String str = null;
              //出现异常java.lang.NullPointerException空指针异常
              System.out.println(str.length());
          }
          private static void test02() {
              String str = "";
              //输出0,不会发生空指针异常
              System.out.println(str.length());
          }
      }
      复制代码
    • ArithmeticException-数学异常

      • 发生前提:整数除以0时发生数学异常

      • 浮点数除以0不会发生数学异常

      复制代码
      1 public class Test {
      2     public static void main(String[] args) {
      3         //不抛出异常,输出Infinity
      4         System.out.println(10.0 / 0);
      5         //数学异常
      6         //抛出异常java.lang.ArithmeticException: / by zero
      7         System.out.println(10 / 0);
      8     }
      复制代码
    • IndexOutOfBoundsException:索引越界异常

      • 字符串索引StringIndexOutOfBoundsException

      • 数组索引ArrayIndexOutOfBoundsException

      • 发生前提:当访问字符串中的字符或者数组中的元素,超过了其长度时

      复制代码
      public class Test {
          public static void main(String[] args) {
              int a[] = new int[3];
              //数组索引java.lang.ArrayIndexOutOfBoundsException: 3
              //异常类型 : 提示信息
              System.out.println(a[3]);
          }
      }
      复制代码
      复制代码
      public class Test {
          public static void main(String[] args) {
              //字符串索引java.lang.StringIndexOutOfBoundsException: String index out of range: 17
              //异常类型 : 提示信息
              String str = "SEVENTEEN";
              System.out.println(str.charAt(17));
          }
      }
      复制代码
    • NumberFormatException-数字格式异常

      • 发生前提:当把一个字符串转换成数字时,字符串内容不是数字时发生

      复制代码
      public class Test {
          public static void main(String[] args) {
              String str = "carat";
              //数字格式异常
              //java.lang.NumberFormatException: For input string: "carat"
              System.out.println(Integer.valueOf(str));
          }
      }
      复制代码
    • ClassCastException-类型转换异常

      • 发生前提:把父类对象转换成不相关的子类类型时

      复制代码
      public class Test {
          public static void main(String[] args) {
              Object o = new Object();
              //类型转换异常
              //java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
              String s = (String)o;
              System.out.println(s);
          }
      }
      复制代码

3.2.非运行时异常:其他的都是非运行时异常

  • 也称为检测异常(checked Exception), 在编译期就会被检测,是必须进行处理 的异常,如果不处理,将发生编译期错误;

    public class Test {
        public static void main(String[] args) {
            //Java: 未报告的异常错误java.io.FileNotFoundException; 必须对其进行捕获或声明以便抛出
            FileReader f = new FileReader("a.txt");
        }
    }

4.标准异常处理流程

转载图

5.try-catch-finally

Java语言中异常处理主要使用到try/catch/finally三种语句

5.1.语法

复制代码
try{
    可能抛出异常的代码块;
}catch(异常类型  变量名){
    处理异常的代码;
}finally{
    不管什么情况,一定被执行的代码块; 
}
复制代码

5.2. 异常会发生三种情况:

  1. 发生异常被捕获处理;
  2. 发生异常没有被捕获处理;
  3. 没发生异常

当try块中中代码抛出了异常对象后,异常处理机制就将这个对象的类型与try后的 catch语句中的异常类型进行匹配,如果类型相同,或者抛出的是捕获的子类,就称 为匹配成功,那么异常就被捕获,就运行catch块中的语句;否则,称为异常没有被 捕获,程序将中断;

5.2.1. 抛出异常并处理成功     

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 0;
            ///抛出 ArithmeticException异常,try中之后代码不运行
            System.out.println("x/y=" + (x / y));
            System.out.println("计算完成");
            //异常处理机制将 ArithmeticException与catch语句的异常类型匹配
            //匹配成功,运行catch代码块,异常被处理;
        } catch (ArithmeticException e) {
            //程序继续运行。
            System.out.println("处理数学异常");
        }
    }
}
复制代码

5.2.2. 抛出异常,但是没有被处理

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 0;
            ///抛出 ArithmeticException异常,try中之后代码不运行
            System.out.println("x/y=" + (x / y));
            System.out.println("计算完成");
            //异常处理机制将 ArithmeticException与catch语句的异常类型匹配
            //匹配失败,不运行catch代码块,异常不被处理;
        } catch (NullPointerException e) {
            //程序中断。
            System.out.println("处理数学异常");
        }
    }
}
复制代码

5.2.3. 没有抛出异常

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 100;
            //没有抛出 ArithmeticException异常
            System.out.println("x/y=" + (x / y));
            System.out.println("计算完成");
            //跳过catch代码块
        } catch (ArithmeticException e) {
            //不执行
            System.out.println("处理数学异常");//运行结束
        }
    }
}
复制代码

5.3. catch语句里都写什么代码

  • 可以写任意需要对异常进行处理的代码;

  • 可以调用异常对象的方法,例如printStackTrace,查看异常发生的栈轨迹

    复制代码
    public class Test {
        public static void main(String[] args) {
            //没有发生异常
            try {
                int x = 100;
                int y = 0;
                System.out.println("x/y=" + x / y);
                System.out.println("x/y计算结束");
            } catch (ArithmeticException e) {
                e.printStackTrace();
                System.out.println("发生了数学异常,注意除数不能为0.");
            }
            System.out.println("main方法运行结束");
        }
    }
    复制代码

5.4. 多个catch语句

  • 如果try块中有多行代码,可能抛出多种类型异常,可以使用多个catch语句,当捕获一个catch时,就不匹配其他的catch;

  • 如果catch的类型Exception放在第一位,后面的catch就没有意义了(编译错误);
  • 注意:catch语句的异常类型必须从子类到父类的顺序,否则编译错误;

    复制代码
    public class Test {
        public static void main(String[] args) {
            try {
                int x = 100;
                int y = 0;
                String s = null;
                //当y=0时,发生数学异常,运行 catch(ArithmeticException e)代码块;
                  System.out.println("x/y=" + x / y);
                System.out.println("x/y计算结束");
                System.out.println("字符串长度" + s.length());
                //当y不等于0时, 发生空指针异常,运行catch(NullPointerException e)代码块;
            } catch (ArithmeticException e) {
                System.out.println("发生了数学异常,注意除数不能为0.");
            } catch (NullPointerException e) {
                System.out.println("发生了空指针异常");
            } catch (Exception e) {
                //从来不会运行这个catch块,因为没有其他类型异 常;可见异常只要被成功捕获一次,就被处理了,不会再继续抛出了。
                System.out.println("发生了其他异常");
            }
            System.out.println("main方法运行结束");
        }
    }
    复制代码

5.5.finally块

如果希望不管什么情况,有一些代码都必须被执行,那么就可以把这些代码写到 finally块中。

5.6. try-finally

必须有try,catch可以有1个或多个,finally最多1个,可以没有,不能有多个; 还有另外一种组合:只有try和finally,没有catch

如果try块中抛出了异常,则肯定不能被捕获,程序中断,但是finally代码块依然会被执行

5.6.1. 发生异常时的运行结果

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 0;
            System.out.println("x/y=" + x / y);
            System.out.println("x/y计算结束");
        } finally {
            System.out.println("finally代码块");
        }
        System.out.println("main方法运行结束");
    }
}
/*
finally代码块
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at com.tjetc.Test.main(Test.java:10)
*/
复制代码

5.6.2. 没发生异常时的运行结果

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 10;
            System.out.println("x/y=" + x / y);
            System.out.println("x/y计算结束");
        } finally {
            System.out.println("finally代码块");
        }
        System.out.println("main方法运行结束");
    }
}
/*
x/y=10
x/y计算结束
finally代码块
main方法运行结束
*/
复制代码

6.throw关键字在方法体中使用

抛出异常其实就是创建了一个异常对象,然后用throw关键字交给异常处理机制去处 理;throw关键字在方法体中使用,用法如下:

1.throw 异常对象  throw new Exception();

2.catch(Exception e){ throw e;}

6.1. 运行时异常是JVM自动抛出

复制代码
public class Test {
    public static void main(String[] args) {
        try {
            int x = 100;
            int y = 0;
            //JVM自行执行了一条抛出异常语句:throw new ArithmeticException();
            System.out.println("x/y=" + x / y);//抛出ArithmeticException异 常,try中之后代码不运行
            System.out.println("x/y计算结束");
        } catch (ArithmeticException e) {//异常处理机制将 ArithmeticException与catch语句的异常类型匹配
            System.out.println("发生了数学异常,注意除数不能为0.");//匹配成 功, 运行catch代码块,异常被处理;
        }
        System.out.println("main方法运行结束");//程序继续运行。
    }
}
/*
发生了数学异常,注意除数不能为0.
main方法运行结束
*/
复制代码

6.2. 非运行时异常需要程序员用throw关键字抛出

6.2.1. throw

以下代码发生编译错误:由于抛出了Exception,是非运行时异常,所以编译期检 测,要求必须处理,处理的方式有两种:

  1. 使用try/catch/finally进行处理;

  2. 不处理,用throws声明异常;

    如果用第一种方法,几乎没有意义,因为调用div方法时,不能再捕获这个异常,不能灵活处理。

    所以,当用throw抛出异常后,基本都使用throws进行声明!

复制代码
public class Calculator {
    public void div(int x,int y){
        //当除数为0时,抛出异常
        if(y == 0){
            throw new Exception();//不处理,编译错误
        }
        System.out.println(x / y);
    }
}
复制代码

6.2.2. throws

  • throws用在方法声明处,声明该方法可能发生的异常类型;

  • throws后可以声明多种类型,用逗号隔开即可;

  • 抽象方法也可以使用throws声明该方法可能抛出的异常类型;

    复制代码
    public class Calculator {
        public void div(int x, int y) throws Exception {
            //当除数为0时,抛出异常
            if (y == 0) {
                throw new Exception();//使用throws后,不再有编译错误
            }
            System.out.println(x / y);
        }
    }
    复制代码

6.2.3. 调用带有throws方法必须处理这些异常

一个方法如果使用了throws,那么调用该方法时,编译期会提醒必须处理这些异常,否则编译错误;

复制代码
public class Calculator {
    public void div(int x, int y) throws Exception {
        //当除数为0时,抛出异常
        if (y == 0) {
            throw new Exception();//使用throws后,不再有编译错误
        }
        System.out.println(x / y);
    }

    public static void main(String[] args) {
        Calculator c = new Calculator();
        //未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出
        c.div(10, 1);
    }
}
复制代码

依然可以用两种方法处理,可以try/catch,可以继续throws(二选一即可)

复制代码
public class Calculator {
    public void div(int x, int y) throws Exception {
        //当除数为0时,抛出异常
        if (y == 0) {
            throw new Exception();//使用throws后,不再有编译错误
        }
        System.out.println(x / y);
    }

    public static void main(String[] args)/*方式一:*/throws Exception {
        Calculator c = new Calculator();
        //1、接续抛出,异常处理推迟
        //2、try-catch
        //方式二
        try {
            c.div(10, 1);
        } catch (Exception e) {
            System.out.println("除数不能为0");
        }
    }
}
复制代码

6.2.4. 异常先统一处理,可以处理后再抛出

如果希望对异常先统一处理,可以处理后再抛出 调用的时候可以继续处理

复制代码
public class Calculator {
    public void div(int x, int y) throws Exception { //当除数为0时,抛出异常
        try {
            if (y == 0) {
                throw new Exception();  //使用throws后,不再有编译错误
            }
        } catch (Exception e) {
            System.out.println("Calculator发生了异常");
            throw e;//将异常继续抛出,调用div的方法可以再次捕获处理;
        }
        System.out.println("x/y=" + x / y);
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        try {
            calculator.div(10, 0);//再次捕获处理了div中的异常对象
        } catch (Exception e) {
            System.out.println("再处理一次,除数不能为0");
        }
    }
}
复制代码

6.2.5. 如果希望不管有没有异常都做统一处理,有异常声明 抛出,可以使用try/finally

复制代码
public class Calculator {
    public void div(int x, int y) throws Exception {
//当除数为0时,抛出异常
        try {
            if (y == 0) {
                throw new Exception();  //使用throws后,不再有编译错误
            }
        } finally {//不管有没有异常,都运行finally,如果有异常,不会捕获,抛出 给调用者。
            System.out.println("不管有没有异常,都运行finally");
        }
        System.out.println("x/y=" + x / y);
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        try {
            calculator.div(10, 0);//捕获处理了div中的异常对象
        } catch (Exception e) {
            System.out.println("除数不能为0");
        }
    }
}
复制代码

6.2.6. 总结throw和throws的区别

throws和throw

throws在方法后边声明异常,其实就是自己不想对异常做出任何的处理,告诉别人 自己可能出现的异常,交给别人处理,然别人处理

throw:就是自己处理一个异常,有两种方式要么是自己捕获异常try...catch代码块,

要么是抛出一个异常(throws 异常)

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

    用在方法声明后面,跟的是异常类名

    可以跟多个异常类名,用逗号隔开

    表示抛出异常,由该方法的调用者来处理 throws表示出现异常的一种可能性,并不一定会发生这些异常

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

    用在方法体内,跟的是异常对象名

    只能抛出一个异常对象名

    表示抛出异常,由方法体内的语句处理 throw则是抛出了异常,执行throw则一定抛出了某种异常

7. 自定义异常类

7.1.多层次异常处理的方式

  • 为了能够标记项目中的异常事件,需要使用throw抛出异常;

  • 如果抛出的是API中的标准异常,那么很可能与API中方法抛出的异常混淆,因 此需要自定义异常;

  • 项目组根据业务需求定义业务异常,对团队协作开发非常有意义

7.2.自定义业务异常的实现方法和一般规则

7.2.1.自定义异常类实现

  • 自定义异常类非常简单,只要继承API中任意一个标准异常类即可;

  • 多数情况下,继承Exception类;也可以选择继承其他类型异常;

  • 一般自定义异常类中不写其他方法,只重载必要的构造方法;

复制代码
public class DataValueException extends Exception {
    public DataValueException() {
    }

    public DataValueException(String message) {
        super(message);
    }

    public DataValueException(Throwable cause) {
        super(cause);
    }

    public DataValueException(String message, Throwable cause) {
    }
}
复制代码

7.2.2.使用自定义异常与使用API中标准异常一样

  • 可以用throw抛出自定义异常对象,使用throws声明自定义异常类型;

  • 可以使用try/catch/finally处理异常

复制代码
public class Employee{
    private String name;
    private double salary;

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSalary(double salary) throws DataValueException {
        if (salary <= 2500) {
            //setSalary方法对参数值有要求,不能低于2500,如果低于2500则抛出自定义的 DataValueException异常;
            throw new DataValueException("薪资不能低于2500元");
        } else {
            this.salary = salary;
        }
}
    public static void main(String[] args) {
        Employee e = new Employee("张晓明", 3000);
        try {
            e.setSalary(2400);
        } catch (DataValueException e1) {
            e1.printStackTrace();
        }
    }
}                                     
复制代码
/*
DataValueException: 薪资不能低于2500元
*/
posted @   carat9588  阅读(130)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示