Skip to content

JavaSe 关键知识点

数据类型

Java 数据类型可简单分为两大类,共 8 种基本类型 + 引用类型:

1、基本数据类型(直接存值,占用固定内存)

类型占用空间描述(范围/用途)
byte1 字节整数(-128 ~ 127),节省内存
short2 字节整数(-32768 ~ 32767)
int4 字节常用整数(-20 亿 ~ 20 亿),默认整数类型
long8 字节大整数(需加 L 后缀,如 100L
float4 字节单精度浮点数(加 F 后缀,如 3.14F
double8 字节双精度浮点数(默认小数类型,如 3.14
char2 字节单个字符(用单引号,如 'A',存 Unicode 码)
boolean1 字节布尔值(truefalse

2、用数据类型(存地址,指向对象)

  • 包括:类(class)、接口(interface)、数组、枚举等。
  • 例:String(字符串,如 "abc")、int[](数组)、自定义类对象等。

核心区别:基本类型直接存值,引用类型存对象的内存地址。

类型转换整理

  1. 自动转换(隐式)
    小范围 → 大范围(自动完成)
    顺序:byte → short → int → long → float → doublechar → int

  2. 强制转换(显式)
    大范围 → 小范围(需手动转换,可能丢失精度)
    语法:(目标类型) 源值

java
// 自动转换
byte b = 10;
int i = b; // byte → int(自动)
double d = 3; // int → double(自动)

// 强制转换
double pi = 3.14;
int num = (int) pi; // double → int(强制,结果3)

long l = 10000000000L;
int x = (int) l; // long → int(可能溢出)

String

Java 中 StringStringBuffer 是处理字符串的常用类,核心区别在于可变性线程安全性,具体如下:

1、可变性

  • String不可变(Immutable)。
    字符串创建后内容无法修改,任何修改操作(如拼接、替换)都会生成新的 String 对象,原对象不变。
    例:

    java
    String s = "a";
    s += "b"; // 实际生成新对象 "ab",原 "a" 仍存在
  • StringBuffer可变(Mutable)。
    内部维护可变的字符数组,修改操作(如 append()insert())直接在原对象上进行,不会创建新对象。
    例:

    java
    StringBuffer sb = new StringBuffer("a");
    sb.append("b"); // 直接在原对象上追加,结果为 "ab"

2、线程安全性

  • String:天然线程安全(因不可变,多线程访问不会引发修改冲突)。
  • StringBuffer:线程安全(方法被 synchronized 修饰,多线程操作时能保证同步)。

3、性能

  • String:频繁修改时性能差(每次修改创建新对象,浪费内存和时间)。
  • StringBuffer:频繁修改时性能好(直接操作原对象,减少内存开销)。

面向对象

1、封装

  • 概念:隐藏类的实现细节,仅通过公共方法暴露接口,保护数据安全。
  • 实现:用 private 修饰属性,提供 publicgetter/setter 方法访问。
  • 例:
java
class Person {
    private String name; // 私有属性
    public String getName() { return name; } // 公开获取方法
    public void setName(String n) { name = n; } // 公开设置方法
}

2、继承

  • 概念:子类继承父类的属性和方法,实现代码复用,用 extends 关键字。
  • 特点:单继承(一个类只能有一个直接父类),子类可重写父类方法。
  • 例:
java
class Animal { void eat() { System.out.println("吃"); } }
class Dog extends Animal { // 继承Animal
    @Override void eat() { System.out.println("狗吃骨头"); } // 重写
}

3、多态

  • 概念:Java 多态是指同一行为(方法)在不同对象上表现出不同形态,需满足:继承、重写、父类引用指向子类对象。
成员类型编译期依据运行期依据是否体现多态
属性(成员变量)父类引用类型父类引用类型
静态成员(属性/方法)父类引用类型父类引用类型
普通函数(实例方法)父类引用类型实际对象类型

4、抽象类

  • 概念:含抽象方法(无实现)的类,用 abstract 修饰,不能实例化,需子类继承并实现抽象方法。
  • 抽象类可以有属性和方法(抽象方法和普通方法),但抽象方法不能有方法体。
java
abstract class Vehicle {
    // 属性(成员变量)
    protected String brand; // 品牌
    protected int speed;    // 速度

    // 普通方法(有方法体)
    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void accelerate(int increment) {
        speed += increment;
        System.out.println("加速后速度:" + speed + "km/h");
    }

    // 抽象方法(无方法体,必须由子类实现)
    public abstract void start();

    // 另一个抽象方法
    public abstract void stop();
}

// 子类继承抽象类并实现所有抽象方法
class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println(brand + "汽车启动,发动机轰鸣");
    }

    @Override
    public void stop() {
        System.out.println(brand + "汽车刹车,停止前进");
    }
}

5、接口

  • 特点:无属性(Java 8+ 可含静态常量)、无方法体(Java 8+ 可含默认方法)、支持多实现(类实现多个接口)。
java
interface Flyable {
    // 静态常量(隐式 public static final)
    int MAX_HEIGHT = 10000;

    void fly();

    // Java 8+ 新增:默认方法(有方法体,子类可直接使用或重写)
    default void land() {
        System.out.println("降落中...");
    }

    // Java 8+ 新增:静态方法(有方法体,通过接口名调用)
    static void showMaxHeight() {
        System.out.println("最大飞行高度:" + MAX_HEIGHT + "米");
    }
}

内部类

1、成员内部类(非静态内部类)

java
class Outer {
    private int num = 10;

    // 成员内部类
    class Inner {
        void print() {
            System.out.println("外部类属性:" + num); // 访问外部类private成员
        }
    }
}

// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.print(); // 输出:外部类属性:10

2、静态内部类

java
class Outer {
    static int staticNum = 20;
    private int num = 10;

    // 静态内部类
    static class StaticInner {
        void print() {
            System.out.println("外部类静态属性:" + staticNum); // 只能访问静态成员
            // System.out.println(num); // 报错:不能访问非静态成员
        }
    }
}

// 使用
Outer.StaticInner inner = new Outer.StaticInner();
inner.print(); // 输出:外部类静态属性:20

3、局部内部类

java
class Outer {
    private int outerNum = 30;

    void method() {
        final int localNum = 40; // 局部变量(需final或隐式final)

        // 局部内部类
        class LocalInner {
            void print() {
                System.out.println("外部类属性:" + outerNum);
                System.out.println("局部变量:" + localNum);
            }
        }

        // 仅在当前方法内使用
        LocalInner inner = new LocalInner();
        inner.print();
    }
}

// 使用
new Outer().method(); // 输出:外部类属性:30  局部变量:40

4、匿名内部类(最常用)

  • 定义:无类名的局部内部类,通常用于快速实现接口或继承类。
  • 特性
    • 只能使用一次,创建对象时直接定义类体。
    • 隐式继承父类或实现接口。
java
// 接口
interface Fly {
    void fly();
}

class Bird {
    // 匿名内部类实现Fly接口
    Fly fly = () -> System.out.println("鸟在飞")
    // Fly fly = new Fly() {
    //     @Override
    //     public void fly() {
    //         System.out.println("鸟在飞");
    //     }
    // };
}

// 使用
new Bird().fly.fly(); // 输出:鸟在飞

异常

Java 异常的分类

Java 异常体系以 Throwable 为根类,分为两大分支:

1. Error(错误)

  • 表示系统级错误(如虚拟机崩溃、内存溢出),由 JVM 抛出,程序无法处理,通常不需要捕获。
  • 示例:StackOverflowError(栈溢出)、OutOfMemoryError(内存溢出)。

2. Exception(异常)

  • 表示程序运行中可预测的错误,可通过代码处理。分为两类:

    • 受检异常(Checked Exception)

      • 编译期强制检查,必须通过 try-catch 捕获或 throws 声明抛出,否则编译报错。
      • 示例:IOException(IO 操作错误)、ClassNotFoundException(类未找到)。
    • 非受检异常(Unchecked Exception)

      • 继承自 RuntimeException,编译期不强制检查,通常由程序逻辑错误导致(如空指针、数组越界)。
      • 示例:NullPointerException(空指针)、IndexOutOfBoundsException(索引越界)。

自定义异常

当内置异常无法满足业务需求时,可自定义异常,通常继承 Exception(受检)或 RuntimeException(非受检)。

自定义步骤:

  1. 继承 ExceptionRuntimeException
  2. 实现构造方法(通常重写无参和带消息的构造方法)。

代码示例

1. 自定义受检异常(继承 Exception
java
// 自定义受检异常:订单金额非法
class InvalidOrderAmountException extends Exception {
    // 无参构造
    public InvalidOrderAmountException() {
        super();
    }

    // 带错误消息的构造
    public InvalidOrderAmountException(String message) {
        super(message); // 调用父类构造传递消息
    }
}
2. 自定义非受检异常(继承 RuntimeException
java
// 自定义非受检异常:用户未登录
class UserNotLoginException extends RuntimeException {
    public UserNotLoginException() {
        super();
    }

    public UserNotLoginException(String message) {
        super(message);
    }
}
3. 使用自定义异常
java
public class OrderService {
    // 检查订单金额(抛受检异常,必须声明)
    public void checkAmount(double amount) throws InvalidOrderAmountException {
        if (amount <= 0) {
            throw new InvalidOrderAmountException("订单金额必须大于0:" + amount);
        }
    }

    // 检查用户登录状态(抛非受检异常,无需声明)
    public void checkLoginStatus(boolean isLogin) {
        if (!isLogin) {
            throw new UserNotLoginException("用户未登录,无法下单");
        }
    }

    public static void main(String[] args) {
        OrderService service = new OrderService();

        // 处理受检异常(必须try-catch)
        try {
            service.checkAmount(-100);
        } catch (InvalidOrderAmountException e) {
            System.out.println("错误:" + e.getMessage());
        }

        // 处理非受检异常(可选try-catch)
        try {
            service.checkLoginStatus(false);
        } catch (UserNotLoginException e) {
            System.out.println("错误:" + e.getMessage());
        }
    }
}

集合框架

一、单列集合(Collection 接口)

  • 特点:存储单个元素(如 StringInteger 等),元素之间是独立的。

  • 核心子接口ListSet

  • 常用实现类及特点

    1. List(有序、可重复、有索引)

      • ArrayList:基于动态数组,查询快、增删慢(末尾增删快),线程不安全。
      • LinkedList:基于双向链表,增删快(首尾操作效率极高)、查询慢,线程不安全。
      • Vector:基于动态数组,线程安全(方法同步),但性能较差(已被 ArrayList 替代)。
    2. Set(无序、不可重复、无索引)

      • HashSet:基于哈希表,查询/增删快,无序(存储顺序与插入顺序无关),依赖 hashCode()equals() 去重。
      • LinkedHashSet:继承 HashSet,底层维护链表记录插入顺序,有序且去重,性能略低于 HashSet
      • TreeSet:基于红黑树,元素自动排序(需实现 Comparable 或传入 Comparator),查询/增删效率中等,无重复。

二、多列集合(Map 接口)

  • 特点:存储键值对(key-value),通过 key 映射 valuekey 不可重复(重复则覆盖),value 可重复。

  • 常用实现类及特点

    1. HashMap:基于哈希表,查询/增删快,key 无序,线程不安全。key 允许为 null(仅一个),value 允许为 null
    2. LinkedHashMap:继承 HashMap,底层维护链表记录插入顺序,key 有序,性能略低于 HashMap
    3. TreeMap:基于红黑树,key 自动排序(需实现 Comparable 或传入 Comparator),key 不可为 null,线程不安全。
    4. Hashtable:基于哈希表,线程安全(方法同步),keyvalue 均不可为 null,性能差(已被 HashMap 替代)。

泛型通配符

Java 泛型通配符(?)用于表示未知类型,解决泛型中类型参数的灵活性问题,主要用于泛型类/接口的引用、方法参数等场景,常见形式有:无界通配符(?上界通配符(? extends T下界通配符(? super T

通配符使用场景总结

通配符形式含义典型用途读写限制
?任意类型仅读取(通用操作)可读(Object),几乎不可写
? extends TT 或 T 的子类读取元素可读(T),不可写(除 null)
? super TT 或 T 的父类添加元素可写(T 及其子类),读取为 Object

多线程

一、Java 创建多线程的 4 种方式

1. 继承 Thread

java
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行:" + Thread.currentThread().getName());
    }
}

public class Test {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start(); // 启动线程(底层调用run())
    }
}

2. 实现 Runnable 接口

  • 步骤:实现 Runnable 接口的 run() 方法,将实例传入 Thread 构造器,调用 start() 启动。
  • 优点:避免单继承限制,可共享资源(多线程操作同一 Runnable 实例)。
java
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行:" + Thread.currentThread().getName());
    }
}

public class Test {
    public static void main(String[] args) {
        Runnable r = new MyRunnable();
        new Thread(r).start(); // 传入Runnable实例
    }
}

3. 实现 Callable 接口(带返回值)

  • 步骤:实现 Callable<T> 接口的 call() 方法(有返回值、可抛异常),结合 FutureTask 获取结果。
  • 优点:支持返回结果和异常处理,适合需要线程执行结果的场景。
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 1 + 1; // 返回计算结果
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        Callable<Integer> c = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(c);
        new Thread(task).start();
        System.out.println("结果:" + task.get()); // 获取返回值(会阻塞直到完成)
    }
}

