前言
去年又重新刷了路遥的《平凡的天下》,最近也在朋友推荐下,来看了路遥的另一部成名作《人生》。
故事中的主人公高加林,虽生在农村,面朝黄土背朝天,却不甘心像父辈一样或者,同心专心想着摆脱民语的束缚,追求他的理想生活。
然而命运多舛,在他所想象的理想生活中,一次次跌倒,最终不得不承认自己的平凡,生活总得继续。
现实天下如此,代码天下里,我们也渴望一切都是理想的。
我们渴望用户输入的数据格式永远是正确的,打开的资源也一定存在,用户的硬件是正常的,用户的操作体系四稳定的,用户的网络也一定是流畅的等等
然而事与愿违,愿望是好的,现实是残酷的。
引入非常机制的目的就在于,当“人生”中出现非常时,能够将其捕获并处理,保证我们能更好的走下去,不至于出现一点插曲,就停滞不前。
一、非常引入
因此呢就出现了非常处理,我们把可能出现非常的业务代码放到try块中定义,把非常处理放到catch块中举行处理,保证程序遇到非常依然能继续运行,保证程序的健壮性。
① 我们来看看高加林的一生中各种非常的处理- try{
- //业务逻辑,高加林的一生
- System.out.println("1、高中毕业,虽然没考上大学。却不断学习,在报纸上发表诗歌和散文,参加考试,谋得一份临时教师职位");
- System.out.println("2、高加林的叔叔从外地回到家乡工作,在县城担任劳动局局长,副局长为了讨好新上任的局长;");
- System.out.println("便私下给高加林走了后门,就这样高加林成为了公职人员");
- System.out.println("3、与高中同学黄亚萍相遇,再次相遇的两个人,志趣相投,相聊甚欢。高加林以为攀上黄亚萍这个高枝,能到大城市一展宏图");
- }catch(ExceptionClass1 e1){
- System.out.println("第一个异常发生,教师职位被他人顶替");
- System.out.println("没有了工作,重新回到农村,成为最不想当的农民");
- System.out.println("很快加入村里的劳动队伍里,白天努力劳作,晚上看书学习,等待着东山再起的机会");
- }catch(ExceptionClass2 e2){
- System.out.println("第二个异常发生,遭人举报走后门");
- System.out.println("再次丢了工作,一直想摆脱农名身份的他,再次成为了农民");
- }catch(ExceptionClass3 e3){
- System.out.println("第三个异常发生,纸包不住火,黄亚萍及其家人知道了高加林遭遇举报");
- System.out.println("被打回原型,和黄亚萍断绝了关系");
- System.out.println("黄亚萍不想高加林回农村,希望高加林去找叔父帮忙,看是否可以继续留下来");
- }catch(Exception e){
- System.out.println("再次跌倒的他,没再去找叔父");
- System.out.println("有了清醒的认知,自己的路还是得靠自己走下去");
- }finally{
- System.out.println("接受现实,更好的走下去");
- }
复制代码 ② 代码中- //未处理异常,程序遇到异常没法继续执行
- public class ExceptionTest {
- public static void main(String[] args) {
- int num1 =7;
- int num2 =0;
- int res = num1/num2;
- System.out.println("程序继续执行......");
- }
- }
- //输出
- Exception in thread "main" java.lang.ArithmeticException: / by zero
- at ExceptionTest.main(ExceptionTest.java:5)
复制代码 参加非常处理- //加入异常处理,程序遇到异常后继续运行
- public class ExceptionTest {
- public static void main(String[] args) {
- int num1 =7;
- int num2 =0;
- try {
- int res = num1/num2;
- } catch (Exception e) {
- System.out.println(e.getMessage());
- }
- System.out.println("程序继续执行......");
- }
- }
- //输出
- / by zero
- 程序继续执行......
复制代码 小结: 如果执行try块里的业务逻辑代码出现非常,体系会自动生成一个非常对象,该非常对象被体骄傲给Java运行时环境,这个过程成为抛出非常
Java运行时环境收到非常对象时,会寻找能处理非常对象的catch块,如果找到合适的catch块,则把该非常对象交给该catch块处理,这个过程被成为非常捕获;
如果Java运行环境找不到捕获非常的catch块,则运行时环境终止,Java程序已将退出
二、根本概念
程序执行中发生的不正常情况(语法错误逻辑错误不是非常)称为非常,全部的非常都继承与java.lang.Throwable 。Throwable 有两个重要子类
- Error(错误):Java虚拟机无法解决的严重问题,我们没办法通过 catch 来举行捕获 。如:内存溢出(OutOfMemoryError)、Java 虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。error 是严重错误,Java虚拟机会选择线程终止
- Exception(非常):编程错误或偶尔的外在因素导致的一般问题,程序本身可以处理的非常,可以通过 catch 来举行捕获。Exception 又可以分 运行时非常(程序运行时发生的非常)和编译时非常(程序编译期间产生的非常,必须要处理的非常,否则代码没法编译通过)。
三、非常继承体系
注:虚线为实现,实线为继承
四、常见运行非常
4.1 NullPointerException 空指针非常
- public class ExceptionTest {
- public static void main(String[] args) {
- String str = null;
- System.out.println(str.length());
- }
- }
- //输出
- Exception in thread "main" java.lang.NullPointerException
- at ExceptionTest.main(ExceptionTest.java:4)
复制代码 4.2 ArithmeticException 数学运算非常
- public class ExceptionTest {
- public static void main(String[] args) {
- int num1=7;
- int num2=0;
- int res = num1/num2;
- }
- }
- //输出
- Exception in thread "main" java.lang.ArithmeticException: / by zero
- at ExceptionTest.main(ExceptionTest.java:5)
复制代码 4.3 ArrayIndexOutOfBoundsException 数组下标越界非常
- public class ExceptionTest {
- public static void main(String[] args) {
- String strarr[] ={"个人博客","www.xiezhrspace.cn","公众号","XiezhrSpace"};
- //注意数组下标时从0开始的
- for (int i = 0; i <= strarr.length; i++) {
- System.out.println(strarr[i]);
- }
- }
- }
- //输出
- 个人博客
- www.xiezhrspace.cn
- 公众号
- XiezhrSpace
- Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
- at ExceptionTest.main(ExceptionTest.java:5)
复制代码 分析: 上述代码中我们通过io流读取D盘xiezhr.txt文件中的内容,并将其内容按行打印出来。最后在finally代码块中将scanner关闭
Java 7 之后,新的语法糖 try-with-resources 可以简写上述代码- public class ExceptionTest {
- public static void main(String[] args) {
- Class1 class1 = new Class2(); //向上转型
- Class2 class2 = (Class2)class1; //向下转型
- Class3 class3= (Class3)class1; //两个类没有关系,转型失败
- }
- }
- class Class1{}
- class Class2 extends Class1{};
- class Class3 extends Class1{};
- //输出
- Exception in thread "main" java.lang.ClassCastException: Class2 cannot be cast to Class3
- at ExceptionTest.main(ExceptionTest.java:5)
复制代码 两段代码实现的功能是一样的,但明显try-with-resources 写法简单了很多多少。我们也更提倡优先利用 try-with-resources 而不是try-finally。
6.2.3 抛出非常
当一个方法产生某种非常,但是不确定如那边理这种非常,那么就需要在该方法的头部表现地申明抛出非常,表明该方法将不对这些非常举行处理,而用该方法调用者负责处理
6.2.3.1 thorows
①语法格式
returnType method_name(paramList) throws Exception 1,Exception2,…{…}
- returnType 表现返回值类型
- method_name 表现方法名
- paramList 表现参数列表;
- Exception 1,Exception2,… 表现非常类
如果有多个非常类,它们之间用逗号分隔。这些非常类可以是方法中调用了可能拋出非常的方法而产生的非常,也可以是方法体中生成并拋出的非常
②利用场景
当前方法不知道如那边理这种类型的非常,该非常应该由向上一级的调用者处理;
如果 main 方法也不知道如那边理这种类型的非常,也可以利用 throws 声明抛出非常,该非常将交给 JVM 处理。
JVM 对非常的处理方法是,打印非常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到非常后自动结束的原因
③实践操作- public class ExceptionTest {
- public static void main(String[] args) {
- String str1 ="123";
- String str2 = "abc";
- System.out.println(Integer.valueOf(str1)); //可以转成功
- System.out.println(Integer.valueOf(str2));
- }
- }
- //输出
- 123
- Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
- at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
- at java.lang.Integer.parseInt(Integer.java:580)
- at java.lang.Integer.valueOf(Integer.java:766)
- at ExceptionTest.main(ExceptionTest.java:6)
复制代码 分析: 以上代码,首先在定义 readFile() 方法时用 throws 关键字声明在该方法中可能产生的非常,然后在 main() 方法中调用readFile() 方法,并利用 catch 语句捕获产生的非常
④ 非常处理流程图
注意:子类方法声明抛出的非常类型应该是父类方法声明抛出的非常类型的子类或相同,子类方法声明抛出的非常不允许比父类方法声明抛出的非常多。
//下面程序编译就报错,原因时子类抛出比父类还大的非常- try {
- 逻辑程序块 //可能有异常的代码
- } catch(Exception e) {
- /*
- ①发生异常时,系统将异常封装成Exception对象e,并传递给catch
- ②得到异常Exception e 后,程序员自行处理
- 注:只有发生异常时候,catch代码块才执行
- */
- 捕获异常
- throw(e);
- } finally {
- 释放资源代码块
- }
复制代码 6.2.3.1 throw
throw用来直接抛出一个非常
①语法
throw ExceptionObject;
- ExceptionObject 必须是 Throwable 类或其子类的对象
- 如果是自定义非常类,也必须是 Throwable 的直接或间接子类
②执行原理
throw 语句执行时,它后面的语句将不执行;
此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的非常处理程序。
如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。如许逐层向上,直到最外层的非常处理程序终止程序并打印出调用栈情况
③实践操作- public class TestException {
- public static void main(String[] args) {
- int num1 =10;
- int num2 =0;
- try {
- int num3=num1/num2;
- }catch (Exception e){
- e.printStackTrace();
- }finally {
- System.out.println("finally代码块被执行了");
- }
- System.out.println("程序继续执行...");
- }
- }
- //输出
- java.lang.ArithmeticException: / by zero
- at com.xiezhr.TestException.main(TestException.java:9)
- finally代码块被执行了
- 程序继续执行...
复制代码 6.2.4 自定义非常
当Java提供的内置非常类型不能满足我们的需求时,我们可以计划自己的非常类型。
①语法格式
class XXXException extends Exception|RuntimeException
- 一般将自定义非常类的类名命名为 XXXException,其中 XXX 用来代表该非常的作用
- 自定义非常类需要继承 Exception 类或其子类,如果自定义运行时非常类需继承 RuntimeException 类或其子类
- 自定义非常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的情势接收一个定制的非常消息,并将该消息传递给超类的构造方法。
②实践操作- //读取文本文件的内容
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.util.Scanner;
- public class TestException {
- public static void main(String[] args) {
- Scanner scanner = null;
- try {
- scanner = new Scanner(new File("D://xiezhr.txt"));
- while (scanner.hasNext()) {
- System.out.println(scanner.nextLine());
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- }
- }
- //输出
- 个人博客:www.xiezhrspace.cn
- 个人公众号:XiezhrSpace
- 欢迎你关注
复制代码 6.2.5 多非常捕获
Java7以后,catch 语句可以有多个,用来匹配多个非常
①语法格式- import java.io.File;
- import java.io.FileNotFoundException;
- import java.util.Scanner;
- public class TestException {
- public static void main(String[] args) {
- try(Scanner scanner = new Scanner(new File("D://xiezhr.txt"))){
- while (scanner.hasNext()) {
- System.out.println(scanner.nextLine());
- }
- }catch (FileNotFoundException e){
- e.printStackTrace();
- }
- }
- }
- //输出
- 个人博客:www.xiezhrspace.cn
- 个人公众号:XiezhrSpace
- 欢迎你关注
复制代码
- 多种非常类型之间用竖线|隔开
- 非常变量有隐式的 final 修饰,因此程序不能对非常变量重新赋值
② 非常誊写方式变化- import java.io.File;
- import java.io.IOException;
- import java.util.Scanner;
- public class TestException {
- //定义方法时声明抛出异常,方法中出现的异常自己不处理,交由调用者处理
- public void readfile() throws IOException {
- // 读取文件
- Scanner scanner = new Scanner(new File("D://xiezhr.txt"));
- while (scanner.hasNext()) {
- System.out.println(scanner.nextLine());
- }
- scanner.close();
- }
- public static void main(String[] args) {
- TestException tt = new TestException();
- try {
- //调用readfile方法
- tt.readfile();
- } catch (IOException e) {
- //打印异常
- e.printStackTrace();
- }
- }
- }
- //输出
- 个人博客:www.xiezhrspace.cn
- 个人公众号:XiezhrSpace
- 欢迎你关注
复制代码 变成- public class OverrideThrows {
- public void test() throws IOException {
- FileInputStream fis = new FileInputStream("a.txt");
- }
- }
- class Sub extends OverrideThrows {
- // 子类方法声明抛出了比父类方法更大的异常
- public void test() throws Exception {
- }
- }
复制代码 ③实践操作- import java.util.Scanner;
- public class TestException {
- public boolean validateUserName(String username) {
- boolean con = false;
- if (username.length() >4) {
- // 判断用户名长度是否大于8位
- if ("admin".equals(username)) {
- con = true;
- }else{
- throw new IllegalArgumentException("你输入的用户名不对");
- }
- } else {
- throw new IllegalArgumentException("用户名长度必须大于 4 位!");
- }
- return con;
- }
- public static void main(String[] args) {
- TestException te = new TestException();
- Scanner input = new Scanner(System.in);
- System.out.println("请输入用户名:");
- String username = input.next();
- try {
- boolean con = te.validateUserName(username);
- if (con) {
- System.out.println("用户名输入正确!");
- }
- } catch (IllegalArgumentException e) {
- System.out.println(e);
- }
- }
- }
- //输出
- ①
- 请输入用户名:
- abc
- java.lang.IllegalArgumentException: 用户名长度必须大于 4 位!
- ②
- 请输入用户名:
- abcdef
- java.lang.IllegalArgumentException: 你输入的用户名不对
- ③
- 请输入用户名:
- admin
- 用户名输入正确!
复制代码 分析:
上面程序中IndexOutOfBoundsException|NumberFormatException|ArithmeticException来定义非常类型,这就表明该 catch 块可以同时捕获这 3 种类型的非常
七、Throwable 类常用方法
- String getMessage(): 返回非常发生时的扼要描述
- String toString(): 返回非常发生时的详细信息
- String getLocalizedMessage(): 返回非常对象的本地化信息。利用 Throwable 的子类覆盖这个方法,可以生本钱地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()返回的结果相同
- void printStackTrace(): 在控制台上打印 Throwable 对象封装的非常信息
八、易混概念
8.1 Error和Exception的异同
- Error 和Exception 都有共同的先人Throwable,即Error 和Exception 都是Throwable的子类
- Exception :程序本身可以处理的非常,可以通过 try-catch 来举行捕获。Exception又可以分为 Checked Exception (受检查非常,必须处理) 和 Unchecked Exception(不受检查非常,可以不处理)。
- Error :Error 属于程序无法处理的错误 ,我们没办法通过 try-catch 来举行捕获 。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些非常发生时,Java 虚拟机(JVM)一般会选择线程终止
8.2 throw和throws的区别
- throws 用来声明一个方法可能抛出的全部非常信息,表现出现非常的一种可能性,但并不一定会发生这些非常;throw 则是指拋出的一个具体的非常类型,执行 throw 则一定抛出了某种非常对象。
- 通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的非常信息,而在方法(类)内部通过 throw声明一个具体的非常信息。
- throws 通常不消表现地捕获非常,可由体系自动将全部捕获的非常信息抛给上级方法; throw 则需要用户自己捕获相关的非常,而后再对其举行相关包装,最后将包装后的非常信息抛出
8.3 Checked Exception 和 Unchecked Exception
- Checked Exception 受检查非常 Java 代码在编译过程中,如果受检查非常没有被 catch或者throws 关键字处理的话,就没办法通过编译
- 常见的受检查非常有: IO 相关的非常、ClassNotFoundException 、SQLException
- 例如,下面就是
-
- Unchecked Exception 即 不受检查非常 ,Java 代码在编译过程中 ,我们即使不处理不受检查非常也可以正常通过编译。
- 我们经常看到的有以下几种
NullPointerException :空指针错误
IllegalArgumentException :参数错误好比方法入参类型错误
NumberFormatException:字符串转换为数字格式错误
ArrayIndexOutOfBoundsException:数组越界错误
ClassCastException:类型转换错误
ArithmeticException:算术错误
SecurityException :安全错误好比权限不够
UnsupportedOperationException:不支持的操作错误好比重复创建同一用户
8.4 try-with-resources 与 try-catch-finally
- try-with-resources 是Java 1.7增加的新语法糖,在try 代码块结束之前会自动关闭资源。
- try-with-resources 适用于任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象, 字节输入流(InputStream),字节输出流(OutputStream),字符输入流(Reader),字符输出流(Writer)均实现了这接口
- try-catch-finally 没有限定条件,finally不仅可以关闭资源,还可以用于执行其他代码块;
- try-with-resources 代码更加简便,有限定条件,资源会立即被关闭
- finally关闭资源不会立即关闭,取决与网络和体系,可能会很快,也可能会等一两天,所以,最好不要利用finally作为业务流程的控制,在《Effective java》一书 的第9条:try-with-resources优先于try-finally 中有相关详细的介绍,其中提到了许多由于finally延迟导致的网络事件
九、SpringBoot 中优雅的处理统一非常返回
日常开辟中,我们处理非常一般都会用到try-catch 、throw和throws 的方式抛出非常。
这种方式不经程序员处理贫苦,对用户来说也不太友好
我们都渴望不消写过多的重复代码处理非常,又能提升用户体验。这时候全局非常处理就显得很便捷很重要了
9.1 全局非常捕获与处理
Springboot对提供了一个 @ControllerAdvice注解以及 @ExceptionHandler注解,分别用于开启全局的非常捕获和说明捕获哪些非常,对那些非常举行处理。
- import java.util.Scanner;
- public class TestException {
- public static void main(String[] args) {
- int age;
- Scanner scanner = new Scanner(System.in);
- System.out.println("请输入你的年龄");
- age=scanner.nextInt();
- try {
- if(age < 0) {
- throw new AgeException("您输入的年龄为负数!输入有误!");
- } else if(age > 100) {
- throw new AgeException("您输入的年龄大于100!输入有误!");
- } else {
- System.out.println("您的年龄为:"+age);
- }
- } catch (AgeException e) {
- e.printStackTrace();
- }
- }
- }
- //输出
- ①
- 请输入你的年龄
- 120
- com.xiezhr.AgeException: 您输入的年龄大于100!输入有误!
- at com.xiezhr.TestException.main(TestException.java:15)
- ②
- 请输入你的年龄
- -34
- com.xiezhr.AgeException: 您输入的年龄为负数!输入有误!
- at com.xiezhr.TestException.main(TestException.java:13)
- ③
- 请输入你的年龄
- 30
- 您的年龄为:30
复制代码 分析 上面这段代码就是说,只要是代码运行过程中有非常就会举行捕获,并输出出这个非常。然后我们随便编写一个会发生非常的代码,测试出来的非常是如许的。
这对于前后端分离来说如许的报错对用户并不好,前后端分离之后唯一的交互就是json了,我们也渴望将后端的非常变成json返回给前端处理。
9.2 用枚举类型记录已知错误信息与成功信息
ErrorEnum枚举类中定义了常见的错误码以及错误的提示信息。
SuccEnum 枚举类中定义了成功码及成功提示信息
至于这里为什么用枚举就不具体说了,网上文章说说的也比较多了
具体可以参照:Java 枚举(enum) 详解7种常见的用法
① 已知错误信息- try{
- // 可能会发生异常的语句
- } catch (IOException | ParseException e) {
- // 异常处理
- }
复制代码 ② 成功信息- try{
- // 可能会发生异常的语句
- } catch (FileNotFoundException e) {
- // 调用方法methodA处理
- } catch (IOException e) {
- // 调用方法methodA处理
- } catch (ParseException e) {
- // 调用方法methodA处理
- }
复制代码 9.3 定义统一结果返回与非常返回
- success:用boolean 类型标识,标识是否成功
- code: 状态码,区分各种报错信息与成功返回
- msg : 成功或错误提示信息
- data : 返回的数据
- try{
- // 可能会发生异常的语句
- } catch (FileNotFoundException | IOException | ParseException e) {
- // 调用方法处理
- }
复制代码 9.4 封装工具类返回结果
这里我们定义好了统一的结果返回,其中里面的静态方法是用来当程序非常的时候转换成非常返回规定的格式。
- import java.util.Scanner;
- public class TestException {
- public static void main(String[] args) {
- int num1;
- int num2;
- try {
- Scanner scanner = new Scanner(System.in);
- System.out.println("请输入num1的值");
- num1=scanner.nextInt();
- System.out.println("请输入num2的值");
- num2=scanner.nextInt();
- int num= num1/num2;
- System.out.println("你输入的两个数相除,结果是" + num);
- } catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
- System.out.println("程序发生了数组越界、数字格式异常、算术异常之一");
- e.printStackTrace();
- }
- catch (Exception e) {
- System.out.println("未知异常");
- e.printStackTrace();
- }
- }
- }
- //输出
- ①
- 请输入num1的值
- 12
- 请输入num2的值
- 0
- 程序发生了数组越界、数字格式异常、算术异常之一
- java.lang.ArithmeticException: / by zero
- at com.xiezhr.TestException.main(TestException.java:15)
- ②
- 请输入num1的值
- 6888888888
- 未知异常
- java.util.InputMismatchException: For input string: "6888888888"
- at java.util.Scanner.nextInt(Scanner.java:2123)
- at java.util.Scanner.nextInt(Scanner.java:2076)
- at com.xiezhr.TestException.main(TestException.java:12)
- ③
- 请输入num1的值
- 12
- 请输入num2的值
- 4
- 你输入的两个数相除,结果是3
复制代码 9.5 自定义非常
内置非常不能满足我们业务需求的时候,我们就需要自定义非常
- @ControllerAdvice
- public class MyExceptionHandler {
- @ExceptionHandler(value =Exception.class)
- public String exceptionHandler(Exception e){
- System.out.println("出现了一个异常"+e);
- return e.getMessage();
- }
- }
复制代码 9.6 定义全局非常处理类
我们自定义一个全局非常处理类,来处理各种非常,包罗自己定义的非常和内部非常。如允许以简化不少代码,不消自己对每个非常都利用try,catch的方式来实现
- public enum ErrorEnum {
- // 数据操作错误定义
- NO_PERMISSION(403,"没有权限访问"),
- NO_AUTH(401,"请先登录系统"),
- NOT_FOUND(404, "未找到该资源!"),
- USER_NOT_FIND(402, "未找到用户信息"),
- INTERNAL_SERVER_ERROR(500, "服务器出问题了"),
- UNKNOW_ERR(-1,"未知错误")
- ;
- /** 错误码 */
- private Integer errorCode;
- /** 错误信息 */
- private String errorMsg;
- ErrorEnum(Integer errorCode, String errorMsg) {
- this.errorCode = errorCode;
- this.errorMsg = errorMsg;
- }
- public Integer getErrorCode() {
- return errorCode;
- }
- public String getErrorMsg() {
- return errorMsg;
- }
- }
复制代码 说明: 方法上面加上一个 @ResponseBody的注解,用于将对象解析成json,方便前后端的交互,也可以利用 @ResponseBody放在非常类上面
9.7 代码测试
9.7.1 定义User实体类
- public enum SuccEnum {
- SUCCESS(200, "success");
- /** 成功码 **/
- private Integer succCode;
- /* 成功信息*/
- private String succMsg;
- SuccEnum(Integer succCode, String succMsg) {
- this.succCode = succCode;
- this.succMsg = succMsg;
- }
- public Integer getSuccCode() {
- return succCode;
- }
- public String getSuccMsg() {
- return succMsg;
- }
- }
复制代码 9.7.2 定义controller类
- @Data
- public class Result<T> {
- //是否成功
- private Boolean success;
- //状态码
- private Integer code;
- //提示信息
- private String msg;
- //数据
- private T data;
- public Result() {
- }
- //自定义返回结果的构造方法
- public Result(Boolean success,Integer code, String msg,T data) {
- this.success = success;
- this.code = code;
- this.msg = msg;
- this.data = data;
- }
- }
复制代码 9.8 接口测试
9.8.1 获取没有非常的数据返回
http://localhost:8090/result/getUser
9.8.2 自定义非常返回
http://localhost:8090/result/getDefException
http://localhost:8090/result/getException?name=xiezhr&pwd=123
9.8.3 其他的非常 返回
http://localhost:8090/result/getException?name=ff&pwd=abc
十、非常处理及规约
非常的处理⽅式有两种。 1、 ⾃⼰处理。 2、 向上抛, 交给调⽤者处理。
非常, 千万不能捕获了之后什么也不做。 或者只是使⽤e.printStacktrace。
具体的处理⽅式的选择其实原则⽐较简明: ⾃⼰明确的知道如那边理的, 就要处理掉。 不知道如那边理的, 就交给调⽤者处理。
下面时阿里巴巴Java开辟手册关于非常处理规则
①【强制】 Java类库中定义的可以通过预检查方式规避的RuntimeException不应该通过catch的方式处理,如NullPointerException、IndexOutOfBoundsException等
说明:无法通过预检查的非常不在此列,好比当解析字符串情势的数字时,可能存在数字格式错误,通过catch NumberFormatException实现
正例:- public class ResultUtil {
- //成功,并返回具体数据
- public static Result success(SuccEnum succEnum,Object obj){
- Result result = new Result();
- result.setSuccess(true);
- result.setMsg(succEnum.getSuccMsg());
- result.setCode(succEnum.getSuccCode());
- result.setData(obj);
- return result;
- }
- //成功,无数据返回
- public static Result succes(SuccEnum succEnum){
- Result result = new Result();
- result.setSuccess(true);
- result.setMsg(succEnum.getSuccMsg());
- result.setCode(succEnum.getSuccCode());
- result.setData(null);
- return result;
- }
- //自定义异常返回的结果
- public static Result defineError(DefinitionException de){
- Result result = new Result();
- result.setSuccess(false);
- result.setCode(de.getErrorCode());
- result.setMsg(de.getErrorMsg());
- result.setData(null);
- return result;
- }
- //其他异常处理方法返回的结果
- public static Result otherError(ErrorEnum errorEnum){
- Result result = new Result();
- result.setSuccess(false);
- result.setMsg(errorEnum.getErrorMsg());
- result.setCode(errorEnum.getErrorCode());
- result.setData(null);
- return result;
- }
- }
复制代码 反例:- public class DefinitionException extends RuntimeException {
- protected Integer errorCode;
- protected String errorMsg;
- public DefinitionException(){
- }
- public DefinitionException(Integer errorCode, String errorMsg) {
- this.errorCode = errorCode;
- this.errorMsg = errorMsg;
- }
- public Integer getErrorCode() {
- return errorCode;
- }
- public void setErrorCode(Integer errorCode) {
- this.errorCode = errorCode;
- }
- public String getErrorMsg() {
- return errorMsg;
- }
- public void setErrorMsg(String errorMsg) {
- this.errorMsg = errorMsg;
- }
- }
复制代码 ②【强制】 非常捕获后不要用来做流程控制和条件控制
说明:非常计划的初衷是解决程序运行中各种不测,且非常的处理效率比条件判定方式要第许多。
③【强制】 catch 时请分清稳定代码和非稳定代码。稳定代码一般指本机运行且执行结果确定性高的代码。对于非稳定代码的catch 尽可能在举行非常类型的分区后,再做对应的非常处理
说明:对大段代码举行try-catch,将使程序无法根据不同的非常做出正确的“应激”反应,也倒霉于定位问题,这是一种不负责的表现
正例:在用户注册场景中,如果用户输入非法字符串,或用户名称已存在,或用户输入的密码过于简单,那么程序会作出分门别类的判定并提示用户。
④【强制】 捕获非常使为了处理非常,不要捕获了却说明都不处理而抛弃之,如果不想处理它,请将非常抛给它的调用者。最外层的业务利用者必须处理非常,将其转换为用户可以理解的内容。
⑤【强制】 在事务场景中,抛出非常被catch 后,如果需要回滚,那么一定要注意手动回滚事务。
⑥【强制】 finally 块必须对资源对象、流对象举行关闭操作,如果有一次要做try-catch操作。
说明:对于JDK7即以上版本,可以利用try-catch-resource 方式
⑦【强制】 不要在finally块中利用return
说明try 块中return 语句执行成功后,并不马上返回,而是继续执行finally 块中的语句,如果此处存在return语句,则在此直接返回,无情地丢弃try块中的返回点。
正例:- @ControllerAdvice
- public class GlobalExceptionHandler {
- /**
- * 处理自定义异常
- *
- */
- @ExceptionHandler(value = DefinitionException.class)
- @ResponseBody
- public Result bizExceptionHandler(DefinitionException e) {
- return ResultUtil.defineError(e);
- }
- /**
- * 处理其他异常
- *
- */
- @ExceptionHandler(value = Exception.class)
- @ResponseBody
- public Result exceptionHandler( Exception e) {
- return ResultUtil.otherError(ErrorEnum.UNKNOW_ERR);
- }
- }
复制代码 ⑧【强制】 捕获非常与抛出非常必须完全匹配,或者捕获非常时抛出非常的父类。
说明:如果以及对方抛出的时绣球,实际接收到的时铅球,就会产生不测
⑨【强制】 在调用RPC、二方包或动态生成类的相关方法时,捕获非常必须利用Throwable拦截。
说明:通过反射机制调用方法,如果找不到方法,则抛出NoSuchMethodException。
在说明情况下抛出NoSuchMethodException呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(好比:ASM)动态创建或修改类时,修改了相应的方法签名。对于这些情况,即使在代码编译期是正确的,在代码运行期也会抛出NoSuchMethodException
⑩【推荐】 方法的返回值可以为null,不强制返回空集合或者空对象等,必须添加注释充实说明在说明情况下会返回null值。此时数据库id不支持存入负数二抛出非常。
说明:本手册明确,防止产生NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑远程调用失败、序列化失败、运行时非常等场景返回null值的情况
⑪【推荐】 防止产生NPE时程序员的根本修养,注意NPE产生的场景。
1)当返回类型为根本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生NPE
反例:- @Data
- public class User {
- //唯一标识id
- private Integer id;
- //姓名
- private String name;
- //性别
- private String sex;
- //年龄
- private Integer age;
- }
复制代码 2)数据库的查询结果可能为null
- 集合里的元素即使isNotEmpty , 取出的数据元素也有可能为null
- 当远程调用返回对象时,一律要求举行空指针判定,以防止产生NPE。
5)对于Session 中获取的数据,发起举行NPE检查,以制止空指针
6)级联调用obj.getA().getB().getC();的一连串调用容易产生NPE。
⑫【推荐】 定义时区分 unchecked/checked 非常,制止直接抛出new RuntimeException(),更不允许抛出Exception 或者Throwable,应该利用业务含义的自定义非常。推荐业界已定义过的自定义非常,如:DAOException / ServiceException 等
⑬ 【参考】 对于公司外的HTTP/API 开放接口,必须利用“errorCode”:应用内部推荐非常抛出;
跨应用间RPC调用优先考虑利用Result 方式,封装isSuccess() 方法、errorCode 和errorMessage。
说明:关于RPC方法返回方式利用Result方式的理由
1)利用抛出非常返回方式,调用方式如果没有捕获到,就会产生运行时错误
2)如果不加栈信息,知识new自定义非常,参加自己理解的errorMesage,对于调用解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能消耗也是问题。
⑭【参考】 制止出现重复的代码(Don't Repeat Yourself),即DRY原则
说明: 随意复制和粘贴代码,必然导致代码的重复,当以后需要修改时,需要修改全部的副本,容易遗漏。必要时抽取共性方法或公共类,甚至将代码组件化。
正例: 一个类中由多个public 方法,都需要举行数行相同的参数校验操作,这个时候请抽取:- @RestController
- @RequestMapping("/result")
- public class ExceptionController {
- @Autowired
- private GlobalExceptionHandler globalExceptionHandler;
- @GetMapping("/getUser")
- public Result getStudent(){
- User user = new User();
- user.setId(100);
- user.setName("xiezhr");
- user.setAge(21);
- user.setSex("男");
- Result result = ResultUtil.success(SuccEnum.SUCCESS, user);
- return result;
- }
- @GetMapping("/getDefException")
- public Result DeException(){
- throw new DefinitionException(400,"我出错了");
- }
- @GetMapping("/getException")
- public Result Exception(@RequestParam("name") String name, @RequestParam("pwd") String pwd){
- Result result = ResultUtil.success(SuccEnum.SUCCESS);
- try {
- if ("admin".equals(name)){
- User user = new User();
- user.setId(101);
- user.setName("xiezhr");
- user.setAge(18);
- user.setSex("男");
- result = ResultUtil.success(SuccEnum.SUCCESS,user);
- }else if (name.equals("xiezhr")){
- result = ResultUtil.otherError(ErrorEnum.USER_NOT_FIND);
- }else{
- int i = 1/0;
- }
- }catch (Exception e){
- result = globalExceptionHandler.exceptionHandler(e);
- }
- return result;
- }
- }
复制代码 本期内容到这就结束了,各位小伙伴们,我们下期见 (●'◡'●)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |