一文搞懂Java中的反射机制

  前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了。最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好。

  好了,言归正传。

  反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车)。先举一个小栗子,大家随意感受一下:

public void testA(){
    String name = "java.lang.String";
    try{
      Class cl = Class.forName(name);
      Class supercl = cl.getSuperclass();
      String modifiers = Modifier.toString(cl.getModifiers());
      if (modifiers.length() > 0){
        System.out.print(modifiers + " ");
      }
      System.out.print(name);
      if (supercl != null && supercl != Object.class){
        System.out.print(" extents " + supercl.getName());
      }
      System.out.print("{\n");
      printFields(cl);
      System.out.println();
      printConstructors(cl);
      System.out.println();
      printMethods(cl);
      System.out.println("}");
    }catch (ClassNotFoundException e){
      e.printStackTrace();
    }
    System.exit(0);
  }
  private static void printConstructors(Class cl){
    Constructor[] constructors = cl.getDeclaredConstructors();

    for (Constructor c : constructors){
      String name = c.getName();
      System.out.print(" ");
      String modifiers = Modifier.toString(c.getModifiers());
      if (modifiers.length() > 0){
        System.out.print(modifiers + " ");
      }
      System.out.print(name + "(");

      Class[] paraTypes = c.getParameterTypes();
      for (int j = 0; j < paraTypes.length; j++){
        if (j > 0){
          System.out.print(", ");
        }
        System.out.print(paraTypes[j].getSimpleName());
      }
      System.out.println(");");
    }
  }

  private static void printMethods(Class cl){
    Method[] methods = cl.getDeclaredMethods();
    for (Method m : methods){
      Class retType = m.getReturnType();
      String name = m.getName();

      System.out.print(" ");
      String modifiers = Modifier.toString(m.getModifiers());
      if (modifiers.length() > 0){
        System.out.print(modifiers + " ");
      }
      System.out.print(retType.getSimpleName() + " " + name +"(");
      Class[] paramTypes = m.getParameterTypes();
      for(int j = 0; j < paramTypes.length; j++){
        if (j > 0){
          System.out.print(", ");
        }
        System.out.print(paramTypes[j].getName());
      }
      System.out.println(");");
    }
  }

  private static void printFields(Class cl){
    Field[] fields = cl.getFields();
    for (Field f : fields){
      Class type = f.getType();
      String name = f.getName();
      System.out.print(" ");
      String modifiers = Modifier.toString(f.getModifiers());
      if (modifiers.length() > 0){
        System.out.print(modifiers + " ");
      }
      System.out.println(type.getSimpleName() + " " + name +";");
    }
  }

  调用testA方法输出如下:

public final java.lang.String{
 public static final Comparator CASE_INSENSITIVE_ORDER;

 public java.lang.String(byte[], int, int);
 public java.lang.String(byte[], Charset);
 public java.lang.String(byte[], String);
 public java.lang.String(byte[], int, int, Charset);
 public java.lang.String(byte[], int, int, String);
 java.lang.String(char[], boolean);
 public java.lang.String(StringBuilder);
 public java.lang.String(StringBuffer);
 public java.lang.String(byte[]);
 public java.lang.String(int[], int, int);
 public java.lang.String();
 public java.lang.String(char[]);
 public java.lang.String(String);
 public java.lang.String(char[], int, int);
 public java.lang.String(byte[], int);
 public java.lang.String(byte[], int, int, int);

 public boolean equals(java.lang.Object);
 public String toString();
 public int hashCode();
 public int compareTo(java.lang.String);
 public volatile int compareTo(java.lang.Object);
 public int indexOf(java.lang.String, int);
 public int indexOf(java.lang.String);
 public int indexOf(int, int);
 public int indexOf(int);
 static int indexOf([C, int, int, [C, int, int, int);
 static int indexOf([C, int, int, java.lang.String, int);
 public static String valueOf(int);
 public static String valueOf(long);
 public static String valueOf(float);
 public static String valueOf(boolean);
 public static String valueOf([C);
 public static String valueOf([C, int, int);
 public static String valueOf(java.lang.Object);
 public static String valueOf(char);
 public static String valueOf(double);
 public char charAt(int);
 private static void checkBounds([B, int, int);
 public int codePointAt(int);
 public int codePointBefore(int);
 public int codePointCount(int, int);
 public int compareToIgnoreCase(java.lang.String);
 public String concat(java.lang.String);
 public boolean contains(java.lang.CharSequence);
 public boolean contentEquals(java.lang.CharSequence);
 public boolean contentEquals(java.lang.StringBuffer);
 public static String copyValueOf([C);
 public static String copyValueOf([C, int, int);
 public boolean endsWith(java.lang.String);
 public boolean equalsIgnoreCase(java.lang.String);
 public static transient String format(java.util.Locale, java.lang.String, [Ljava.lang.Object;);
 public static transient String format(java.lang.String, [Ljava.lang.Object;);
 public void getBytes(int, int, [B, int);
 public byte[] getBytes(java.nio.charset.Charset);
 public byte[] getBytes(java.lang.String);
 public byte[] getBytes();
 public void getChars(int, int, [C, int);
 void getChars([C, int);
 private int indexOfSupplementary(int, int);
 public native String intern();
 public boolean isEmpty();
 public static transient String join(java.lang.CharSequence, [Ljava.lang.CharSequence;);
 public static String join(java.lang.CharSequence, java.lang.Iterable);
 public int lastIndexOf(int);
 public int lastIndexOf(java.lang.String);
 static int lastIndexOf([C, int, int, java.lang.String, int);
 public int lastIndexOf(java.lang.String, int);
 public int lastIndexOf(int, int);
 static int lastIndexOf([C, int, int, [C, int, int, int);
 private int lastIndexOfSupplementary(int, int);
 public int length();
 public boolean matches(java.lang.String);
 private boolean nonSyncContentEquals(java.lang.AbstractStringBuilder);
 public int offsetByCodePoints(int, int);
 public boolean regionMatches(int, java.lang.String, int, int);
 public boolean regionMatches(boolean, int, java.lang.String, int, int);
 public String replace(char, char);
 public String replace(java.lang.CharSequence, java.lang.CharSequence);
 public String replaceAll(java.lang.String, java.lang.String);
 public String replaceFirst(java.lang.String, java.lang.String);
 public String[] split(java.lang.String);
 public String[] split(java.lang.String, int);
 public boolean startsWith(java.lang.String, int);
 public boolean startsWith(java.lang.String);
 public CharSequence subSequence(int, int);
 public String substring(int);
 public String substring(int, int);
 public char[] toCharArray();
 public String toLowerCase(java.util.Locale);
 public String toLowerCase();
 public String toUpperCase();
 public String toUpperCase(java.util.Locale);
 public String trim();
}

  这里把String类型的所有方法和变量都获取到了,使用的仅仅是String类型的全名。当然,反射的功能不仅仅是获取类的信息,还可以在运行时动态创建对象,回想一下,我们正常的对象使用,都是需要在代码中先声明,然后才能使用它,但是使用反射后,就能在运行期间动态创建对象并调用其中的方法,甚至还能直接查看类的私有成员变量,还能获取类的注解信息,在泛型中类型判断时也经常会用到。反射可以说完全打破了类的封装性,把类的信息全部暴露了出来。

  上面的代码看不太明白也没关系,只要稍微感受一下反射的能力就好了。介绍完了反射能做的事情,本篇教程就不再写一些玩具代码了,这次以一个实用型的代码为媒介来介绍反射。

  在开发中,经常会遇到两个不同类对象之间的复制,把一个类中的字段信息get取出来,然后set到另一个类中,大部分情况下,两个类对应的字段是一样,每次这样使用是很麻烦的,那么利用反射就可以实现一个封装,只需要调用一个方法即可实现简单的类字段复制。

  那么,先来想想,要复制一个类对象的所有字段信息到另一个类对象中,首先,怎么获取一个类的某个字段的值呢?我们先来编写一个方法:

  /**
   * 获取对象的指定字段的值
   * @param obj 目标对象
   * @param fieldName 目标字段
   * @return 返回字段值
   * @throws Exception 可能抛出异常
   */
  private static Object getFieldValue(Object obj, String fieldName) throws Exception{
    //获取类型信息
    Class clazz = obj.getClass();
    //取对应的字段信息
    Field field = clazz.getDeclaredField(fieldName);
    //设置可访问权限
    field.setAccessible(true);
    //取字段值
    Object value = field.get(obj);
    return value;
  }

  这里使用了两个之前没有说过的类,一个是Class,是不是很眼熟,想一想,我们每次定义一个类的时候是不是都要用到它,哈哈,那你就想错了,那是class关键词,java是大小写的敏感的,这里的Class是一个类名,那这个类是干嘛用的呢?

  虚拟机在加载每一个类的时候,会自动生成一个对应的Class类来保存该类的信息,可以理解为Class类是那个类的代理类,是连接实际类与类加载器的桥梁,可以通过它来获取虚拟机的类加载器引用,从而实现更多的骚操作。Class类是一个泛型类,每个类都有对应的一个Class类,比如String对应的Class类就是Class<String>。

  Class有很多方法来获取更多关于类的信息,这里使用getDeclaredField方法来获取指定字段信息,返回的是Field类型对象,这个对象里存储着关于字段的一些信息,如字段名称,字段类型,字段修饰符,字段可访问性等,setAccessible方法可以设置字段的可访问性质,这样就能直接访问private修饰的字段了,然后使用get方法来获取指定对象的对应字段的值。

  我们来测试一下:

  public void testB(){
    try{
      Employee employee = new Employee();
      employee.setName("Frank");
      employee.setSalary(6666.66);
      System.out.println((String)getFieldValue(employee,"name"));
      System.out.println((double)getFieldValue(employee,"salary"));
    }catch (Exception e){
      e.printStackTrace();
    }
  }

  输出如下:

Frank
6666.66

  接下来,我们需要获取类中所有字段,然后在另一个类中查找是否有对应字段,如果有的话就设置字段的值到相应的字段中。

  /**
   * 复制一个类对象属性到另一个类对象中
   * @param objA 需要复制的对象
   * @param objB 复制到的目标对象类型
   * @return 返回复制后的目标对象
   */
  private static void parseObj(Object objA,Object objB) throws Exception{
    if (objA == null){
      return;
    }
    //获取objA的类信息
    Class classA = objA.getClass();
    Class classB = objB.getClass();
    try {
      //获取objA的所有字段
      Field[] fieldsA = classA.getDeclaredFields();
      //获取objB的所有字段
      Field[] fieldsB = classB.getDeclaredFields();
      if (fieldsA == null || fieldsA.length <= 0 || fieldsB == null || fieldsB.length <= 0){
        return;
      }

      //生成查询map
      Map<String,Field> fieldMap = new HashMap<>();
      for (Field field:fieldsA){
        fieldMap.put(field.getName(),field);
      }

      //开始复制字段信息
      for (Field fieldB : fieldsB){
        //查找是否在objB的字段中存在该字段
        Field fielaA = fieldMap.get(fieldB.getName());
        if (fielaA != null){
          fieldB.setAccessible(true);
          fieldB.set(objB,getFieldValue(objA,fielaA.getName()));
        }
      }
    } catch (IllegalStateException e) {
      throw new IllegalStateException("instace fail: " ,e);
    }
  }

  这里获取到classA和classB的所有字段之后,先生成了一个map用于查找,可以减少遍历次数,然后之后只需要遍历一次就可以判断相应字段是否存在,如果存在则取出对应值设置到相应的字段里去。

  接下来测试一下:

  public void testB(){
    try{
      //生成Employee对象
      Employee employee = new Employee("Frank",6666.66);
      //生成一个Manager对象
      Manager manager = new Manager();
      //复制对象
      parseObj(employee,manager);
      System.out.println(manager.getName());
      System.out.println(manager.getSalary());
    }catch (Exception e){
      e.printStackTrace();
    }
  }
public class Employee {
  private String name;
  private Double salary;

  public Employee(String name, Double salary) {
    this.name = name;
    this.salary = salary;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Double getSalary() {
    return salary;
  }

  public void setSalary(Double salary) {
    this.salary = salary;
  }
}
public class Manager {
  private String name;
  private Double salary;
  private Double bonus;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Double getSalary() {
    return salary;
  }

  public void setSalary(Double salary) {
    this.salary = salary;
  }

  public Double getBonus() {
    return bonus;
  }

  public void setBonus(Double bonus) {
    this.bonus = bonus;
  }
}

  输出如下:

Frank
6666.66

  完美,这样我们就利用了反射机制完美的把相同的字段在不同类的对象之间进行了复制,这里仅仅是两个字段,所以可能好处不明显,但事实上,实际开发中,经常会有将BO转换为VO的操作,这时候,这个操作就很有必要了,简单的一行命令就可以代替一大堆的get和set操作。

  当然,使用反射机制固然高端大气上档次,但是也是一把双刃剑,使用不当很可能会带来严重后果,而且使用反射的话,会占用更多资源,运行效率也会降低,上述工具类是用运行效率换开发效率。开发中不建议大量使用,还是那句话,技术只是手段,需要使用的时候再使用,不要为了使用而使用。

  至于反射中的其他方法和姿势,大家尽可以慢慢去摸索,这里仅仅是抛砖引玉。

  至此,本篇讲解完毕,欢迎大家继续关注。

以上就是一文搞懂Java中的反射机制的详细内容,更多关于Java反射机制的资料请关注我们其它相关文章!

(0)

相关推荐

  • java使用反射创建并操作对象的方法

    Class 对象可以获得该类里的方法(由 Method 对象表示).构造器(由 Constructor 对象表示).成员变量(由 Field 对象表示),这三个类都位于 java.lang.reflect 包下,并实现了 java.lang.reflect.Member 接口.程序可以通过对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,能通过 Field 对象直接访问并修改对象的成员变量值. 创建对象 通过反射来生成对象需要先使用 Class 对象获取指定的

  • 全面了解Java反射机制

    什么是反射 反射 (Reflection) 是Java的特征之一,它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性. 通俗的来讲就是:通过反射机制,可以在运行时获得程序或程序集中每一个类型的成员和成员的信息. 注意这里的重点是:运行时,而不是编译时.我们常规情况下写的对象类型都是在编译期就确定下来的.而Java反射机制可以动态地创建对象并调用其属性,这样创建对象的方式便异常灵活了. 虽然通过反射可以动态的创建对象,增加了灵活性,但也不是什么地方都可用,还要考虑性能.编码量

  • Java基础之反射原理与用法详解

    本文实例讲述了Java基础之反射原理与用法.分享给大家供大家参考,具体如下: 1.什么是反射? 反射其实就是动态的加载类,我们在写JDBC的时候加载驱动Class.forName("xxx")时就涉及到了反射. 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 2.反射机制能做什么? 1. 在运行时判断任意一个对象的所属的类Class.

  • 详解JAVA 反射机制

    什么是反射? 反射机制是在程序运行状态中,对于任意一个类,都能够获取这个类的所有属性和方法: 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制. 反射的作用 1.可以实现简单的反编译,获取类中的属性和方法等基本信息,.class->java 2.通过反射机制获取类的属性.方法等 在使用eclipse时,通过对象引用.的方式,eclipse就会将这个对象中的所有属性和方法展示出来,这个就是利用的反射机制.其实反射应用最多的

  • Java通过反射将 Excel 解析成对象集合实例

    1.这是一个通过Java反射机制解析的工具类 2.使用时只需创建对应的对象,并在Excel的第一行填上对应的属性名 3.首先要添加相关的jar包: poi-3.8.jar poi-ooxml-3.9.jar poi-ooxml-schemas-3.9.jar xmlbeans-2.6.0.jar 4.看一下Excel的内容: 5.创建对应的实体类: package com.office.user.dto; public class UserDTO { private String idUser;

  • 深入分析JAVA 反射和泛型

    从 JDK5 以后,Java 的 Class 类增加了泛型功能,从而允许使用泛型来限制 Class 类,例如,String.class 的类型实际上是 Class<String>.如果 Class 对应的类暂时未知,则使用 Class<?>.通过在反射中使用泛型,可以避兔使用反射生成的对象需要强制类型转换. 泛型和 Class 类 使用 Class<T> 泛型可以避免强制类型转换.例如,下面提供一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例. public

  • Java反射框架Reflections示例详解

    MAVEN 坐标 <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency> Reflections 的作用 Reflections通过扫描classpath,索引元数据,并且允许在运行时查询这些元数据. 获取某个类型的所有

  • 一文搞懂Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好. 好了,言归正传. 反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车).先举一个小栗子

  • 一文搞懂Java中的注解和反射

    目录 1.注解(Annotation) 1.1 什么是注解(Annotation) 1.2 内置注解 1.3 元注解(meta-annotation) 1.4 自定义注解 2.反射(Reflection) 2.1 反射和反射机制 2.2 Class类的获取方式和常用方法 2.3 反射的使用 1.注解(Annotation) 1.1 什么是注解(Annotation) 注解不是程序本身,可以在程序编译.类加载和运行时被读取,并执行相应的处理.注解的格式为"@注释名(参数值)",可以附加在

  • 一文搞懂Java中的序列化与反序列化

    目录 序列化和反序列化的概念 应用场景 序列化实现的方式 继承Serializable接口,普通序列化 继承Externalizable接口,强制自定义序列化 serialVersionUID的作用 静态变量不会被序列化 使用序列化实现深拷贝 常见序列化协议对比 小结 序列化和反序列化的概念 当我们在Java中创建对象的时候,对象会一直存在,直到程序终止时.但有时候可能存在一种"持久化"场景:我们需要让对象能够在程序不运行的情况下,仍能存在并保存其信息.当程序再次运行时 还可以通过该对

  • 一文搞懂Java中对象池的实现

    目录 1. 什么是对象池 2. 为什么需要对象池 3. 对象池的实现 4. 开源的对象池工具 5. JedisPool 对象池实现分析 6. 对象池总结 最近在分析一个应用中的某个接口的耗时情况时,发现一个看起来极其普通的对象创建操作,竟然每次需要消耗 8ms 左右时间,分析后发现这个对象可以通过对象池模式进行优化,优化后此步耗时仅有 0.01ms,这篇文章介绍对象池相关知识. 1. 什么是对象池 池化并不是什么新鲜的技术,它更像一种软件设计模式,主要功能是缓存一组已经初始化的对象,以供随时可以

  • 一文搞懂Java中的日期类

    目录 一.日期类 1.1 第一代日期类 1.2 第二代日期类Calendar 1.3 第三代日期类 一.日期类 在程序的开发中我们经常会遇到日期类型的操作,Java对日期类型的操作提供了很好的支持.在最初的版本下,java.lang包中的System.currentTimeMillis();可以获取当前时间与协调时间(UTC)1970年1月1日午夜之间的时间差(以毫秒为单位测量).我们往往通过调用该方法计算某段代码的耗时. public class TestTime { public stati

  • 一文搞懂Java中的抽象类和接口到底是什么

    目录 什么是抽象类 抽象类在实现多态中的意义 接口是什么 通过接口实现多态

  • 一文搞懂Spring中的注解与反射

    目录 前言 一.内置(常用)注解 1.1@Overrode 1.2@RequestMapping 1.3@RequestBody 1.4@GetMapping 1.5@PathVariable 1.6@RequestParam 1.7@ComponentScan 1.8@Component 1.9@Service 1.10@Repository 二.元注解 @Target @Retention @Documented @Inherited 三.自定义注解 四.反射机制概述 4.1动态语言与静态语

  • 一文搞懂Java项目中枚举的定义与使用

    目录 什么是枚举 为什么需要枚举类 枚举类的定义和使用 什么是枚举 最近写新项目!有很多数据字典常量需要定义和使用.就顺便记录一下.什么是枚举类呢?就是用enum修饰是一种Java特殊的类,枚举是class.底层是继承了java.lang.Enum类的实体类.使用枚举可以很方便的定义数据常量.方便清晰我们使用 为什么需要枚举类 下面就举例说明一下吧 1)出于类型安全考虑,没用枚举类之前,常用静态常量来表示. 比如对于性别的表示: public static final int WOMAN = 0

  • 一文带你搞懂Java中的泛型和通配符

    目录 概述 泛型介绍和使用 泛型类 泛型方法 类型变量的限定 通配符使用 无边界通配符 通配符上界 通配符下界 概述 泛型机制在项目中一直都在使用,比如在集合中ArrayList<String, String>, Map<String,String>等,不仅如此,很多源码中都用到了泛型机制,所以深入学习了解泛型相关机制对于源码阅读以及自己代码编写有很大的帮助.但是里面很多的机制和特性一直没有明白,特别是通配符这块,对于通配符上界.下界每次用每次百度,经常忘记,这次我就做一个总结,加

  • 一文带你搞懂Java中Get和Post的使用

    目录 1 Get请求数据 1.1 Controller 1.2 Service 1.3 Application 1.4 Postman 2 Post接收数据 2.1 Controller 2.2 Service 2.3 Application 2.4 Postman 3 Post发送数据 3.1 Controller 3.2 Service 3.3 ResponseResult 3.4 Config 3.5 Application 3.6 Postman 1 Get请求数据 项目地址:https

随机推荐