二、同步锁的几种方式

1. synchronized 锁实现(同步方法)

如果是静态方法,锁为当前类对象(Class 对象),否则锁为当前实例对象。

java
// 1. 票源类:synchronized同步
class TicketSync {
    private int tickets = 10; // 总票数

    // 同步方法:锁为当前TicketSync实例
    public synchronized void sellTicket() {
        if (tickets > 0) {
            try {
                Thread.sleep(100); // 模拟售票耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 售票,剩余:" + (--tickets));
        }
    }

    // 供线程判断是否还有票
    public int getTickets() {
        return tickets;
    }
}

// 2. 售票线程类
class SellThreadSync implements Runnable {
    private TicketSync ticket; // 共享的票源

    public SellThreadSync(TicketSync ticket) {
        this.ticket = ticket;
    }

    @Override
    public void run() {
        // 循环售票直到售罄
        while (ticket.getTickets() > 0) {
            ticket.sellTicket();
        }
    }
}

// 3. 测试类
public class TestSync {
    public static void main(String[] args) {
        TicketSync ticket = new TicketSync();
        // 3个窗口线程
        new Thread(new SellThreadSync(ticket), "窗口1").start();
        new Thread(new SellThreadSync(ticket), "窗口2").start();
        new Thread(new SellThreadSync(ticket), "窗口3").start();
    }
}

synchronized 锁实现(同步代码块)

java
// 1. 票源类:显式使用对象锁(同步代码块)
class TicketWithObjectLock {
    private int tickets = 10; // 共享票数
    // 定义一个专门的锁对象(对象锁的载体)
    private final Object lockObj = new Object();

