Java第一阶段(day08)异常

java语言具有健壮性。
 1. GC  grabage collector  垃圾处理器 
  堆+方法区: 对象   回收无用引用/对象---->判断一个引用是有用还是无用?
      1. 引用次数
      2. 可达性分析算法
      
 2. 异常处理机制
    一段程序出现了问题(异常/错误) 不影响其他程序正常执行。

不正常的现象:  异常/错误

1. 错误 Error

与磁盘  与机器jvm----> 内存    Java虚拟机已损坏或已耗尽资源

不能解决。jvm规范: 不允许对错误执行任何处理。 良好提示给用户  终止程序。

public class Error  extends Throwable

2. 异常 Exception

public class  Exception
extends Throwable

Error  VS Exception
共同点:
   运行期间 出现的不正常现象
   都继承了同一个父类  Throwable
   
Error:  不应该处理  只能程序终止。
Exception: 可以处理,提供异常处理机制。

3. 体系结构

  • 在java规范里面  不推荐对任意error以及运行时异常进行处理。
  • 必须处理编译时异常。
  • 程序出现异常  当前线程就终止。后期学习里面 还是会对所有的异常进行全局处理。--->良好提示给用户信息即可。

3. 异常处理

3.0 处理方式

1.捕获异常。 真正处理异常的方式

try....catch....finally

    try{
        //有可能出现异常的代码逻辑
    }catch(具体的异常类型 变量名称){//捕获住了具体的异常
        //在catch进行对具体的异常类型处理
        //1.sout
        //2.异常引用调用异常类里面方法 printStackTrace()
        //3.后期: 异常+日志  将异常信息存储到不同级别的日志文件中
        //4. return 数据;
        //5. 异常信息的传递
    }
....
    catch(){
        
    }finally{
        //程序里面有没有异常 finally的逻辑都会执行。
        //释放物理资源
        input.close();
        //解锁
    }

try{
        
}catch(){
        
}

try{
    
}finally{
    
}

对于异常类而言  常用构造

RuntimeException() 
RuntimeException(String message) 具体异常信息串
RuntimeException(String message, Throwable cause)  
    一般是用于实现异常信息传递。

2. 抛出异常  throws     没有对异常执行相关的处理,消极处理。 只是将异常抛出给上级。
谁调用这个方法 就把异常抛出给谁。

在方法签名的后面 使用throws抛出具体的异常类型,
public void a() throws 异常类1,异常类2{

}

3.1 编译时异常

检测到的异常。 checked exception

编译器自动检测程序里面 有异常存在。 必须处理。

案例:   计算租金。 归还共享单车的时候  计算需要支付多少钱。

//计算使用单车时长。 一小时1元
//借车解锁那一刻 ----->锁车那一刻    借车时间---还车时间
/**
     * 求租金
     *
     * @param borrowTime 借车时间
     * @param backTime   还车时间
     * @return 支付的钱---->求2个时间的间隔
     */
