Java 注解和反射学习笔记
mybatis、spring、springboot等框架底层的实现机制就是注解和反射。
1. 注解
注解就是对程序做出的解释,和注释一样,区别是注释是给人看的,注解是给其它程序(比如编译器)看的。
1. 内置注解
- @Override
- @Deprecated
- @SuppressWarings
2. 元注解
注解的注解。
- @Target
表示注解可以用在哪些地方,参数如:value = ElementType.METOD ,多个 value = {ElementType.METHOD, ElementType.TYPE, ...},没有该元注解的话,该注解在类和方法上都有可以使用 - @Retention
表示在什么级别保存该注解信息,SOURCE < CLASS < RUNTIME,即存在的生命周期,参数如 value = RetentionPolicy.SOURCE,一般都用 RUNTIME - @Documented
该注解包含在Javadoc中 - @Inherited
子类可以继承父类中的该注解
3. 自定义注解
@interface
- 方法实际是一个配置参数,返回值只能是基本类型、Class、String、enum,如果返回值是数组,使用的时候只有一个值时就不用加 {},如 @Target(ElementType.METHOD)
- 用default声明默认值,如:String name() default ""
- 如果只有一个参数,一般名为value,如果是 value,使用的时候可以不写value,如 @SuppressWarnings("all")、@Target({ElementType.TYPE, ElementType.METHOD})
- 使用有参数的注解时,必须要有值,定义注解元素时,经常使用空字符串、0作为默认值,此时使用该注解时,就不用显式赋值了
- 如果注解本身有多个参数,其中包含value,使用时没有写参数名称,直接赋值,也代表给value赋值
2. 反射
1. 概述
1. 动态语言
运行时可以改变其结构的语言,代码甚至可以被引进,如Object-C、C#、JavaScript、PHP、Python等
2. 静态语言
与动态语言相反,如Java、C、C++,但由于有反射机制,Java被称为“准动态语言”。
2. 优缺点
1. 优点
体现动态的创建对象和编译,拥有了很大的灵活性。
2. 缺点
对性能有影响,因为是一种解释的操作。
3. API
1. Class
- Class clazz = Class.forName("com....");
- 一个类在内存中只有一个Class对象
- 一个类被加载后,类的整个结构都会被封装到Class对象中
- newInstance()、getName()、getSuperclass()、getInterfaces()、getClassLoader()、getConstructors()、getMethod(...)、getDeclaredFields()
- 获取Class对象的方法
- Class clazz = person.getClass();
- Class clazz = Class.forName(...);
- Class clazz = Person.class;// 最安全、性能最高
- 基本内置类型的包装类都有一个type属性,Class
clazz = Integer.type; - Class superClazz = clazz.getSuperclass();
- 哪些类型可以有Class对象
- class 外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类 Object.class
- interface 接口 Comparable.class
- [] 数组 String[].class int[][].class 只要数组的类型和维度一样,就是同一个Class对象,与数组元素的个数无关
- enum 枚举 ElementType.class
- annotation 注解 Override.class
- primitive type 基本数据类型 Integer.class
- void void.class
2. Method
3. Field
4. Constructor
4. 类加载
1. 内存分析
1. 堆
存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用
2. 栈
存放基本变量类型(包含具体数值),引用对象的变量(在堆中具体的地址)
3. 方法区(特殊的堆)
可以被所有的线程共享,包含了所有的class和static变量
2. 过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化
1. 类的加载(Load)
大体:将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成
具体:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
2. 类的链接(Link)
大体:将类的二进制数据合并到JRE中
具体:将Java类的二进制代码合并到JVM的运行状态中的过程。
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3. 类的初始化(Initialize)
1. 概述
大体:JVM负责对类进行初始化
具体:
- 执行类构造器
()的过程,类构造器 ()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器。) - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的
()方法在多线程环境中被正确加锁和同步。
2.什么时候会发生类的初始化
- 类的主动引用,一定会发生类的初始化
- 当虚拟机启动,先初始化main方法所在的类
- new 一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类
- 类的被动引用,不会发生类的初始化
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化。
- 通过数组定义类引用,不会触发此类的初始化。
- 引用常量不会触发此类的初始化。(常量在链接阶段就存入调用类的常量池中了)
4. 类加载器
1. 类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
2. 类缓存
标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,他将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
3. 类型
类加载器的作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。自底向上检查类是否已装载,自顶向下尝试加载类。
1. 引导类加载器(Bootstap Classloader)
用C++编写的,是JVM自带的类加载器,负责Java平台核心库(rt.jar),用来装载核心类库。该加载器无法直接获取。
2. 扩展类加载器(Extension Classloader)
负责jar/lib/ext目录下的jar包或-Djava.ext.dirs 指定目录下的jar包装入工作库
3. 系统类加载器(System Classloader 或 Application Classloader)
负责java -classpath 或 -Djava.class.path 所指的目录下的类与jar包装入工作,是最常用的加载器
4. 自定义类加载器
4. 双亲委派机制
加载类之前会先看父类加载器是否已加载了相同包名和类名的类,增加安全性。
5. 通过反射操作泛型
1. 泛型概述
- Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
- 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
- ParameterizedType:表示一种参数化类型,比如Collection
- GenericType:表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable:是各种类型变量的公共父接口
- WildcardType:代表一种通配符类型表达式
6. 获取注解信息
1. 练习ORM(Object relationship Mapping -> 对象关系映射)
利用注解和反射完成类和表结构的映射关系,见最后代码。
- 类和表结构对应
- 属性和字段对应
- 对象和记录对应
3. 代码演示
1. 测试类初始化静态代码和静态变量的顺序
package com.example.demo;
public class Test05 {
public static void main(String[] args) {
A a = new A();
System.out.println("main方法中输出A类的m:" + A.m);
}
}
class A {
static {
System.out.println("A类的静态代码块初始化");
m = 300;
}
static int m = 100;
public A() {
System.out.println("A类的无参构造器初始化");
}
}
输出:
A类的静态代码块初始化
A类的无参构造器初始化
main方法中输出A类的m:100
分析:
- 加载:首先类的信息会加载到方法区中,有 Test05类 和 A类 的数据(静态变量、静态方法、常量池、代码),在堆中生成两个Class对象
- 链接:在栈中执行main方法,A 的 m 首先默认是 0
- 初始化:准备工作做完了,开始初始化A,执行了new A(),所以在堆中 new 了一个A类的对象,这个对象通过堆中的A类的Class对象,找到方法区中所有数据,通过这些数据给A类中的属性显示赋值,执行
()方法,该方法是由编译器合并静态代码块和赋值语句产生的,合并的时候是按照顺序合并的,按照顺序先给 m 赋值为300,又赋值为100
2. 测试类什么时候会初始化
1. new 主动引用
package com.example.demo;
public class Test06 {
static {
System.out.println("Main类静态代码块");
}
public static void main(String[] args) {
Son son = new Son();
}
}
class Father {
static {
System.out.println("父类静态代码块");
}
}
class Son extends Father {
static {
System.out.println("子类静态代码块");
}
}
输出:
Main类静态代码块
父类静态代码块
子类静态代码块
2. 反射主动引用
package com.example.demo;
public class Test07 {
static {
System.out.println("Main类静态代码块");
}
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.example.demo.Son07");
}
}
class Father07 {
static {
System.out.println("父类静态代码块");
}
}
class Son07 extends Father07 {
static {
System.out.println("子类静态代码块");
}
}
输出:
Main类静态代码块
父类静态代码块
子类静态代码块
3. 通过子类调用父类的静态属性和方法不会初始化子类
package com.example.demo;
public class Test08 {
static {
System.out.println("Main类的静态代码块");
}
public static void main(String[] args) {
System.out.println(Son08.msg);
}
}
class Father08 {
static String msg = "父类的静态属性";
static {
System.out.println("父类的静态代码块");
}
}
class Son08 extends Father08 {
static {
System.out.println("子类的静态代码块");
}
}
输出:
Main类的静态代码块
父类的静态代码块
父类的静态属性
4. 通过数组定义类引用,不会初始化
package com.example.demo;
public class Test09 {
static {
System.out.println("Main类静态代码块");
}
public static void main(String[] args) {
Son09[] array = new Son09[5];
}
}
class Father09 {
static {
System.out.println("父类静态代码块");
}
}
class Son09 extends Father09 {
static {
System.out.println("子类静态代码块");
}
}
输出:
Main类静态代码块
4. 引用常量,不会初始化
package com.example.demo;
public class Test10 {
static {
System.out.println("Main类的静态代码块");
}
public static void main(String[] args) {
System.out.println(Son10.MSG);
}
}
class Father10 {
static {
System.out.println("父类静态代码块");
}
}
class Son10 extends Father10 {
static final String MSG = "子类的常量属性";
static {
System.out.println("子类静态代码块");
}
}
输出:
Main类的静态代码块
子类的常量属性
3. 测试加载器
package com.example.demo;
public class Test11 {
public static void main(String[] args) throws ClassNotFoundException {
// 系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 系统类加载器的父类加载器->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 扩展类加载器的父类加载器->根加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
// 当前类是由哪个类加载器加载的
ClassLoader classLoader = Class.forName("com.example.demo.Test11").getClassLoader();
System.out.println(classLoader);
// JDK的类是由哪个类加载加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
// 获取系统类加载器可以加载的路径
for (String s : System.getProperty("java.class.path").split(";")) {
System.out.println(s);
}
}
}
4. 获取类的运行时结构
package com.example.demo.entity;
public class User {
private Long id;
private String name;
public Long pubId;
public String pubName;
private int age;
private User() {
}
public User(Long id, String name, Long pubId, String pubName) {
this.id = id;
this.name = name;
this.pubId = pubId;
this.pubName = pubName;
}
public User(int age) {
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getPubId() {
return pubId;
}
public void setPubId(Long pubId) {
this.pubId = pubId;
}
public String getPubName() {
return pubName;
}
public void setPubName(String pubName) {
this.pubName = pubName;
}
private void privateMethod() {
}
}
package com.example.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> clazz = Class.forName("com.example.demo.entity.User");
// 获取类的名字
System.out.println("=============获取类的名字=================");
System.out.println(clazz.getName());// 类名加包名
System.out.println(clazz.getSimpleName());// 类名
// 获得类的属性
System.out.println("=============获得类的属性=================");
System.out.println("=============获得类的public属性=================");
for (Field field : clazz.getFields()) {// 只能找到public属性
System.out.println(field);
}
System.out.println("=============获得类的所有属性=================");
for (Field declaredField : clazz.getDeclaredFields()) {// 可以找到所有属性
System.out.println(declaredField);
}
System.out.println("=============根据属性名获取指定属性=================");
System.out.println(clazz.getDeclaredField("id"));
System.out.println(clazz.getField("pubId"));
// 获取类的方法
System.out.println("=============获得类的方法=================");
System.out.println("=============获得类的所有public方法包括继承的=================");
for (Method method : clazz.getMethods()) {// 获得本类及其父类的所有方法
System.out.println(method);
}
System.out.println("=============获得类的方法不包括继承的,包含私有的=================");
for (Method declaredMethod : clazz.getDeclaredMethods()) {// 只获取本类的方法,包含私有的
System.out.println(declaredMethod);
}
System.out.println("=============获得类的指定方法=================");
System.out.println(clazz.getMethod("setName", String.class));
System.out.println(clazz.getMethod("getName", null));
System.out.println(clazz.getDeclaredMethod("privateMethod"));
// 获取构造器
System.out.println("=============获得类的构造器=================");
System.out.println("=============获得public构造器=================");
for (Constructor<?> constructor : clazz.getConstructors()) {
System.out.println(constructor);
}
System.out.println("=============获得所有构造器=================");
for (Constructor<?> declaredConstructor : clazz.getDeclaredConstructors()) {
System.out.println(declaredConstructor);
}
System.out.println("=============获得指定的构造器=================");
System.out.println(clazz.getDeclaredConstructor(null));
System.out.println(clazz.getConstructor(Long.class, String.class, Long.class, String.class));
System.out.println(clazz.getConstructor(int.class));
}
}
5. 动态创建对象执行
package com.example.demo.entity;
public class Role {
private long id;
private String name;
public Role() {
}
public Role(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static String staticMethod(String para) {
return "staticMethod-" + para;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package com.example.demo;
import com.example.demo.entity.Role;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test13 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> clazz = Class.forName("com.example.demo.entity.Role");
System.out.println("=============类使用newInstance()=============");
Role role = (Role) clazz.newInstance();// 本质上是调用了类的无参构造器
System.out.println(role);
System.out.println("=============指定的构造器使用newInstance()=============");
Constructor<?> constructor = clazz.getDeclaredConstructor(long.class, String.class);
role = (Role) constructor.newInstance(1, "法师");
System.out.println(role);
System.out.println("=============通过反射调用方法=============");
Object obj = clazz.newInstance();
Method setName = clazz.getDeclaredMethod("setName", String.class);
setName.invoke(obj, "智者");
System.out.println(obj);
Method getName = clazz.getDeclaredMethod("getName");
System.out.println(getName.invoke(obj));
Method staticMethod = clazz.getDeclaredMethod("staticMethod", String.class);
System.out.println(staticMethod.invoke(null, "参数"));
System.out.println("=============通过反射操作属性=============");
obj = clazz.newInstance();
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);// 私有属性需要关掉权限检测才能赋值,同时可以提高反射的效率
name.set(obj, "战士");
System.out.println(obj);
}
}
6. 性能对比分析
package com.example.demo;
import com.example.demo.entity.Role;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test14 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test01();
test02();
test03();
}
/**
* 普通方式
*/
public static void test01() {
long startTime = System.currentTimeMillis();
Role role = new Role();
for (int i = 0; i < 1_000_000_000; i++) {
role.getId();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次耗时:" + (endTime - startTime) + " ms");
}
/**
* 反射方式
*/
public static void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
long startTime = System.currentTimeMillis();
Class<?> clazz = Class.forName("com.example.demo.entity.Role");
Object o = clazz.newInstance();
Method getName = clazz.getMethod("getName", null);
for (int i = 0; i < 1_000_000_000; i++) {
getName.invoke(o);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次耗时:" + (endTime - startTime) + " ms");
}
/**
* 反射方式关闭检测
*/
public static void test03() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
long startTime = System.currentTimeMillis();
Class<?> clazz = Class.forName("com.example.demo.entity.Role");
Object o = clazz.newInstance();
Method getName = clazz.getMethod("getName", null);
getName.setAccessible(true);
for (int i = 0; i < 1_000_000_000; i++) {
getName.invoke(o);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式关闭检测执行10亿次耗时:" + (endTime - startTime) + " ms");
}
}
7. 反射操作泛型
package com.example.demo;
import com.example.demo.entity.Role;
import com.example.demo.entity.User;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Test15 {
public static void main(String[] args) throws NoSuchMethodException {
// 通过反射获取参数的泛型
System.out.println("===========参数的泛型============");
Method test01 = Test15.class.getMethod("test01", Map.class, List.class);
for (Type genericParameterType : test01.getGenericParameterTypes()) {
System.out.println("#" + genericParameterType);
if (genericParameterType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
System.out.println("===========返回值的泛型============");
Method test02 = Test15.class.getMethod("test02");
Type genericReturnType = test02.getGenericReturnType();
System.out.println("#" + genericReturnType);
if (genericReturnType instanceof ParameterizedType) {
for (Type actualTypeArgument : ((ParameterizedType) genericReturnType).getActualTypeArguments()) {
System.out.println(actualTypeArgument);
}
}
}
public void test01(Map<String, Role> map, List<User> list) {
System.out.println("test01");
}
public Map<String, User> test02() {
System.out.println("test02");
return null;
}
}
8. 获取注解信息
package com.example.demo;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test16 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> clazz = Class.forName("com.example.demo.Student");
// 获取类所有注解
for (Annotation annotation : clazz.getAnnotations()) {
System.out.println(annotation);
}
// 获取类的指定注解的值
MyTable myTable = clazz.getAnnotation(MyTable.class);
System.out.println(myTable.value());
// 获取属性的指定注解的值
Field id = clazz.getDeclaredField("id");
MyField myField = id.getAnnotation(MyField.class);
System.out.println(myField.columnName());
System.out.println(myField.type());
System.out.println(myField.length());
}
}
@MyTable("db_student")
class Student {
@MyField(columnName = "db_id", type = "bigint", length = 10)
private long id;
@MyField(columnName = "db_name", type = "varchar", length = 10)
private String name;
@MyField(columnName = "db_age", type = "int", length = 3)
private int age;
public Student() {
}
public Student(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTable {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField {
String columnName();
String type();
int length();
}
浙公网安备 33010602011771号