    // 售票方法:通过同步代码块锁定 lockObj
    public void sellTicket() {
        // 显式锁定 lockObj 对象(对象锁)
        synchronized (lockObj) {
            if (tickets > 0) {
                try {
                    Thread.sleep(100); // 模拟售票耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 售票,剩余:" + (--tickets));
            }
        }
    }

    public int getTickets() {
        return tickets;
    }
}

// 2. 售票线程类(复用之前的,逻辑不变)
class SellThread implements Runnable {
    private TicketWithObjectLock ticket;

    public SellThread(TicketWithObjectLock ticket) {
        this.ticket = ticket;
    }

    @Override
    public void run() {
        while (ticket.getTickets() > 0) {
            ticket.sellTicket();
        }
    }
}

// 3. 测试类
public class TestObjectLock {
    public static void main(String[] args) {
        // 创建共享票源(包含锁对象 lockObj)
        TicketWithObjectLock ticket = new TicketWithObjectLock();

        // 3个窗口线程共享同一票源(即共享同一 lockObj 对象锁)
        new Thread(new SellThread(ticket), "窗口1").start();
        new Thread(new SellThread(ticket), "窗口2").start();
        new Thread(new SellThread(ticket), "窗口3").start();
    }
}

ReentrantLock 锁实现(显式锁)

基于 JUC 包的 ReentrantLock,需手动 lock() 加锁、unlock() 释放锁,支持灵活控制(如公平性、中断)。

java
import java.util.concurrent.locks.ReentrantLock;

// 1. 票源类:ReentrantLock显式锁
class TicketLock {
    private int tickets = 10;
    private final ReentrantLock lock = new ReentrantLock(); // 显式锁(默认非公平)

