> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/java-xi-lie/0101java-jin-jie-pian.md).

# 01 Java进阶篇

Java 是一门面向对象编程语言，不仅吸收了 C++ 语言的各种优点，还摒弃了 C++ 里难以理解的多继承、指针等概念，因此，Java 语言具有功能强大和简单易用两个特征。Java 语言作为静态面向对象编程语言的代表，极好地实现了面向对象理论，允许程序员以优雅的思维方式进行复杂的编程。

Java 具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。

Java 可以编写桌面应用程序、Web 应用程序、分布式系统和嵌入式系统应用程序等。

## 面向对象设计

面向对象的程序设计是通过**对象**对程序进行设计，对象代表一个实体，实体可以清楚地被识别。

Java 作为一种面向对象语言，有以下基本概念：

* 包（Package）
* 接口（Interface）
* 抽象类（Abstract Class）
* 类（Class）
* 对象（Object）
* 封装（Encapsulation）
* 继承（Inheritance）
* 多态（Polymorphism）
* 消息解析（Message Parsing / Method Resolution）
* 注解（Annotation）

## 源文件声明规则

当在一个源文件中定义多个类，并且还有 import、package 语句时，要特别注意这些规则。

* package 语句应该在源文件的首行。
* 在同一源文件中，不能给不同的类不同的包声明。
* import 语句应该放在 package 语句和类定义之间。
* import 语句和 package 语句对源文件中定义的所有类都有效。
* 源文件的名称应该和 public 类的类名保持一致。
* 一个源文件中只能有一个 public 类。
* 一个源文件可以有多个非 public 类。

## 包（Package）

包是 Java 中组织类和接口的机制，相当于文件系统中的文件夹。它用于将相关的类和接口分组，避免命名冲突，并控制访问权限。

**详细说明：**

* **命名规范**：通常使用公司域名的倒写作为包名的前缀，例如 `com.example.project.module`。全部小写，点号分隔。
* **物理结构**：包名必须与源代码文件的目录结构完全一致。例如，包 `com.lizhao.vehicle` 表示类文件位于 `com/lizhao/vehicle/` 目录下。
* **包声明**：每个 `.java` 文件最多只能有一个 `package` 语句，且必须放在文件的第一行（注释除外）。
* **默认包**：如果没有声明 `package`，类属于“无名包”，一般不推荐用于正式项目。
* **导入包**：使用 `import` 语句可以简化类名书写。`import java.util.*;` 导入包下的所有**类和接口**，但不递归导入子包。
* **静态导入**：`import static` 可以导入类的静态成员，使代码更简洁。

```shell
src
│
├── com
│   └── lizhao
│       └── vehicle
│           ├── Vehicle.java
│           ├── Car.java
│           └── Plane.java
└── VehicleApplication.java
```

```java
// src/VehicleApplication.java
import com.lizhao.vehicle.Vehicle;
import com.lizhao.vehicle.Car;
import com.lizhao.vehicle.Plane;
public class VehicleApplication {
  public static void main (String[] args) {
    Vehicle vehicle = new Vehicle();
    System.out.println(vehicle.toString());

    Car car = new Car("小汽车", "black");
    System.out.println(car.getColor());

    Plane plane = new Plane();
    plane.fly();
  }
}
```

```java
// src/com/lizhao/vehicle/Vehicle.java
package com.lizhao.vehicle;
public class Vehicle {
    public String name = "vehicle";
    public String toString() {
        return this.name;
    };
}
```

```java
// src/com/lizhao/vehicle/Car.java
package com.lizhao.vehicle;
public class Car {
    String name;
    String color;
    public Car () {}
    public Car (String name, String color) {
        this.name = name;
        this.color = color;
    }
    public void setName (String name) {
        this.name = name;
    }
    public String getName () {
        return this.name;
    }
    public void setColor (String color) {
        this.color = color;
    }
    public String getColor () {
        return this.color;
    }
}
```

```java
// src/com/lizhao/vehicle/Plane.java
package com.lizhao.vehicle;

public class Plane {
    String name = "飞机";
    public void fly() {
        System.out.println("fly：" + this.name);
    };
}
```

**常用 Java 包：**

* `java.lang`：核心类（String、Math、System），自动导入。
* `java.util`：工具类、集合框架（List、Set、Arrays）。
* `java.io`：输入输出操作。
* `java.net`：网络编程。
* `java.sql`：数据库编程。
* `java.awt / javax.swing`：图形界面。

