Java反射、注解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Alexwll/article/details/82842460

最近本着不求甚解的态度重温了一下主流框架的源码,所以趁热打铁的总结一下,学习框架或开发框架所必备的基础知识:

一、反射

1.1、简介

本文简单总结一下Java反射和注解,反射可以算是必备条件了,基本学习的主流框架中都会看到反射的身影,他的灵活使用也为很多逻辑的扩展提供了可能,通过Java的反射机制,可以在程序中访问已经加载到JVM中的Java对象的描述,实现访问、检测、修复和修改描述Java本身对象的功能,Java中的java.lang.reflect包提供使用注解功能。

Java中的所有类均即成了Object类,在Object类中定义了getClass方法,该方法返回一个Class的对象:

Class c = object.getClass();

利用返回的Class,即可访问Class描述信息,如:属性、方法‘构造函数等;

1.2、访问构造函数

  • Java提供了一下通过反射获取构造函数的方法:
  1. getConstructors():获取权限位Public的构造函数
  2. getConstructors(Class<?> … parameterTypes):获取权限为public,指定参数类型的构造函数
  3. getDeclaredConstructors():按照声明顺序返回所有构造函数
  4. getConstructors(Class<?> … parameterTypes):获取指定参数类型的构造函数
  • 针对Constructor常用的方法:
  1. isVarArgs():是否允许可变数量参数,返回boolean ,true表示有,false表示没有
  2. getParameterTypes():按照声明的顺序返回参数类型
  3. getExceptionTypes():返回抛出异常的类型
  4. newInstance():根据传入的参数调用构造函数创建实例
  5. setAccessible():默认不允许反射privite修饰的方法和属性,但如果先执行并设置为true,则可以反射
  6. getModifiers():获得可以解析出方法修饰符的整数
  • Modifier常用的解析方法

  1. isPublic(int mode):是否为Public修饰符
  2. isProtected(int mode):是否为Protected修饰符
  3. isPrivate(int mode):是否为Private修饰符
  4. isStatic(int mode):是否为Static修饰符
  5. isFinal(int mode):是否为Final修饰符
  6. toString(int mode):以字符串返回修饰符
  • 实例:
  1. 创建实例ConstructExample
public class ConstructorExample {

   int number;
   public String name;
   protected float aFloat;
   private ConstructorExample() {
   }

   protected ConstructorExample(int number) {
      this.number = number;
   }

   public ConstructorExample(String name, int number) throws NumberFormatException {
      this.number = number;
      this.name = name;
   }

   public void prinln() {
      System.out.println("name = " + name);
      System.out.println("number = " + number);
   }
}

   2、反射获取构造函数信息

