大白话之 Java 反射 - 初学最迷的概念:能干啥?咋用?

本贴最后更新于 2123 天前,其中的信息可能已经东海扬尘

前言

初学时,反射、网络编程、IO 是我最不能理解的三大模块。
当时查资料,给我的感觉是:写文章的都觉得我已经会了。
如今,我用实例尽可能简单地讲述反射这个东西。

能干啥?

人类文明分为三个阶段:

  1. 女娲创造人类
  2. 人类文明建立,地球产生秩序
  3. 人类灭亡

实例化

平常我们实例化一个类,都是用:

Main main = new Main();

那如果要读取这个类中的公共变量,我们需要:

public class Main { public String value = "HelloWorld!"; } public class Test { public static void main(String[] args) { Main main = new Main(); System.out.println(main.value); } }

那么我们如果将这个变量设置为私有呢?

public class Main { private String value = "HelloWorld!"; }

很明显,我们无法获得该变量的值,因为变量的作用域止步于自己的类中。

那么如果我们使用反射,我们就能获取到这个类的任何信息,无论它是私有的还是什么乱七八糟的,不但能看,还能改。

本章主要讲述方法的调用。修改值的方法八九不离十,在阅读本篇文章后,阅读其它文章将会很好理解。

所以...

回到那个人类文明的话题。

如果女娲完成了自己的任务,并且人类已经进入了文明时代后。
我们想操控所有人的思想和命运,怎么办?

对,上帝模式。

如果我们是上帝,我们就能掌握自己管辖区域内的任何物质,包括人类和大自然。

那么...

Java 的反射就犹如上帝模式一样。我们一般使用 Java 反射可以用来:

加载类; 得到类的包名、父类和实现的接口; 得到类默认方法的参数; 得到类中的变量参数; 修改类中的变量参数; 得到类的方法名,方法的参数,方法的返回类型; 调用类中的方法; 得到类中的注解信息; 得到类中的泛型信息; 修改类中的数组; 等等等等......

类的运行过程中,有些参数可能是不可控的。通过反射,我们可以深入掌握自己编写的类,同时在以后的项目和调试中也会有所应用。

咋用?

假设我们再写一个需求(伪代码):

Class Values { String value1 = "Hello"; String value2 = "World"; } Class Main { String str = "value2"; }

假设变量 str 的内容是不可控的,但 str 的值一定是 Values 类中的一个变量,我们想将制定 str 值的变量修改... 应该怎么做呢?

首先,修改 Values 类中的方法:

public class Values { public static String value1 = "Hello"; public static String value2 = "World"; public String getValue1() { return value1; } public void setValue1(String value1) { this.value1 = value1; } public String getValue2() { return value2; } public void setValue2(String value2) { this.value2 = value2; } }

此时,如果我们想修改 Value2 的值,只需执行 Values 类中的方法 setValue2(String value2) 即可。例:

setValue2("NaN");

此时,Value2 的值为“NaN”。

Brain Storm

如果你仔细阅读了上方的内容,你会发现反射支持一个特性:

调用类中的方法。

那么,如果我们利用反射调用这个方法,就可行了。

编写 Main.java 中的代码:

import java.lang.reflect.Method; public class Main { //将要调用的方法 private static String str = "setValue2"; public static void main(String[] args) { try { //执行Class.forName("类")时,JVM会查找对应的类并加载,这时JVM会执行该类的静态代码段。 Class clazz = Class.forName("Values"); /* 不使用 > Values values = new Values(); < 是因为 > Class.forName() < 是获取正在运行的实例,而 > new() < 会创建一个新的实例。 Values中的变量是静态的,所以它的值在启动时已经写入了内存,我们必须直接修改内存中的值,而不是再新建一个。 */ Values values = (Values) clazz.newInstance(); /* getMethod(方法名, 传值1类型, 传值2类型, ...) 获取指定的方法对象 传值类型:String.class Integer.class 等等 */ Method method = clazz.getMethod(str, String.class); System.out.println("修改前:" + Values.value2); //调用指定方法的 > 方法对象.invoke(值实例, 传值1, 传值2, ...) < method.invoke(values, "NaN"); System.out.println("修改后:" + Values.value2); } catch (Exception E) { E.printStackTrace(); } } }

得到返回结果:

修改前:World 修改后:NaN

后语

这样,我们就相当于将字符动态转换为了一段可运行的代码,就能实现更多的功能了。

调用方法,修改值只是反射的一小部分作用。在将来的学习中你会学习到更多的使用姿势,帮助你完成更复杂的设计需求!

  • 大白话
    17 引用 • 27 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3195 引用 • 8215 回帖
  • 反射
    20 引用 • 29 回帖
  • 原理
    16 引用 • 44 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...