## 接口（Interface）

接口是 Java 中的一种引用类型，是抽象方法的集合。接口定义了一种规范或契约，任何实现该接口的类都必须提供接口中所有方法的具体实现（**除非该类是抽象类**）。

**详细说明**：

* 接口中的方法默认是 `public abstract`（Java 8 之前）。
* 接口中的变量默认是 `public static final`，即常量。
* 接口不能实例化，没有构造方法。
* 一个类可以实现多个接口（多实现），弥补了单继承的不足。
* 接口之间可以多继承（`extends` 多个接口）。

```java
src
│
├── com
│   └── lizhao
│       └── sports
│           ├── Sports.java
│           ├── Player.java
└── Main.java
```

```java
// src/Main.java
import com.lizhao.sports.Sports;
import com.lizhao.sports.Player;

public class Main {
    public static void main(String[] args) {
        Player player = new Player();
        player.play(Sports.BASKETBALL);
        player.play(Sports.FOOTBALL);
    }
}
```

```java
// src/com/lizhao/sports/Sports.java
package com.lizhao.sports;

public interface Sports {
    int BASKETBALL = 1;
    int FOOTBALL = 2;
    int TENNIS = 3;

    void play(int sportType);
}
```

```java
// src/com/lizhao/sports/Player.java
package com.lizhao.sports;

public class Player implements Sports {
    @Override
    public void play(int sportType) {
        switch(sportType) {
            case BASKETBALL:
                System.out.println("打篮球");
                break;
            case FOOTBALL:
                System.out.println("踢足球");
                break;
            case TENNIS:
                System.out.println("打网球");
                break;
            default:
                System.out.println("未知运动");
        }
    }
}
```

**注意事项：**

* 实现接口方法时，不能抛出比接口声明更宽泛的强制性异常。
* 重写方法必须保持方法名一致，返回值类型需相同或兼容（支持协变返回）。
* 一个类只能继承一个父类，但能同时实现多个接口。
* 接口支持多重继承，一个子接口可以继承多个父接口，`extends` 后跟多个接口名（逗号分隔）。
* 标识接口是没有方法和属性的空接口，用于标记类属于某种特定类型。一般用于建立公共父接口、为类添加数据类型（通过多态）。

## 抽象类（Abstract Class）

抽象类是使用 `abstract` 关键字修饰的类，它不能被实例化，通常作为父类被其他类继承。抽象类可以包含抽象方法（没有方法体）和具体方法。

**详细说明：**

* **抽象方法**：只有声明，没有实现，使用 `abstract` 修饰。子类必须重写所有抽象方法（除非子类也是抽象类）。
* **普通方法**：抽象类中可以定义普通方法，子类直接继承使用。
* **成员变量**：抽象类可以拥有普通成员变量，与普通类相同。
* **构造方法**：抽象类可以有构造方法，但只能被子类通过 `super` 调用。
* **使用场景**：当多个子类有共同的属性和行为，但某些方法需要子类各自实现时，使用抽象类。

```shell
src
│
├── com
│   └── lizhao
│       └── shape
│           ├── Shape.java
│           ├── Circle.java
│           ├── Rectangle.java
└── Main.java
```

```java
// src/Main.java
import com.lizhao.shape.Shape;
import com.lizhao.shape.Circle;
import com.lizhao.shape.Rectangle;

public class Main {
    public static void main(String[] args) {
        Shape c = new Circle("blue", 5.0);
        Shape r = new Rectangle("green", 3.0, 4.0);

        System.out.println(c.area());
        System.out.println(r.area());
        c.displayColor();
    }
}
```

```java
// src/com/lizhao/shape/Shape.java
package com.lizhao.shape;

public abstract class Shape {
  protected String color;

  public Shape(String color) {
    this.color = color;
  }

  // 抽象方法：子类必须实现
  public abstract double area();

  // 具体方法：子类可以直接使用
  public void displayColor() {
    System.out.println("Color: " + color);
  }
}
```

```java
// src/com/lizhao/shape/Circle.java
package com.lizhao.shape;

public class Circle extends Shape {
  private double radius;

  public Circle(String color, double radius) {
    super(color);
    this.radius = radius;
  }

  @Override
  public double area() {
    return Math.PI * radius * radius;
  }
}
```

