首页 > 编程语言 >【JavaSE系列】反射机制

【JavaSE系列】反射机制

时间:2024-09-14 12:48:56浏览次数:3  
标签:反射 系列 String System println JavaSE studentClass Class out

目录

前言

一、概述

二、获取Class对象

三、反射构造方法

1. 获取构造方法

2. 获取修饰符、名称和形参

3. 创建对象

四、反射成员变量 

1. 获取成员变量

2. 获取修饰符、名称和类型

3. 赋值/获取值

五、 反射成员方法

1. 获取成员方法

2. 获取修饰符、形参、返回值和异常

3. 运行成员方法

总结


前言

  在当今的软件开发领域,灵活性和可扩展性成为了软件设计的重要考量因素。Java反射机制作为实现这一目标的强大工具之一,为开发人员提供了在运行时动态获取类的信息或动态调用对象的方法的能力。通过使用反射,开发人员可以在不知道具体实现细节的情况下,操作类、构造函数、方法及字段等。

一、概述

  Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。主要核心就是Class类、Constructor类、Field类、Method类等API。 反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。

二、获取Class对象

  在Java中,我们有三种方式获取Class对象,下面是这三种方式:

方式 示例使用时机
方式一:使用Class类的forName()方法Class clazz = Class.forName("java.lang.Object");源代码阶段
方式二:使用“类.class”语法Class clazz = Object.class;加载阶段
方式三:调用Object类的getClass()方法

Object obj = new Object();

Class clazz = obj.getClass();

运行阶段

  这三种方式其实就对应Java里面三种不同的阶段,如果我想创建一个类的对象,是分以下三个阶段的:第一个阶段,要先编写Java文件,然后把他编译成.class字节码文件,这个阶段还没有把代码加载到内存的,只是在硬盘里面进行的操作,所以这个阶段我们也叫做源代码阶段,这个阶段我们会用方式一去获取字节码文件对象。接下来要运行代码了,是不是首先要把这个类的字节码文件加载到内存?这个阶段我们叫做加载阶段,在这个阶段我们会用方式二。接下来去内存当中创建这个类的对象,比如A a = new A(),此时就叫做运行阶段,这个阶段用方式三。下图是对这三个方式的进一步使用的示例:

三、反射构造方法

1. 获取构造方法

  Class类中有四种用于获取构造方法的方法,这四种方法的简单描述如下所示:

方法描述
Constructor<?>[] getConstructors();返回所有“公共”构造方法对象的数组
Constructor<?>[] getDeclaredConstructors();返回所有构造方法对象的数组
Constructor getConstructor(Class<?>…parameterTypes);返回单个“公共”构造方法对象
Constructor getDeclaredConstructor(Class<?>…parameterTypes);返回单个构造方法对象

  下面,我们可以编写一个Student对象,该类中的属性和相关方法如下所示,后面的反射示例均会使用到这个类:

public class Student {

    private String name;
    private int age;
    public String gender;

    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    protected Student(int age) {
        this.age = age;
    }
    private Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    void sayHello() {
        System.out.println("学生说你好!");
    }

    private String sayHello(String str) throws RuntimeException {
        System.out.println("你好你好!");
        return str;
    }

}

  下面我们编写Java代码来测试上面提到的四种获取构造方法的代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    // 方式一
    Constructor<?>[] constructors = studentClass.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor); // 仅输出public修饰的构造方法
    }
    System.out.println("==================================================================");
    // 方式二
    Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
    for (Constructor<?> constructor : declaredConstructors) {
        System.out.println(constructor);
    }
    System.out.println("==================================================================");
    // 方式三
    Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);
    System.out.println(constructor); // 仅能获取public修饰的构造方法,如果不是,则会报错
    System.out.println("==================================================================");
    // 方式四
    Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class);
    System.out.println(declaredConstructor);
}

  测试结果如下图所示:

2. 获取修饰符、名称和形参

  如何获取构造方法上的修饰符、名称和形参呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);
    int modifiers = constructor.getModifiers();
    System.out.println("修饰符整数标识:" + modifiers);
    String stringModifiers = Modifier.toString(modifiers);
    System.out.println(stringModifiers);
    System.out.println("===========================================");
    String name = constructor.getName();
    System.out.println(name); // 打印构造方法名称
    System.out.println("===========================================");
    Class<?>[] parameterTypes = constructor.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println(parameterType); // 打印参数类型
    }
    System.out.println("===========================================");
    Parameter[] parameters = constructor.getParameters();
    for (Parameter parameter : parameters) {
        System.out.println(parameter); // 打印参数名称
    }
}

  测试结果如下图所示:

3. 创建对象

  Constructor类中用于创建对象的方法有:

