浮点数
其表示形式为:
$$
\text{value} = (-1)^S \times 1.M \times 2^{(E-1023)}
$$
M是二进制小数,省略了前导 1 节省空间。
E用于移动小数点
十进制到二进制转换
整数:除2取余,相当于移动小数点 向左,或数值 右移
小数: 乘2取余,相当于移动小数点 向右,或数值 左移
运算
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1 | ( ) | 从左向右 |
| 2 | ~ - + (强制类型转换) ++ – | 从右向左 |
| 3 | * / % | 从左向右 |
| 4 | + - | 从左向右 |
| 5 | << >> >>> | 从左向右 |
| 6 | > < >= >= | 从左向右 |
| 7 | == != | 从左向右 |
| 8 | & | 从左向右 |
| 9 | ^ | 从左向右 |
| 10 | | | 从左向右 |
| 11 | && | 从左向右 |
| 12 | || | 从左向右 |
| 13 | ? : | 从右向左 |
| 14 | = += -= *= /= %= &=|= ^= <<= >>= >>>= | 从右向左 |
运算符结合性
右结合
1 | a = b = c |
左结合
1 | a + b + c |
强制类型转换
如果空间不够会发生截断
类
访问权限
| 当前类 | 同一个包下的类 | 不同包下的子类 | 不同包下的类 | |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | ❌ |
| 默认 | ✅ | ✅ | ❌ | ❌ |
| private | ✅ | ❌ | ❌ | ❌ |
instanceof 返回bool,判断是否是对应类/子类
所有class继承自Object
abstract 抽象,相当于C++的纯虚函数,可以声明 抽象类、抽象接口
[!Note]
你甚至可以将构造函数 private ,让调用者必须通过你public的static方法创建实例
接口
interface 默认定义的方法为public abstract 可以省略
接口用implements 实现,类用extends 继承
接口可以通过extends 继承其他接口
接口用default 可以默认实现方法
Cloneable接口
1 |
|
注意为 浅拷贝
枚举类
关键字 enum
1 | public enum Status { |
包装类
特殊:BigInteger BigDecimal
BigInteger 主要使用一个 int[] 数组来存储数值,而 BigDecimal 结合了 BigInteger 和标度来表示高精度的定点浮点数。
内部类
成员内部类
内部类的创建需要一个外部类的实例作为载体,如
1 | Test b = new Test("小红"); |
同名成员访问可以使用外部类名和this,如:
1 | // 假设都有name成员 |
静态内部类
可以直接new,new Test.Inner。
但是在 静态上下文 下,只能访问静态内容
局部内部类
在方法中声明,仅限方法作用域访问
匿名内部类
1 | public static void main(String[] args) { |
可以实例化一个 接口/类 的子类,在实例化的同时声明这个匿名类相关的成员。
通常用于 抽象接口/类,临时使用对其进行初始化工作。
如果抽象类/接口 只有一个 抽象方法,可以用lambda表达式简写。
1 | public static void main(String[] args) { |
1 | public interface Study { |
使用 双冒号 进行方法引用,格式为类名::方法名
1 | public static void main(String[] args) { |
字符串
比较用equals ,常量字符串地址一致,创建不同的用new
String和char[]可以互转,用toCharArray()
StringBuilder 进行编辑操作。
数组
声明:type[] = new type[len]{......}
数组 不支持 自动装/拆箱
Lambda表达式
语法(类似箭头函数)
1 | ([type arg1]...) -> {body} | exp |
利用lambda表达式作为抽象方法实现
异常
基类:Excpetion 类
运行时异常:RuntimeExcpetion
抛出
语法thorw (exp实例)
编译器强制调用者显式处理这些异常(通过 try-catch 或继续使用 throws 声明)
一般来说,必须抛出受检异常。比如I/O操作可能因为外部原因失败,这类异常必须被处理。
异常捕获
1 | try { |
注意catch按顺序执行,匹配项后面的catch不会被执行。
finally 为始终执行的语句
断言
1 | assert (exp) : (msg) |
如果exp == false 程序将throw AssertionExcpetion
断言用于测试。
常用工具类
数学工具类
| 方法名 | 例子 | 输出 |
|---|---|---|
Math.abs(int a) |
Math.abs(-5) |
5 |
Math.max(int a, int b) |
Math.max(3, 7) |
7 |
Math.min(int a, int b) |
Math.min(3, 7) |
3 |
Math.pow(double a, double b) |
Math.pow(2, 3) |
8.0 |
Math.sqrt(double a) |
Math.sqrt(16) |
4.0 |
Math.random() |
Math.random() |
0.0 到 1.0 之间的一个随机数 |
Math.round(float a) |
Math.round(5.5f) |
6 |
Math.ceil(double a) |
Math.ceil(5.3) |
6.0 |
Math.floor(double a) |
Math.floor(5.7) |
5.0 |
Math.sin(double a) |
Math.sin(Math.PI / 2) |
1.0 |
数组工具类
多维数组的方法大多带deep前缀
| 方法名 | 例子 | 输出 |
|---|---|---|
Arrays.sort(int[] a) |
int[] arr = {5, 3, 8}; Arrays.sort(arr); |
[3, 5, 8] |
Arrays.binarySearch(int[] a, int key) |
int[] arr = {1, 3, 5, 7}; Arrays.binarySearch(arr, 5); |
2 |
Arrays.fill(int[] a, int val) |
int[] arr = new int[3]; Arrays.fill(arr, 7); |
[7, 7, 7] |
Arrays.equals(int[] a, int[] b) |
int[] arr1 = {1, 2, 3}; int[] arr2 = {1, 2, 3}; Arrays.equals(arr1, arr2); |
true |
Arrays.toString(int[] a) |
int[] arr = {1, 2, 3}; Arrays.toString(arr); |
[1, 2, 3] |
Arrays.asList(T... a) |
String[] arr = {"a", "b"}; List<String> list = Arrays.asList(arr); |
[a, b] |
Arrays.copyOf(int[] original, int newLength) |
int[] arr = {1, 2, 3}; int[] newArr = Arrays.copyOf(arr, 5); |
[1, 2, 3, 0, 0] |
Arrays.copyOfRange(int[] original, int from, int to) |
int[] arr = {1, 2, 3, 4}; int[] newArr = Arrays.copyOfRange(arr, 1, 3); |
[2, 3] |
Arrays.deepToString(Object[] a) |
int[][] arr = {{1, 2}, {3, 4}}; Arrays.deepToString(arr); |
[[1, 2], [3, 4]] |
Arrays.stream(int[] array) |
int[] arr = {1, 2, 3}; int sum = Arrays.stream(arr).sum(); |
6 |
泛型
泛型类
泛型没办法存放基本类型,只能用引用类型替代。
1 | public static void main(String[] args) { |
泛型在进行实现或继承时,子类可以选择保持泛型或指定类型
1 | static class A extends B<String> {...} // 确定类型 |
泛型方法
1 | public <T> T test(T a) // 需要放在返回类型 前面 |
泛型的界限
1 | <T extends Number> // 限定T为number或其子类 |
在对通配符使用上界后,对应的类型会变成 上界 的类型
下界
泛型的下界只适用于通配符,使用super 关键字
1 | Score<? super Number> score = new Score<>("数据结构与算法基础", "EP074512", 10); |
类型擦除
实际上,泛型是通过强制类型转换,以及Object的使用完成的。
而子类对父类泛型方法的重写,是通过编译器创建一个中转(桥接)方法,调用子类的“新”“重写”方法完成的。
这一对早期版本的兼容,造成了如下限制:
instanceof 不能判断具体的泛型
1
2a instanceof Test<String> // 不行,不支持泛型判断
a instanceof Test // 可以不支持泛型数组:
1
2
3
4
5Test<String>[] test = new Test<String>[19]; // 不行
Test[] test = new Test[19]; // 可以
// 不过可以这样代替
Test<String>[] test = new Test[19];
函数式接口
语法为
1 | // 装饰器 |
这些接口是专用于Lambda表达式的接口,以下介绍主要四个:
Supplier供给型接口
1 |
|
例子:供给学生
1 | //专门供给Student对象的Supplier |
Consumer消费型接口
1 |
|
andThen的链式调用会返回一个新的Consumer,它会顺序执行传入的accept内容。
例子:
1 | //专门消费Student对象的Consumer |
Function函数型接口
这个接口消费一个对象,然后会向外供给一个对象(前两个的融合体)
1 |
|
Predicate断言型函数式接口
1 |
|
示例:
1 | public class PredicateExample { |
判空包装
语法
1 | Optional.ofNullable(exp) |
例子
1 | private static void test(String str){ |
1 | Integer i = Optional |
数据结构
平衡二叉树AVL
目的:解决二叉查找树的痛点。
平衡因子(Balance Factor):左子树高度 - 右子树高度
失衡调整:失衡分为4种类型,LL LR RL RR
简记:
正为L,负为R
[!Important]
以下所有情况为插入情况,如果是删除请依次检查父节点的失衡情况!
1.LL右旋
条件:失衡节点 平衡因子>1,左孩子 平衡因子=1
首先通过平衡因子计算,找到最小不平衡子树:
进行右旋操作,即将中间节点作为根节点。
2.RR左旋
条件:失衡节点 平衡因子<-1,右孩子 平衡因子=-1
和上面一样,中间节点作为根节点。
3.RL右左
条件:失衡节点 平衡因子<-1,右孩子 平衡因子=1
要插入的是16,通过计算得到最小不平衡子树15 20 17
子树的形状是RL。

4.LR左右
条件:失衡节点 平衡因子>1,左孩子 平衡因子=-1
如图
子树形状LR
红黑树RedBlack

- 规则1:是二叉搜索树
- 规则2:根结点和叶子节点(NULL)一定是黑色。
- 规则3:红色结点的父结点和子结点不能为红色,也就是说不能有两个连续的红色。
- 规则4:任一节点到叶节点所有路径的黑色节点数量相同
具有以下性质:
- 1.最长路径不超过最短路径的两倍
因为最短路径为 全黑节点,最长路径为 黑红交替节点
也就是 任一节点左右子树高度差不超过两倍 - 2.插入节点必定是 红色
插入节点为黑色,必然导致路径的黑色数量不匹配
插入策略
- 插入根节点 —— 变黑
当违反了 红红 原则时,应用:
- 插入点叔叔为红 —— 叔叔父亲爷爷变色,从爷爷继续判定
- 插入点叔叔为黑(NULL/黑)—— (LL,LR,RL,RR)旋转,然后 旋转点,中心点 变色
删除策略
核心:保持黑路同
下称经过该节点会减少一个黑色计数的节点为 缺失节点
- 无子节点(叶节点)
- 红节点——直接删除
- 黑节点——删除后会路径会少一个黑色
- 兄弟是黑色
- 兄弟有红孩子 (LL, LR, RL, RR)变色+旋转
- 兄弟无红孩子
- 兄弟是红色:兄父变色,朝缺失节点旋转。此时缺失节点的兄弟是黑色。
- 兄弟是黑色
- 只有左/右子树——代替后变黑
该情况只可能存在 黑父-红子,因为 黑路同 和 不红红 两条规则 - 都有左右子树——寻找直接前驱/后继,代替后,转移到直接前驱/后继进行删除处理。
[兄弟是黑色]兄弟有红孩子
此处r为红节点,s为兄弟节点,p为父节点(相对于删除节点)
按如下策略变色+旋转后,即可结束。
- LL RR型
变色:r->s, s->p, p->黑 - LR RL型
变色:r->p, p->黑
r->p:旋转后r是s的父节点,应当和原树结构保持一致(不变会破坏p的子树的黑色计数,以及p兄弟的子树的黑色计数)
p->黑:p会和s当兄弟,而兄弟是黑色,所以P应该是黑色以保持黑色计数一致
[兄弟是黑色]兄弟无红孩子
兄弟变红(使得兄弟路径也减少黑色计数),将缺失节点放在父节点上
- 父节点为红色/根节点:父节点变黑,结束
此时,因为兄弟变红、自己被删,以父节点为根的子树的路径同时少了1个黑节点,此时将红转黑即可补上
如果父节点为根节点,整个树都少了1个黑节点 - 父节点为黑色:父节点为缺失节点,继续按策略处理
集合类
ArrayList
注意:
最好使用包装类添加/删除元素
有误认为下标的风险
Arrays.asList("A", "b", "c") 使用该工具类的方法可以快速生成一个 只读 列表
Iterator
| 方法 | 描述 |
|---|---|
| boolean hasNext() | 有下一个 |
| E next() | |
LinkedList
可以当队列/双端队列使用
内部是由链表实现的
Queue
有 ArrayDeque 双端队列, PriorityQueue 优先队列
优先队列即堆
Set
和C++ STL的Set一样。
HashSet 底层用 HashMap 是无序的
有序可以使用 LinkedHashSet ,用链表实现
TreeSet 会在插入时按照给定条件排序,默认是升序
Map
| 方法 | 描述 |
|---|---|
| put | 放入键值对 |
| putIfAbsent | 如果无键,才放入 |
| get | 获取键,可能得到null |
| getOrDefault | 获取键,或默认值 |
| compute | 计算保存新值 |
| computeIfPresent | 有Key则计算 |
| computeIfAbsent | 无Key则计算 |
| replace | 快速替换Key的值/对应Key-Value的值 |
| remove | 同上,支持Key和Key-Value匹配 |
HashMap 的底层使用哈希表维护,当发生的哈希碰撞达到一定阈值后,会转化为红黑树。
LinkedHashMap 底层用链表维护,有序
TreeMap 底层用红黑树维护,创建时给出排序规则即可。
关于Set的实现。
Set的实现基本都是创建一个空Object,作为Value。
然后使用Map保存Key,是很聪明的偷懒做法。
Stream流
List可以通过stream()方法得到流。
流是链式操作,只有执行 collect 终止方法,才会
Collections工具类
这是一个用来操作集合的工具类。
| 方法 | 作用 |
|---|---|
| binarySearch(list, val) | 二分搜搜 |
| fill(list, ) | |
| checked…(list, …) | 生成一个集合,会动态检查元素是否全为xx类型 |
| emptyList() | 创建只读空集合 |
| unmodifiableList(list) | 创建只读集合 |
| indexOfSubList(list, list2) | 子集合位置 |
因为泛型的类型擦除,可能集合内会保存不符合预期的元素,可以使用checked…进行检查。
[!Important]
注意,使用Map和基于Map的类,需要注意重写equals和hashcode
Java I/O
语法糖,避免close的二次处理
1 | try (FileInputStream stream = new FileInputStream("......")) {} |
常用方法
| 方法 | 描述 |
|---|---|
| read(bytes, start, len) | 读取,EOF返回-1。不传参默认读1字节 |
| write(bytes, start, len) | 写入 |
| skip() | 跳过指定字节的内容 |
| available() | 查看剩余字节(对于网络IO是预估量) |
| flush() | 刷新缓存区 |
文件字节流
FileInputStream FileOutputStream
适用于二进制文件读取
文件字符流
类名为 FileReader FileWriter
适用于纯文本文件,可以方便的进行中英文读取,此处注意Java使用UTF-16编码,则一个Char为2字节
不同的是,FileWriter的append()方法可以很方便追加内容,并且支持链式调用
File类
封装了和文件对象有关的方法
1 | public static void main(String[] args) { |
1 | File file = new File("/"); |
File类可以作为文件流的传入参数
缓冲类
将磁盘文件暂存到内存中,在频繁读取时提升访问速度。

缓冲字节流
BufferedInputStream
支持 reset() 和 mark(limit)
mark() 会保存当前位置之后的limit个字符,使用reset会将读取位置重置到mark的位置
实际上,保存的内容大小是max(buffer_len, limit)
缓冲字符流
**BufferedReader **
传入一个Reader对象使用
可以按行读取得到字符串
1 | public static void main(String[] args) { |
可以用lines()得到一个Stream<String>,然后进行流操作
1 | public static void main(String[] args) { |
BufferedWriter
特殊API如下:
1 | public static void main(String[] args) { |
转换类
InputStreamReader 和 OutputStreamWriter
可以传入字节流,变为字符流。
打印流
示意图

System.out就是一个打印流
Scanner
对应的,拥有一个扫描类Scanner。
可以扫描其他输入流
1 | public static void main(String[] args) { |
数据流
**DataInputStream **和 **DataOutputStream **
支持基本数据类型的读取和写入, 写入二进制数据
1 | System.out.println(dataInputStream.readBoolean()); //直接将数据读取为任意基本数据类型 |
对象流
ObjectOutputStream 和 ObjectInputStream
可以对实现了 Serializable 可序列化接口的类,进行二进制保存
1 | People people = new People("lbw"); |
对应的类在实现Serializable的时候,会添加版本标识,确保类改变后不会被错误的从文件反序列化
对于不想进行序列化的成员可以使用 transient 关键字标识
1 | static class People implements Serializable{ |
多线程
创建和启动
通过Thread类实现,传入一个Runnable接口,可以直接用lambda函数
1 | Thread t1 = new Thread(() -> { sout("我是子线程") }); |
可以通过Thread.currentThread()获取当前进程的上下文
休眠和中断
| 方法 | 作用 |
|---|---|
| sleep(ms) | 休眠当前线程 单位ms |
| interrupt() | 添加中断标记 |
| interrupted() | 去除中断标记 |
| isInterrupted() | 是否中断 |
sleep会抛出InterruptedException ,代表着进行长时间操作时,进程被中断
优先级
- MIN_PRIORITY 最低优先级
- MAX_PRIORITY 最高优先级
- NOM_PRIORITY 常规优先级
通过t.setPriority设置优先级
yield()方法
表示线程建议把资源让给其他线程
join()方法
等待某个线程终止后,再继续运行,是一个阻塞方法
线程锁和线程同步

这是JAVA内存模型,子线程会从主线程中拿相关内存到自己的工作内存,再复制回去。
高速缓存通过保存内存中数据的副本来提供更加快速的数据访问,但是如果多个处理器的运算任务都涉及同一块内存区域,就可能导致各自的高速缓存数据不一致,在写回主内存时就会发生冲突,这就是引入高速缓存引发的新问题,称之为:缓存一致性。
我们可以使用synchronized 关键字解决,将并行(异步)的部分代码,进行同步
synchronized 可以传入一个对象、类实例作为锁。
synchronized 可以作用在方法。
- 静态方法:锁为类实例
- 普通方法:锁为对象
1 | synchronized (object) {...} // 方法锁 |
在同步代码内,当一个线程拿到锁后,其他线程的同步代码会进行阻塞,确保异步下的资源竞争安全。
死锁
死锁即两个线程互相持有对方所需的锁,并且不愿意释放。

可以通过jconsole检查死锁。
wait()和notify()
是属于 Object 的方法,只有 同步代码,可以调用该方法-
wait() notify() notifyAll() 需要配合Synchronized 使用。
不同的是,notify会随机唤醒一个进程,notifyAll会唤醒全部。
注意
当进程被wait的时候,会释放拥有的所有锁。
ThreadLocal
可以使用ThreadLocal类将线程的专用变量存到线程的专用内存了,不同的线程访问ThreadLocal对象时,只能获取到当前线程所属的变量。
1 | ThreadLocal<String> str = new ThreadLocal<>(); |
InheritableThreadLocal 可以让线程的子线程获取到父线程的变量
1 | InheritableThreadLocal<String> str = new InheritableThreadLocal<>(); |
定时器
我们可以使用Timer类来执行定时任务。
Timer类的实例化需要传入一个TimerTask为参数,可惜这是一个抽象类非接口,就无法使用lambda表达式
1 | Timer timer = new TImer(new TimerTask(){ |
注意,timer必须通过 cancel() 方法取消,否则他会持续等待新的任务,不会关闭线程。
timer内部通过维护一个任务队列来实现消息通信。
守护线程
守护线程会在其他线程结束后,再结束
守护线程必须在其开始之前设置
1 | Thread t = new Thread(); |
多线程下的集合类
| 数据 | 介绍 |
|---|---|
| spliterator() | 获取可拆分迭代器(用于多线程) |
| parallelStream() | 获取并行流 |
| forEachOrdered() | 在并行流下进行单线程(默认的forEach是多线程) |
| Arrays.parallelSort(arr) | 并行排序 |
以后在多线程环境使用集合类,就需要注意加锁避免资源竞争了。
反射
Java类加载机制
在Java程序启动时,JVM会将一部分类(class文件)先加载(并不是所有的类都会在一开始加载),通过ClassLoader将类加载,在加载过程中,会将类的信息提取出来(存放在元空间中,JDK1.8之前存放在永久代),同时也会生成一个Class对象存放在内存(堆内存),注意此Class对象只会存在一个,与加载的类唯一对应!
为了方便各位小伙伴理解,你们就直接理解为默认情况下(仅使用默认类加载器)每个类都有且只有一个唯一的Class对象存放在JVM中,我们无论通过什么方式访问,都是始终是那一个对象。Class对象中包含我们类的一些信息,包括类里面有哪些方法、哪些变量等等。
Class类对象
可以通过以下三种方法获取
类名.class对象.getClass();Class.forName("包名");
1 | public static void main(String[] args) throws ClassNotFoundException { |
不过只有第一种能获取到具体的Class,其他两种都是用通配符作为返回值。
基本类型与包装类
基本类型也可以获取class,也可以通过其对应的包装类获取class
1 | int.class // 直接获取 |
注意:包装类的class与基本类型的class是不一样的
1 | Integer.class != int.class // 返回true |
数组也是一种类型,可以获取数组的class
1 | public static void main(String[] args) { |
Class对象与多态
一些常用的Class类方法
| 方法 | 描述 |
|---|---|
| asSubClass(Class<>); | 进行类型转换,看看该类是否是对应类的子类,失败会异常 |
| getSuperclass(); | 获取父类的Class对象,基类返回null |
| getGenericSuperclass(); | 获取父类的泛型类型的Type对象(比如ArrayList<String>)基类返回null 父类非泛型返回对应的Class对象 |
| getInterfaces(); | 获取接口数组 |
| getGenericInterfaces(); | 获取泛型接口数组 |
getGenericSuperclass可能会得到 ParameterizedType、TypeVariable、GenericArrayType或WildcardType
其中,ParameterizedType是参数化类型,即需要传入参数的类型,如List<String>
TypeVariable 是类型变量,比如Student<T> 中的 T
GenericArrayType 是泛型数组,如T[]
WildcardType 是通配符类型,如? extends Student
创建对象
1.直接创建实例
通过Class<>.newInstance()创建调用的是无参构造
如果是private则无权访问
如果不具备无参构造则抛出异常
1 | Class<Integer> integerClass = int.class; |
该方法已在JAVA 9弃用
2.通过构造器
用法:
1 | Constructor <Class<>> c = Class<>.getConstructor(Class<>...) |
需要填写构造的参数的Class,如果没有对应的构造函数会抛出异常
可以获取从父类继承的构造函数
可以通过newInstance方法创建实例,需要传入对应的参数。
没有访问权限时
可以通过getDeclaredConstructor 获取
该方法可以获取类所有的构造函数,包括所有权限
不能获取从父类继承的构造函数
1 | Class<Student> clazz = Student.class; |
对应的还有getConstructors和getDeclaredConstructors方法,会返回构造方法列表
调用方法
1 | Method method = clazz.getMethod("test", String.class); //通过方法名和形参类型获取类中的方法 |
通过getMethod获取public方法,invoke执行
包括从父类和接口继承的方法
需要传递方法名和参数列表
对于可变长参数,可以传入对应的数组类对象
注意的是,invoke需要传入一个类或对象作为执行载体
- 静态方法:传入类对象
- 成员方法:传入类实例
获取所有方法
getDeclaredMethod 可以获取所有权限的方法
不包括从父类和接口继承的方法
1 | Method method = clazz.getDeclaredMethod("test", String.class); //通过方法名和形参类型获取类中的方法 |
获得字段
1 | Field field = clazz.getField("i"); //获取类的成员字段i |
通过getField获取public字段,set修改,get获取
同样需要传递类对象或类实例
获取所有字段
getDeclaredField 获取所有权限的字段
不包括从父类继承的字段
1 | Field modifiersField = Field.class.getDeclaredField("modifiers"); //这里要获取Field类的modifiers字段进行修改 |
修改final字段
如果要修改final字段,可以去除final修饰符再进行修改
field.getModifiers()&~Modifier.FINAL
1 | Field modifiersField = Field.class.getDeclaredField("modifiers"); //这里要获取Field类的modifiers字段进行修改 |
类加载器
双亲委派机制
每个类的加载会通过三个加载器进行加载,每个加载器会将加载委托给自己的父加载器,进行递归委托加载。
这确保了JDK的类不会被篡改,保证了安全性。
手写ClassLoader
可以通过自定义的ClassLoader加载Class文件,用动态的方式从外部(文件或网络)加载类。
1 | //定义一个自己的ClassLoader |
注解
元注解
元注解是作用于注解上的注解,用于我们编写自定义的注解:
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
注解的声明
1 | @元注解 |
当有且只有一个字段value时,在使用注解时可以不用写字段名,直接传值。
字段为数组时,也可以直接传一个值作参数。
1 | // 数组可以用初始化列表传值,也可以直接传单个元素 |
无论是方法、类、还是字段,都可以使用getAnnotations()方法(还有几个同名的)来快速获取我们标记的注解。