```java
// src/com/lizhao/shape/Rectangle.java
package com.lizhao.shape;

public class Rectangle extends Shape {
  private double width, height;

  public Rectangle(String color, double width, double height) {
    super(color);
    this.width = width;
    this.height = height;
  }

  @Override
  public double area() {
    return width * height;
  }
}
```

## 类（Class）

类是 Java 中面向对象编程的基本单元，是创建对象的模板。

类定义了对象所拥有的属性（成员变量）和行为（方法）。

**类的组成**：

* 构造方法：用于创建并初始化对象。如果没有显式地为类定义构造方法，Java 编译器将会为该类提供一个默认构造方法。构造方法的名称必须与类同名。
* 成员变量（字段）：存储对象的状态。
* 方法：定义对象的行为。
* 初始化块：在构造方法之前执行。
* 内部类：定义在类内部的类。

```java
// src/Main.java
import com.lizhao.Student;
public class Main {
    public static void main(String[] args) {

        Student s1 = new Student("Hello", 18);
        s1.introduce();
        s1.study("数学");

        Student s2 = new Student("Lizhao", 19);
        s2.introduce();

        Student.school = "第二中学";
        s1.introduce();
        s2.introduce();

        System.out.println("\n--- 内部类示例 ---");
        Student.Score score1 = s1.new Score(90, 88);
        score1.printScore();

        Student.SchoolInfo schoolInfo = new Student.SchoolInfo("教育路123号");
        schoolInfo.printInfo();

        Student.printSchool();
    }
}
```

```java
// src/com/lizhao/Student.java
package com.lizhao;

public class Student {
    public static String school = "第一中学";
    private static int studentCount = 0;

    private String name;
    private int age;
    private String id;

    // ========== 初始化块 ==========
    // 实例初始化块：每次创建对象时，在构造方法之前执行
    {
        id = generateId();
        studentCount++;  // 每创建一个学生，总数加1
        System.out.println("执行初始化块，当前学生总数：" + studentCount);
    }

    // 静态初始化块：类加载时执行一次
    static {
        System.out.println("静态初始化块：Student类被加载");
        System.out.println("学校：" + school);
    }

    // ========== 构造方法 ==========
    public Student() {
        this("未知", 0);
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("执行构造方法：创建学生 " + name);
    }

    // ========== 方法 ==========
    // 实例方法
    public void introduce() {
        String info = "我叫" + name + "，今年" + age + "岁";
        System.out.println(info + "，学号：" + id + "，学校：" + school);
    }

    public void study(String subject) {
        String action = "正在学习";
        System.out.println(name + action + subject);
    }

    // 静态方法
    public static void printSchool() {
        System.out.println("全校名称：" + school + "，" + "学生总数：" + studentCount);
    }

    // getter/setter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 私有方法：生成学号
    private String generateId() {
        return "S" + System.currentTimeMillis();
    }

    // ========== 内部类 ==========
    // 成员内部类
    public class Score {
        private int chinese;
        private int math;

        public Score(int chinese, int math) {
            this.chinese = chinese;
            this.math = math;
        }

        public void printScore() {
            // 内部类可以访问外部类的私有成员
            System.out.println(name + "的成绩 - 语文：" + chinese + "，数学：" + math);
        }
    }

    // 静态内部类
    public static class SchoolInfo {
        private String address;

        public SchoolInfo(String address) {
            this.address = address;
        }

        public void printInfo() {
            System.out.println("学校：" + school + "，地址：" + address);
        }
    }
}
```

## 对象（Object）

对象是类的具体实例，具有状态和行为。在内存中，对象是存储具体数据的一块区域。

**详细说明：**

* **创建对象**：使用 `new` 关键字调用构造方法。
* **对象引用**：变量存储的是对象的引用（内存地址），而不是对象本身。
* **对象生命周期**：从 `new` 创建开始，到不再被引用后被垃圾回收器回收。
* **匿名对象**：没有引用的对象，例如 `new Student("Tom").introduce();`，使用一次后即成为垃圾。

**注意事项：**

* `==` 比较两个引用是否指向同一个对象；`equals()` 一般用于比较内容，需要根据业务需求重写。
* 对象的销毁由垃圾回收器自动完成，不需要手动释放内存（除非使用了外部资源如文件、网络连接，需要显式关闭）。

