异常
定义:一个需要处理的、不正常的错误(比如比较空指针的值,数组越界之类的)
异常分类
所有非运行时异常没有统一的父类,叫检查异常只是方便统称,翻译成英文的这个类是没有继承自Exception
的,检查异常中包含访问文件,文件末尾读入,类不存在等错误
Error
和RuntimeException
属于非检查异常,前者一般是Java虚拟机或内存上(堆栈)的问题,而后者一般是程序编写错误导致的,属于可掌控的异常
图中已经展示了我们一般需要处理的异常是Exception
,而RuntimeException
又是相对处理更多的
public class Demo01{
public static void main(String [] args){
int [] nums = new int [3];
System.out,println(nums[3]);//RuntimeException,编写时不会报错
}
}
Class.forName("ex.Demo01");//检查异常
解决异常的方法
如果不知道发生的具体是什么异常,一是执行看报错,二是直接Exception
处理,下面给出一般解决异常三种方法
-
try catch finally
:类似于if - else if - else
的处理方式try catch//捕获异常 //自己(当前方法)能够处理,使用try catch //如果try里执行的代码发生了异常,在发生异常前的语句仍会执行,随后跳转到相应的catch执行 finally //无论正常,还是异常,始终都会执行的代码 /* 不论执行完try还是执行完catch,最终都会执行finally的代码 1.即使在过程中遇到return,也仍然会执行finally 2.除非虚拟机关闭(System.exit(1)),才不会执行finally里面的语句 3.finally里一般只写关闭资源的语句,不写IO相关,返回等处理语句,容易出问题,所以在打开资源时通常建议在try里的语句执行完后嵌套一个try-finally语句用于关闭资源 */ public static void test03{ try { int[] nums = new int[3]; nums[3] = 3; } catch(NullPointerException e) { System.out.println("空指针异常"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("数组越界异常"); } catch(Exception e) {//不知道是什么异常时采取的最终手段 System.out.println("其他异常"); } finally { System.out.println("最终执行"); } } //try with Resources打开资源并自动关闭(常规方法打开两个资源后通常要嵌套两个try-finally语句关闭),类似于try后面写点参数 try (var in = new Scanner( new FileInputStream("/user/share/dict/words"), StandardCharsets.UTF_8); var out = new PrintWriter("out.txt", StandardCharsets.UTF_8)) { while (in.hasNext()) { out.println(in.next().toUpperCase()); } } //等价于 try { in = new Scanner(new FileInputStream("/user/share/dict/words"), StandardCharsets.UTF_8); out = new PrintWriter("out.txt", StandardCharsets.UTF_8); while (in.hasNext()) { out.println(in.next().toUpperCase()); } } finally { // 嵌套的try-finally用于关闭资源 try { if (in != null) { in.close(); } } finally { if (out != null) { out.close(); } } } //可以看出上部分的代码相当简洁 //在后期的设计中catch里通常会实现”再抛出“,即将捕获到的异常扔给其他异常,隐藏具体的异常细节 //如开发中用户使用时出现数据库的异常,可以通过再抛出将数据库的异常隐藏,只给用户显现为后端的异常,用户收到报错后进行反馈,而后端再根据这个异常去修改,找到预先设定好的获取真正异常信息的语句,知道具体的异常信息后进行修改 try { ..... } catch (SQLException original) {//这里是用一个自定义的异常进行处理,将在下面提到 var e = new ServletException("database error"); e.initCause(original); throw e;//抛出其他异常,隐藏具体的异常 } Throwable original = caughtException.getCause();//获取具体的异常原因
-
throws
:抛出异常,将异常直接抛给上一级处理,这是极其无脑的做法//自己(当前方法)不能够处理,使用throws public static void test04() throws NullPointerexception{//抛出异常,抛出给上级,不知道是什么异常也可以写Exception Objext obj = null; obj.equals(""); } public static void main(String[] args) throws Exception{//继续往上抛出异常给JVM(虚拟机),这是相当消极的做法,通常这一层需要自己写一个try-catch语句处理,而不是扔给JVM当甩手掌柜 test04(); }
-
throw
:自定义异常终止(异常太多,Java自带的异常不够用时用)//1.创建类继承Exception,调用super("要提示的异常信息") public class MyException extends Exception{//这个继承写啥异常都可以,毕竟异常间有层层的继承关系 public MyException(String messsage){//自定义异常,在构造器中书写,把发生的具体异常扔给父类处理 super(message); } } //2.使用throw声明一个自定义异常,并且进行try catch或者throws //第一种写法 public class testthrow{ public static void main(String[] args){ int age = 188; if(age < 0 || age > 120) { try{ throw new MyException("年龄不能大于120岁或小于0岁"); } catch(MyException e) {//throw向你扔出了异常,这里的catch捕获相应异常 e.printStackTrace();//查看异常的所有信息 System.out.println(e.getMessage());//查看异常的基本信息 } } } } //第二种写法 public class start { public static void main(String[] args) throws Exception{ int age = 188; if(age < 0 || age > 120) { throw new MyException("年龄不能大于120岁"); } } }
常见运行时异常
NullPointerException
:当应用程序试图在需要对象的地方使用null
时,抛出此异常IndexOutOfBoundsException
:当索引值为负数或大于等于列表大小时,访问列表、数组或字符串时,抛出此异常ArrayIndexOutOfBoundsException
:这是IndexOutOfBoundsException
的一个子类,专门用于数组索引越界的情况ArithmeticException
:当出现异常的运算条件时,抛出此异常。例如,整数除零时抛出ArithmeticException
的一个子类ArithmeticException("/ by zero")
ClassCastException
:当试图将对象强制转换为不是实例的子类时,抛出此异常IllegalArgumentException
:抛出一个表示向方法传递了一个不合法或不适当参数的情况IllegalStateException
:在对象状态不允许进行请求的操作时,抛出此异常ConcurrentModificationException
:当在迭代过程中(例如通过增强型for循环或迭代器)直接修改集合时,抛出此异常NumberFormatException
:当应用程序试图将一个字符串转换成一种数值类型,但该字符串不具有适当的格式时,抛出此异常UnsupportedOperationException
:当不支持请求的操作时,抛出此异常
Annotation
注解
地位:和继承类,实现接口一样都是用来增强一个类的功能的,创建时同创建接口和类,关键字是@interface
作用:贴标签(形象化),一般放在要贴标签的类型前
内置(预置)注解
-
@Override
用于子类重写父类的方法前,避免拼写导致的错误调用 -
@Deprecated
在类的方法前,提示该方法不建议使用(体现为删除线),开发中通常加上该注解,意为过期了下个版本要删除的部分 -
@SuppressWarnings(value = "xxx")
忽略警告信息xxx
:unchecked
(忽略对泛型的检查),deprecation
(不提示过期了),unused
(忽略未使用的警告),fallthrough
(忽略没有break
的switch case
),path
(忽略可以对路径的检查),serialversionUID
(忽略未被序列化),all
(所有)@SuppressWarnings(value = "xxx") public class Demo01{ public static void main(String[] args){ List list = new ArrayList();//有的编译器中未写泛型会有黄色波浪线,这里采用注解可以让其消失 } }
-
@SafeVarargs
参数安全类型注解,提醒开发者不要用参数做不安全的操作,阻止编译器产生警告 -
@FunctionalInterface
函数式接口
自定义注解
一般结合反射使用,在开发框架时会大量用到
下面给出一个自定义注解以及相应测试
@Retention(RetentionPolicy.RUNTIME)//记得注明声明周期,否则无法测试打印,对自定义注解的限制会在元注解部分展示
public @interface MyAnnotation {
/*
1.用定义方法的形式,定义一个属性,不能定义方法
2.方法的名字就是属性的名字,方法的返回值就是属性的类型。
3.如果想定义属性的默认值,可以用default关键词
*/
public String value() default "张三"; //String value = "张三";
public int age() default 22;// int age = 22;
}
@SuppressWarnings("all")
public class TestMyAnnotation {
@MyAnnotation(value = "李四", age = 23)//使用自定义注解,如果注解里只有一个属性则可以只写赋值的内容
@Deprecated//使用没有成员变量的注解时可以不加括号
public static void Test() throws Exception {
//用反射获取某个类里的某个方法前的所有注解,检查注解里的值,标签是否正确
Annotation[] annotations = TestMyAnnotation.class.getMethod("Test").getAnnotations();
for (Annotation a : annotations) {//判断后强转打印出注解数组里的内容,检查效果
if (a instanceof MyAnnotation) {
System.out.println(((MyAnnotation) a).value());
System.out.println(((MyAnnotation) a).age());
}
else {
System.out.println("@Deprecated");
}
}
}
public static void main(String[] args) throws Exception {
//获取多个注解测试
Test();
//获取单个注解测试
//利用反射获取单个注解,通常获取前需要检查是否存在
boolean hasAnnotation = TestMyAnnotation.class.isAnnotationPresent(SuppressWarnings.class);
if (hasAnnotation) {
SuppressWarnings test = TestMyAnnotation.class.getAnnotation(SuppressWarnings.class);
System.out.println(test.value());//这里因为生命周期是SOURCE而无法显示打印结果
}
}
}
元注解
元数据:修饰数据的数据(数据库里的概念)
元注解:修饰注解的注解,可以用来限制自定义注解的使用
@Target
限制注解使用的位置
public enum ElementType {
TYPE,//类型,如类,接口,枚举
FIELD,//属性
METHOD,//方法
PARAMETER,//方法参数
CONSTRUCTOR,//构造方法
LOCAL_VARIABLE,//本地变量
ANNOTATION_TYPE,//对一个注解注解
PACKAGE,//包
TYPE_PARAMETER,//参数
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})//限制了该注解能放在哪些类型前
public @interface MyAnnotaiton {
String value() default "张三";
int age() default 22;
}
@Retention
标明注解的生命周期
public enum RetentionPolicy {
SOURCE,//只有.java阶段可用,相当于jvm直接丢弃该注解
CLASS,//java转化未class进行编译时可用
RUNTIME//编译运行时都可用
}
@Retention(RetentionPolicy.RUNTIME)//限制什么时候能用这个注解
public @interface MyAnnotaiton {
String value() default "张三";
int age() default 22;
}
@Document
让javadoc
文件中也包含对注解的声明
默认下javadoc:java
帮助文档 ABC.java
- >帮助文档中不包含注解的解释
@Inherited
子类继承父类时同时继承其注解
默认下子类不会继承父类的注解
-
@Repeatable
可重复,一个注解的成员是另一个注解,使用了另一个注解后调用该注解也能实现相应效果,测试使用如下@Retention(RetentionPolicy.RUNTIME) public @interface Persons { Person[] value();//Persons注解的成员是Person注解数组 } @Repeatable(Persons.class)//标明后可以声明该注解后调用另一个注解进行使用 public @interface Person { String value() default ""; } //标明Person注解 @Person(value = "artist") @Person(value = "coder") @Person(value = "PM") public class SuperMan { public static void main(String[] args) { //用反射获取单个注解,也可以不单独用一个变量去记录是否存在 Persons personsAnnotation = SuperMan.class.getAnnotation(Persons.class);//测试时使用Persons注解 if (personsAnnotation != null) { Person[] persons = personsAnnotation.value(); for (Person person : persons) { System.out.println("Role: " + person.value()); } } } }
参考:
颜群老师的Java课最全最详细的Java异常处理机制_1.java异常处理机制 目标:掌握java异常处理机制和常见的java异常处理方法。 (1)-CSDN博客