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; }