## 封装（Encapsulation）

**面向对象编程的三大特征**：**封装、继承、多态**。

封装是将对象的状态（属性）隐藏在对象内部，通过公共方法（getter/setter）来访问和修改，从而保护数据的完整性和安全性。

**详细说明：**

* 将成员变量声明为 `private`。
* 提供公共的 `getter` 和 `setter` 方法。
* 在 `setter` 中可以加入校验逻辑。

```java
package com.lizhao;

public class Student {
    private String name;
    private int age;
    private double score;

    public Student(String name, int age, double score) {
        this.name = name;
        setAge(age);
        setScore(score);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        } else {
            System.out.println("姓名不能为空");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("年龄必须在 0-150 之间");
        }
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        if (score >= 0 && score <= 100) {
            this.score = score;
        } else {
            System.out.println("成绩必须在 0-100 之间");
        }
    }

    public void printInfo() {
        System.out.println("姓名：" + name + "，年龄：" + age + "，成绩：" + score);
    }
}
```

## 继承（Inheritance）

**面向对象编程的三大特征**：**封装、继承、多态**。

继承是面向对象的核心特性之一，子类可以复用父类的属性和方法，并可以添加新的成员或重写父类的方法。

**详细说明：**

* **关键字**：`extends` 用于类继承，`implements` 用于实现接口。
* **单继承**：Java 中一个类只能有一个直接父类，但可以间接继承多个祖先类。
* **方法重写**：子类可以重新实现父类的方法，但需要保持方法签名一致。
* **`super` 关键字**：用于访问父类的成员或调用父类的构造方法。`super()` 必须放在子类构造方法的第一行。
* **访问性**：子类可以访问父类的 `public` 和 `protected` 成员，以及同包下的默认成员。不能访问 `private` 成员。

```java
package com.lizhao.shape;

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}
```

## 多态（Polymorphism）

**面向对象编程的三大特征**：**封装、继承、多态**。

多态指同一个行为具有多个不同表现形式。在 Java 中，多态分为编译时多态（重载）和运行时多态（重写）。

通常所说的多态指的是**运行时多态**：父类引用指向子类对象，调用方法时表现出子类的行为。

**多态的必要条件**：

* 继承关系或接口实现。
* 子类重写父类方法（或实现接口方法）。
* 父类引用指向子类对象。

**多态的体现**：

* 方法重写。
* 接口实现。
* 抽象类。

\*\*注意：\*\*静态方法不被多态影响，始终根据引用类型调用。

```java
public class Main {
    static class Shape {
        public void draw() {
            System.out.println("绘制一个形状");
        }

        public double getArea() {
            return 0;
        }
    }

    static class Circle extends Shape {
        private double radius;

        public Circle(double radius) {
            this.radius = radius;
        }

        @Override
        public void draw() {
            System.out.println("⚪ 绘制一个半径为 " + radius + " 的圆形");
        }

        @Override
        public double getArea() {
            return Math.PI * radius * radius;
        }
    }

    static class Rectangle extends Shape {
        private double width;
        private double height;

        public Rectangle(double width, double height) {
            this.width = width;
            this.height = height;
        }

        @Override
        public void draw() {
            System.out.println("■ 绘制一个 " + width + " x " + height + " 的矩形");
        }

        @Override
        public double getArea() {
            return width * height;
        }
    }

    public static void main(String[] args) {
        // 1. 父类引用指向子类对象
        System.out.println("【1. 父类引用指向子类对象】");
        Shape shape1 = new Circle(5);
        Shape shape2 = new Rectangle(4, 6);

        // 调用同一个方法，表现出不同的行为
        shape1.draw();
        shape2.draw();
        System.out.println();

        // 2. 多态参数：方法接收父类类型，可以传入任何子类
        System.out.println("【2. 多态参数】");
        printShapeInfo(new Circle(3));
        printShapeInfo(new Rectangle(5, 8));
        System.out.println();

        // 3. 多态数组：可以存放不同类型的对象
        System.out.println("【3. 多态数组】");
        Shape[] shapes = {
            new Circle(2.5),
            new Rectangle(3, 4),
            new Circle(4),
            new Rectangle(2, 9)
        };

        // 统一遍历处理，不需要关心具体是什么形状
        for (int i = 0; i < shapes.length; i++) {
            System.out.print("第" + (i+1) + "个：");
            shapes[i].draw();
        }
        System.out.println();

        // 4. 计算总面积（多态的便利）
        System.out.println("【4. 计算总面积（多态的便利）】");
        double totalArea = 0;
        for (Shape shape : shapes) {
            totalArea += shape.getArea();
            System.out.printf("面积：%.2f\n", shape.getArea());
        }
        System.out.printf("总面积：%.2f\n", totalArea);
    }

    // 多态参数：不需要为每个形状单独写方法
    public static void printShapeInfo(Shape shape) {
        System.out.print("形状信息：");
        shape.draw();
        System.out.printf("面积：%.2f\n\n", shape.getArea());
    }
}
```

