一、什么是反射

java.lang包供给java言语程序设计的基础类,在lang包下存在一个子包:reflect,与反射相关的APIs均在此处;

官方对reflect包的介绍如下:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

java.lang.reflect官方介绍

简略来说,反射机制就像类对照安静的湖面,湖面映射出类的字段、办法、结构函数等信息;反射机制不只能够看到类信息,还能针对字段、办法等做出相应的操作。

二、预备测验:实体类的创立

为便利解说阐明,首要创立一个实体类,用于测验运用

package cn.byuan.entity;
/**
 * 学生实体类
 *
 * @author byuan
 * @date 2022-01-25
 */
public class Student {
    /**
     * 学生学号, 公共变量, 默认值: defaultStudentNo
     * */
    public String studentNo = "defaultStudentNo";
    /**
     * 学生名字, 公共变量, 默认值: defaultStudentName
     * */
    public String studentName = "defaultStudentName";
    /**
     * 学生性别, 私有变量, 默认值: defaultStudentSex
     * */
    private String studentSex = "defaultStudentSex";
    /**
     * 学生年纪, 私有变量, 默认值: 0
     * */
    private Integer studentAge = 0;
    /**
     * 公有无参结构办法
     * */
    public Student() {
    }
    /**
     * 公有满参结构办法
     * */
    public Student(String studentNo, String studentName, String studentSex, Integer studentAge) {
        this.studentNo = studentNo;
        this.studentName = studentName;
        this.studentSex = studentSex;
        this.studentAge = studentAge;
    }
    /**
     * 私有结构办法
     * */
    private Student(String studentSex, Integer studentAge) {
        this.studentSex = studentSex;
        this.studentAge = studentAge;
    }
    /**
     * get/set 办法
     * */
    public String getStudentNo() {
        return studentNo;
    }
    public Student setStudentNo(String studentNo) {
        this.studentNo = studentNo;
        return this;
    }
    public String getStudentName() {
        return studentName;
    }
    public Student setStudentName(String studentName) {
        this.studentName = studentName;
        return this;
    }
    public String getStudentSex() {
        return studentSex;
    }
    public Student setStudentSex(String studentSex) {
        this.studentSex = studentSex;
        return this;
    }
    public Integer getStudentAge() {
        return studentAge;
    }
    public Student setStudentAge(Integer studentAge) {
        this.studentAge = studentAge;
        return this;
    }
    @Override
    public String toString() {
        return "Student{" +
                "studentNo = " + this.studentNo + ", " +
                "studentName = " + this.studentName + ", " +
                "studentSex = " + this.studentSex + ", " +
                "studentAge = " + this.studentAge +"}";
    }
    /**
     * 学生类说话办法
     * */
    private String speak(String message) {
        return this.studentName + " : " + message;
    }
}

三、反射中的几个重要类及办法

在了解反射前,先来梳理下一个类(Class)自身中包含了哪些内容。

  1. 字段,字段又由修饰符、字段类型、字段称号、对应值等部分组成
  2. 结构办法,结构办法可简略分为无参加有参两大类
  3. 非结构办法,又称一般办法,办法由修饰符、返回值类型、办法名、形参表、办法体等部分构成

本文所论述的反射中几个重要类及其对应办法正基于以上内容

(一)反射中的重要类之Class

Class类代表了类自身,类自身包含字段,结构办法,非结构办法等内容,因此运用反射的第一步便是获取方针所对应的Class类。

仅就运用反射而言,咱们需侧重了解Class类的获取办法,下面给出实例

1. Class类测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
/**
 * 获取 Class 的几种办法
 *
 * @author byuan
 * @date 2022-01-25
 */
public class GetClassExample {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取 class 办法一: 经过类的全途径字符串获取 Class 方针
        Class getClassExample1 = Class.forName("cn.byuan.entity.Student");
        // 获取 class 办法二: 经过类名直接获取
        Class getClassExample2 = Student.class;
        // 获取 class 办法三: 经过已创立的方针获取对应 Class
        Student student1 = new Student();
        Class getClassExample3 = student1.getClass();
    }
}

(二)反射中的重要类之Field

Field类是对类中特点的描述,凭借Class与Field的合作,咱们能够了解类中有哪些字段,并做出相应处理;

1. Field类的获取与常用办法

Class类为咱们供给了两个办法用以获取Field类:

  1. getDeclaredFields() :获取一切声明的字段(包含公有字段和私有字段)
  2. getFields() :仅可获取公有字段

Field类代表了类中的特点字段,常用类中特点字段可抽象分为两种,公有字段(public)与私有字段(private);

每个字段又具有四个特点:修饰符,字段类型,字段称号,对应值;Field也自然供给了对应办法对这四种特点进行获取:

  1. getModifiers() :获取字段修饰符相加值,想要获取清晰标识需求经过Modifier常量的toString办法对相加值进行解码
  2. getType() :获取字段类型
  3. getName() :获取字段称号
  4. get(Object) :获取字段对应值

下面给出实例

2. Field类测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
 * 获取类字段的几种办法
 *
 * @author byuan
 * @date 2021-01-25
 */
public class GetFieldExample {
    public static void main(String[] args) throws IllegalAccessException {
        Class studentClass = Student.class;
        // 获取类字段的两个办法: getDeclaredFields, getFields
        // 1. getDeclaredFields: 获取一切声明的字段(包含公有字段和私有字段)
        Field[] declaredFieldArray = studentClass.getDeclaredFields();
        printFieldInformation(declaredFieldArray);
        // 2. getFields: 仅可获取公有字段
        Field[] fieldArray = studentClass.getFields();
        printFieldInformation(fieldArray);
        // 获取字段对应值
        Student student = new Student()
                .setStudentSex("女")
                .setStudentAge(18);
        printFieldValue(student);
    }
    /**
     * 打印类字段信息
     *
     * @param fieldArray 类字段方针列表
     * */
    public static void printFieldInformation(Field[] fieldArray) {
        for (Field fieldPart : fieldArray) {
            System.out.println("直接打印类字段方针: " + fieldPart);
            // 获取字段修饰符
            String fieldModifier = Modifier.toString(fieldPart.getModifiers());
            // 获取字段类型
            String fieldType = fieldPart.getType().getName();
            // 获取字段称号
            String fieldName = fieldPart.getName();
            System.out.println(fieldModifier + " " + fieldType + " " + fieldName);
        }
        System.out.println();
    }
    /**
     * 打印类字段特点
     *
     * @param t 泛型方针
     * */
    private static <T> void printFieldValue(T t) throws IllegalAccessException {
        Field[] fieldValueArray = t
                .getClass()
                .getDeclaredFields();
        for (Field fieldValuePart : fieldValueArray) {
            // 关于有可能存在的 private 字段撤销言语拜访查看
            fieldValuePart.setAccessible(true);
            // 字段称号
            String filedName = fieldValuePart.getName();
            // 字段对应值
            String fieldValue = fieldValuePart.get(t).toString();
            System.out.println(filedName + " = " + fieldValue);
        }
    }
}

运转截图

我都忘记了

(三)反射中的重要类之Constructor

Constructor代表了某个类的结构办法,是用来管理一切的结构函数的类

1. Constructor类的获取与常用办法

Class类为咱们供给了两个办法用以获取Constructor类:

  1. getDeclaredConstructors() :获取一切结构办法
  2. getConstructors() :仅可获取公有结构办法

关于结构器,可简略将其分为两大类,无参结构器与带参结构器,结构器也是办法的一种,因此关于恣意一结构器都由修饰符,办法名,形参表,办法体四部分组成;Constructor自然也为其组成部分供给了对应办法:

  1. 与字段类似,Constructors相同供给了getModifiers() 办法:获取结构器修饰符相加值,想要获取清晰标识需求经过Modifier常量的toString办法进行解码
  2. getName() :获取结构器称号
  3. getParameterTypes() :获取结构器参数列表

下面给出实例

2. Constructor类测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
/**
 * 获取结构办法的几种办法
 *
 * @author byuan
 * @date 2022-01-25
 */
public class GetConstructorsExample {
    public static void main(String[] args) {
        Class studentClass = Student.class;
        // 获取类结构器的两个办法: getDeclaredConstructors, getConstructors
        // 1. getDeclaredConstructors: 获取一切结构办法
        Constructor[] declaredConstructorArray = studentClass.getDeclaredConstructors();
        printConstructorInformation(declaredConstructorArray);
        // 2. getConstructors, 仅可获取公有结构办法
        Constructor[] constructorArray = studentClass.getConstructors();
        printConstructorInformation(constructorArray);
    }
    /**
     * 打印结构器信息
     *
     * @param constructorArray 结构器方针列表
     * */
    public static void printConstructorInformation(Constructor[] constructorArray) {
        for (Constructor constructorPart : constructorArray) {
            System.out.println("直接打印结构器方针: " + constructorPart);
            // 获取结构器修饰符
            String constructorModifier = Modifier.toString(constructorPart.getModifiers());
            // 获取结构器称号
            String constructorName = constructorPart.getName();
            // 获取结构器参数列表
            Class[] constructorParameterArray = constructorPart.getParameterTypes();
            // 打印结构器参数列表
            System.out.print(constructorModifier + " " + constructorName + "(");
            for (Class constructorParameterPart : constructorParameterArray) {
                System.out.print(constructorParameterPart.getName() + " ");
            }
            System.out.println(")");
        }
        System.out.println();
    }
}

运转截图

我都忘记了

3. 运用Constructor类实例化方针

一般来说,咱们关心的不只是获取到方针结构器的详细信息,更重要的是怎么运用反射实例化方针,针对方针实例化,Constructor供给了两种类型的办法:

  1. getConstructor(Class<?>… parameterTypes) :获取指定形参表的结构办法,可凭借其获取无参/指定参数的结构办法;

  2. newInstance(Object … initargs) :经过形参表传递实例化方针参数,与getConstructor合作运用;

    注意:Class也存在newInstance()办法,但仅能用来调用类的无参结构器

4. Constructor实例化方针测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
 * 结构器调用实例
 *
 * @author byuan
 * @date 2022-01-26
 */
public class ConstructorsInvokeExample {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class studentClass = Student.class;
        // Class 类的 newInstance 办法
        studentClass.newInstance();
        // 1. 调用公有无参结构器
        Object object1 = studentClass
                .getConstructor()
                .newInstance();
        System.out.println(object1.getClass());
        // 2. 调用公有满参结构器
        Constructor studentConstructorFull = studentClass
                .getConstructor(String.class, String.class, String.class, Integer.class);
        Object object2 = studentConstructorFull
                .newInstance("2022001", "赵一", "男", 18);
        System.out.println(object2);
        // 3. 调用私有结构器
        Constructor studentConstructorPrivate = studentClass
                .getDeclaredConstructor(String.class, Integer.class);
        // 私有结构器需将 accessible 设置为 true, 撤销言语拜访查看
        studentConstructorPrivate.setAccessible(true);
        Object object3 = studentConstructorPrivate
                .newInstance("女", 19);
        System.out.println(object3);
    }
}

运转截图

我都忘记了

(四)反射中的重要类之Method

Method类描述了类的办法信息

1. Method类的获取与常用办法

Class类为咱们供给了两个办法用以获取Method类:

  1. getDeclaredMethods() :获取一切非结构办法
  2. getMethods() :仅可获取公有非结构办法

关于恣意办法都可分为修饰符,办法名,形参表,办法体四部分;Method为其组成部分供给了对应办法:

  1. getModifiers() :获取办法修饰符相加值,想要获取清晰标识需求经过Modifier常量的toString办法进行解码
  2. getName() :获取办法称号
  3. getParameterTypes() :获取办法形参表

2. Method类测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
 * 获取非结构办法的几种办法
 *
 * @author byuan
 * @date 2022-01-26
 */
