定义:一个需要处理的、不正常的错误(比如比较空指针的值,数组越界之类的)
所有非运行时异常没有统一的父类,叫检查异常只是方便统称,翻译成英文的这个类是没有继承自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博客
java注解-最通俗易懂的讲解_java 注解-CSDN博客