文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java高级语法学习之反射详解

2024-04-02 19:55

关注

一、什么是反射

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;


public class Student {

    
    public String studentNo = "defaultStudentNo";

    
    public String studentName = "defaultStudentName";

    
    private String studentSex = "defaultStudentSex";

    
    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;
    }

}

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

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

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

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

Class类代表了类本身,类本身包含字段,构造方法,非构造方法等内容,因此使用反射的第一步就是获取对象所对应的Class类。

仅就使用反射而言,我们需着重了解Class类的获取方式,下面给出实例

1. Class类测试实例

package cn.byuan.example;
import cn.byuan.entity.Student;

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类:

Field类代表了类中的属性字段,常用类中属性字段可笼统分为两种,公有字段(public)与私有字段(private);

每个字段又具有四个属性:修饰符,字段类型,字段名称,对应值;Field也自然提供了对应方法对这四种属性进行获取:

下面给出实例

2. Field类测试实例

package cn.byuan.example;

import cn.byuan.entity.Student;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;


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);
    }

    
    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();
    }

    
    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;


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);
    }

    
    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;


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;


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);
    }

    
    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;


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;


public class Student {

    
    public String studentNo = "defaultStudentNo";

    
    public String studentName = "defaultStudentName";

    
    private String studentSex = "defaultStudentSex";

    
    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;


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;


public class BeanUtil {
    
    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;
    }


    
    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);
    }
}

运行截图

 总结

到此这篇关于Java高级语法学习之反射的文章就介绍到这了,更多相关Java高级语法反射内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