public static long getMoney(String borrowTime, String backTime) {
    /*if (borrowTime == null || backTime == null) {
            throw new NullPointerException("时间不能为null");
        }*/
    //java.util.Objects  工具类  一般都是null对比较的一些封装
    Objects.requireNonNull(borrowTime, "借车时间不能为null");
    Objects.requireNonNull(backTime, "还车时间不能为null");
    //算术运算--->数值类型  整数byte short int long  小数float double   char
    //字符串的时间数据转换成数值类型的数据  但是不能直接转换  间接转换
    //1. 字符串时间 ----> 格式化Date的类型 java.text.DateFormat ----> java.util.Date ---> 数值数据

    //1.1 字符串时间转换成Date类型的数据
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//创建格式化日期对象  并指定pattern格式  将指定格式的字符串进行转换

    //出现了编译时异常: 自动检测到了异常  必须处理
    //异常处理方式:  捕获try..catch..finally   抛出 throws
    Date borrowDate = null;
    Date backDate = null;
    try {
        borrowDate = dateFormat.parse(borrowTime);
        backDate = dateFormat.parse(backTime);
    } catch (ParseException e) {
        //字符串转换Date的时候 有可能出现的异常
        //System.out.println("字符串的日期的数据与指定的pattern格式不符的时候  会触发异常");
        //e.printStackTrace();
        //异常信息传递。1. 分层 dao-->service--->controller   2. 父与子----> 子类型异常传递给父级类型的异常
        //throw
        try {
            throw new Exception(e);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    //1.2 Date类型的时间转换成数值类型的数据
    long time1 = borrowDate.getTime();//获得特定日期时间的毫秒数
    long time2 = backDate.getTime();//获得特定日期时间的毫秒数
    long duration = time2 - time1;//2个日期时间间隔的毫秒数
    //毫秒数转换成小时
    return duration / 1000 / 3600;
}

com.javsm.exception1.ExceptionDemo5
java.text.ParseException: Unparseable date: "2022-01-01 12:30"
	at java.base/java.text.DateFormat.parse(DateFormat.java:395)
	at com.javsm.exception1.ExceptionDemo5.getMoney(ExceptionDemo5.java:45)
	at com.javsm.exception1.ExceptionDemo5.main(ExceptionDemo5.java:70)
Exception in thread "main" java.lang.NullPointerException
	at com.javsm.exception1.ExceptionDemo5.getMoney(ExceptionDemo5.java:54)
	at com.javsm.exception1.ExceptionDemo5.main(ExceptionDemo5.java:70)

java.lang.Exception: java.text.ParseException: Unparseable date: "2022-01-01 12:30"
	at com.javsm.exception1.ExceptionDemo5.getMoney(ExceptionDemo5.java:54)
	at com.javsm.exception1.ExceptionDemo5.main(ExceptionDemo5.java:79)
Caused by: java.text.ParseException: Unparseable date: "2022-01-01 12:30"
	at java.base/java.text.DateFormat.parse(DateFormat.java:395)
	at com.javsm.exception1.ExceptionDemo5.getMoney(ExceptionDemo5.java:45)
	... 1 more
Exception in thread "main" java.lang.NullPointerException
	at com.javsm.exception1.ExceptionDemo5.getMoney(ExceptionDemo5.java:61)
	at com.javsm.exception1.ExceptionDemo5.main(ExceptionDemo5.java:79)

@SneakyThrows
public static void a() /*throws ClassNotFoundException*/{
   // try {
        Class.forName("com.javsm.exception1.ExceptionDemo10");
  /*  } catch (ClassNotFoundException e) {
        e.printStackTrace();
        System.out.println("类的路径写错了  项目里面不存在此类");
    }*/

    System.out.println(111);
}

3.2 运行时异常

RuntimeException

1. try...catch...finally

public static void main(String[] args) {
    try {
        System.out.println(3 / 0);//这里出现异常  其他的逻辑就不执行了
        //异常处理机制:  一段程序出现了异常 不影响其他程序的正常执行
    } catch (ArithmeticException a) {
        //禁忌
        //System.out.println("除数不能为0");
        a.printStackTrace();//将具体的异常信息打印输出到控制台上
    } finally {
        System.out.println("finally代码逻辑必须执行......");
    }
    System.out.println("异常之后的逻辑.......");
}

多个catch

int[] array = new int[]{1, 2, 3};
try {
    System.out.println(str.equals("hello"));
    //在try里面  异常之后的代码就不执行了
    System.out.println("11111111111111");
    System.out.println(array[3]);
} catch (NullPointerException n) {
    System.out.println("数据不能为null");
    n.printStackTrace();
} catch (ArrayIndexOutOfBoundsException a) {
    System.out.println("索引越界了");
    a.printStackTrace();
}
//有多个catch块  有且只执行一个
//不要把很多代码都放到try  能够区分稳定代码和非稳定代码

for (int i : array) {
    System.out.println("数组元素是:" + i);
}

多个catch合并一个catch

try {
    System.out.println(str.equals("hello"));
    //在try里面  异常之后的代码就不执行了
    System.out.println("11111111111111");
    System.out.println(array[3]);
} catch (NullPointerException | ArrayIndexOutOfBoundsException | ClassCastException  n) {
    n.printStackTrace();
}

2. throws(没有能力处理)

对于处理运行时异常  没有任何意义。

从语法上:  可以这样编写。

public static void main(String[] args) throws ArithmeticException, NullPointerException {

    //jvm默认不处理任意一个运行时异常
    System.out.println(3 / 0);
    System.out.println("11111111111111111111111");
}

4. throw

产生异常。

public static double divide() {

    Scanner input = new Scanner(System.in);
    System.out.println("请录入第一个数据:");
    int num1 = input.nextInt();

    System.out.println("请录入第2个数据:");
    int num2 = input.nextInt();

    if (num2 == 0) {
        //程序结束
        //System.exit(-1);
        //1.给用户提示
        //2.结束当前程序
        //手动产生异常  throw 抛出
        //使用在方法体内部的  throw new 异常类构造;
        throw new ArithmeticException("除数不能为0");//后期都有具体技术进行全局的异常处理
    }
    return num1 / num2;
}

public static boolean login(String name, String pass) {
    if (name == null || pass == null) {
        throw new NullPointerException("用户名或者密码是无效的数据");
    }
    //所有的用户信息都在容器
    User user = findUserByNameAndPass(name, pass);
    if (user == null) {
        throw new RuntimeException("用户名或者密码不符");
    }
    System.out.println("登录成功:"+user.getName());
    return true;
}

throw VS  throws

都是抛出。
throws:  处理异常的其中一种方式, 消极处理。将异常抛出给调用者。没有真正的处理。
         在方法的起签名后面使用throws抛出具体的异常类型。
         
throw: 产生异常。 在方法体里面 使用throw抛出具体的异常对象。 throw new 异常类型构造;
      1.1 控制流程  遇见throw 当前程序终止。提前预判
      1.2 异常信息的传递  一般在catch里面 
      1.3 与自定义异常结合使用

5. 自定义异常类

与功能开发期间 与业务有关。

使用自定义异常类实现用户支付的功能。

案例:

public class UserPayDemo {

    private static double balance = 2000;

    //throws+throw
    public static void pay(int money) throws UserException {
        if (money > balance) {
            throw new UserException(StatusEnum.USER_MONEY_NOT_ENOUGH);
        }
        balance -= money;
        System.out.println("成功支付" + money + ",账户余额还剩下:" + balance);
    }

    public static void main(String[] args) {
        try {
            pay(3000);
        } catch (UserException e) {
            e.printStackTrace();
            //System.out.println(e.getMessage());
            //获得具体的错误码值和错误信息
            StatusEnum statusEnum = e.getStatusEnum();
            System.out.println(statusEnum.getCode());
            System.out.println(statusEnum.getMsg());
            return;
        }
        System.out.println("其他逻辑。。。。。");
    }
}

@Getter
@AllArgsConstructor
public enum StatusEnum {

   USER_MONEY_NOT_ENOUGH(1001,"用户余额不足") ;
    private final int code;
    private final String msg;
}

@Setter
@Getter
public class UserException extends Exception {
    //异常都有错误码+信息
    private StatusEnum statusEnum;

    /*public UserException(String msg){
        super(msg);
    }*/

    //在异常类里面 一般都会重载本类的构造  调用父类的构造
    public UserException(StatusEnum statusEnum) {
        super(statusEnum.getMsg());
        this.statusEnum = statusEnum;
    }

    public UserException(StatusEnum statusEnum, Throwable cause) {
        super(statusEnum.getMsg(), cause);
        this.statusEnum = statusEnum;
    }
}

6.课堂练习

1.用try.......catch......处理运行时异常(捕获异常---运行时异常的处理方式)

public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println(3 / 0);    //这里出现异常  其他的逻辑就不执行了(下面的代码不走了)
        System.out.println("异常之后的逻辑.......");
    }
}

