深入解析java反射(上)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 本博文主要记录Java 反射(reflect)的使用,在了解反射之前,你应该先了解 Java 中的 Class 类,如果你不是很了解,可以先简单了解下。

一、什么是反射?

反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

Oracle 官方对反射的解释是:

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....


简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

二、反射的主要用途

很多人都认为反射在实际的 Java 开发应用中并不广泛,其实不然。当我们在使用 IDE(如 Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。

举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如:

<action name="login"
       class="org.xxx.SimpleLoginAction"
       method="execute">
   <result>/shop/shop-index.jsp</result>
   <result name="error">login.jsp</result>
</action>

配置文件与Action建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截,然后 StrutsPrepareAndExecuteFilter 会去动态地创建 Action 实例。比如我们请求 login.action,那么 StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索actionnameloginAction,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。

对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

三、反射的基本运用

3.1、通过反射获取class对象

通过反射获取对象有三种方式

3.1.1、Class.forName()获取
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

比如,在 JDBC 开发中常用此方法加载数据库驱动

Class.forName("包名.类名");
3.1.2、类名.class获取

例如:

Class<?> intClass = int.class;
Class<?> integerClass = Integer.TYPE;
#RelfectEntity类为本文的例子
Class relfectEntity2 = RelfectEntity.class;
3.1.3、对象getClass()获取
StringBuilder str = new StringBuilder("hello world");
Class<?> strClass = str.getClass();

三种方法都可以实现获取class对象,框架开发中,一般第一种用的比较多。

3.2、获取类的构造器信息

获取类构造器的用法,主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)

3.3、获取类的实例

通过反射来生成对象主要有两种方式。

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
Class<?> c = String.class;
Object str = c.newInstance();

先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。

//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

这种方法可以用指定的构造器来创建实例。

3.4、获取类的变量

实体类:

/**
 * 基类
 */
public class BaseClass {
  public String publicBaseVar1;
  public String publicBaseVar2;
}
/**
 * 子类
 */
public class ChildClass extends BaseClass{
  public String publicOneVar1;
  public String publicOneVar2;
  private String privateOneVar1;
  private String privateOneVar2;
}

测试:

public class VarTest {
  public static void main(String[] args) {
    //1.获取并输出类的名称
    Class mClass = ChildClass.class;
    System.out.println("类的名称:" + mClass.getName());
    System.out.println("----获取所有 public 访问权限的变量(包括本类声明的和从父类继承的)----");
    //2.获取所有 public 访问权限的变量(包括本类声明的和从父类继承的)
    Field[] fields = mClass.getFields();
    //遍历变量并输出变量信息
    for (Field field : fields) {
      //获取访问权限并输出
      int modifiers = field.getModifiers();
      System.out.print(Modifier.toString(modifiers) + " ");
      //输出变量的类型及变量名
          System.out.println(field.getType().getName() + " " + field.getName());
    }
    System.out.println("----获取所有本类声明的变量----");
    //3.获取所有本类声明的变量
    Field[] allFields = mClass.getDeclaredFields();
    for (Field field : allFields) {
      //获取访问权限并输出
      int modifiers = field.getModifiers();
      System.out.print(Modifier.toString(modifiers) + " ");
      //输出变量的类型及变量名
          System.out.println(field.getType().getName() + " " + field.getName());
    }
  }
}

输出结果:

类的名称:com.example.java.reflect.ChildClass
----获取所有 public 访问权限的变量(包括本类声明的和从父类继承的)----
public java.lang.String publicOneVar1
public java.lang.String publicOneVar2
public java.lang.String publicBaseVar1
public java.lang.String publicBaseVar2
----获取所有本类声明的变量----
public java.lang.String publicOneVar1
public java.lang.String publicOneVar2
private java.lang.String privateOneVar1
private java.lang.String privateOneVar2

3.5、修改类的变量

修改子类

/**
 * 子类
 */
public class ChildClass extends BaseClass{
  public String publicOneVar1;
  public String publicOneVar2;
  private String privateOneVar1;
  private String privateOneVar2;
  public String printOneMsg() {
    return privateOneVar1;
  }
}

测试:

public class VarModfiyTest {
  public static void main(String[] args) throws Exception {
    //1.获取并输出类的名称
    Class mClass = ChildClass.class;
    System.out.println("类的名称:" + mClass.getName());
    System.out.println("----获取ChildClass类中的privateOneVar1私有变量----");
    //2.获取ChildClass类中的privateOneVar1私有变量
    Field privateField = mClass.getDeclaredField("privateOneVar1");
    //3. 操作私有变量
      if (privateField != null) {
          //获取私有变量的访问权
          privateField.setAccessible(true);
          //实例化对象
          ChildClass obj = (ChildClass) mClass.newInstance();
          //修改私有变量,并输出以测试
          System.out.println("privateOneVar1变量,修改前值:" + obj.printOneMsg());
          //调用 set(object , value) 修改变量的值
          //privateField 是获取到的私有变量
          //obj 要操作的对象
          //"hello world" 为要修改成的值
          privateField.set(obj, "hello world");
          System.out.println("privateOneVar1变量,修改后值:" + obj.printOneMsg());
      }
  }
}

输出结果:

类的名称:com.example.java.reflect.ChildClass
----获取ChildClass类中的privateOneVar1私有变量----
privateOneVar1变量,修改前值:null
privateOneVar1变量,修改后值:hello world
相关文章
|
6天前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
54 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
13天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
11天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
1月前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
1月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
11天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
13天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
13天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
14天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
37 3
|
14天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
96 2