### 重载（Overload）

在同一个类中，多个方法拥有相同的名称但参数列表不同（**参数数量、类型、顺序不同**），称为重载。

**重载是编译时多态。**

**详细说明：**

* **参数列表区分**：编译器根据参数个数、类型、顺序来区分不同方法。
* **返回类型无关**：仅返回类型不同不足以构成重载，也不是重写，而是编译错误。
* **访问修饰符无关**：可以不同。
* **异常无关**：可以抛出不同的异常。
* **发生在同一类或继承关系中**：子类也可以重载父类的方法（方法名相同但参数不同）。

```java
public class Calculator {
    // 参数个数不同
    public int add(int a, int b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 参数类型不同
    public double add(double a, double b) {
        return a + b;
    }

    // 参数顺序不同
    public void print(String name, int age) {
        System.out.println(name + " is " + age);
    }

    public void print(int age, String name) {
        System.out.println("Age " + age + ", name " + name);
    }
}

// 不是重载，也不是重写，而是编译错误
// 父类已有 int add(int, int)，子类参数列表相同、返回类型不兼容，不构成重写，也不构成重载，属于编译错误。
class AdvancedCalculator extends Calculator {
    public double add(int a, int b) {
        return (double) a + b;
    }
}
```

### 重写（Override）

子类重新实现父类中的某个方法，要求\*\*方法名、参数列表、返回类型（或父类方法返回类型的子类）\*\*完全相同。

**重写是运行时多态。**

**详细说明：**

* **注解**：推荐使用 `@Override`，让编译器检查是否正确地重写了父类方法。
* **访问权限**：子类重写方法的访问权限不能低于父类方法的权限（例如父类是 `protected`，子类可以是 `public`）。
* **返回类型**：可以返回父类方法返回类型的子类（协变返回类型）。
* **异常**：子类方法不能抛出比父类方法更宽泛的检查异常；可以不抛出异常或抛出更具体的异常。
* **构造方法**：不能被重写。
* **静态方法**：不能被重写。如果在子类中定义了相同的静态方法，那是“隐藏”而不是重写。
* **`final` 和 `private`**：`final` 方法不能被重写；`private` 方法不能被继承，也就不存在重写。
* **Super 关键字：** 子类中调用父类的被重写方法时，要使用 super 关键字。

```java
class Parent {
    protected Object getValue() throws Exception {
        return "Parent";
    }

    public static void say() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    @Override
    public String getValue() {   // 返回类型从 Object 变成 String（子类）
        try {
            super.getValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "Child";
    }

    // 这不是重写，是隐藏
    // 静态方法不存在多态，永远根据引用类型决定调用哪个方法。
    public static void say() {
        System.out.println("Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p1 = new Parent();
        Parent p2 = new Child(); // 父类引用指向子类对象
        Child c = new Child();

        try {
            System.out.println(p1.getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 真正调用的方法由引用类型决定，不是对象类型
        p1.say(); // Parent
        p2.say(); // Parent
        c.say(); // Child
    }
}
```

## 消息解析（Message Parsing / Method Resolution）

在面向对象编程中，“消息”是指对象之间的方法调用。消息解析（或方法解析）是指编译器或运行时如何确定调用的是哪个方法的过程。

Java 中消息解析分为**编译时解析**（静态绑定）和**运行时解析**（动态绑定）。

**编译时解析（静态绑定）：**

* **发生场景**：`private` 方法、`static` 方法、`final` 方法、构造方法以及重载方法的调用。
* **解析时机**：在编译阶段，编译器根据变量的**声明类型**（编译时类型）和方法签名（包括参数类型）确定调用的具体方法。
* **特点**：效率高，因为调用地址在编译时确定。