方法描述
T newInstance(Object… initargs)根据指定构造方法创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查

  下面是一个示例代码:

public static void main(String[] args) throws Exception {
    Class<Student> studentClass = Student.class;
    Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class);
    constructor.setAccessible(true); // 取消访问检查
    Student student = constructor.newInstance("张三");
    System.out.println(student);
}

  运行结果如下图所示:

四、反射成员变量 

1. 获取成员变量

  Class类中用于获取成员变量的方法有四种,这四类方法如下:

方法描述
Field[] getFields();返回所有"公共"成员变量对象的数组
Field[] getDeclaredFields();返回所有成员变量对象的数组
Field getField(String name);返回单个公共成员变量对象
Field getDeclaredField(String name);返回单个成员变量对象

  下面我们编写Java代码对这四个方法进行测试:

public static void main(String[] args) throws NoSuchFieldException {
    Class<Student> studentClass = Student.class;
    // 方式一
    Field[] fields = studentClass.getFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    System.out.println("===========================================================================");
    // 方式二
    Field[] declaredFields = studentClass.getDeclaredFields();
    for (Field field : declaredFields) {
        System.out.println(field);
    }
    System.out.println("===========================================================================");
    // 方式三
    Field gender = studentClass.getField("gender");
    System.out.println(gender);
    System.out.println("===========================================================================");
    // 方式四
    Field name = studentClass.getDeclaredField("name");
    System.out.println(name);
}

  代码运行结果如下图所示:

2. 获取修饰符、名称和类型

  如何获取成员变量上的修饰符、名称和形参呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchFieldException {
    Class<Student> studentClass = Student.class;
    Field gender = studentClass.getField("gender");
    System.out.println(Modifier.toString(gender.getModifiers())); // 修饰符
    System.out.println(gender.getName()); // 名称
    System.out.println(gender.getType()); // 类型
}

  测试结果如下图所示:

 

3. 赋值/获取值

  Field类中用于赋值和获取值的方法:

方法描述
void set(Object obj, Object value)赋值
Object get(Object obj)获取值

  下面是一个示例代码: 

public static void main(String[] args) throws Exception {
    Student student = new Student("张三", 18);
    Class<? extends Student> studentClass = student.getClass();
    Field name = studentClass.getDeclaredField("name");
    name.setAccessible(true);
    Object oldValue = name.get(student);
    System.out.println(oldValue); // 获取值
    name.set(student, "李四"); // 设置值
    System.out.println(student);
}

  运行结果如下: 

五、 反射成员方法

1. 获取成员方法

  Class类中用于获取成员方法的方法有四个,这四个方法如下所示:

方法描述
Method[] getMethods();返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods();返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>… parameterTypes);返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?> … parameterTypes);返回单个成员方法对象

  下面是这四个方法的使用示例代码: 

public static void main(String[] args) throws NoSuchMethodException {
    // 方式一
    Class<Student> studentClass = Student.class;
    Method[] methods = studentClass.getMethods();
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("=======================================================================");
    // 方式二
    Method[] declaredMethods = studentClass.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println(method);
    }
    System.out.println("=======================================================================");
    // 方式三
    Method setName = studentClass.getMethod("setName", String.class);
    System.out.println(setName);
    System.out.println("=======================================================================");
    // 方式四
    Method sayHello = studentClass.getDeclaredMethod("sayHello");
    System.out.println(sayHello);
}

  运行结果如下图所示: 

2. 获取修饰符、形参、返回值和异常

  如何获取成员变量上的修饰符、形参、返回值和异常呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);
    System.out.println(Modifier.toString(sayHello.getModifiers())); // 修饰符
    System.out.println("=============================================");
    Class<?>[] parameterTypes = sayHello.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println(parameterType); // 形参类型
    }
    System.out.println("=============================================");
    Parameter[] parameters = sayHello.getParameters();
    for (Parameter parameter : parameters) {
        System.out.println(parameter); // 形参
    }
    System.out.println("=============================================");
    Class<?> returnType = sayHello.getReturnType();
    System.out.println(returnType); // 返回值类型
    System.out.println("=============================================");
    Class<?>[] exceptionTypes = sayHello.getExceptionTypes();
    for (Class<?> exceptionType : exceptionTypes) {
        System.out.println(exceptionType);
    } // 异常
}

  测试结果如下图所示:

 

补充:我们还可以反射方法上的注解,进一步提高程序的扩展性,下面是一篇参考博文:

Java Reflect - 利用反射获取类上的注解

3. 运行成员方法

  Method类中用于创建对象的方法如下所示:

方法描述
Object invoke(Object obj, Object… args);

