JAVA反射
本文最后更新于 133 天前,其中的信息可能已经有所发展或是发生改变。

Java反射是一个强大的特性,允许程序在运行时查询,访问和修改类,接口,字段和方法的信息

反射提供了一种动态操作类的能力,通过这种机制能够在运行时获得类的信息,并且运行时修改类的结构,调用类的方法,实例化对象等

先来个例子

import java.util.*;
import java.lang.reflect.*;

class Dog {
  public void cry() {
      System.out.println("汪汪!");
  }
}

class Person {
  public void hi() {
      System.out.println("你好,我是 Person!");
  }
}

public class Main {
  public static void main(String[] args) throws Exception {
      Scanner scanner = new Scanner(System.in); // 变量名小写
      System.out.println("请输入 key(1 或 2):");
      String key = scanner.next();

      switch (key) {
          case "1":
              Dog dog = new Dog(); // 首字母大写
              dog.cry();
              break;
          case "2":
              Class<?> cls = Class.forName("Person"); // 反射获取类
              Object obj = cls.getDeclaredConstructor().newInstance(); // 实例化对象
              Method method = cls.getMethod("hi"); // 获取方法
              method.invoke(obj); // 调用方法
              break;
          default:
              System.out.println("无效的 key!");
              break;
      }
  }
}

在此之前要知道一个

静态和动态

概念静态(Static)动态(Dynamic)
发生时机编译时(编译器确定)运行时(程序运行时才决定)
示例String.class、方法重载、变量类型str.getClass()、多态调用、反射
优点快、类型安全灵活、可适应不确定情况
缺点死板、无法适应运行时变化慢一些,可能不安全(比如反射访问私有字段)

反射

forName()

可以获取对象

Class.forName(classname) //获取classname类中的所有属性
public class TEST1 {
  public String name;
  public int age;

  public static void main(String[] args) throws Exception {
      Class<?> clazz = Class.forName("TEST1");
      System.out.println("类名: " + clazz.getName());
  }
}

Java的反射API提供了一系列的类和接口来操作Class对象

  • java.lang.Class:标识嘞的对象,提供了方法来获取类的字段、方法、构造函数等
  • java.lang.reflect.Field:表示类的字段(属性)。提供了访问和修改字段的能力
  • java.lang.reflect.Method:表示类的方法。提供了调用方法的能力
  • java.lang.reflect.Constructor:表示类的构造函数。提供了创建对象的能力

工作流程

  1. 获取Class对象
  2. 获取成员信息
  3. 操作成员

获取对象

通过类字面量

Class<?> clazz = String.class

通过对象实例

String str = "hello";
Class<?> clazz = str.getClass();

通过Class.forName()方法

Class<?> clazz = Class.forName("java.lang.String");

代码示例

public class Java_infect_test1 {
  public static void main(String[] args) throws Exception {
      Test sun = new Test();
      Class<?> clazz = sun.getClass();
      System.out.println(clazz); // 输出 class Test

      Class<?> clazz2 = Class.forName("Test");
      System.out.println(clazz2);
  }
}

class Test {
}

对于java.lang.String,可以用Class.forName("java.lang.String")获取这个Class,但是其他两种方法不需要throws Exception,而这种方法由于运行时可能找不到指定的类,所以必须抛出异常

forName有两个函数重载

  • Class forName(String name)
  • Class forName(String name, boolean initialize, ClassLoader loader)
Class.forName(className)
// 等于
Class.forName(className, true, currentLoader)

默认情况下, forName 的第⼀个参数是类名;第⼆个参数表示是否初始化;第三个参数就 是 ClassLoader

ClassLoader 是什么呢?它就是⼀个“加载器”,告诉Java虚拟机如何加载这个类。关于这个点,后⾯还 有很多有趣的漏洞利⽤⽅法,这⾥先不展开说了。Java默认的 ClassLoader 就是根据类名来加载类, 这个类名是类完整路径,如 java.lang.Runtime

这里去看了狗哥的文章,引用一下狗哥的例子

public class Java05_Reflect_test {
  public static void main(String[] args) throws Exception {
      new Test1();
  }
}

class Test1 {
  public Test1() {
      System.out.println("我是构造方法");
  }

  static {
      System.out.println("我是静态初始化块");
  }

  {
      System.out.println("我是实例初始块");
  }
}

运行结果:
我是静态初始化块
我是实例初始块
我是构造方法
  • 静态初始化块(static {})在类被加载到 JVM 时执行,仅执行一次,只能访问静态成员,同时在任何对象创建之前执行。
  • 实例初始化块({})每次创建对象时,在构造方法之前执行。
  • 构造方法(public ClassName() {})每次创建对象时,在实例初始化块之后执行