**运行时解析（动态绑定）：**

* **发生场景**：普通实例方法（非 `private`、非 `static`、非 `final`）的重写方法调用。
* **解析时机**：运行时，JVM 根据对象的**实际类型**（运行时类型）来确定调用哪个方法。
* **特点**：支持多态，但有一定的性能开销（JVM 使用虚方法表优化）。

**重载解析：**

* 当调用一个重载方法时，编译器会尝试匹配最合适的方法。匹配规则：
  * 精确匹配（参数类型完全相同）。
  * 自动类型转换（如 `int` 转 `long`，`int` 转 `double`）。
  * 装箱/拆箱（如 `int` 转 `Integer`）。
  * 可变参数（`...`）。
* 如果多个方法匹配，编译器会选择最具体的一个；如果无法确定，则报歧义错误。

**重写解析：**

* 对于重写的方法，JVM 在运行时通过**动态分派**决定调用子类还是父类的方法。
* 使用 `invokevirtual` 指令，查找对象的实际类型的方法表。

## 注解（Annotation）

注解是 Java 5 引入的一种元数据（关于数据的数据），它可以被添加到类、方法、字段、参数等程序元素上，**为程序提供额外的信息**，但**不会直接影响代码的执行**。

**注解的作用**：

* **编译器指令**：告诉编译器如何处理代码。如：@Override、@SuppressWarnings。
* **编译时处理**：生成代码、配置文件等。如：@Data（Lombok）。
* **运行时处理**：通过反射读取注解，执行逻辑。如：@Test（JUnit）、@RequestMapping（Spring）。

**Java 内置注解（最常用）：**

* `@Override`：标记方法是重写父类方法，编译器会检查。
* `@Deprecated`：标记方法/类已过时，不推荐使用。
* `@SuppressWarnings`：抑制编译器警告。
* `@FunctionalInterface`：标记函数式接口（只有一个抽象方法）。
* `@SafeVarargs`：抑制堆污染警告。

```java
// src/Main.java
import com.lizhao.annotation.MyAnnotation;
import com.lizhao.processor.MyAnnotationRunner;

public class Main {
    public static void main(String[] args) {
        MyAnnotationRunner.processAnnotations(Main.class);
    }

    @MyAnnotation(value = "MyAnnotation1", message = "message")
    public static void useMyAnnotation1() {
        System.out.println("useMyAnnotation1");
    }
}
```

```java
// src/com/lizhao/annotation/MyAnnotation.java
package com.lizhao.annotation;

import java.lang.annotation.*;

@Target({
    ElementType.TYPE,       // 类、接口、枚举
    ElementType.METHOD,     // 方法
    ElementType.FIELD,      // 字段
    ElementType.PARAMETER,  // 参数
    ElementType.CONSTRUCTOR // 构造方法
})
//@Retention(RetentionPolicy.SOURCE)  // 只在源码中
//@Retention(RetentionPolicy.CLASS)   // 默认，字节码中存在
@Retention(RetentionPolicy.RUNTIME) // 保留：运行时可见
public @interface MyAnnotation {
    // 注解属性（看起来像方法）
    String value() default "";
    String message() default "";
}
```

```java
// src/com/lizhao/processor/MyAnnotationRunner.java
package com.lizhao.processor;

import java.lang.reflect.Method;

public class MyAnnotationRunner {
    public static void processAnnotations(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            if (method.isAnnotationPresent(com.lizhao.annotation.MyAnnotation.class)) {
                com.lizhao.annotation.MyAnnotation annotation =
                    method.getAnnotation(com.lizhao.annotation.MyAnnotation.class);

                String value = annotation.value();
                String message = annotation.message();

                System.out.println("========================================");
                System.out.println("发现被注解标记的方法：" + method.getName());
                System.out.println("  - value 值：" + value);
                System.out.println("  - message 值：" + message);
                System.out.println("========================================");

                try {
                    // 静态方法：invoke 的第一个参数传 null
                    method.invoke(null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
```

## 参考链接

[Java 官方文档 - w3cschool](https://www.w3cschool.cn/java/dict)

[IntelliJ IDEA 2023.3](https://www.jetbrains.com/help/idea/2023.3/getting-started.html)
