【反射知识点详解】

2024-09-08 14:12
文章标签 知识点 详解 反射

本文主要是介绍【反射知识点详解】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect包中的类和接口来实现。

通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。

反射的用途

反射主要用于以下几种情况:

  1. 动态创建对象:通过类的Class对象动态地创建其实例。
  2. 访问类的字段和方法:即使字段和方法是私有的,也可以通过反射机制来访问和修改它们。
  3. 动态调用方法:可以在运行时决定调用哪个方法,而不需要在编写代码时就知道。
  4. 框架开发:很多Java框架(如Spring)都大量使用反射来简化配置和初始化过程。

反射学什么?

  • 学习获取类的信息、操作它们:这是反射学习的核心目标。你需要学会如何获取一个类的信息(如类名、方法、字段等),并能够在运行时操作这些信息。

反射第一步:加载类,获取类的字节码:Class对象

  • 加载类:Java的类加载器(ClassLoader)负责将类的.class文件加载到JVM中。
  • 获取Class对象:一旦类被加载,就可以通过多种方式来获取其Class对象。例如,使用Class.forName(String className)obj.getClass()Type.class(其中Type是某个类名)。

以下是获取Class对象的三种主要方式:

1. 使用.class语法

对于已知的类,你可以直接使用.class语法来获取其Class对象。这种方式在编译时就已经确定了要操作的类,因此不需要处理ClassNotFoundException

2. 使用Class.forName(String className)方法

这是最常用的动态加载类的方式。Class.forName(String className)方法会加载指定的类,并返回该类的Class对象。如果指定的类不存在于类路径中,或者因为其他原因无法被加载,那么会抛出ClassNotFoundException异常。

3. 使用对象的getClass()方法

如果你已经有了类的实例,那么可以通过调用该实例的getClass()方法来获取其Class对象。这个方法返回的是该对象的实际运行时类的Class对象,这允许你在运行时发现对象的实际类型(包括继承体系中的具体类型)。

代码演示3种方式

测试类

