我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

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

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

一、什么是反射

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

  • getDeclaredFields():获取所有声明的字段(包括公有字段和私有字段)
  • getFields():仅可获取公有字段

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

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

  • getModifiers():获取字段修饰符相加值,想要获取明确标识需要通过Modifier常量的toString方法对相加值进行解码
  • getType():获取字段类型
  • getName():获取字段名称
  • get(Object):获取字段对应值

下面给出实例

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

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python基础学习之反射机制详解

在Python中,反射是指通过一组内置的函数和语句,在运行时动态地访问、检查和修改对象的属性、方法和类信息的机制。本文将通过简单的示例和大家讲讲Python中的反射机制,希望对大家有所帮助
2023-03-22

Go语言学习之WaitGroup用法详解

目录前言小试牛刀总览底层实现结构体AddDoneWait易错点总结前言 在前面的文章中,我们使用过 WaitGroup 进行任务编排,Go语言中的 WaitGroup 和 Java 中的 CyclicBarrier、CountDownLat
2022-06-11

java反射之Method的invoke方法实现教程详解

反射是Java语言的一种特性,它可以在运行时动态地获取类的信息并操作类的成员。Method类是反射机制中用于表示方法的类,在Method类中有一个invoke方法可以用于调用方法。invoke方法的声明如下:public Object in
2023-08-17

Java学习笔记之Pattern类的用法详解

Pattern类是Java中正则表达式的主要类之一,它提供了一系列方法来操作正则表达式。正则表达式是一种用来匹配字符串的强大工具,它可以用来检查一个字符串是否符合某种模式,或者从一个字符串中提取出符合某种模式的部分。Pattern类的用法如
2023-09-02

Go语言学习之new函数的用法详解

这篇文章主要为大家详细介绍了Go语言中new()函数的相关知识以及具体用法,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
2023-05-20

kotlin 官方学习教程之基础语法详解

kotlin 官方学习教程之基础语法详解Google 在今天的举行了 I/O 大会,大会主要主要展示内有容 Android O(Android 8.0)系统、Google Assistant 语音助手、Google 智能音箱、人工智能、机器
2023-05-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录