public static void main(String[] args) {
ConstructorExample constructorExample = new ConstructorExample(10);
Class<? extends ConstructorExample> classExample = constructorExample.getClass();
Constructor[] constructors = classExample.getDeclaredConstructors(); // 获取所有构造函数
for (int i = 0; i < constructors.length; i++) {
   Constructor constructor = constructors[i]; //获取构造函数
   System.out.println("是否有可变参数");
   System.out.println(constructor.isVarArgs());
   System.out.println("构造函数的参数类型");
   Class[] classes = constructor.getParameterTypes(); // 获取参数类型
   System.out.println("构造函数的修饰类型");
   int modifier = constructor.getModifiers();
   if (modifier == Modifier.PRIVATE){ // 判断修饰符类型
      System.out.println("Private");
   }else if (modifier == Modifier.PROTECTED){
      System.out.println("Protected");
   }else if (modifier == Modifier.PUBLIC){
      System.out.println("Public");
   }
   for (Class c : classes) {
      System.out.println(c);
   }
   System.out.println("可能抛出的异常类型");
   Class[] classException = constructor.getExceptionTypes(); // 获取抛出异常类型
   for (Class c : classException) {
      System.out.println(c);
   }
   ConstructorExample example = null;
   while (example == null) {
      try {
         if (i == 2) {
            example = (ConstructorExample) constructor.newInstance();// 创建实例
         } else if (i == 1) {
            example = (ConstructorExample) constructor.newInstance(20);
         } else {
            example = (ConstructorExample) constructor.newInstance("ABC", 50);
         }
      } catch (Exception e) {
         System.out.println("私有构造函数抛出异常,设置Accessible为true");
         constructor.setAccessible(true); // 处理私有构造函数
      }
   }
   if (example != null) {
      example.prinln();  // 调用方法
      System.out.println();
   }
}

3、运行代码输出信息(依次为三个构造函数信息)

1.3、访问成员变量

  • Java提供反射获取变量方法
  1. getFields():获取所有权限为Public的属性
  2. getFields(String name):获取名称为name,修饰符Public的属性
  3. getDeclaredFields():按声明顺序返回所有属性
  4. getDeclaredFields(String name):获取指定成员变量
  • Fields的常用方法
  1. getName():获取成员变量的名称
  2. getType():获取成员变量的Class对象
  3. get(Object o):获取指定对象Object的成员变量
  4. set(Object,Value):指定Object对象的值为value
  5. getInt()/setInt():获取和设置Object中类型为Int的属性值
  6. getFloat()/setFloat():获取和设置Object中类型为Float的属性值
  7. getBoolean()/setBoolean():获取和设置Object中类型为Boolean的属性值
  8. setAccessible():默认不允许反射privite修饰的方法和属性,但如果先执行并设置为true,则可以反射
  9. getModifiers():获得可以解析出方法修饰符的整数
  • 实例
  1. 在上面的类中添加成员变量(Private 处理和构造函数一致,这里不做处理)
int number;
public String name;
protected float aFloat;

    2、反射获取成员变量信息

Field[] fields = classExample.getDeclaredFields();
for (Field f: fields) {
   System.out.println("属性名称 = "+f.getName());
   System.out.println("属性类型 = "+f.getType());
   try {
      System.out.println("属性默认值 = "+f.get(constructorExample));
      if (f.getType().equals(int.class)){
         f.set(constructorExample,5);
         System.out.println("属性值修改 = "+ f.get(constructorExample));
      }else if (f.getType().equals(String.class)) {
         f.set(constructorExample,"ABC");
         System.out.println("属性值修改 = "+f.get(constructorExample));
      }else {
         f.set(constructorExample,99.99f);
         System.out.println("属性值修改 = "+f.get(constructorExample));
      }
   } catch (Exception e) {
      e.printStackTrace();
   }
   System.out.println("-------------");
}

   3、实例输出信息

1.4 、访问方法

  • Java提供反射获取方法属性的方法:
  1. getMethod():获取所有为Public的方法
  2. getMethod(String name,Class<?> … parameterTypes):获取指定方法名和参数类型的public函数
  3. getDeclaredMethod():获取所有方法
  4. getDeclaredMethod(String name,Class<?> … parameterTypes):获取指定方法名和参数类型的函数
  • Method的常用方法
  1. getName():获取方法名
  2. getParameterType():按声明顺序返回参数类型
  3. getReturnType():获取方法的返回数据类型
  4. getExceptionTypes():返回抛出异常的类型
  5. invoke(Object o,Object…argbs):利用参数argbs执行对象Object中的该方法
  6. isVarArgs():是否允许可变数量参数,返回boolean ,true表示有,false表示没有
  7. getModifiers():获得可以解析出方法修饰符的整数
  • 实例
  1. 在上面类中添加几个简单方法
public void prinln() {
   System.out.println("name = " + name);
   System.out.println("number = " + number);
}

public int action(int i,int j){
   System.out.println("执行了action");
   System.out.println(i+j);
   return i+j;
}

protected void function() throws NullPointerException{
   System.out.println("执行了function");
}

    2、反射获取并调用方法

Method[] methods = classExample.getDeclaredMethods();
System.out.println("-------------");
for (Method method : methods){
   System.out.println("方法名:"+method.getName());
   System.out.println("返回值类型:"+method.getReturnType());
   System.out.println("方法抛出的异常类型为");
   Class[] classException = method.getExceptionTypes();
   for (Class c : classException) {
      System.out.println(c);
   }
   Class[] classes = method.getParameterTypes();
   System.out.println("方法的参数类型为");
   for (Class c:classes
       ) {
      System.out.println(c);
   }
      try {
         if (classes.length != 0) {
            method.invoke(constructorExample, 3, 7);  // 执行方法
         }else {
            method.invoke(constructorExample); // 执行方法
         }
      } catch (IllegalAccessException e) {
         e.printStackTrace();
      } catch (InvocationTargetException e) {
         e.printStackTrace();
      }
   System.out.println("-------------");
}

   3、输出信息

以上就是反射获取类的构造方法、属性和方法的过程,有了这三步就可以访问类中的所有信息,极大地提高了代码的灵活性;下面来介绍下注解,基本反射和注解是一个很好的搭档,加入我们要为某种行为创造框架,那么就可以在需要使用的地方,添加注解标记,然后用反射找到指定的类或方法属性,执行相应的过程,仔细想想框架是不是就是这样出来的呢?

二、注解

2.1、注解形式

public @interface Action {  // 无属性值
}

public @interface Action { // 有属性值 
   String action() default "A";
   int value() default  0;
}

2.2、注解的标记

  • @target:设置使用的元素种类,如果未设置就表示用于所有元素,使用ElementType设置
  • ElementType 枚举常量有:
  1. ANNOTATION_TYPE:表示用于Annotation的类型
  2. TYPE:作用与类、接口、枚举
  3. CONSTRUCTOR:作用于构造方法
  4. FIELD:作用于属性
  5. METHOD:作用于方法
  6. PARAMETER:作用于参数
  7. LOCAL_VARIABLE:表示局部变量
  8. PACKAGE:表示用于包
  • @Retention:设置注解的有效范围
  1. SOURCE:不编译到Annotation类的文件中
  2. CLASS:编译到Annotation的文件中,运行时不加在到JVM中
  3. RUNTING:运行时加载到JVM中,有效范围最大
  • CONSTRUCTOR、METHOD、FIELD都继承了AccessibleObject类,在AccessibleObject中定义了三个方法
  1. isAnnomationPresent(Class<? extends Annotation> class):返回boolean,表示是否有指定的注解
  2. getAnnotation(Class class):获取指定的注解
  3. getAnnotations():获取所有的注解

2.3、实例

  • 声明两个注解
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
   String action() default "构造函数";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
   String describe() default "描述";
   Class type() default void.class;
}