执行顺序如下:

  1. 类加载,JVM发现new Test1(),需要先加载类,于是执行静态代码块,只执行一次,类加载时执行
  2. 创建对象,JVM会创建一个新的实例,会先实现初始化块
  3. 执行构造函数,最后执行构造器

即静态块 → 实例块 → 构造方法

而forName的这个参数就是控制静态初始化块的,可以看到它的优先级非常高,如果我们可以编写恶意类,就可以把恶意代码放在static{}里,然后用第二个参数直接对这个类进行调用

引用一下代码

public class Java05_Reflect_test {
  public static void main(String[] args) throws Exception {
      Class<?> clazz = Class.forName("Test1");
  }
}

class Test1 {
  public Test1() {
      System.out.println("我是构造方法");
  }

  static {
      System.out.println("我先执行");
  }

  {
      System.out.println("我是实例初始块");
  }
}

创建对象

可以利用反射动态创建对象

Class<?> clazz = Class.forName("java.lang.String");
Object obj = clazz.getDeclaredConstructor().newInstance();

dome

import java.lang.reflect.InvocationTargetException;

public class reflect_01 {
  public reflect_01() {
      System.out.println("我是构造方法");
  }

  static {
      System.out.println("我先执行");
  }

  {
      System.out.println("我是实例初始块");
  }
}
class Test01 {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
      Class<?> clazz = Class.forName("reflect_01");
      Object obj = clazz.getDeclaredConstructor().newInstance();
  }
}

/*
我先执行
我是实例初始块
我是构造方法
*/

通过反射访问字段

获取类变量

clazz.getField(String name):获取公有字段(包括继承的)
clazz.getDeclaredField(String name):获取本类中声明的字段(包括私有字段)
field.setAccessible(true):允许访问私有字段
field.get(Object obj):获取字段值
field.set(Object obj, Object value):设置字段值

dome

reflect_01.java


public class reflect_01 {

  private String name="salyfish";

  public String age;

  public reflect_01() {
      System.out.println("我是构造方法");
  }

  static {
      System.out.println("我先执行");
  }

  {
      System.out.println("我是实例初始块");
  }
}

Test01.java

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

class Test01 {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {

      System.out.println("1. 准备加载 reflect_01 类...");

      Class<?> clazz = Class.forName("reflect_01");
      System.out.println("2. 类加载完成。");

      System.out.println("3. 准备创建实例...");

      // 这会触发实例初始块,然后触发构造方法
      Object obj = clazz.getDeclaredConstructor().newInstance();
      System.out.println("4. 实例创建完成。");


      Field ageField = clazz.getField("age");
      System.out.println("5. 成功获取 public 字段: " + ageField.getName());


      ageField.set(obj, "20");


      System.out.println("6. 读取 'age' 字段的值: " + ageField.get(obj));

      System.out.println("--- 开始获取 private 字段 ---");

      Field nameField = clazz.getDeclaredField("name");

      nameField.setAccessible(true);

      Object nameValue = nameField.get(obj);

      System.out.println("7. 成功获取 'name' 字段的值: " + nameValue);
  }
}

/*
1. 准备加载 reflect_01 类...
我先执行
2. 类加载完成。
3. 准备创建实例...
我是实例初始块
我是构造方法
4. 实例创建完成。
5. 成功获取 public 字段: age
6. 读取 'age' 字段的值: 30
--- 开始获取 private 字段 ---
7. 成功获取 'name' 字段的值: salyfish
*/

调用方法

clazz.getMethod(String name, Class… parameterTypes):获取公有方法,包括继承的
clazz.getDeclaredMethod(String name, Class… parameterTypes):获取 本类声明的方法(包括私有方法)
method.setAccessible(true):允许访问私有方法
method.invoke(Object obj, Object… args):调用方法,第一个参数是对象,后面是传入方法的参数

MethodDome

