Java笔记——枚举注解异常

枚举

创建Season对象有如下特点: 1. 季节的值是有限的几个值(spring, summer, autumn, winter)

  1. 只读,不需要修改

解决方案: 1. 枚举对应英文(enumeration,简写enum)

  1. 枚举是一组常量的集合

  2. 可以理解为:枚举属于一种特殊的类,里面只包含一组有限的特定对象

枚举的两种实现方式

自定义类实现枚举

  • 不需要提供 setXxx 方法,因为枚举对象通常为只读
  • 对枚举对象/属性使用 final + static 共同修饰,实现底层优化
  • 枚举对象通常使用全部大写,常量的命名规范
  • 枚举对象根据需要,可以有多个属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.company.enum_;  

    public class Enumeration {
    public static void main(String[] args) {
    System.out.println(Season.SPRING);
    }
    }

    class Season {
    private String name;
    private String desc;

    public final static Season SPRING = new Season("春天", "温暖");
    public final static Season SUMMER = new Season("夏天", "炎热");
    public final static Season AUTUMN = new Season("秋天", "凉爽");
    public final static Season WINTER = new Season("冬天", "寒冷");

    private Season(String name, String desc) {
    this.name = name;
    this.desc = desc;
    }

    public String getName() {
    return name;
    }

    public String getDesc() {
    return desc;
    }

    @Override
    public String toString() {
    return "Season{" +
    "name='" + name + '\'' +
    ", desc='" + desc + '\'' +
    '}';
    }
    }
    小结:进行自定义类实现枚举,有如下特点:
  • 构造器私有
  • 本类内部创建一组对象
  • 对外暴露对象(通过为对象添加 public final static 修饰符)
  • 可以提供 get 方法,但是不要提供 set 方法

使用 enum 关键字实现枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum Season {  
SPRING("春天", "温暖"), SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷");

private String name;
private String desc;

Season(String name, String desc) {
this.name = name;
this.desc = desc;
}

public String getName() {
return name;
}

public String getDesc() {
return desc;
}

@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

注意事项

  1. 当使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类 (javap反编译)
  2. 传统的 public static final Season SPRING = new Season("春天", "温暖"); 简化成 SPRING("春天", "温暖") , 这里需清楚它调用的是哪个构造器
  3. 如果无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
  4. 当有多个枚举对象时,使用 , 间隔,最后有一个分号结尾
  5. 枚举对象必须放在枚举类的行首

enum常用方法

使用关键字 enum 时,会隐式继承 Enum 类,因此可以使用 Enum 类相关方法

1
2
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serialization {
}

方法名 详细描述
valueOf 传递枚举类型的Class对象和枚举常量名称给静态方法valueOf,会得到与参数匹配的枚举常量。
toString 得到当前枚举常f的名称。你可以通过重写这个方法来使得到的结果更易读。
equals 在枚举类型中可以直接使用 "==" 来比较两个枚举常量是否相等。Enum提供的这个equals()方法,也是直接使用 "==" 实现的。它的存在是为了在Set、List和Map中使用。注意,equals()是不可变的。
hashCode Enum 实现了hashCode()来和equals()保持一致。它也是不可变的。
getDeclaringClass 得到枚举常量所属枚举类型的Class对象。可以用它来判断两个枚举常量是否属于同一个枚举类型。
name 得到当前枚举常量的名称。建议优先使用toString()。
ordinal 得到当前枚举常量的次序。(从0开始编号)
compareTo 枚举类型实现了Comparable接口,这样可以比较两个枚举常量的大小(按照声明的顺序排列)
clone 枚举类型不能被Clone。为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupportedException异常的不变Clone()。
values 返回当前枚举类中所有的常量, 用对象数组接收

enum 实现接口

  1. 使用enum关键字后,就不能继承其它类了,因为enum会隐式继承Enum,而Java时单继承机制。
  2. 枚举类和普通类一样,可以实现接口
    1
    enum 类名 implements 接口1, 接口2 {}

注解

注解的理解

  1. 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
  2. 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
  3. 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和xml配置等。

基本的 Annotation 介绍

使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素

三个基本的Annotation:

  1. @Override: 限定某个方法,是重写父类方法,该注解只能用于方法
  2. @Deprecated: 用于表示某个程序元素(类,方法等)已过时
  3. @SuppressWarnings: 抑制编译器警告 ({"all"})

补充说明: @interface不是interface,是注解类,是jdk5.0之后加入的

1
2
3
4
@Target(ElementType.METHOD)  // 修饰注解的注解
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

四种元注解

  1. Retention //指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
  2. Target //指定注解可以在哪些地方使用
  3. Documented //指定该注解是否会在javadoc体现
  4. Inherited //子类会继承父类注解

异常

异常基本介绍

  • 基本概念 Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

  • 执行过程中所发生的异常事件可分为两类

  1. Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如: StackOverflowError[栈溢出]和OOM(out of memory), Error是严重错误,程序会崩溃。
  2. Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常编译时异常

异常体系图

  • 体现了继承和实现关系

编译异常: Java源程序 -> 字节码文件 运行异常: 字节码文件 -> 在内存种加载、运行类

异常体系图的小结

  1. 异常分为两大类,运行时异常和编译时异常;
  2. 运行时异常,编译器检测不出。一般是指编程时的逻辑错误,是 程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常;
  3. 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程 序的可读性和运行效率产生影响;
  4. 编译时异常,是编译器要求必须处置的异常。

常见的运行时异常

  • NullPointerException 空指针异常
  • ArithmeticException 数据运算异常
  • ArrayIndexOutOfBoundsException 数组下标越界异常
  • ClassCastException 类型转换异常 当试图将对象强制转换为不是实例的子类时,抛出该异常
  • NumberFormatException 数据格式不正确异常

常见的编译时异常

  • SQLException 操作数据库时,查询表可能发生的异常
  • IOException 操作文件时,发生的异常
  • FileNotFoundException 当操作一个不存在的文件时,发生异常
  • ClassNotFoundException 加载一个不存在的类时,发生的异常
  • EOFException 操作文件,到文件末尾,发生异常
  • IllegalArguementException 参数异常

异常处理

基本介绍

异常处理就是当异常发生时,对异常处理的方式。 ### 异常处理的方式 1)try-catch-finally 程序员在代码种捕获发生的异常,自行处理

1
2
3
4
5
6
7
8
9
10
11
try {
// 可能有有异常的代码
} catch(Exception e) {
// 捕获到异常
// 当异常发生时,系统将异常封装成Exception对象e,传递给catch
// 得到异常后,程序员自己处理
// 若没有发生异常则catch代码块不执行
} finally {
// 1. 不管try代码块是否有异常发生,始终要执行finally
// 2. 通常将释放资源的代码放在这里
}
2)throws 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM,输出异常信息,退出程序

