概要
所有的资料来源都是以【狂神说Java】注解和反射 为基础
【狂神说Java】注解和反射
建议看视频,这篇博客是笔记
注解 Annotation
作用: 是帮助程序&编译器识别然后做出结束
格式: @注解名, 也可以在后面加括号添加参数值
使用位置: package,class,method,field
常用内置注解:
- override 重写
- Deprecated 不推荐使用
- SuppressWarmings 镇压警告
元注解
作用: 注解其他注解
四个标准的meta-annotation类型:
-
@Target
用于描述注解的使用范围(即: 被描述的注解可以用在什么地方)
-
@Retention
表示需要在什么级别保存该注释信息,用于描述注解的生命周期
- (SOURCE < CLASS < RUNTIME)
-
@Document
说明该注解将被包含在javadoc中
-
@Inherited
说明子类可以继承父类中的该注解
使用样例:
package cn.livorth.Annotation;
import java.lang.annotation.*;
//定义注解的应用范围,样例中为:模块、类型(包括类等)
@Target(value = {ElementType.MODULE, ElementType.TYPE})
//定义注解有效的范围 runtime>class>sources
@Retention(value = RetentionPolicy.RUNTIME)
//决定是否将注解生成到javadoc中
@Documented
//决定是否要让子类继承父类的注解
@Inherited
/**
* 定义注解类,然后再进行相关配置
*/
public @interface Test01 {
}
自定义注解
对于自定义注解:
-
继续采用元注解进行相关配置
-
注解的参数格式为:
类型 + 参数名+ () + ;
当只有一个参数的时候建议用value
-
可以使用default设置默认值,比如:
String name() default "livorth";
注意: 在使用的时候,注解中的参数没有先后顺序
使用样例:
package cn.livorth.Annotation;
import java.lang.annotation.*;
public class Test02 {
@TestAnnotation(id = 1)
public void test(){
}
}
@Target(value = {ElementType.MODULE, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface TestAnnotation {
//注解参数的格式
String name() default "livorth";
int id();
}
对于这种注解而言,需要配合反射一起使用
从而实现对元数据的读取
反射 Reflection
了解类
1. 能够获取类的结构
// 利用类路径获取类
Class c1 = Cless.forName("类的详细地址") ;
// 利用具体类的Class属性获取
Class c2 = Person.class;
// 通过实例获取
Class c3 = person.getClass();
// 一个加载类在JVM中只有一个Class示例
2. 类的初始化:
- 主动引用:
- 虚拟机启动,先初始化main方法所在类
- new 一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.long.reflect包的方法对类进行反射调用
- 初始化某类时其父类未初始化,则先初始化父类
- 被动引用:
- 访问一个静态域时,只有真正申明这个域的类才会被初始化
- 数组定义类引用
- 引用常量
3. 拥有Class对象的类型:
class、interface、数组、enum、annotation、基础数据类型、void
反射常用的使用方式
获取类的运行时结构
也就是通过反射获取类相关的信息
- 获取目标类的class对象
- 直接
.getxxx
获取相关信息
常用方法相关样例:
package cn.livorth.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws Exception {
Class userClass = Class.forName("cn.livorth.Annotation.User");
// 获取类的名字
System.out.println(userClass.getName());
// 带包名一起:
System.out.println(userClass.getSimpleName());
// 进类名:
// 获取类的属性
Field[] publicFields = userClass.getFields();// 仅找到public属性
Field[] declaredFields = userClass.getDeclaredFields();// 能获取到private属性
Field name = userClass.getDeclaredField("name");// 获取具体属性
// 获取类的方法
Method[] methods = userClass.getMethods();// 获取本类以及父类全部的public方法
Method[] declaredMethods = userClass.getDeclaredMethods();// 仅获取本类全部的方法,包括private方法
Method setId = userClass.getDeclaredMethod("setId", int.class);// 获取单个方法
Method getName = userClass.getDeclaredMethod("getName", null); // 传入参数的类型主要是为了避免在重载时出现问题
// 获取类的构造器
Constructor[] declaredConstructors = userClass.getDeclaredConstructors();
Constructor declaredConstructor = userClass.getDeclaredConstructor(String.class, int.class); // 获取指定构造器,通过传参控制
}
}
动态创建对象并操作
package cn.livorth.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) throws Exception {
Class userClass = Class.forName("cn.livorth.Annotation.User");
// 通过类的无参构造器创建对象,如果目标类没有构造器那么就
User user = (User) userClass.newInstance();
// 通过指定构造器构建对象
Constructor declaredConstructor = userClass.getDeclaredConstructor(String.class, int.class);
User user1 = (User) declaredConstructor.newInstance("livorth", 1);
// 通过反射调用类的方法
Method setName = userClass.getDeclaredMethod("setName", String.class);
setName.invoke(user, "steve");
System.out.println(user.getName()); // 输出steve
// 通过反射操作属性
Field name = userClass.getDeclaredField("name");
name.setAccessible(true); //设置权限检测,使得反射能够访问private属性
name.set(user, "Alex");
System.out.println(user.getName()); //返回Alex,但是要是不修改权限检测,那么就会报错
}
}
注意:
- 普通方式调用方法, 运行速度远高于下面两种
- 反射调用, 很慢很慢
- 关闭权限检测的反射, 虽然慢,但是比直接反射快
反射操作泛型
表示不能被归一到Class类中的类型但是又和原始类型齐名的类型
- ParameterizedType :表示一种参数化类型,比如Collection
- GenericArrayType :表示一种元素类型是参数化类型或者类型变 量的数组类型
- TypeVariable :是各种类型变量的公共父接口
- WildcardType :代表一种通配符类型表达式
package cn.livorth.Annotation;
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 Test05 {
public Map<String, User> test(Map<String, User> map, List<User> list){
list = null;
return map;
}
public static void main(String[] args) throws Exception {
// 获取方法
Method test = Test05.class.getMethod("test", Map.class, List.class);
System.out.println(test);
// 获取传入的类型数组
Type[] parameterTypes = test.getGenericParameterTypes();
// 获取返回值参数类型
Type returnType = test.getGenericReturnType();
for (Type type : parameterTypes) {
System.out.println(type);
// java.util.Map<java.lang.String, cn.livorth.Annotation.User>
// java.util.List<cn.livorth.Annotation.User>
// 如果是参数化类型
if(type instanceof ParameterizedType){
// 获取真实的泛型参数信息
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
// class java.lang.String
// class cn.livorth.Annotation.User
}
}
}
}
}
注解与反射的结合运用
反射操作注解
利用注解和反射完成类和表结构的映射关系,也就是使用反射通过注解将数据映射到数据库中的表内
-
获取类
-
获取类的注解:
Annotation[] annotations = class.getAnnotations()
-
获取类注解的value:
User user = (User)c1.getAnnotation(User.class);
value = user.value;
-
获得类中指定属性的注解:
Field f = c1ass.getDeclaredField( name: "id"); User annotation = f.getAnnotation(User.class);