// 文件名: TestDemo.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class TestDemo {

  // 为了简洁,我们直接 throws Exception
  public static void main(String[] args) throws Exception {

      // --- 准备工作:获取 Class 对象和实例 ---
      System.out.println("1. 加载 MethodDemo 类...");
      Class<?> clazz = Class.forName("MethodDemo");

      System.out.println("2. 创建 MethodDemo 实例...");
      Constructor<?> constructor = clazz.getDeclaredConstructor();
      Object obj = constructor.newInstance(); // obj 就是一个 MethodDemo 实例
      System.out.println("----------------------------------------");


      // --- 场景 1 & 3: 调用 public 方法 (getName) ---
      System.out.println("-> 场景 1/3: 调用 public, 无参, 有返回值 的 getName()");
       
      // 1. 获取 Method 对象: getDeclaredMethod(方法名, 参数类型...)
      Method getNameMethod = clazz.getDeclaredMethod("getName");
       
      // 2. 调用方法: method.invoke(实例对象, 方法参数...)
      // 因为 getName() 没有参数,所以 invoke 只需要传入实例 obj
      Object returnValue = getNameMethod.invoke(obj);
       
      System.out.println("   getName() 的返回值: " + returnValue);
      System.out.println("----------------------------------------");


      // --- 场景 2: 调用 public 有参方法 (setInfo) ---
      System.out.println("-> 场景 2: 调用 public, 有参 的 setInfo()");
       
      // 1. 获取 Method 对象,必须指明参数类型
      Method setInfoMethod = clazz.getDeclaredMethod("setInfo", String.class, int.class);
       
      // 2. 调用方法,传入实例 obj 和对应的参数
      setInfoMethod.invoke(obj, "Salyfish", 99);
       
      // (验证一下) 我们再次调用 getName() 看看 name 是不是被改了
      System.out.println("   (验证) 再次调用 getName(): " + getNameMethod.invoke(obj));
      System.out.println("----------------------------------------");


      // --- 场景 4: 调用 private 私有方法 (getSecret) ---
      System.out.println("-> 场景 4: 调用 private 的 getSecret()");
       
      // 1. 获取 private 方法
      Method getSecretMethod = clazz.getDeclaredMethod("getSecret", String.class);
       
      // 2. 关键:解除私有方法的访问限制!
      getSecretMethod.setAccessible(true);
       
      // 3. 调用,并传入参数 "SECRET:"
      Object secretValue = getSecretMethod.invoke(obj, "SECRET:");
       
      System.out.println("   getSecret() 的返回值: " + secretValue);
      System.out.println("----------------------------------------");


      // --- 场景 5: 调用 public static 静态方法 (staticMethod) ---
      System.out.println("-> 场景 5: 调用 static 的 staticMethod()");
       
      // 1. 获取 static 方法
      Method staticMethod = clazz.getDeclaredMethod("staticMethod");
       
      // 2. 调用 static 方法
      // 关键:静态方法不属于任何实例,所以 invoke 的第一个参数传入 null
      staticMethod.invoke(null);
       
      System.out.println("----------------------------------------");
  }
}

TestDome

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class TestDome {


  public static void main(String[] args) throws Exception {


      System.out.println("1. 加载 MethodDemo 类...");
      Class<?> clazz = Class.forName("MethodDome");

      System.out.println("2. 创建 MethodDemo 实例...");
      Constructor<?> constructor = clazz.getDeclaredConstructor();
      Object obj = constructor.newInstance();
      System.out.println("----------------------------------------");



      System.out.println("-> 场景 1/3: 调用 public, 无参, 有返回值 的 getName()");


      Method getNameMethod = clazz.getDeclaredMethod("getName");



      Object returnValue = getNameMethod.invoke(obj);

      System.out.println("   getName() 的返回值: " + returnValue);
      System.out.println("----------------------------------------");



      System.out.println("-> 场景 2: 调用 public, 有参 的 setInfo()");


      Method setInfoMethod = clazz.getDeclaredMethod("setInfo", String.class, int.class);


      setInfoMethod.invoke(obj, "Salyfish", 99);


      System.out.println("   (验证) 再次调用 getName(): " + getNameMethod.invoke(obj));
      System.out.println("----------------------------------------");



      System.out.println("-> 场景 4: 调用 private 的 getSecret()");


      Method getSecretMethod = clazz.getDeclaredMethod("getSecret", String.class);


      getSecretMethod.setAccessible(true);


      Object secretValue = getSecretMethod.invoke(obj, "SECRET:");

      System.out.println("   getSecret() 的返回值: " + secretValue);
      System.out.println("----------------------------------------");



      System.out.println("-> 场景 5: 调用 static 的 staticMethod()");


      Method staticMethod = clazz.getDeclaredMethod("staticMethod");


      staticMethod.invoke(null);

      System.out.println("----------------------------------------");
  }
}

/*
1. 加载 MethodDemo 类...
2. 创建 MethodDemo 实例...
MethodDemo 的无参构造函数被调用
----------------------------------------
-> 场景 1/3: 调用 public, 无参, 有返回值 的 getName()
...getName() 被调用...
  getName() 的返回值: Default User
----------------------------------------
-> 场景 2: 调用 public, 有参 的 setInfo()
...setInfo(String, int) 被调用, 设置为: Salyfish, 99
...getName() 被调用...
  (验证) 再次调用 getName(): Salyfish
----------------------------------------
-> 场景 4: 调用 private 的 getSecret()
...private getSecret(String) 被调用...
  getSecret() 的返回值: SECRET: Salyfish is 99 years old.
----------------------------------------
-> 场景 5: 调用 static 的 staticMethod()
...public static staticMethod() 被调用...
----------------------------------------
*/
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