反射和注解

通过反射获取类信息和注解信息

一、什么是反射

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

二、反射优缺点

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

三、反射的基本使用
  1. 通过反射获取类信息的三种方式:

    1. //"reflex.test"为带包名的类路径
      //该Class为test类的类元信息。
      Class test = Class.forName("reflex.test")
      
    2. //test为类名
      Class test = test.class;
      
    3. //先实例化类。再通过类的引用变量获取Class对象
      test test = new test();
      Class test = test.getClass();
      

    ​ 三种方式中,常用第一种,第二种对象都有了还要反射干什么,第三种需要导入类包,依赖太强,不导包就抛编译错误。一般都使用第一种,一个字符串可以传入也可以写在配置文件中等多种方法。

    注意: 通过反射获取Class类信息都是一个类元信息。所以上面三种方法获取反射类后的内存地址和hashcode地址都是一样的。(相当于一个类的模板。从始至终都只有一个。在jvm(jdk1.8)中的metaspace元空间中)

  2. 通过反射获取类中构造方法、类内字段信息、类内方法等

    • 反射类.getDeclaredFields()。是获取该反射类中所有字段(一般为被private修饰符所修饰的字段)
    • 反射类.getFields()。是获取该反射类中所有类内公共字段(一般为被public修饰符所修饰的字段)和父类公共字段
    • 反射类.getDeclaredMethods()。是获取该反射类中所有类内方法
    • 反射类.getMethods()。是获取该反射类中所有类内公共方法(一般为被public修饰符所修饰的字段)和父类公共方法
    • 反射类.getDeclaredConstructors()。是获取该反射类中所有类内构造方法
    • 反射类.getConstructors()。是获取该反射类中所有类内公共构造方法(一般为被public修饰符所修饰的字段)
    • 代码测试如下:
    package reflex;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.security.AlgorithmConstraints;
    
    /**
     * @PROJECT_NAME: deemo
     * @DESCRIPTION:
     * @USER: zhaodongjian
     * @DATE: 2021/7/28 15:13
     */
    public class test02 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            String a = "23";
            int i = Integer.parseInt(a);
            System.out.println("=====================字段========================");
            Class aClass = Class.forName("reflex.test");
            //
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println("类内全部的"+declaredField);
            }
            declaredFields = aClass.getFields();
            for (Field declaredField : declaredFields) {
                System.out.println("类public(包括父类)"+declaredField);
            }
            System.out.println("=====================方法========================");
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                System.out.println("类内全部的"+declaredMethod);
            }
            declaredMethods = aClass.getMethods();
            for (Method declaredMethod : declaredMethods) {
                System.out.println("类public(包括父类)"+declaredMethod);
            }
            //此处是使用invoke()方法改变类内私有成员变量
            //通过newInstance()获取test类的实例化对象
            test o = (test)aClass.newInstance();
            //通过aClass.getMethod("方法明",该方法的参数类型数组)获取反射类方法或实列方法。后面的参数类型可以有多个。
            Method setName = aClass.getMethod("setName", String.class);
            System.out.println("指定方法:"+setName);
            //使用反射类方法.invoke("实例化对象","要改变的参数值")
            setName.invoke(o,"八嘎雅鹿");
            System.out.println(o.getName());
            System.out.println("=====================构造器========================");
            Constructor[] constructors = aClass.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                System.out.println("类内全部的"+constructor);
            }
            constructors = aClass.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println("类public()"+constructor);
            }
            Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class,boolean.class);
            System.out.println(declaredConstructor);
            
            test o1 = (test)aClass.newInstance();
            //获取本类的私有字段flag。(注意用getDeclaredField()方法获取。如果用getField()会报NoSuchFieldException异常)
            Field flag = aClass.getDeclaredField("flag");
            //去除权限控制访问修饰符。即无视private修饰符即可进行字段值修改。
            //不加---flag.setAccessible(true);这句会---(Class reflex.test02 can not access a member of class reflex.test with modifiers "private")
            flag.setAccessible(true);
            flag.set(o1,false);
            System.out.println("去除权限控制访问修饰符后的flag--"+o1.isFlag());
        }
    }
    class test extends father{
        private String name;
        private int age;
        private boolean flag;
        public int aa;
    
        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;
        }
    
        public boolean isFlag() {
            return flag;
        }
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    
        public test() {
        }
    
        public test(String name, int age, boolean flag) {
            this.name = name;
            this.age = age;
            this.flag = flag;
        }
    
        private test(String name, int age){
            this.name = name;
            this.age = age;
        }
    private void gettest(){
        System.out.println("1");
    }
    
    }
    class father{
        private String fathername;
        public int fatherage;
    
        public String getFathername() {
            return fathername;
        }
    
        public void setFathername(String fathername) {
            this.fathername = fathername;
        }
    
        public int getFatherage() {
            return fatherage;
        }
    
        public void setFatherage(int fatherage) {
            this.fatherage = fatherage;
        }
    
        public father(String fathername, int fatherage) {
            this.fathername = fathername;
            this.fatherage = fatherage;
        }
    
        public father() {
        }
    }
    

    运行结果如下:

    =====================属性========================
    类内全部的private java.lang.String reflex.test.name
    类内全部的private int reflex.test.age
    类内全部的private boolean reflex.test.flag
    类内全部的public int reflex.test.aa
    类public(包括父类)public int reflex.test.aa
    类public(包括父类)public int reflex.father.fatherage
    =====================方法========================
    类内全部的public java.lang.String reflex.test.getName()
    类内全部的public void reflex.test.setName(java.lang.String)
    类内全部的public boolean reflex.test.isFlag()
    类内全部的public void reflex.test.setFlag(boolean)
    类内全部的public void reflex.test.setAge(int)
    类内全部的public int reflex.test.getAge()
    类内全部的private void reflex.test.gettest()public(包括父类)public java.lang.String reflex.test.getName()public(包括父类)public void reflex.test.setName(java.lang.String)public(包括父类)public boolean reflex.test.isFlag()public(包括父类)public void reflex.test.setFlag(boolean)public(包括父类)public void reflex.test.setAge(int)public(包括父类)public int reflex.test.getAge()public(包括父类)public int reflex.father.getFatherage()public(包括父类)public void reflex.father.setFathername(java.lang.String)public(包括父类)public void reflex.father.setFatherage(int)public(包括父类)public java.lang.String reflex.father.getFathername()public(包括父类)public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic(包括父类)public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic(包括父类)public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic(包括父类)public boolean java.lang.Object.equals(java.lang.Object)public(包括父类)public java.lang.String java.lang.Object.toString()public(包括父类)public native int java.lang.Object.hashCode()public(包括父类)public final native java.lang.Class java.lang.Object.getClass()public(包括父类)public final native void java.lang.Object.notify()public(包括父类)public final native void java.lang.Object.notifyAll()
    制定方法:public void reflex.test.setName(java.lang.String)
    八嘎雅鹿
    =====================构造器========================
    类内全部的public reflex.test()
    类内全部的private reflex.test(java.lang.String,int)
    类内全部的public reflex.test(java.lang.String,int,boolean)public()public reflex.test()public()public reflex.test(java.lang.String,int,boolean)
    public reflex.test(java.lang.String,int,boolean)
    去除权限控制访问修饰符后的flag--false
    
    1. 通过反射获取泛型参数

      • 通过反射获取方法的信息
      • 反射方法.getGenericParameterTypes()获取泛型参数类型数组
      public class Geneictest {
          public Map<String,test> test(){
              System.out.println("testMap");
              return null;
          }
          public void test02(Map<String,test> map,List<test> list){
      
          }
          public static void main(String[] args) throws NoSuchMethodException {
              Method test02 = Geneictest.class.getMethod("test02", Map.class, List.class);
              //方法反射获取泛型参数类型数组
              Type[] genericParameterTypes = test02.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("actual"+actualTypeArgument);
                      }
                  }
              }
          }
      }
      
      
      //运行结果:
      java.util.Map<java.lang.String, reflex.test>
      actualclass java.lang.String
      actualclass reflex.test
      java.util.List<reflex.test>
      actualclass reflex.test
      
      1. 反射获取注解信息

        (1)注解(Annotation)。Java类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

        (2)自定义注解: (这里只是简单的写两个自定义注解)

        //Target标识这个注解应该是哪种 Java 成员。如类、方法、或者类内字段
        //Retention标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
        //RetentionPolicy.RUNTIME指的是在运行时访问。
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @interface MyTable{
            String value();
        }
        
        @Target(ElementType.FIELD)
        @Retention(RetentionPolicy.RUNTIME)
        @interface MyTableFiled{
            String columnName();
            String type();
            int length();
        }
        
        1. public class AnnotationTest {
              public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
                  Class demo = Class.forName("reflex.demo");
                  Annotation[] annotations = demo.getAnnotations();
                  for (Annotation annotation : annotations) {
                      System.out.println(annotation);
                  }
                  MyTable myTable = (MyTable)demo.getAnnotation(MyTable.class);
                  System.out.println(myTable.value());
                  //获取某个属性的注解信息
                  System.out.println("================获取某个属性的注解信息=================");
                  //先通过getDeclaredField获取声明字段类型
                  Field f = demo.getDeclaredField("name");
                  //在字段领域获取其注解反射类
                  MyTableFiled annotation = f.getAnnotation(MyTableFiled.class);
                  //获取该字段的注解的值
                  System.out.println(annotation.columnName());
                  System.out.println(annotation.type());
                  System.out.println(annotation.length());
              }
          }
          @MyTable("demo")
          class demo{
              @MyTableFiled(columnName = "name",type = "varchar",length = 10)
              private String name;
              @MyTableFiled(columnName = "age",type = "int",length = 2)
              private int age;
              @MyTableFiled(columnName = "height",type = "int",length = 10)
              private int height;
              public demo() {
              }
          
              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;
              }
          
              public int getHeight() {
                  return height;
              }
          
              public void setHeight(int height) {
                  this.height = height;
              }
          
              public demo(String name, int age, int height) {
                  this.name = name;
                  this.age = age;
                  this.height = height;
              }
          }
          
          
          //运行结果:
          @reflex.MyTable(value=demo)
          demo
          ================获取某个属性的注解信息=================
          name
          varchar
          10
          

    通过反射获取类相关信息就介绍到这。如有错误。希望大佬批评指正哈。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值