package demo14;public class Test {public static void main(String[] args) throws Exception {//通过.class获取Class c1=Student.class;//getName()方法返回类的全限定名,即demo14.StudentSystem.out.println(c1.getName());//getSimpleName()方法返回类的简单名称,即StudentSystem.out.println(c1.getSimpleName());//通过Class.forName()获取Class c2=Class.forName("demo14.Student");System.out.println(c1==c2);//创建了一个Student类的实例并通过该实例的getClass()方法获取Class对象Student student=new Student();Class c3=student.getClass();System.out.println(c3==c2);//这比较获取的Class对象是否相同。由于Java虚拟机为每个类管理一个唯一的Class对象// 所以这3个对象是相同的,输出结果为true}
}

Student类 

package demo14;public class Student {
}

获取类构造器:Constructor对象

Java中Class提供了从类中获取构造器的方法

方法名称描述返回值类型
getConstructor(Class<?>... parameterTypes)获取具有指定参数类型的公共构造器Constructor<T>
getDeclaredConstructor(Class<?>... parameterTypes)获取具有指定参数类型的构造器(包括私有构造器)Constructor<T>
getConstructors()获取类的所有公共构造器Constructor<?>[]
getDeclaredConstructors()获取类的所有构造器(包括私有构造器)Constructor<?>[]

部分方法演示 

 

获取类的成员变量:Field对象

方法声明说明
void set(Object obj, Object value)为对象obj的某个属性赋值为value
Object get(Object obj)从对象obj中获取某个属性的值,并返回该值
public void setAccessible(boolean flag)设置为true时,表示允许通过反射访问私有成员,绕过Java的访问控制检查

代码 

假设我们有一个Cat类,它有两个私有成员变量nameage,以及一个无参构造器和一个有参构造器。我们的目标是使用Java反射来获取这些成员变量的信息,并打印它们的名称和类型。

Cat类定义

package com.itheima.d2_reflect;  public class Cat {  private String name;  private int age;  public Cat() {  System.out.println("无参数构造器执行");  }  public Cat(String name, int age) {  this.name = name;  this.age = age;  System.out.println("有参数构造器执行");  }  }

反射获取成员变量

接下来是使用反射API获取Cat类成员变量的代码示例:

package com.itheima.d2_reflect;  import java.lang.reflect.Field;  public class Test3Field {  public static void testGetFields() throws NoSuchFieldException, IllegalAccessException {  // 获取Cat类的Class对象  Class<?> c = Cat.class;  // 获取类的全部成员变量  Field[] fields = c.getDeclaredFields();  // 遍历成员变量数组  for (Field field : fields) {  // 打印成员变量的名称和类型  System.out.println(field.getName() + " ---> " + field.getType());  }  // 定位某个特定的成员变量(例如name)  Field fName = c.getDeclaredField("name");  // 打印该成员变量的名称和类型  System.out.println(fName.getName() + "--->" + fName.getType());  // 注意:直接访问私有成员变量会抛出IllegalAccessException,因此需要设置可访问性  fName.setAccessible(true);  // 创建一个Cat对象  Cat cat = new Cat();  // 通过反射给私有成员变量赋值  fName.set(cat, "卡菲猫");  // 注意:由于我们没有直接获取或修改age字段,这里不展示其操作  // 打印修改后的cat对象(这里假设有合适的toString方法)  System.out.println(cat); // 需要Cat类有适当的toString()实现  }  // 主方法,用于测试  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {  testGetFields();  }  
}

获取类的成员方法:Method对象

  • Method对象:表示类的方法。通过Class对象的getMethod(String name, Class<?>... parameterTypes)getDeclaredMethod(String name, Class<?>... parameterTypes)方法,可以获取到方法的Method对象。
  • 使用:获取到Method对象后,可以调用其invoke(Object obj, Object... args)方法来执行对象的方法。同样,如果方法是私有的,可能需要先调用Method.setAccessible(true)
类别方法名签名说明
Class提供的成员方法getMethods()Method[] getMethods()获取类的全部public成员方法(按名称排序)
Class提供的成员方法getDeclaredMethods()Method[] getDeclaredMethods()获取类的全部成员方法(不考虑访问修饰符)
Class提供的成员方法getMethod(String name, Class<?>... parameterTypes)Method getMethod(String name, Class<?>... parameterTypes)获取类的某个public成员方法,需指定方法名和参数类型
Class提供的成员方法getDeclaredMethod(String name, Class<?>... parameterTypes)Method getDeclaredMethod(String name, Class<?>... parameterTypes)获取类的某个成员方法,需指定方法名和参数类型,不考虑访问修饰符
Method提供的成员方法setAccessible(boolean flag)void setAccessible(boolean flag)设置方法的访问权限。如果flag为true,则忽略Java的访问控制检查(暴力反射)
Method提供的成员方法invoke(Object obj, Object... args)Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException触发某个对象的该方法执行,需传入对象实例和参数

代码示例

以下是一个完整的Java代码示例,展示了如何使用上述方法:

import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  public class ReflectionDemo {  public static void main(String[] args) {  try {  // 假设有一个类Person  Class<?> personClass = Class.forName("Person");  // 获取所有public方法  Method[] publicMethods = personClass.getMethods();  System.out.println("Public Methods:");  for (Method method : publicMethods) {  System.out.println(method.getName());  }  // 获取所有方法(包括private)  Method[] declaredMethods = personClass.getDeclaredMethods();  System.out.println("\nDeclared Methods:");  for (Method method : declaredMethods) {  System.out.println(method.getName());  }  // 获取特定方法(假设有一个public方法sayHello)  Method sayHelloMethod = personClass.getMethod("sayHello");  // 假设Person类有一个无参构造器  Object personInstance = personClass.newInstance();  // 调用sayHello方法  sayHelloMethod.invoke(personInstance);  // 获取特定方法(假设有一个private方法secretMethod)  Method secretMethod = personClass.getDeclaredMethod("secretMethod");  // 设置访问权限为true(暴力反射)  secretMethod.setAccessible(true);  // 调用secretMethod方法  secretMethod.invoke(personInstance);  } catch (ClassNotFoundException e) {  e.printStackTrace();  } catch (InstantiationException e) {  e.printStackTrace();  } catch (IllegalAccessException e) {  e.printStackTrace();  } catch (NoSuchMethodException e) {  e.printStackTrace();  } catch (InvocationTargetException e) {  e.printStackTrace();  }  }  // 假设的Person类  static class Person {  public void sayHello() {  System.out.println("Hello!");  }  private void secretMethod() {  System.out.println("This is a secret method.");  }  }  
}

这篇关于【反射知识点详解】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1148330

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc

019、JOptionPane类的常用静态方法详解

目录 JOptionPane类的常用静态方法详解 1. showInputDialog()方法 1.1基本用法 1.2带有默认值的输入框 1.3带有选项的输入对话框 1.4自定义图标的输入对话框 2. showConfirmDialog()方法 2.1基本用法 2.2自定义按钮和图标 2.3带有自定义组件的确认对话框 3. showMessageDialog()方法 3.1

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

脏页的标记方式详解

脏页的标记方式 一、引言 在数据库系统中,脏页是指那些被修改过但还未写入磁盘的数据页。为了有效地管理这些脏页并确保数据的一致性,数据库需要对脏页进行标记。了解脏页的标记方式对于理解数据库的内部工作机制和优化性能至关重要。 二、脏页产生的过程 当数据库中的数据被修改时,这些修改首先会在内存中的缓冲池(Buffer Pool)中进行。例如,执行一条 UPDATE 语句修改了某一行数据,对应的缓