注解@Action 使用于构造函数,有一个描述属性,@FieldAnnotation适用于注解属性常量,包含一个String类型的描述和修饰的类型

  • 使用注解
public class ConstructorExample {

   @FieldAnnotation(describe = "名字" ,type = String.class)
   public String name;
   @FieldAnnotation(describe = "分数",type = int.class)
   protected int core;

   @Action()
   public ConstructorExample(String name, int core) throws NumberFormatException {
      this.core = core;
      this.name = name;
   }
}
  • 反射获取
public class ReflectConstructor {

   public static void main(String[] args) {
      ConstructorExample constructorExample = new ConstructorExample("ABC",10);
      Class<? extends ConstructorExample> classExample = constructorExample.getClass();
      Constructor[] constructors = classExample.getDeclaredConstructors();
      for (int i = 0; i < constructors.length; i++) {
         Constructor constructor = constructors[i];
         if (constructor.isAnnotationPresent(Action.class)){//是否包含Action.class注解
            System.out.println("包含Action注解");
            Action action = (Action) constructor.getAnnotation(Action.class);
            System.out.println("注解值为 :"+action.action());
         }
         System.out.println("-------------");
      }

      Field[] fields = classExample.getDeclaredFields();
      for (Field f: fields) {
         System.out.println("属性名称 = "+f.getName());
         System.out.println("属性类型 = "+f.getType());
         try {
            if (f.isAnnotationPresent(FieldAnnotation.class)) {
               System.out.println("使用FieldAnnotation注解");
               FieldAnnotation annotation = f.getAnnotation(FieldAnnotation.class);
               System.out.println("FieldAnnotation注解参数:");
               System.out.println("Describe:"+annotation.describe());
               System.out.println("Type:"+annotation.type());
            }

         } catch (Exception e) {
            e.printStackTrace();
         }
         System.out.println("-------------");
      }
   }
}
  • 输出信息

反射和注解的介绍,以及二者的使用介绍完毕,在平时的使用过程中也基本就是这样使用的,只不过功能逻辑的实现复杂程度不同,后面会有一篇关于注解自动生成代码的文章,也是框架中注解的常用实例。

猜你喜欢

转载自blog.csdn.net/Alexwll/article/details/82842460