public class GetMethodExample {
    public static void main(String[] args) {
        Class studentClass = Student.class;
        // 获取非结构办法的两个办法: getDeclaredMethods, getMethods
        // 1. getDeclaredMethods: 获取一切非结构办法
        Method[] declaredMethodArray = studentClass.getDeclaredMethods();
        printMethodInformation(declaredMethodArray);
        // 2. getMethods, 仅可获取公有非结构办法
        Method[] methodArray = studentClass.getMethods();
        printMethodInformation(methodArray);
    }
    /**
     * 打印非结构器办法信息
     *
     * @param methodArray 结构器方针列表
     * */
    public static void printMethodInformation(Method[] methodArray) {
        for (Method methodPart : methodArray) {
            System.out.println("直接打印非结构办法方针: " + methodArray);
            // 获取非结构器办法修饰符
            String methodModifier = Modifier.toString(methodPart.getModifiers());
            // 获取非结构器办法称号
            String methodName = methodPart.getName();
            String methodReturnType = methodPart.getReturnType().getName();
            // 获取非结构办法参数列表
            Class[] constructorParameterArray = methodPart.getParameterTypes();
            // 打印非结构办法参数列表
            System.out.print(methodModifier + " " + methodReturnType + " " + methodName + "(");
            for (Class methodParameterPart : constructorParameterArray) {
                System.out.print(methodParameterPart.getName() + " ");
            }
            System.out.println(")");
        }
        System.out.println();
    }
}

运转截图

我都忘记了

3. 运用Method调用非结构办法

与运用Constructor实例化方针类似,Method相同需求两个办法用以调用非结构办法

  1. getDeclaredMethod(办法名, 形参表数组) : 获取一切结构办法
  2. invoke(方针实例, 参数数组) :办法调用

4. Method调用非结构办法测验实例

package cn.byuan.example;
import cn.byuan.entity.Student;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * 非结构器办法调用实例
 *
 * @author byuan
 * @date 2022-01-26
 */
public class MethodInvokeExample {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class studentClass = Student.class;
        // 关于私有的非结构办法, 需求运用 getDeclaredMethod 进行获取
        // getDeclaredMethod(办法名, 形参表数组)
        Method studentSpeakMethod = studentClass.getDeclaredMethod("speak", new Class[]{String.class});
        // 撤销言语拜访查看
        studentSpeakMethod.setAccessible(true);
        // invoke(方针实例, 参数数组)
        Object object = studentSpeakMethod.invoke(studentClass.newInstance(), "Hello, world");
        System.out.println(object);
    }
}

运转截图

我都忘记了

四、综合实战:运用反射机制编写方针复制东西类

在实际项目中,咱们经常会遇到POJO与VO等类型方针进行彼此转化的情况,如果直接进行转化则会运用大量的样板代码,为消除这样的代码,咱们能够写一个简略的方针复制东西类进行处理;

(一)事务分析

经过反射获取源方针Field数组,并运用Field类供给的set/get办法完成同名特点的复制;

(二)实体类预备

创立Student类

package cn.byuan.entity;
/**
 * 学生实体类
 *
 * @author byuan
 * @date 2022-01-25
 */
public class Student {
    /**
     * 学生学号, 公共变量, 默认值: defaultStudentNo
     * */
    public String studentNo = "defaultStudentNo";
    /**
     * 学生名字, 公共变量, 默认值: defaultStudentName
     * */
    public String studentName = "defaultStudentName";
    /**
     * 学生性别, 私有变量, 默认值: defaultStudentSex
     * */
    private String studentSex = "defaultStudentSex";
    /**
     * 学生年纪, 私有变量, 默认值: 0
     * */
    private Integer studentAge = 0;
    /**
     * 公有无参结构办法
     * */
    public Student() {
    }
    /**
     * 公有满参结构办法
     * */
    public Student(String studentNo, String studentName, String studentSex, Integer studentAge) {
        this.studentNo = studentNo;
        this.studentName = studentName;
        this.studentSex = studentSex;
        this.studentAge = studentAge;
    }
    /**
     * 私有结构办法
     * */
    private Student(String studentSex, Integer studentAge) {
        this.studentSex = studentSex;
        this.studentAge = studentAge;
    }
    public String getStudentNo() {
        return studentNo;
    }
    public Student setStudentNo(String studentNo) {
        this.studentNo = studentNo;
        return this;
    }
    public String getStudentName() {
        return studentName;
    }
    public Student setStudentName(String studentName) {
        this.studentName = studentName;
        return this;
    }
    public String getStudentSex() {
        return studentSex;
    }
    public Student setStudentSex(String studentSex) {
        this.studentSex = studentSex;
        return this;
    }
    public Integer getStudentAge() {
        return studentAge;
    }
    public Student setStudentAge(Integer studentAge) {
        this.studentAge = studentAge;
        return this;
    }
    @Override
    public String toString() {
        return "Student{" +
                "studentNo = " + this.studentNo + ", " +
                "studentName = " + this.studentName + ", " +
                "studentSex = " + this.studentSex + ", " +
                "studentAge = " + this.studentAge +"}";
    }
    /**
     * 学生类说话办法
     * */
    private String speak(String message) {
        return this.studentName + " : " + message;
    }
}