    public void sellTicket() {
        lock.lock(); // 手动加锁
        try {
            if (tickets > 0) {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + " 售票,剩余:" + (--tickets));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 必须在finally中释放锁(避免异常导致锁泄漏)
        }
    }

    public int getTickets() {
        return tickets;
    }
}

// 2. 售票线程类
class SellThreadLock implements Runnable {
    private TicketLock ticket;

    public SellThreadLock(TicketLock ticket) {
        this.ticket = ticket;
    }

    @Override
    public void run() {
        while (ticket.getTickets() > 0) {
            ticket.sellTicket();
        }
    }
}

// 3. 测试类
public class TestLock {
    public static void main(String[] args) {
        TicketLock ticket = new TicketLock();
        new Thread(new SellThreadLock(ticket), "窗口1").start();
        new Thread(new SellThreadLock(ticket), "窗口2").start();
        new Thread(new SellThreadLock(ticket), "窗口3").start();
    }
}

ReadWriteLock 锁实现(读写分离锁)

基于 JUC 包的 ReadWriteLock,拆分“读锁(共享)”和“写锁(独占)”,适合 读多写少 场景(如“查票”和“售票”分离)。

java
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

// 1. 票源类:ReadWriteLock读写分离
class TicketRWLock {
    private int tickets = 10;
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); // 读锁(共享)
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); // 写锁(独占)

