Appearance
JavaSe 关键知识点
数据类型
Java 数据类型可简单分为两大类,共 8 种基本类型 + 引用类型:
1、基本数据类型(直接存值,占用固定内存)
| 类型 | 占用空间 | 描述(范围/用途) |
|---|---|---|
byte | 1 字节 | 整数(-128 ~ 127),节省内存 |
short | 2 字节 | 整数(-32768 ~ 32767) |
int | 4 字节 | 常用整数(-20 亿 ~ 20 亿),默认整数类型 |
long | 8 字节 | 大整数(需加 L 后缀,如 100L) |
float | 4 字节 | 单精度浮点数(加 F 后缀,如 3.14F) |
double | 8 字节 | 双精度浮点数(默认小数类型,如 3.14) |
char | 2 字节 | 单个字符(用单引号,如 'A',存 Unicode 码) |
boolean | 1 字节 | 布尔值(true 或 false) |
2、用数据类型(存地址,指向对象)
- 包括:类(
class)、接口(interface)、数组、枚举等。 - 例:
String(字符串,如"abc")、int[](数组)、自定义类对象等。
核心区别:基本类型直接存值,引用类型存对象的内存地址。
类型转换整理
自动转换(隐式)
小范围 → 大范围(自动完成)
顺序:byte → short → int → long → float → double;char → int强制转换(显式)
大范围 → 小范围(需手动转换,可能丢失精度)
语法:(目标类型) 源值
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 中 String、StringBuffer 是处理字符串的常用类,核心区别在于可变性和线程安全性,具体如下:
1、可变性
String:不可变(Immutable)。
字符串创建后内容无法修改,任何修改操作(如拼接、替换)都会生成新的String对象,原对象不变。
例:javaString s = "a"; s += "b"; // 实际生成新对象 "ab",原 "a" 仍存在StringBuffer:可变(Mutable)。
内部维护可变的字符数组,修改操作(如append()、insert())直接在原对象上进行,不会创建新对象。
例:javaStringBuffer sb = new StringBuffer("a"); sb.append("b"); // 直接在原对象上追加,结果为 "ab"
2、线程安全性
- String:天然线程安全(因不可变,多线程访问不会引发修改冲突)。
- StringBuffer:线程安全(方法被
synchronized修饰,多线程操作时能保证同步)。
3、性能
- String:频繁修改时性能差(每次修改创建新对象,浪费内存和时间)。
- StringBuffer:频繁修改时性能好(直接操作原对象,减少内存开销)。
面向对象
1、封装
- 概念:隐藏类的实现细节,仅通过公共方法暴露接口,保护数据安全。
- 实现:用
private修饰属性,提供public的getter/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(); // 输出:外部类属性:102、静态内部类
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(); // 输出:外部类静态属性:203、局部内部类
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 局部变量:404、匿名内部类(最常用)
- 定义:无类名的局部内部类,通常用于快速实现接口或继承类。
- 特性:
- 只能使用一次,创建对象时直接定义类体。
- 隐式继承父类或实现接口。
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(非受检)。
自定义步骤:
- 继承
Exception或RuntimeException; - 实现构造方法(通常重写无参和带消息的构造方法)。
代码示例
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 接口)
特点:存储单个元素(如
String、Integer等),元素之间是独立的。核心子接口:
List、Set常用实现类及特点:
List(有序、可重复、有索引)
ArrayList:基于动态数组,查询快、增删慢(末尾增删快),线程不安全。LinkedList:基于双向链表,增删快(首尾操作效率极高)、查询慢,线程不安全。Vector:基于动态数组,线程安全(方法同步),但性能较差(已被ArrayList替代)。
Set(无序、不可重复、无索引)
HashSet:基于哈希表,查询/增删快,无序(存储顺序与插入顺序无关),依赖hashCode()和equals()去重。LinkedHashSet:继承HashSet,底层维护链表记录插入顺序,有序且去重,性能略低于HashSet。TreeSet:基于红黑树,元素自动排序(需实现Comparable或传入Comparator),查询/增删效率中等,无重复。
二、多列集合(Map 接口)
特点:存储键值对(
key-value),通过key映射value,key不可重复(重复则覆盖),value可重复。常用实现类及特点:
- HashMap:基于哈希表,查询/增删快,
key无序,线程不安全。key允许为null(仅一个),value允许为null。 - LinkedHashMap:继承
HashMap,底层维护链表记录插入顺序,key有序,性能略低于HashMap。 - TreeMap:基于红黑树,
key自动排序(需实现Comparable或传入Comparator),key不可为null,线程不安全。 - Hashtable:基于哈希表,线程安全(方法同步),
key和value均不可为null,性能差(已被HashMap替代)。
- HashMap:基于哈希表,查询/增删快,
泛型通配符
Java 泛型通配符(?)用于表示未知类型,解决泛型中类型参数的灵活性问题,主要用于泛型类/接口的引用、方法参数等场景,常见形式有:无界通配符(?)、上界通配符(? extends T)、下界通配符(? super T)。
通配符使用场景总结
| 通配符形式 | 含义 | 典型用途 | 读写限制 |
|---|---|---|---|
? | 任意类型 | 仅读取(通用操作) | 可读(Object),几乎不可写 |
? extends T | T 或 T 的子类 | 读取元素 | 可读(T),不可写(除 null) |
? super T | T 或 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() {
}
}五、反射操作注解(核心)
通过反射可在运行时读取注解信息,并根据注解内容执行逻辑(这是注解的核心价值,如框架的自动配置)。
步骤:反射解析注解
- 获取类/方法/属性的
Class对象(反射入口)。 - 调用
getAnnotation(Class)或getAnnotations()方法获取注解实例。 - 访问注解的成员变量,获取配置信息。
示例:反射读取注解
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
}
}
}