运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

  下面是我们的测试代码: 

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Student student = new Student();
    Class<Student> studentClass = Student.class;
    Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);
    sayHello.setAccessible(true);
    Object res = sayHello.invoke(student, "人生入戏");
    System.out.println(res);
}

  运行结果如下:

 

总结

  本文探讨了Java反射机制的基础知识,包括获取Class对象的不同方式、反射构造方法、获取成员方法和成员变量等。通过一系列的实际代码示例,我们展示了如何利用反射机制来动态地获取类的信息、创建对象、获取构造方法和成员方法的细节、以及如何对成员变量进行操作。尽管反射机制为Java开发带来了极大的灵活性,但也应注意到它可能会带来的性能影响。因此,在实际项目中,应当合理使用反射,确保在灵活性与性能之间取得良好的平衡。掌握反射机制不仅有助于深入理解Java语言的内部工作原理,而且还能帮助开发者更有效地使用现有的框架和技术栈,提高软件的可维护性和扩展性。 

标签:反射,系列,String,System,println,JavaSE,studentClass,Class,out
From: https://blog.csdn.net/2301_76244254/article/details/142214876

相关文章

  • 1、.Net UI框架:Blazor Hybrid - .Net宣传系列文章
    BlazorHybrid是.NET6中引入的一个新特性,它允许开发者使用Blazor技术开发跨平台的桌面和移动应用程序。BlazorHybrid结合了BlazorWebAssembly和BlazorServer的优势,提供了一种在桌面和移动设备上运行Blazor应用的方法。BlazorHybrid的关键特性包括:跨......
  • java中的Map系列的集合HashMap、HashTable、TreeMap以及Collections和Collection的区
    1.Map的特性特性:key键-value值身份证号--->人可以通过key获取到valueMap它的key是唯一的,Map中的key是无序的而且是不重复的value是可以重复的。Map集合的基本方法:Vput(Kkey,Vvalue)添加元素如果put的key存在那么会用新的value的值替换掉原有的value值key......
  • 【新品上市】正点原子ZYNQ7015开发板发布!ZYNQ 7000系列、双核ARM、PCIe2.0、SFPX2,性能
    【新品发布】正点原子ZYNQ7015开发板发布!ZYNQ7000系列、双核ARM、PCIe2.0、SFPX2,性能强悍,资料丰富!正点原子Z15ZYNQ开发板,搭载XilinxZynq7000系列芯片,核心板主控芯片的型号是XC7Z015CLG485-2。开发板由核心板+底板组成,外设资源丰富,板载1路PS端千兆以太网接口、PCle2.0x2、SFP光......
  • 反射&动态代理
    1.反射1.1反射的概述:**专业的解释(了解一下):**是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。**通俗的理解:(掌握)**......
  • 前端基本功——面试必问系列(1):都2024了,还没吃透Promise?一文搞懂
    写在前面:大家好,我是山里看瓜,该系列文章是为了帮助大家不管面试还是开发对前端的一些基本但是很重要的知识点认识更加深入和全面。想写这个系列文章的初衷是:我发现前端的很多基本知识,使用起来很简单,定义看起来也很简单。很多人你在问他相关问题的时候,他也能说上几句。但是为什么......
  • 2、.Net 前端框架:ASP.Net Core - .Net宣传系列文章
    ASP.NETCore是一个跨平台、高性能、开源的框架,用于构建现代化的、基于云的、互联网连接的应用程序。它是微软对原始ASP.NET框架的重构和扩展,提供了更多的灵活性和改进的性能。ASP.NETCore可以用于开发Web应用程序、WebAPI、以及服务端渲染的Web页面。ASP.NETC......
  • 【Python爬虫系列】_016.关于登录和验证码
    我的个人主页:......
  • WebGL系列教程七(二维及三维旋转、平移、缩放)
    目录1前言2二维2.1平移2.2旋转2.3缩放3三维3.1平移3.2旋转3.2.1绕XXX轴旋转3.2.2绕......
  • CSS基本布局理解——WEB开发系列38
    对CSS学习已经接近尾声,下面你可以对以下两道“小卡拉米”测试进行测试下CSS理解程度。题1:基于栅格布局的现代博客首页设计题目要求:创建一个博客首页布局,包含一个顶部导航栏、一个主要的内容区域(左侧为博客文章列表,右侧为一个侧边栏显示推荐内容),以及一个底部的页脚。要求......
  • 微信小程序开发系列10----页面配置--事件冒泡和阻止
       下图点击里面,外面的事件也触发  场景:广告点击先看广告,之后跳转到功能页面 会冒泡的事件源码获取方式(免费):(1)登录-注册:http://resources.kittytiger.cn/(2)签到获取积分(3)搜索:8-wxmleventMp事件冒泡和阻止......