    // 读操作:查票(多线程可同时查)
    public int queryTicket() {
        readLock.lock();
        try {
            return tickets;
        } finally {
            readLock.unlock();
        }
    }

    // 写操作:售票(仅单线程可写)
    public void sellTicket() {
        writeLock.lock();
        try {
            if (tickets > 0) {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + " 售票,剩余:" + (--tickets));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }
}

// 2. 售票线程类(写操作)
class SellThreadRW implements Runnable {
    private TicketRWLock ticket;

    public SellThreadRW(TicketRWLock ticket) {
        this.ticket = ticket;
    }

    @Override
    public void run() {
        while (ticket.queryTicket() > 0) {
            ticket.sellTicket();
        }
    }
}

// 3. 查票线程类(读操作)
class QueryThreadRW implements Runnable {
    private TicketRWLock ticket;

    public QueryThreadRW(TicketRWLock ticket) {
        this.ticket = ticket;
    }

    @Override
    public void run() {
        while (ticket.queryTicket() > 0) {
            System.out.println(Thread.currentThread().getName() + " 查票:剩余" + ticket.queryTicket() + "张");
            try {
                Thread.sleep(50); // 查票间隔
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 4. 测试类
public class TestRWLock {
    public static void main(String[] args) {
        TicketRWLock ticket = new TicketRWLock();
        // 3个售票窗口(写)
        new Thread(new SellThreadRW(ticket), "售票窗口1").start();
        new Thread(new SellThreadRW(ticket), "售票窗口2").start();
        // 2个查票窗口(读,可同时执行)
        new Thread(new QueryThreadRW(ticket), "查票窗口1").start();
        new Thread(new QueryThreadRW(ticket), "查票窗口2").start();
    }
}

自定义线程池

java
import java.util.concurrent.*;

public class CustomThreadPool {
    //  1. 有界队列  new ArrayBlockingQueue<>(10)
    //  2. 无界队列  new LinkedBlockingQueue<>()
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,                              // 核心线程数 (corePoolSize)
                5,                              // 最大线程数 (maximumPoolSize)
                60,                             // 空闲线程存活时间 (keepAliveTime)
                TimeUnit.SECONDS,               // 时间单位 (unit)
                new ArrayBlockingQueue<>(10),   // 工作队列 (workQueue)
                Executors.defaultThreadFactory(), // 线程工厂 (threadFactory)
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 (handler)
                );

        for(int i = 1; i <= 16; i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "--- 提交了任务");
                }
            });
        }

    }
}

反射

反射(Reflection)是 Java 语言的一种特性,允许程序在运行时获取类的内部信息,并能直接操作类或对象的内部属性、方法。

反射的作用

  • 在运行时分析类的能力
  • 在运行时查看和操作对象
  • 实现通用数组操作代码
  • 利用 Method 对象实现方法调用

优缺点

优点:

  • 灵活性:运行时动态获取类信息
  • 通用性:编写通用框架和工具
  • 扩展性:支持动态加载和创建对象

缺点:

  • 性能开销:反射操作比直接调用慢
  • 安全性限制:突破封装性,访问私有成员
  • 复杂性:代码可读性降低,调试困难

反射核心类

类名作用主要方法
Class代表类的实体forName(), newInstance(), getMethods()
Field代表类的成员变量get(), set(), getType()
Method代表类的方法invoke(), getParameterTypes()
Constructor代表类的构造方法newInstance(), getParameterTypes()
Array提供动态创建和访问数组的方法newInstance(), get(), set()

