注解与反射

2021/11/12 23:12:55

本文主要是介绍注解与反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

注解与反射

注解加反射,框架构建基础

注解(Annotation)

annotation,对程序作出解释,被程序理解

附加在方法,属性上,给他们添加额外的信息,或者进行什么操作

@Override Java内置对象

元注解

@Target 用于描述注解的使用范围

@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

value属性返回值ElementType 是enum类型,比如上面的ElementType.ANNOTATION_TYPE

查阅JDKAPI:

public enum ElementType {
        TYPE, //类,接口(包括注释类型)或枚举声明 

        FIELD,//字段声明(包括枚举常数) 

        METHOD,//方法声明 

        PARAMETER,//正式参数声明 

        CONSTRUCTOR,//构造函数声明 

        LOCAL_VARIABLE,//局部变量声明 

        ANNOTATION_TYPE,//注解类型声明 

        PACKAGE,//包装声明 

        TYPE_PARAMETER,//键入参数声明 

        TYPE_USE//使用类型 
}

Retention:用于解释注解的生命周期(source-源码, class-编译 ,runtime-运行)

@Retention(RetentionPolicy.RUNTIME)
public @interface Retention {
    RetentionPolicy value();
}
public enum RetentionPolicy {
    SOURCE,
    
    CLASS,
    
    RUNTIME
}

Document 说明该注解将被包含在Javadoc中,即其注释将成为注释元素的公共API的一部分

Inherited 说明子类可以继承父类中的该注解

自定义注解

@interface自动继承了Java.lang.annotation.Annotation接口

声明注解@interface

要有元注解中的Target与Retention

属性就是一个配置参数

属性类型(可以是数组)就是在注解输入的参数类型

只有一个名为value的属性,可以不用在注解引用时写

其他的要写

有多个引用方法时全都要写,除非配置了default

注解元素必须要有值,在定义注解元素时,经常使用空字符串和0作为默认值

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TableStudent(value = "学生表注释",name = {"姓名","性别"},age = 18)
 @interface TableStudent{
    String value() default "";

    String[] name()default {};

    int age() default 0;
}

反射(Reflection)

程序在运行时,对未知的类进行探索的过程。

获取一个类有哪些属性、方法、构造器。Java因为反射获得了动态语言的特性,即在运行时可改变其结构

1、获取类的Class对象

        Class   to1=TestObject.class;
        Class   to2=Class.forName("com.hs.TestObject");

        TestObject   oo=new TestObject(10);
        Class to3 = oo.getClass();

2、一个实体类只有一个Class对象,如下图两个对象的内存地址相同

image-20211110221505677

3、一个类被加载后,整个类的结构被封装到Class对象中

即一个加载的类在JVM中只有一个Class实例

在类加载器加载类的时候会生成类的Class对象,然后在类初始化时通过

4、Class对象只能由系统建立对象,只能获得这个对象

类什么时候初始化:

​ 类主动引用发送类的初始化:

​ JVM启动,先初始化主程序入口所在类,即main方法所在类

​ new一个类的对象时,下次再new类的初始化已完成

​ 调用类的静态成员(除了final修饰的变量)和静态方法

​ 初始化一个类,发现父类未初始化,则会先初始化它的父类,父类在类加载的链接阶段

​ 类的被动引用:

​ 通过数组定义类引用,不会触发次类的初始化

​ 引用常量不会触发类的初始化,因为常量在类加载的链接阶段就存入到调用类的常量池中了

获取类运行时的完整结构

实体类

public class Category implements Serializable {
    private static final long serialVersionUID = -95736938451252186L;

 
    private Integer cateid;

    public Integer getCateid() {
        return cateid;
    }

    public void setCateid(Integer cateid) {
        this.cateid = cateid;
    }
      public Category() {
    }
    
    public Category(Integer cateid) {
        this.cateid = cateid;
    }
  }

获得指定的方法,因为重载,所以通过方法名与参数识别指定方法,比如使用有参构造器,传入参数String.Class

 	
    Class<Category> c1 = Category.class;
        //通过获得无参构造器实例化
        Constructor<Category> constructor = c1.getConstructor();
        Category category1 = constructor.newInstance();

        //通过class对象得到该对象的一个实例
        Category category = c1.newInstance();
        
           //通过有参构造器实例化
        Constructor<Category> constructor1 = c1.getConstructor(String.class);
        Category category2 = constructor1.newInstance();
 //只能获得public修饰的属性
        Field[] fields = c1.getFields();
        //循环所有属性
        Field[] declaredFields = c1.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
   
//获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法。
Method[] declaredMethods = c1.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
//获取本类以及父类或者父接口中所有的公共方法
  Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

私有的不能直接使用

可以通过access为true授权使用

        //得到Class对象
        Class<Category> c1 = Category.class;
        //通过class对象得到该对象的一个实例
        Category category = c1.newInstance();
        //得到setName方法,方法没有参数写null
        Method method = c1.getDeclaredMethod("setName",String.class);
        //通过方法调用invoke给方法赋值,确定是category实例的方法,方法没有参数写null
        method.invoke(category,"闪闪");

        //输出该实例的Name值
        System.out.println(category.getName());

		//属性同理
        Field field = c1.getDeclaredField("name");
		//私有需要授权
        field.setAccessible(true);
        field.set(category,"我");
        System.out.println(category.getName());

想要获得反射效率,可以关闭权限检查

反射获取泛型

public void test1(Map<String,Integer> map, List<Double> list){
        System.out.println("test1");
    }
    public Map<String,Character> test2(){
        System.out.println("test2");
        return  null;
    }
 @Test
    public void  test() throws NoSuchMethodException {
        Class<GenericTest> genericTestClass = GenericTest.class;
        Method test1 = genericTestClass.getMethod("test1", Map.class, List.class);
        Type[] genericParameterTypes = test1.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("参数类型"+genericParameterType);
            if (genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {

                    System.out.println(actualTypeArgument);
                }
            }
        }

        Method test2 = genericTestClass.getMethod("test2", null);

        Type genericReturnType = test2.getGenericReturnType();

        if (genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

反射操作注解

框架原理就是通过反射找到注解中的值,然后将值赋值给对应的属性,所以Springboot可以通过注解实现自动配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableStudent{
    String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public  @interface  FiledStudent{
    String value() ;

    String name();

    int column();
}
@TableStudent("Student_tb")
public  class Student {
@FiledStudent(value = "属性",name = "张三",column =1)
    private  String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  @Test
    public void  test1() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class<Student> studentClass = Student.class;

        Annotation[] annotations = studentClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        TableStudent annotation = studentClass.getAnnotation(TableStudent.class);
        String value = annotation.value();
        System.out.println(value);

        Field name = studentClass.getDeclaredField("name");

        FiledStudent annotation1 = name.getAnnotation(FiledStudent.class);
        System.out.println(annotation1.column());
        System.out.println(annotation1.name());
        System.out.println(annotation1.value());
        
        //通过注解获得值,通过反射给创建实例的属性赋值
        Student student = studentClass.newInstance();
        name.setAccessible(true);
        name.set(student,annotation1.name());
        System.out.println(student.getName());
    }
       


这篇关于注解与反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程