创立StudentOut类

package cn.byuan.api.out;
import cn.byuan.entity.Student;
/**
 * 学生类出参
 *
 * @author byuan
 * @date 2022-01-26
 */
public class StudentOut {
    /**
     * 学生学号, 公共变量
     * */
    private String studentNo;
    /**
     * 学生名字, 公共变量
     * */
    private String studentName;
    /**
     * 学生性别, 私有变量
     * */
    private String studentSex;
    /**
     * 学生年纪, 私有变量
     * */
    private Integer studentAge;
    public String getStudentNo() {
        return studentNo;
    }
    public StudentOut setStudentNo(String studentNo) {
        this.studentNo = studentNo;
        return this;
    }
    public String getStudentName() {
        return studentName;
    }
    public StudentOut setStudentName(String studentName) {
        this.studentName = studentName;
        return this;
    }
    public String getStudentSex() {
        return studentSex;
    }
    public StudentOut setStudentSex(String studentSex) {
        this.studentSex = studentSex;
        return this;
    }
    public Integer getStudentAge() {
        return studentAge;
    }
    public StudentOut setStudentAge(Integer studentAge) {
        this.studentAge = studentAge;
        return this;
    }
    @Override
    public String toString() {
        return "StudentOut{" +
                "studentNo = " + this.studentNo + ", " +
                "studentName = " + this.studentName + ", " +
                "studentSex = " + this.studentSex + ", " +
                "studentAge = " + this.studentAge +"}";
    }
}

(三)东西类编写

package cn.byuan.util;
import cn.byuan.api.out.StudentOut;
import cn.byuan.entity.Student;
import java.lang.reflect.Field;
/**
 * 方针特点复制东西类
 *
 * @author byuan
 * @date 2022-01-26
 */
public class BeanUtil {
    /**
     * 方针复制东西
     *
     * @param sourceObject 源方针
     * @param destClass 意图方针对应 Class
     *
     * @return 复制完毕后方针
     * */
    public static <T> T copyObject(Object sourceObject, Class<T> destClass) {
        if (sourceObject == null) {
            return null;
        }
        try {
            T destObject = destClass.newInstance();
            copyField(sourceObject, destObject);
            return destObject;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 方针特点复制东西
     *
     * @param sourceObject 源方针
     * @param destObject 意图方针
     * */
    private static void copyField(Object sourceObject, Object destObject) {
        if (sourceObject == null || destObject == null) {
            return;
        }
        // 获取源方针一切字段
        Field[] sourceFieldArray = sourceObject.getClass().getDeclaredFields();
        for (Field sourceFieldPart : sourceFieldArray) {
            // 撤销言语拜访查看
            sourceFieldPart.setAccessible(true);
            String sourceFieldName = sourceFieldPart.getName();
            try {
                // 根据特点称号获取方针方针对应类字段
                Field destField = destObject
                        .getClass()
                        .getDeclaredField(sourceFieldName);
                destField.setAccessible(true);
                destField.set(destObject, sourceFieldPart.get(sourceObject));
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Student student = new Student("2022001", "赵一", "男", 18);
        StudentOut studentOut = copyObject(student, StudentOut.class);
        System.out.println(studentOut);
    }
}

运转截图

我都忘记了