获取 Class 对象的三种方式

java
// 1. 类名.class (最安全,性能最好)
Class<String> stringClass = String.class;

// 2. 对象.getClass()
String str = "hello";
Class<? extends String> strClass = str.getClass();

// 3. Class.forName() (最常用,可动态加载)
Class<?> clazz = Class.forName("java.lang.String");

常用方法

java
public class ClassApiDemo {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.example.User");

        // 获取类信息
        System.out.println("类名: " + clazz.getName());
        System.out.println("简单类名: " + clazz.getSimpleName());
        System.out.println("包名: " + clazz.getPackage().getName());
        System.out.println("修饰符: " + Modifier.toString(clazz.getModifiers()));

        // 获取父类和接口
        System.out.println("父类: " + clazz.getSuperclass());
        System.out.println("接口: " + Arrays.toString(clazz.getInterfaces()));

        // 获取注解
        Annotation[] annotations = clazz.getAnnotations();
    }
}

Field 类使用(字段操作)

java
public class FieldReflectionDemo {

    static class User {
        private String name;
        public int age;
        protected String email;
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = User.class;

        // 获取字段信息
        System.out.println("=== 所有公共字段 ===");
        Field[] publicFields = clazz.getFields();
        for (Field field : publicFields) {
            System.out.println(field.getName() + " : " + field.getType());
        }

        System.out.println("=== 所有字段(包括私有)===");
        Field[] allFields = clazz.getDeclaredFields();
        for (Field field : allFields) {
            System.out.println(field.getName() + " : " + field.getType() +
                             " | 修饰符: " + Modifier.toString(field.getModifiers()));
        }

        // 访问和修改字段值
        User user = new User();
        Field nameField = clazz.getDeclaredField("name");
        Field ageField = clazz.getField("age");

        // 访问私有字段需要设置可访问性
        nameField.setAccessible(true);
        nameField.set(user, "张三");
        ageField.set(user, 25);

        System.out.println("姓名: " + nameField.get(user));
        System.out.println("年龄: " + ageField.get(user));
    }
}

Method 类使用

java
public class MethodReflectionDemo {

    static class Calculator {
        private int baseValue = 10;

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

        private int multiply(int a, int b) {
            return a * b;
        }

        public static void staticMethod() {
            System.out.println("静态方法被调用");
        }
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Calculator.class;
        Calculator calculator = new Calculator();

        // 获取方法信息
        System.out.println("=== 所有公共方法 ===");
        Method[] publicMethods = clazz.getMethods();
        for (Method method : publicMethods) {
            System.out.println(method.getName() + " : " +
                             Arrays.toString(method.getParameterTypes()));
        }

        System.out.println("===  所有声明的方法 ===");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method.getName() + " : " +
                             Arrays.toString(method.getParameterTypes()) +
                             " | 修饰符: " + Modifier.toString(method.getModifiers()));
        }

        // 调用方法
        Method addMethod = clazz.getMethod("add", int.class, int.class);
        Object result = addMethod.invoke(calculator, 5, 3);
        System.out.println("add方法结果: " + result);

        // 调用私有方法
        Method multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);
        multiplyMethod.setAccessible(true);
        Object privateResult = multiplyMethod.invoke(calculator, 5, 3);
        System.out.println("multiply方法结果: " + privateResult);

        // 调用静态方法
        Method staticMethod = clazz.getMethod("staticMethod");
        staticMethod.invoke(null);
    }
}

Constructor 类使用

java
public class ConstructorReflectionDemo {

    static class Person {
        private String name;
        private int age;

        public Person() {
            this.name = "默认姓名";
            this.age = 0;
        }

        public Person(String name) {
            this.name = name;
            this.age = 0;
        }

        private Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{name='" + name + "', age=" + age + "}";
        }
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Person.class;

        // 获取构造方法信息
        System.out.println("=== 所有公共构造方法 ===");
        Constructor<?>[] publicConstructors = clazz.getConstructors();
        for (Constructor<?> constructor : publicConstructors) {
            System.out.println(constructor.getName() + " : " +
                             Arrays.toString(constructor.getParameterTypes()));
        }