try - catch 注意事项

快捷键:ctrl + alt + t 1. 如果异常发生,则异常发生后面的代码不会执行,直接进入到catch块 2. 如果异常没有发生,则顺序执行try的代码块,不会进入到catch 3. 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用finally 4. 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch 5. 可以进行 try - finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。

1
2
3
4
5
6
7
8
9
try {
String str = "123";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("异常信息 = " + e.getMessage());
}
System.out.println("程序继续执行...");

练习1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Exception01 {  
public static int method() {
int i = 1;
try {
i++;
String[] names = new String[3];
if (names[1].equals("tom")) {
System.out.println(names[1]);
} else {
names[3] = "kuhn";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return i++; // i = 2 => 保存临时变量 tmp = 2, i = 3 } finally {
++i; // i = 4
System.out.println("i = " + i); // i = 4
}
}

public static void main(String[] args) {
System.out.println(method()); // 2
}
}

练习2

循环输入,直到是整数为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Exception01 {  
public static void method() {
Scanner scanner = new Scanner(System.in);
int num = 0;
while (true) {
System.out.println("请输入一个整数:");

try {
num = Integer.parseInt(scanner.next());
break; } catch (NumberFormatException e) {
System.out.println("非法输入,请重试!");
}
}
System.out.println("输入的值为:" + num);
}

public static void main(String[] args) {
method();
}
}

throws 异常处理

1)如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法地调用者负责处理; 2)在方法声明中用throws语句可以声明抛出异常地列表,throws后面地异常类型可以是方法中产生地异常类型,也可以是该异常的父类;

1
2
3
4
5
6
public static readFile(String file) throws FileNotFoundException {
// ...
// 读取文件的操作可能产生FileNotFoundException类型的异常
FileInputStream fis = new FileInputStream("./tmp.txt");
// ...
}
- 注意事项和使用细节 1)对于编译异常,程序中必须处理,比如 try-catch 或者 throws 2)对于运行时异常,程序中如果没有处理,默认就是throws的方式处理 3)子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型 4)在throws 过程中,如果有方法 try - catch ,就相当于处理异常,此时就不必 throws
1
2
3
4
5
6
7
8
9
public static void f1() {
f2(); // 报错
// f2()抛出编译异常,f1()调用就要处理它的异常
// 要么 throws 抛出, 要么 try-catch
}

public static void f2() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("./tmp.txt");
}
## 自定义异常 ### 自定义异常的步骤 1. 定义类:自定义异常类名继承Exception或者RuntimeException 2. 如果继承Exception,属于编译异常 3. 如果继承RuntimeException,属于运行异常(最常见)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CustomException {
public static void main(String[] args) {
int age = 180;
if (!(age >= 18 && age <= 120)) {
throw new AgeException("年龄需要在18 ~ 120之间");
}
System.out.println("输入正确");
}
}

class AgeException extends RuntimeException {
public AgeException(String message) { // 构造器
super(message);
}
}
## throw 和 throws的对比 | | 意义 | 位置 | 后面跟的东西 | | ------ | ------------------------ | ---------- | ------------ | | throws | 异常处理的一种方式 | 方法声明处 | 异常类型 | | throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ReturnExceptionDemo {  
static void methodA() {
try {
System.out.println("1. 进入方法A");
throw new RuntimeException("3. 制造异常");
} finally {
System.out.println("2. 用A方法的finally");
}
}

static void methodB() {
try {
System.out.println("4. 进入方法B");
return; } finally {
System.out.println("5. 调用B方法的finally");
}
}
}


public static void main(String[] args) {
try {
ReturnExceptionDemo.methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
ReturnExceptionDemo.methodB();
}


Java笔记——枚举注解异常
https://nessaj7.github.io/2022/04/25/9198f0f5f6dc.html
作者
kuhn
发布于
2022年4月25日
许可协议