JavaSE核心知识点03高级特性
🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
- JavaSE核心知识点03高级特性
- JavaSE核心知识点03高级特性03-01(集合框架)
- 一、什么是集合框架(Collection Framework)?
- 为什么需要集合框架?
- 二、集合框架的核心结构
- 1. **Collection 接口(单列集合)**
- 2. **Map 接口(双列集合)**
- 三、核心接口详解与代码示例
- 1. **List 接口**
- 特点:有序、可重复、可通过索引访问
- 2. **Set 接口**
- 特点:无序、不可重复(通过`equals()`和`hashCode()`判断)
- 3. **Map 接口**
- 特点:键值对存储,Key唯一
- 四、集合框架的通用操作
- 1. **遍历集合**
- 2. **集合工具类 `Collections`**
- 五、如何选择集合类?
- 六、常见面试问题
- 七、学习路线建议
- JavaSE核心知识点03高级特性03-02(多线程)
- **一、多线程基础概念**
- **二、Java中创建线程的两种方式**
- **1. 继承Thread类**
- **2. 实现Runnable接口(推荐)**
- **三、线程的生命周期**
- **四、线程安全与同步**
- **1. 问题:多个线程共享数据导致竞态条件**
- **2. 解决方案:synchronized关键字**
- **3. 使用Lock接口(更灵活)**
- **4. volatile关键字**
- **五、线程间通信**
- **六、线程池(重点掌握)**
- **1. 为什么使用线程池?**
- **2. 使用Executors创建线程池**
- **3. 自定义线程池(推荐)**
- **七、常见问题与注意事项**
- **八、实战练习**
- JavaSE核心知识点03高级特性03-03(IO流)
- **一、IO 流的作用**
- **二、IO 流的分类**
- 1. **按数据流向分**
- 2. **按数据类型分**
- **三、常用 IO 类**
- 1. **字节流**
- 2. **字符流**
- 3. **其他工具类**
- **四、代码示例**
- 1. **字节流示例:复制图片**
- 2. **字符流示例:读取文本文件**
- 3. **控制台输入**
- **五、关键注意事项**
- **六、IO 流的选择**
- **七、总结**
- JavaSE核心知识点03高级特性03-04(Lambda表达式)
- 一、Lambda表达式简介
- 二、Lambda表达式基础语法
- 示例对比:Lambda vs 匿名内部类
- 三、函数式接口(Functional Interface)
- 自定义函数式接口:
- 四、Lambda表达式使用场景
- 1. 替代匿名内部类
- 2. 集合操作与Stream API
- 3. 作为方法参数
- 五、Lambda表达式进阶特性
- 1. 方法引用(Method Reference)
- 2. 变量作用域
- 3. 类型推断
- 六、注意事项
- 七、综合示例
- JavaSE核心知识点03高级特性03-05(注解)
- 一、注解是什么?
- 类比理解:
- 二、Java 内置的常用注解
- 1. `@Override`
- 2. `@Deprecated`
- 3. `@SuppressWarnings`
- 三、如何自定义注解?
- 1. 定义注解
- 2. 元注解(Meta-Annotation)
- 四、注解的处理方式
- 1. 反射处理(运行时)
- 2. 编译时处理(如 Lombok)
- 五、实际应用场景
- 六、注意事项
- 七、总结
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。
JavaSE核心知识点03高级特性
JavaSE核心知识点03高级特性03-01(集合框架)
好的!作为Java集合框架的入门学习指南,我将从基础概念、核心接口、常用实现类、使用场景到代码示例,逐步为你讲解。内容会尽量通俗易懂,配有代码示例和对比表格,帮助你快速掌握。
一、什么是集合框架(Collection Framework)?
集合框架 是Java中用于存储和操作一组对象的统一架构。它提供了一套标准化的接口和实现类,解决了数组的局限性(如固定长度、类型单一、操作复杂等)。
为什么需要集合框架?
- 动态扩容:无需手动管理容量
- 类型安全:通过泛型确保数据类型一致
- 高性能操作:提供丰富的API(增删改查、排序、遍历等)
- 统一标准:所有集合类遵循相同的接口规范
二、集合框架的核心结构
Java集合框架主要分为两大类:单列集合(Collection) 和 双列集合(Map)。
1. Collection 接口(单列集合)
- List:有序、可重复
- 常用实现类:
ArrayList
,LinkedList
,Vector
- 常用实现类:
- Set:无序、不可重复
- 常用实现类:
HashSet
,LinkedHashSet
,TreeSet
- 常用实现类:
- Queue:队列,先进先出(FIFO)
- 常用实现类:
LinkedList
,PriorityQueue
- 常用实现类:
2. Map 接口(双列集合)
- 键值对(Key-Value)存储,Key不可重复
- 常用实现类:
HashMap
,LinkedHashMap
,TreeMap
,Hashtable
三、核心接口详解与代码示例
1. List 接口
特点:有序、可重复、可通过索引访问
常用实现类对比:
实现类 | 数据结构 | 特点 |
---|---|---|
ArrayList | 动态数组 | 查询快,增删慢(需扩容或移动元素) |
LinkedList | 双向链表 | 增删快,查询慢(需遍历节点) |
Vector | 同步的动态数组 | 线程安全,性能较低(已逐渐被淘汰) |
示例代码:
// ArrayList示例
List<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add(1, "Orange"); // 在索引1插入元素
System.out.println(arrayList); // [Apple, Orange, Banana]// LinkedList示例
List<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.addFirst(5); // 头部插入
linkedList.addLast(20); // 尾部插入
System.out.println(linkedList.get(1)); // 10
2. Set 接口
特点:无序、不可重复(通过equals()
和hashCode()
判断)
常用实现类对比:
实现类 | 数据结构 | 特点 |
---|---|---|
HashSet | 哈希表 | 最快查询,无顺序 |
LinkedHashSet | 哈希表+链表 | 保留插入顺序 |
TreeSet | 红黑树 | 自然排序或自定义排序 |
示例代码:
// HashSet示例
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("Java"); // 重复元素不会被添加
System.out.println(hashSet); // 输出顺序不确定,如 [Java, Python]// TreeSet示例(自然排序)
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
System.out.println(treeSet); // [2, 5, 8]
3. Map 接口
特点:键值对存储,Key唯一
常用实现类对比:
实现类 | 数据结构 | 特点 |
---|---|---|
HashMap | 哈希表 | 最快查询,无顺序(允许null键值) |
LinkedHashMap | 哈希表+链表 | 保留插入顺序或访问顺序 |
TreeMap | 红黑树 | 按键自然排序或自定义排序 |
Hashtable | 哈希表 | 线程安全,不允许null键值(已过时) |
示例代码:
// HashMap示例
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Alice", 25);
hashMap.put("Bob", 30);
hashMap.put("Alice", 26); // 覆盖原有值
System.out.println(hashMap.get("Bob")); // 30// TreeMap示例(按键排序)
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 5);
treeMap.put("Apple", 3);
System.out.println(treeMap); // {Apple=3, Orange=5}
四、集合框架的通用操作
1. 遍历集合
-
for-each循环(推荐):
List<String> list = Arrays.asList("A", "B", "C"); for (String s : list) {System.out.println(s); }
-
迭代器(Iterator):
Iterator<String> it = list.iterator(); while(it.hasNext()) {String s = it.next();if (s.equals("B")) it.remove(); // 安全删除元素 }
2. 集合工具类 Collections
提供静态方法操作集合:
List<Integer> numbers = new ArrayList<>(Arrays.asList(3,1,4,2));
Collections.sort(numbers); // 排序 → [1,2,3,4]
Collections.reverse(numbers); // 反转 → [4,3,2,1]
Collections.shuffle(numbers); // 随机打乱
五、如何选择集合类?
根据需求选择最合适的集合:
场景 | 推荐实现类 |
---|---|
快速随机访问 | ArrayList |
频繁插入删除 | LinkedList |
去重且无需排序 | HashSet |
去重且保留插入顺序 | LinkedHashSet |
需要排序 | TreeSet 或 TreeMap |
键值对存储 | HashMap (最常用) |
线程安全场景 | ConcurrentHashMap |
六、常见面试问题
-
ArrayList和LinkedList的区别?
ArrayList
基于动态数组,适合随机访问;LinkedList
基于链表,适合频繁增删。
-
HashMap的工作原理?
- 通过哈希函数计算键的哈希值,存储到对应桶(数组位置),解决哈希冲突使用链表或红黑树(JDK8+)。
-
如何保证集合的线程安全?
- 使用
Collections.synchronizedList()
包装,或选择ConcurrentHashMap
、CopyOnWriteArrayList
等并发集合。
- 使用
七、学习路线建议
- 基础阶段:掌握
ArrayList
、HashMap
、HashSet
的常用操作 - 进阶阶段:理解底层数据结构(如哈希表、红黑树)、泛型、迭代器
- 高级阶段:学习线程安全集合、性能优化、
Stream API
操作集合
练习建议:
- 实现一个简单的学生管理系统(用集合存储学生信息)
- 统计一篇文章中每个单词出现的次数(用
HashMap
) - 比较不同集合在10万次操作下的性能差异
如果有具体问题或需要更多代码示例,欢迎随时提问!
JavaSE核心知识点03高级特性03-02(多线程)
学习Java多线程是一个循序渐进的过程,下面我会从基础到应用为你详细讲解,并附上代码示例帮助你理解。
一、多线程基础概念
1. 什么是线程?
- 线程是程序执行的最小单位,一个进程可以包含多个线程,多个线程共享进程的内存资源。
- 多线程允许程序同时执行多个任务,提高资源利用率和响应速度。
类比:想象一家餐厅,一个厨师(单线程)同时处理多个订单会手忙脚乱,多个厨师(多线程)分工协作效率更高。
二、Java中创建线程的两种方式
1. 继承Thread类
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}public static void main(String[] args) {MyThread t1 = new MyThread();t1.start(); // 启动线程,JVM自动调用run()}
}
缺点:Java是单继承,继承Thread后无法继承其他类。
2. 实现Runnable接口(推荐)
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}public static void main(String[] args) {Thread t2 = new Thread(new MyRunnable());t2.start();}
}
优点:避免单继承限制,适合资源共享(如多个线程操作同一Runnable实例)。
三、线程的生命周期
线程的状态包括:
- 新建(New):创建未启动。
- 就绪(Runnable):调用
start()
后,等待CPU调度。 - 运行(Running):执行
run()
方法。 - 阻塞(Blocked):等待锁、I/O等资源。
- 终止(Terminated):
run()
执行完毕或异常退出。
四、线程安全与同步
1. 问题:多个线程共享数据导致竞态条件
public class Counter {private int count = 0;public void increment() {count++; // 非原子操作,可能被多个线程同时修改}
}
2. 解决方案:synchronized关键字
- 同步方法:锁住当前对象实例。
public synchronized void increment() {count++;
}
- 同步代码块:更细粒度控制。
public void increment() {synchronized(this) {count++;}
}
3. 使用Lock接口(更灵活)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock(); // 确保释放锁}}
}
4. volatile关键字
- 保证变量可见性,但不保证原子性。
private volatile boolean flag = false;
五、线程间通信
使用wait()
、notify()
、notifyAll()
实现线程协作(必须在同步块内调用)。
public class ProducerConsumer {private Queue<Integer> queue = new LinkedList<>();private int capacity = 5;public synchronized void produce(int item) throws InterruptedException {while (queue.size() == capacity) {wait(); // 队列满,等待}queue.add(item);notifyAll(); // 唤醒消费者}public synchronized int consume() throws InterruptedException {while (queue.isEmpty()) {wait(); // 队列空,等待}int item = queue.poll();notifyAll(); // 唤醒生产者return item;}
}
六、线程池(重点掌握)
1. 为什么使用线程池?
- 避免频繁创建/销毁线程的开销。
- 控制并发数量,防止资源耗尽。
2. 使用Executors创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {pool.execute(() -> {System.out.println(Thread.currentThread().getName() + " 执行任务");});}pool.shutdown(); // 关闭线程池}
}
3. 自定义线程池(推荐)
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // 核心线程数10, // 最大线程数60L, TimeUnit.SECONDS, // 空闲线程存活时间new LinkedBlockingQueue<>(100) // 任务队列
);
七、常见问题与注意事项
-
死锁:多个线程互相等待对方释放锁。
- 避免方法:按固定顺序获取锁,使用超时机制(
tryLock
)。
- 避免方法:按固定顺序获取锁,使用超时机制(
-
线程泄漏:线程未正确关闭导致资源耗尽。
- 解决:使用
try-finally
确保资源释放。
- 解决:使用
-
避免使用已废弃方法:
stop()
:强制终止线程,可能导致数据不一致。- 改用标志位控制线程退出:
public class SafeStop implements Runnable {private volatile boolean running = true;public void stop() {running = false;}@Overridepublic void run() {while (running) {// 执行任务}} }
八、实战练习
题目:模拟10个窗口卖100张票(线程安全)。
public class TicketSales implements Runnable {private int tickets = 100;private final Object lock = new Object();@Overridepublic void run() {while (true) {synchronized (lock) {if (tickets <= 0) break;System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets-- + " 张票");}try {Thread.sleep(100); // 模拟操作耗时} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {TicketSales task = new TicketSales();for (int i = 0; i < 10; i++) {new Thread(task, "窗口" + (i+1)).start();}}
}
通过以上内容,你应该对Java多线程有了全面的认识。学习时建议多写代码调试,观察不同情况下的输出,逐步理解线程的行为。遇到问题可以查阅Java官方文档或可靠的教程资源。
JavaSE核心知识点03高级特性03-03(IO流)
好的!Java 中的 IO 流是处理输入/输出操作的核心工具,我会尽量用通俗易懂的方式为你讲解,分为以下几个部分:
一、IO 流的作用
IO(Input/Output)流用于处理数据的输入和输出,例如:
- 从文件读取数据(输入)
- 向文件写入数据(输出)
- 网络数据传输
- 控制台输入输出
二、IO 流的分类
Java IO 流主要分为两大类:
1. 按数据流向分
- 输入流(Input Stream):从外部(如文件)读取数据到程序。
- 输出流(Output Stream):将程序中的数据写入到外部(如文件)。
2. 按数据类型分
- 字节流(Byte Stream):以字节(8-bit)为单位处理数据,适用于所有文件类型(如图片、视频、文本等)。
- 核心类:
InputStream
、OutputStream
- 核心类:
- 字符流(Character Stream):以字符(16-bit Unicode)为单位处理数据,专为文本文件设计(如
.txt
、.java
)。- 核心类:
Reader
、Writer
- 核心类:
三、常用 IO 类
1. 字节流
- 文件读写:
FileInputStream
:读取字节文件FileOutputStream
:写入字节文件
- 缓冲流(高效读写):
BufferedInputStream
:带缓冲的字节输入流BufferedOutputStream
:带缓冲的字节输出流
2. 字符流
- 文件读写:
FileReader
:读取字符文件FileWriter
:写入字符文件
- 缓冲流(高效读写):
BufferedReader
:带缓冲的字符输入流BufferedWriter
:带缓冲的字符输出流
3. 其他工具类
Scanner
:方便读取控制台输入或解析文件。PrintWriter
:格式化输出文本。
四、代码示例
1. 字节流示例:复制图片
try (FileInputStream fis = new FileInputStream("input.jpg");FileOutputStream fos = new FileOutputStream("output.jpg")
) {byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) != -1) {fos.write(buffer, 0, length);}
} catch (IOException e) {e.printStackTrace();
}
2. 字符流示例:读取文本文件
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))
) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}
} catch (IOException e) {e.printStackTrace();
}
3. 控制台输入
Scanner scanner = new Scanner(System.in);
System.out.print("请输入内容:");
String input = scanner.nextLine();
System.out.println("你输入的是:" + input);
scanner.close();
五、关键注意事项
- 资源关闭:
- 必须用
close()
关闭流,或使用 try-with-resources(Java 7+)自动关闭。
- 必须用
- 缓冲流提升性能:
- 使用
BufferedInputStream
、BufferedReader
等缓冲类减少IO次数。
- 使用
- 字符编码问题:
- 读写文本文件时,需注意编码(如 UTF-8),可使用
InputStreamReader
指定编码:new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8);
- 读写文本文件时,需注意编码(如 UTF-8),可使用
六、IO 流的选择
- 字节流:处理非文本文件(如图片、音频)或未知文件类型。
- 字符流:处理文本文件,避免乱码问题。
七、总结
Java IO 流的核心是理解字节流与字符流的区别,掌握常用类的用法,并注意资源管理和编码问题。建议通过实际项目(如文件复制、日志记录)加深理解。
如果有具体问题或需要更多示例,可以随时问我! 😊
JavaSE核心知识点03高级特性03-04(Lambda表达式)
好的,让我们一步步深入了解Java中的Lambda表达式。
一、Lambda表达式简介
Lambda表达式是Java 8引入的一种简洁的匿名函数写法,主要用于简化函数式接口(仅含一个抽象方法的接口)的实现。它使得代码更简洁、易读,尤其在处理集合和并发任务时效果显著。
二、Lambda表达式基础语法
Lambda的基本结构如下:
(参数列表) -> { 方法体 }
- 参数列表:与接口方法参数一致,类型可省略。
- 箭头符号
->
:分隔参数和方法体。 - 方法体:单行代码可省略
{}
和return
,多行则必须使用。
示例对比:Lambda vs 匿名内部类
匿名内部类:
Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("Hello World");}
};
Lambda表达式:
Runnable r = () -> System.out.println("Hello World");
三、函数式接口(Functional Interface)
函数式接口是Lambda表达式的核心,Java内置了多种常用接口:
接口 | 方法 | 用途 |
---|---|---|
Runnable | void run() | 无参无返回值 |
Consumer<T> | void accept(T) | 消费一个参数 |
Supplier<T> | T get() | 提供返回值 |
Function<T,R> | R apply(T) | 接收T类型,返回R类型 |
Predicate<T> | boolean test(T) | 条件判断 |
自定义函数式接口:
@FunctionalInterface
interface Greeting {void sayHello(String name);
}// 使用Lambda
Greeting greet = name -> System.out.println("Hello, " + name);
greet.sayHello("Alice"); // 输出:Hello, Alice
四、Lambda表达式使用场景
1. 替代匿名内部类
简化事件监听、线程创建等代码:
// 传统方式
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Running!");}
}).start();// Lambda方式
new Thread(() -> System.out.println("Running!")).start();
2. 集合操作与Stream API
结合Stream处理集合,代码更简洁:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// 过滤并打印长度>3的名字
names.stream().filter(name -> name.length() > 3).forEach(System.out::println);
// 输出:Alice, Charlie
3. 作为方法参数
传递行为而非值:
public static void processList(List<Integer> list, Consumer<Integer> processor) {for (Integer num : list) {processor.accept(num);}
}// 调用
processList(Arrays.asList(1, 2, 3), num -> System.out.println(num * 2));
// 输出:2, 4, 6
五、Lambda表达式进阶特性
1. 方法引用(Method Reference)
进一步简化Lambda,四种形式:
- 静态方法:
类名::方法名
- 实例方法:
对象::方法名
- 任意对象的实例方法:
类名::方法名
- 构造方法:
类名::new
示例:
// Lambda写法
Consumer<String> c1 = s -> System.out.println(s);
// 方法引用
Consumer<String> c2 = System.out::println;List<String> names = Arrays.asList("Alice", "Bob");
names.forEach(System.out::println); // 输出每个元素
2. 变量作用域
Lambda可以访问final或等效final的局部变量:
int limit = 5; // 等效final
List<Integer> numbers = Arrays.asList(1, 2, 3, 6);
numbers.stream().filter(n -> n < limit) // 正确:limit不可修改.forEach(System.out::println);
3. 类型推断
编译器根据上下文推断参数类型:
// 完整写法
Function<String, Integer> f1 = (String s) -> s.length();
// 简化写法(省略类型)
Function<String, Integer> f2 = s -> s.length();
六、注意事项
- 仅用于函数式接口:Lambda只能替代单一抽象方法的接口。
- 省略规则:
- 单参数可省略
()
:s -> s.length()
- 单行代码可省略
{}
和return
:(a, b) -> a + b
- 单参数可省略
- this关键字:Lambda中的
this
指向外部类,而非Lambda自身。
七、综合示例
场景:使用Lambda和Stream对用户列表进行排序和过滤。
List<User> users = Arrays.asList(new User("Alice", 25),new User("Bob", 30),new User("Charlie", 20)
);// 按年龄排序,过滤年龄≥25,提取用户名
List<String> names = users.stream().sorted((u1, u2) -> u1.getAge() - u2.getAge()).filter(u -> u.getAge() >= 25).map(User::getName).collect(Collectors.toList());System.out.println(names); // 输出:[Alice, Bob]
通过以上内容,你应该能够掌握Lambda的基本用法和常见场景。建议多实践,尝试将现有的匿名类重构为Lambda,并熟悉Stream API的使用,以充分发挥Lambda的优势。
JavaSE核心知识点03高级特性03-05(注解)
好的!我来为你详细讲解 Java 中的注解(Annotation),内容会循序渐进,尽量通俗易懂。以下是完整的学习路径:
一、注解是什么?
注解(Annotation)是 Java 中的一种元数据(描述数据的数据),它本身不直接影响代码逻辑,但可以为代码提供额外的信息。类似于“标签”,用来标记代码的特殊行为或配置。
类比理解:
- 想象你在一本书上贴便利贴,便利贴本身不改变书的内容,但能提醒你注意某些部分(如“重点”、“待办”)。注解的作用类似。
二、Java 内置的常用注解
Java 提供了一些内置注解,以下是几个常见的:
1. @Override
- 作用:标记方法是为了覆盖父类或接口中的方法。
- 示例:
@Override public String toString() {return "This is a custom toString()"; }
- 用途:帮助编译器检查是否正确覆盖了父类方法(避免拼写错误)。
2. @Deprecated
- 作用:标记方法、类或字段已过时,不推荐使用。
- 示例:
@Deprecated public void oldMethod() {// 过时的方法 }
- 效果:调用该方法时,编译器会生成警告。
3. @SuppressWarnings
- 作用:抑制编译器警告。
- 示例:
@SuppressWarnings("unchecked") public void someMethod() {List list = new ArrayList(); // 这里会有“未检查类型”的警告,但被注解抑制 }
三、如何自定义注解?
你可以创建自己的注解,以下是步骤:
1. 定义注解
使用 @interface
关键字:
// 定义一个名为 MyAnnotation 的注解
public @interface MyAnnotation {String value() default ""; // 注解的参数int priority() default 0;
}
2. 元注解(Meta-Annotation)
元注解是用于修饰注解的注解,用来定义注解的行为:
元注解 | 作用 |
---|---|
@Target | 指定注解可以应用的位置(如类、方法、字段等)。 |
@Retention | 指定注解的保留策略(源码级、编译期、运行时)。 |
@Documented | 注解会被包含在 Javadoc 中。 |
@Inherited | 子类可以继承父类的注解。 |
示例:定义一个用于方法的运行时注解
import java.lang.annotation.*;@Target(ElementType.METHOD) // 只能用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
public @interface MyMethodAnnotation {String description() default "默认描述";boolean enabled() default true;
}
四、注解的处理方式
注解本身没有行为,需要通过反射或编译时处理来触发逻辑。
1. 反射处理(运行时)
通过反射读取注解信息:
public class Test {@MyMethodAnnotation(description = "这是一个测试方法")public void testMethod() {// 方法内容}public static void main(String[] args) throws NoSuchMethodException {Method method = Test.class.getMethod("testMethod");if (method.isAnnotationPresent(MyMethodAnnotation.class)) {MyMethodAnnotation annotation = method.getAnnotation(MyMethodAnnotation.class);System.out.println("描述:" + annotation.description()); // 输出:这是一个测试方法System.out.println("是否启用:" + annotation.enabled()); // 输出:true}}
}
2. 编译时处理(如 Lombok)
通过注解处理器(Annotation Processing Tool, APT)在编译时生成代码。例如 Lombok 的 @Data
注解会自动生成 getter/setter。
五、实际应用场景
-
框架配置(如 Spring):
@Service public class UserService {@Autowiredprivate UserRepository userRepository; }
-
单元测试(如 JUnit):
@Test public void testAddition() {assertEquals(4, 2 + 2); }
-
序列化/反序列化(如 Jackson):
public class User {@JsonProperty("user_name")private String userName; }
六、注意事项
- 保留策略:如果需要在运行时获取注解,必须设置
@Retention(RetentionPolicy.RUNTIME)
。 - 目标范围:用
@Target
明确注解的应用位置(如ElementType.TYPE
表示类)。 - 默认值:注解参数需定义默认值(如
default ""
),否则使用时必须显式赋值。 - 参数类型限制:注解的参数只能是基本类型、String、Class、枚举或数组。
七、总结
- 注解的作用:提供元数据,辅助代码分析或配置。
- 核心步骤:定义注解 → 用元注解修饰 → 处理注解逻辑(反射/APT)。
- 学习建议:从简单注解开始,尝试结合反射实现功能(如自定义日志注解)。
动手练习:
- 定义一个注解
@LogExecutionTime
,用于记录方法的执行时间。 - 通过反射在方法执行前后计算时间差。
如果有疑问,可以随时提出!
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