        System.out.println("=== 所有构造方法 ===");
        Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : allConstructors) {
            System.out.println(constructor.getName() + " : " +
                             Arrays.toString(constructor.getParameterTypes()) +
                             " | 修饰符: " + Modifier.toString(constructor.getModifiers()));
        }

        // 使用构造方法创建对象
        // 1. 无参构造
        Constructor<?> noArgConstructor = clazz.getConstructor();
        Person person1 = (Person) noArgConstructor.newInstance();
        System.out.println("无参构造: " + person1);

        // 2. 单参数构造
        Constructor<?> oneArgConstructor = clazz.getConstructor(String.class);
        Person person2 = (Person) oneArgConstructor.newInstance("李四");
        System.out.println("单参构造: " + person2);

        // 3. 私有构造
        Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class, int.class);
        privateConstructor.setAccessible(true);
        Person person3 = (Person) privateConstructor.newInstance("王五", 30);
        System.out.println("私有构造: " + person3);
    }
}

注解

Java 注解(Annotation)是一种特殊的“标记”,用于在代码中添加元数据(描述数据的数据),可被编译器、虚拟机或框架读取,实现配置化编程。注解本身不直接影响代码逻辑,但能通过工具(如反射)解析并执行相应逻辑。

一、注解的基本概念

  • 作用:标记类、方法、属性等元素,传递额外信息(如配置、约束、说明)。
  • 常见内置注解@Override(标记重写方法)、@Deprecated(标记过时元素)、@SuppressWarnings(抑制编译警告)等。

二、元注解(Meta Annotation)

元注解是修饰注解的注解,用于定义注解的生命周期、作用范围等特性。Java 提供 5 种元注解:

元注解作用可选值
@Retention定义注解的生命周期(保留到哪个阶段)SOURCE(源码)、CLASS(字节码)、RUNTIME(运行时,可被反射读取)
@Target定义注解可修饰的元素类型TYPE(类)、METHOD(方法)、FIELD(属性)等
@Documented标记注解是否被 javadoc 文档记录
@Inherited标记注解是否可被子类继承
@Repeatable允许注解在同一元素上重复使用需指定容器注解

三、自定义注解

通过 @interface 关键字定义注解,结合元注解指定其特性,可包含成员变量(类似方法声明)。

示例:自定义注解

java
import java.lang.annotation.*;

// 元注解:注解保留到运行时(可被反射读取)
@Retention(RetentionPolicy.RUNTIME)
// 元注解:注解可修饰类和方法
@Target({ElementType.TYPE, ElementType.METHOD})
// 元注解:生成javadoc时包含该注解
@Documented
public @interface MyAnnotation {
    // 注解成员变量(默认值为"default")
    String value() default "default";

    // 带默认值的int类型成员
    int version() default 1;
}
  • 注解成员变量的声明格式:类型 名称() [default 默认值]
  • 若成员变量名为 value,使用时可省略名称(如 @MyAnnotation("test"));其他成员必须显式指定名称(如 @MyAnnotation(value="a", version=2))。

四、使用自定义注解

将注解标记在类、方法、属性等元素上:

java
// 使用自定义注解修饰类(使用默认值)
@MyAnnotation
public class AnnotationDemo {

    // 使用自定义注解修饰方法(指定value和version)
    @MyAnnotation(value = "hello", version = 2)
    public void testMethod() {
    }
}

五、反射操作注解(核心)

通过反射可在运行时读取注解信息,并根据注解内容执行逻辑(这是注解的核心价值,如框架的自动配置)。

步骤:反射解析注解

  1. 获取类/方法/属性的 Class 对象(反射入口)。
  2. 调用 getAnnotation(Class)getAnnotations() 方法获取注解实例。
  3. 访问注解的成员变量,获取配置信息。

示例:反射读取注解

java
import java.lang.reflect.Method;

public class AnnotationParser {
    public static void main(String[] args) throws Exception {
        // 1. 获取类的Class对象
        Class<AnnotationDemo> clazz = AnnotationDemo.class;

        // 2. 读取类上的注解
        if (clazz.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
            System.out.println("类上的注解:value=" + classAnnotation.value() + ", version=" + classAnnotation.version());
            // 输出:类上的注解:value=default, version=1
        }

        // 3. 读取方法上的注解
        Method method = clazz.getMethod("testMethod");
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
            System.out.println("方法上的注解:value=" + methodAnnotation.value() + ", version=" + methodAnnotation.version());
            // 输出:方法上的注解:value=hello, version=2
        }
    }
}