public class ExceptionDemo {
    public static void main(String[] args) {
        
        try {
            System.out.println(3 / 0);
        } catch (ArithmeticException a) {
            a.printStackTrace();             //将具体的异常信息打印输出到控制台上
        } finally {
            System.out.println("finally代码逻辑必须执行......");
        }

        System.out.println("异常之后的逻辑.......");
    }
}

2.多个catch处理运行时异常(捕获异常---运行时异常的处理方式)

public class ExceptionDemo2 {
    public static String str;                   //null

    public static void main(String[] args) {
        //判断比较str的数据是否等于hello
        System.out.println(str.equals("hello"));     //这里是运行异常的空指针问题


        int[] array = new int[]{1, 2, 3};           
        System.out.println(array[3]);                //上面出问题这里不会执行。若上面没问题,运行到这里是越界。
        for (int i : array) {
            System.out.println("数组元素是:" + i);
        }
        
    }
}

用(1)处理:

public class ExceptionDemo2 {
    public static String str;
    public static void main(String[] args) {
        
        try {
            System.out.println(str.equals("hello"));
        } catch (NullPointerException e) {
            e.printStackTrace();
        }

        int[] array = new int[]{1, 2, 3};
        try {
            System.out.println(array[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        
        for (int i : array) {
            System.out.println("数组元素是:" + i);
        }
    }
}

用多个catch处理:
public class ExceptionDemo2 {
    public static String str;//null


    public static void main(String[] args) {
        
        int[] array = new int[]{1, 2, 3};
        try {
            System.out.println(str.equals("hello"));   //在try里面,异常之后的代码就不执行了(即只走这一句)。
            System.out.println("11111111111111");      //不要把很多代码都放到try  能够区分稳定代码和非稳定代码
            System.out.println(array[3]);
        } catch (NullPointerException n) {             //有多个catch块,有且只执行一个(执行try里捕获的第一个异常对应的catch块)
            System.out.println("数据不能为null");
            n.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException a) {
            System.out.println("索引越界了");
            a.printStackTrace();
        }

        //对上面的多个catch ,也可以合并一个catch (要为一个等级)(必须是单或,双或不允许存在)
        try {
            System.out.println(str.equals("hello"));
            System.out.println("11111111111111");
            System.out.println(array[3]);
        } catch (NullPointerException | ArrayIndexOutOfBoundsException | ClassCastException n) {
            n.printStackTrace();
        }
        
        for (int i : array) {
            System.out.println("数组元素是:" + i);
        }
    }
}

3.throw(抛出) 产生异常(不是处理异常的方式)后期会处理

模拟: 计算2个数字相除的结果

public class ExceptionDemo3 {
    public static double divide() {
        Scanner input = new Scanner(System.in);
        System.out.println("请录入第一个数据:");
        int num1 = input.nextInt();

        System.out.println("请录入第2个数据:");
        int num2 = input.nextInt();

        if (num2 == 0) {
            //这里有两个功能:1.给用户提示  2.结束当前程序
            //System.out.println("除数不能为0");
            //System.exit(-1);     //它在服务器里以后没一点意义,和服务器没任何关系(以后我们的项目跑服务器,不能结束当前服务器)  以后该操作不好使了
            //在这里使用throw(抛出)手动产生异常   达到和上面两句一样的效果
            //throw,使用在方法体内部的,语法:throw new 异常类构造;
            throw new ArithmeticException("除数不能为0");           //后期都有具体技术进行全局的异常处理
        }
        return num1 / num2;
    }
    
    public static void main(String[] args) throws Exception {
        System.out.println(divide());

    }
}

模拟: 用户登录(正常开发中的逻辑)

public class ExceptionDemo3 {

    public static boolean login(String name, String pass) {
        if (name == null || pass == null) {
            throw new NullPointerException("用户名或者密码是无效的数据");
        }
        //所有的用户信息都在容器(用户名和密码有效的前提下,和信息库比较,看有这个人没)
        User user = findUserByNameAndPass(name, pass);    //方法findUserByNameAndPass(),这里不写了。
        if (user == null) {
            throw new RuntimeException("用户名或者密码不符");   //实在找不到具体异常,也可用一个父级
        }
        System.out.println("登录成功:" + user.getName());
        return true;
    }

    public static void main(String[] args) throws Exception {
        A a = new A();
    }
}

4.try.......finally 有时并非处理异常,是释放资源。

//try....catch....finally      catch可以省略(只是为了释放资源)   有没有异常,finally都会走
public class ExceptionDemo3 {

    public static double divide() {
        Scanner input = new Scanner(System.in);
        int result;
        try {
            System.out.println("请录入第一个数据:");
            int num1 = input.nextInt();

            System.out.println("请录入第2个数据:");
            int num2 = input.nextInt();

            if (num2 == 0) {
                throw new ArithmeticException("除数不能为0");
            }
            result = num1 / num2;
        } finally {
            input.close();
        }
        return result;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(divide());

    }
}

//使用lombok,可在局部变量上加 @Cleanup ,有规则:只有类实现了Closeable接口的时候,才能对这些变量加@Cleanup
//@Cleanup 释放资源,自动调用close方法
//故,可不用try.....finally,用 @Cleanup
class A implements Closeable {
    @Override
    public void close() {
        System.out.println("我被关闭了......");
    }
}


public class ExceptionDemo3 {

    public static double divide() {
        @Cleanup
        Scanner input = new Scanner(System.in);
        System.out.println("请录入第一个数据:");
        int num1 = input.nextInt();

        System.out.println("请录入第2个数据:");
        int num2 = input.nextInt();

        if (num2 == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        return  num1 / num2;
    }


    public static void main(String[] args) throws Exception {
        System.out.println(divide());

    }
}

5.throws -----处理运行时异常的第二种方式

throws(抛出异常)

//用法:在方法签名的后面,使用关键字throws抛出具体的异常类型(多个异常类型用逗号隔开,异常间也是平级关系)
//捕获异常才是真正处理异常的方式。    throws没有对异常执行相关的处理,是一种消极的处理,只是将异常抛出给上级(谁调用该方法就把异常抛出给谁)
public class ExceptionDemo4 {
    public static void main(String[] args) throws ArithmeticException, NullPointerException {

        //jvm调用main方法,但jvm默认不处理任意一个运行时异常
        System.out.println(3 / 0);               //产生异常,抛给jvm,jvm不处理,写throws***和不写没区别
        System.out.println("11111111111111111111111");
    }
}

6. 编译时的异常处理

求租金(编译时异常)-----try....catch....去处理

//jdk1.7之后提供了Objects(在java的util包里)  java.util.Objects  工具类  一般都是对一些变量是否为空的判断以及是否相等的比较,一般是null值对比较的一些封装方法
//Objects.requireNonNull()要求指定的变量不能为null值
//求两个时间之间的间隔:下面的时间是字符串,字符串之间不能直接相减,需要字符串的时间数据转换成数值类型的数据
//类似“100”这种的字符串转数值类型数据:
//String num = "100";
//int i = Integer.parseInt(num);    将字符型转为整型(要求字符串是数字格式)
//类似String borrowTime = "2022-01-01 12:30";  字符串有空格,横线等,不能直接转换,要间接转换
//字符串时间 ----> java.util.Date ---> 数值数据
//Date date = new Date();
//System.ot.println(date);   输出当前系统时间
//还需要一个中介  字符串时间 ----> 格式化Date的类型 java.text.DateFormat ----> java.util.Date ---> 数值数据
//1. 字符串时间转换成Date类型的数据
//DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//borrowDate = dateFormat.parse(borrowTime);   将字符串解析为Date对象
//2. Date类型的时间转换成数值类型的数据
//long time1 = borrowDate.getTime();       获得特定日期时间的毫秒数
public class ExceptionDemo5 {
    /**
     * 求租金
     *
     * @param borrowTime 借车时间
     * @param backTime   还车时间
     * @return 支付的钱---->求2个时间的间隔
     */
    public static long getMoney(String borrowTime, String backTime) {
        /*if (borrowTime == null || backTime == null) {
            throw new NullPointerException("时间不能为null");
        }
        下面的代码与其等价
        */
        Objects.requireNonNull(borrowTime, "借车时间不能为null");
        Objects.requireNonNull(backTime, "还车时间不能为null");
        //1.1 字符串时间转换成Date类型的数据
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//创建格式化日期对象  并指定pattern格式  将指定格式的字符串进行转换

        //出现了编译时异常: 编译器自动检测到了异常,必须处理
        //异常处理方式:1.捕获try..catch..finally   2.抛出throws
        Date borrowDate = null;
        Date backDate = null;
        try {
            borrowDate = dateFormat.parse(borrowTime);
            backDate = dateFormat.parse(backTime);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //1.2 Date类型的时间转换成数值类型的数据
        long time1 = borrowDate.getTime();   //获得特定日期时间的毫秒数
        long time2 = backDate.getTime();   //获得特定日期时间的毫秒数
        long duration = time2 - time1;    //2个日期时间间隔的毫秒数
        //毫秒数转换成小时
        return duration / 1000 / 3600;
    }

    public static void main(String[] args)  {

        String borrowTime = "2022-01-01 12:30:30";
        String backTime = "2022-01-01 15:50:30";

        System.out.println(getMoney(borrowTime, backTime));
    }
}

求租金-----其他编译时异常,eg:异常信息传递

//ParseException解析异常,本代码中是字符串转换Date的时候,有可能出现的异常。何时触发?
//字符串的日期的数据与指定的pattern格式不符的时候,会触发异常(若字符串里的内容大于等于pattern,比如又多了星期,不会报错)
//在控制台找引起问题的根本原因,找字眼CausedBy,此时e.printStackTrace();就不够用了,需要用到异常信息传递
//异常信息传递,传递有两层概念:1. 分层 dao-->service--->controller(低的传高的,在controller里统一处理)  2. 父与子----> 子类型异常传递给父级类型的异常
//异常信息传递,必须用关键字throw来实现(低级传到高级,快速定位根本原因)
public class ExceptionDemo5 {
    /**
     * 求租金
     *
     * @param borrowTime 借车时间
     * @param backTime   还车时间
     * @return 支付的钱---->求2个时间的间隔
     */
    public static long getMoney(String borrowTime, String backTime) {

        Objects.requireNonNull(borrowTime, "借车时间不能为null");
        Objects.requireNonNull(backTime, "还车时间不能为null");
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Date borrowDate = null;
        Date backDate = null;
        try {
            borrowDate = dateFormat.parse(borrowTime);
            backDate = dateFormat.parse(backTime);
        } catch (ParseException e) {
            //System.out.println("字符串的日期的数据与指定的pattern格式不符的时候  会触发异常");
            //e.printStackTrace();
            //异常信息传递。1. 分层 dao-->service--->controller   2. 父与子----> 子类型异常传递给父级类型的异常
            //throw
            try {
                throw new Exception(e);       //传给父级
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }

        long time1 = borrowDate.getTime();
        long time2 = backDate.getTime();
        long duration = time2 - time1;
        return duration / 1000 / 3600;
    }



    public static void main(String[] args)  {


        String borrowTime = "2022-01-01 12:30";
        String backTime = "2022-01-01 15:50:30";

        System.out.println(getMoney(borrowTime, backTime));

    }
}

其他编译时异常

//一写Class.forName("a,b,c");就报错,还未运行,故为编译时异常。
//处理:用try...catch....(真正处理)      或者 throws ClassNotFoundException 类未找到异常   (并未真正处理,消极处理)
//编译时异常一定得写,要处理。  运行时异常可以不用管,后期会学。可提前加一写预判
public class ExceptionDemo5 {

    @SneakyThrows    //等同于throws ClassNotFoundException  ,可让代码不报错,也未处理异常
    public static void a() /*throws ClassNotFoundException*/{
        // try {
        Class.forName("com.javsm.exception1.ExceptionDemo10");    //找类名,需要给路径
      /*  } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("类的路径写错了  项目里面不存在此类");
        }*/

        System.out.println(111);
    }

    public static void main(String[] args)  {

        a();

    }
}

7.finally-return

public class Demo1 {
    public static int a() {

        try {
            System.out.println(3 / 1);    //若为3/0,这一行有问题  就不走return 1;  会走catch模块,有return 2;就结束了  但又有finally,一定会走,最终返回的是3。故,禁止在finally里做返回操作,会模糊视线     这里是3/1,最后也会返3
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return 2;
        } finally {
                          //一般,finally里是释放资源
            return 3;
        }
    }
    public static void main(String[] args) {
        System.out.println(a());
    }
}

8. 自定义异常---枚举类 (throw与自定义异常结合使用)

模拟用户支付(jdk提供的很多异常类不符合业务需求)

public class UserPay {
    private static int balance = 2000;       //余额

    /**
     * 支付的总钱数
     * @param money
     */
    public static void pay(int money){
        if(money>balance){
            //System.out.println("余额不足,无法支付");   //后期跑服务器,没有控制台,需要把信息展示到文件,故要通过异常来维护
            //用throw输出什么? 需要自己创建异常类   一般给客户是code码和message提示       创建支付异常类
            throw new BalanceException(1001,"用户余额不足,无法支付");
        }
        balance-=money;
    }

    public static void main(String[] args) {
        int money = 3000;
        System.out.println("需要支付:"+money);
        pay(money);
    }


}

@Setter
@Getter
//很少继承Throwable,范围太大   一般继承Exception(编译时异常类)或RuntimeException(运行时异常)
public class BalanceException extends RuntimeException{
    private String msg;
    private int code;

    public BalanceException() {
    }

    public BalanceException(int code,String message){
    super(code + ":"+message);
    this.code = code;
}

    public BalanceException(String message, Throwable cause) {
        super(message, cause);
    }

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

}

本题改成编译时异常:

@Setter
@Getter
//1.若要将code和message返回给前台页面做,就要动态拿code和message。拿的时候通过对象走,这是继承Exception更好
public class BalanceException extends Exception{
    private String msg;
    private int code;

    public BalanceException() {
    }

    public BalanceException(int code,String message){
    super(code + ":"+message);
    this.code = code;
    this.msg =message;
}

    public BalanceException(String message, Throwable cause) {
        super(message, cause);
    }

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

}

public class UserPay {
    private static int balance = 2000;       //余额

    /**
     * 支付的总钱数
     * @param money
     */
    public static void pay(int money) throws BalanceException{       //3.没能力处理就抛给上级
        if(money>balance){
            throw new BalanceException(1001,"用户余额不足,无法支付");  //2.继承了Exception就报红,throw没能力处理
        }
        balance-=money;
    }

    public static void main(String[] args) {
        int money = 3000;
        System.out.println("需要支付:"+money);
        try {                                         //4.try...catch...一下
            pay(money);
        } catch (BalanceException e) {
            e.printStackTrace();                       //不写也行
            System.out.println(e.getCode());          //5.拿code和msg
            System.out.println(e.getMessage());
        }
    }

}

本题若再创建一个自定义异常类:

public class GoodException extends Exception{
    private String msg;
    private int code;

    public GoodException() {
    }

    public GoodException(String message, int code) {
        super(message);
        this.code = code;
        this.msg = message;
    }

    public GoodException(String message, Throwable cause, int code) {
        super(message, cause);
        this.code = code;
        this.msg = message;
    }

    public GoodException(Throwable cause, int code) {
        super(cause);
        this.code = code;
    }

}

重复的地方很多,此时考虑建立一个父类异常,让两个自定义异常继承。如下,为最终结果:

@Setter
@Getter
//既然有相同的地方,就建一个父类(两个自定义异常的父类)
public class BaseException extends Exception{
    private String msg;
    private int code;

    public BaseException() {
        super();
    }

    public BaseException(String message, int code) {
        super(message);
        this.code = code;
        this.msg = message;
    }

    public BaseException(String message, Throwable cause, int code) {
        super(message, cause);
        this.code = code;
        this.msg = message;
    }

    public BaseException(Throwable cause, int code) {
        super(cause);
        this.code = code;
    }

    public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, int code) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.code = code;
        this.msg = message;
    }
}

public class GoodException extends BaseException{
    public GoodException() {
    }

    public GoodException(String message, int code) {
        super(message, code);
    }
}

@Setter
@Getter
//1.若要将code和message返回给前台页面做,就要动态拿code和message。拿的时候通过对象走,这是继承Exception更好
public class BalanceException extends BaseException{
    public BalanceException() {
    }

    public BalanceException(String message, int code) {
        super(message, code);
    }
}

public class UserPay {
    private static int balance = 2000;       //余额

    /**
     * 支付的总钱数
     * @param money
     */
    public static void pay(int money) throws BalanceException{       //3.没能力处理就抛给上级
        if(money>balance){
            throw new BalanceException("用户余额不足,无法支付",1001);  //2.继承了Exception就报红,throw没能力处理
        }
        balance-=money;
    }

    public static void main(String[] args) {
        int money = 3000;
        System.out.println("需要支付:"+money);
        try {                                         //4.try...catch...一下
            pay(money);
        } catch (BalanceException e) {
            e.printStackTrace();                       //不写也行
            System.out.println(e.getCode());          //5.拿code和msg
            System.out.println(e.getMessage());
        }
    }

}

7.课后作业

1. 需通过控制台接收用户输入的两个整数,然后做除法。要求用异常处理输入非数字的异常,和除数为0的异常。

/**
 * @author: sunshine
 * @description:  需通过控制台接收用户输入的两个整数,然后做除法。要求用异常处理输入非数字的异常,和除数为0的异常
 * @data: 2022/2/24  19:38
 * @version: 0.1
 * @since: jdk11
 */
public class Exercise {

    public static double divide() {
        @Cleanup                           //用注解释放资源,或者不用注解,后面用input.close();或者用try...finally...
        Scanner input = new Scanner(System.in);
        while (true) {
            try {                                     //可能第一个或第二个数据录入的不是数字,故为不稳定代码,放入try
                System.out.println("请录入第一个数据:");
                int num1 = input.nextInt();          //若第一次在这里录入a,等价于在IO流里面一直有一个数据是a,若不读走,就是死循环
                System.out.println("请录入第2个数据:");
                int num2 = input.nextInt();
                return num1 / num2;
            } catch (InputMismatchException e) {
                System.out.println("录入的数据不合法,请重新录入");
                input.next();                              //此处将a读走,再重新录入
            } catch (ArithmeticException e) {              //若未发生录入是非数字的异常,发生算术异常,自然会走这个
                System.out.println("除数不能为0  请重新录入");
            }
        }                             //try里代码从上往下全部走完,自然跳出循环

    }


    public static void main(String[] args) {
        System.out.println("结果:" + divide());
    }
}

2. 模拟实现用户购买商品的功能,使用数组模拟商品列表,当购买的商品不存在或者商品库存为0时,抛出自定义异常。用户购买某一个商品时,对异常进行处理,并对库存进行改变。

public class BuyDemo {

    private static Product[] products;
    private static Scanner input;

    static {
        products = new Product[10];
        input = new Scanner(System.in);
        products[0] = Product.builder().id(1).name("apple").price(3).store(1000).build();
        products[1] = Product.builder().id(2).name("banana").price(5).store(20).build();
        products[2] = Product.builder().id(3).name("orange").price(2).store(10).build();
    }

    public void buy() throws ProductException {
        System.out.println("目前在售的商品列表如下:");
        showProductInfo();
        //看着商品列表进行购买的商品
        System.out.println("请录入要购买的商品id:");
        int pid = input.nextInt();
        //有可能会出现购买的商品id是不存在的
        //与数组的元素进行比对
        Product buyProduct = null;
        for (Product product : products) {
            if (product == null) break;
            if (product.getId() == pid) {
                //存在
                buyProduct = product;
                break;
            }
        }
        if (buyProduct == null) {
            throw new ProductException(StatusEnum.PRODUCT_NOT_EXITS);
        }
        System.out.println("购买的商品详情如下:" + buyProduct);
        String name = buyProduct.getName();
        int store = buyProduct.getStore();
        System.out.println("请录入要购买<<" + name + ">>数量:");
        int buyNum = input.nextInt();
        if (buyNum > store) {
            throw new ProductException(StatusEnum.PRODUCT_STORE_NOT_ENOUGH);
        }
        System.out.println("购买" + buyNum + "个<<" + name + ">>成功,需要支付:" + (buyNum * buyProduct.getPrice()));

        //商品的库存减少
        buyProduct.setStore(store - buyNum);
        //浏览商品列表 库存数量是否变化
        showProductInfo();

    }

    private void showProductInfo() {
        for (Product product : products) {
            if (product == null) {
                break;
            }
            System.out.println(product);
        }
    }

    public static void main(String[] args) {
        try {
            new BuyDemo().buy();
        } catch (ProductException e) {
            System.out.println("用户看到了这样的信息:" + e.getStatusEnum().getMsg());
            System.out.println("用户看到了这样的status:" + e.getStatusEnum().getStatus());
        }
    }
}

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Builder
public class Product {

    private int id;
    private String name;
    private double price;
    private int store;

}

@Setter
@Getter
public class ProductException extends Exception {

    private StatusEnum statusEnum;

    public ProductException(StatusEnum statusEnum) {
        super(statusEnum.getMsg());
        this.statusEnum = statusEnum;
    }
}

@Getter
@AllArgsConstructor
public enum StatusEnum {

    PRODUCT_NOT_EXITS("A1001", "购买的商品不存在"),
    PRODUCT_STORE_NOT_ENOUGH("A1002", "购买的商品库存不足"),
    ;
    private final String status;
    private final String msg;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值