Java反射

7 篇文章 2 订阅

Java反射-安全篇

-反射

Java反射是一种强大的机制,允许程序在运行时动态地获取和操作类的信息,包括类的字段、方法、构造函数等。虽然Java反射本身并不是漏洞,但它可以被恶意利用来执行一些危险的操作,从而导致安全漏洞。

以下是Java反射可能被恶意利用的几个方面:

·1、访问私有成员:通过反射,攻击者可以绕过对私有成员(如私有字段和方法)的访问限制,直接操作和修改这些私有成员,破坏封装性,可能导致意外行为或数据泄露。

·2、调用敏感方法:反射可以使攻击者调用敏感方法,即使这些方法通常不应该被公开调用。这可能导致未经授权的访问和操作,例如执行特权操作、篡改数据等。

·3、突破安全检查:通过反射,攻击者可以突破一些安全检查,例如绕过访问控制修饰符(public、protected、private)的限制,执行受限制的操作。

·4、注入恶意代码:通过反射,攻击者可以动态加载和执行恶意代码,例如加载远程类文件或执行非法指令,从而导致远程执行代码漏洞。

来看下边这个例子:

public void execute(String className, String methodName) throws Exception {
  Class clazz = Class.forName(className);
  clazz.getMethod(methodName).invoke(clazz.newInstance());
}

这个例子,演示了几个在反射里极为重要的方法:

·获取类:forName()

·实例化类对象:newInstance()

·获取方法:getMethod()

·执行方法:invoke()

基本上,这几个方法包揽了Java安全里各种和反射有关的Payload。

—Class.forName方法执行static代码

forName方法不是获取“类”的唯一途径,通常来说我们有如下三种方式获取一个“类”,也就是java.lang.Class对象:

·1、“obj.getClass()”:如果上下文中存在某个类的实例obj,那么我们可以直接通过

“obj.getClass()”来获取它的类。

·2、“Test.class”:如果你已经加载了某个类,只是想获取到它的java.lang.Class对象,那么就直接拿它的class属性即可。这个方法其实不属于反射。

·3、“Class.forName()”:如果你知道某个类的名字,想获取到这个类,就可以使用forName方法来获取。

·其实还有第四种:

ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime")

利用类加载机制,也可以获取Class对象。

在安全研究中,我们使用反射的一大目的,就是绕过某些沙盒。比如,上下文中如果只有

Integer类型的数字,我们如何获取到可以执行命令的Runtime类呢?也许可以这样(伪代码):

111.getClass().forName("java.lang.Runtime")

forName有两个方法重载:

Class<?> forName(String name)

Class<?> forName(String name, boolean initialize, ClassLoader loader)

第一个就是我们最常见的获取Class的方式,其实可以理解为第二种方式的一个封装:

也就是说

Class.forName(className)

等于

Class.forName(className, true, currentLoader)

默认情况下,forName方法的第一个参数是类名,第二个参数表示是否初始化,第三个参数就是ClassLoader

ClassLoader是什么呢?它就是一个“加载器”,告诉Java虚拟机如何加载这个类。Java默认的ClassLoader就是根据类名来加载类,这个类名是类完整路径,如java.lang.Runtime。

第二个参数initialize常常被人误解,勾陈安全实验室有篇讲反射机制的文章

(http://www.polaris-lab.com/index.php/archives/450/)里说到:

注意,有一点很有趣,使用“.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName方法会自动初始化该Class对象。注意,这里所说的初始化,并不是构造方法。

那么这个初始化究竟指什么呢?

可以将这个“初始化”理解为类的初始化。我们先来看看如下这个类:

public class TrainPrint {
  {
    System.out.printf("Empty block initial %s\n", this.getClass());
  }

  static {
    System.out.printf("Static initial %s\n", TrainPrint.class);
  }

  public TrainPrint() {
    System.out.printf("Initial %s\n", this.getClass());
  }
}

上述的三个“初始化”方法有什么区别呢,调用顺序是什么,在安全上有什么价值?

其实你运行一下就知道了,首先调用的是“static {}”,其次是“{}”,最后是构造方法

其中,“static {}”就是在“类初始化”的时候调用的,而“{}”中的代码会放在构造方法的“super()”后面,但在当前构造方法内容的前面

例如:

Person p = new Person("zhangsan", 20);

该句话都做了什么事情?

1、因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中。

2、执行该类中的static代码块,如果有的话,给Person.class类进行初始化。

3、在堆内存中开辟空间,分配内存地址。

4、在堆内存中建立对象的特有属性,并进行默认初始化。

5、对属性进行显示初始化。

6、对对象进行构造代码块初始化。

7、对对象进行对应的构造方法初始化。

8、将内存地址给到栈内存中的p变量。

测试下:

public static void main(String[] args) throws Exception {
  Object object = Class.forName("com.example.demo.Reflect.TrainPrint");
}

运行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以说,forName方法中的“initialize=true”其实就是告诉Java虚拟机是否执行

“类初始化”

那么,假设我们有如下方法,其中方法的参数name可控:

public static void ref(String name) throws Exception {
  Class.forName(name);
}

我们就可以编写一个恶意类,将恶意代码放置在“static {}”中,从而执行:

package com.example.demo.Reflect;

public class TouchFile {
  static {
    try {
      Runtime rt = Runtime.getRuntime();
      String[] commands = {"calc"};
      Process pc = rt.exec(commands);
      pc.waitFor();
    } catch (Exception e) {
      // do nothing
    }
  }
}

接着,调用ref方法测试一下:

public static void main(String[] args) throws Exception {
  ref("com.example.demo.Reflect.TouchFile");
}

可直接弹出计算器

在正常情况下,除了系统类,如果我们想拿到一个类,需要先import才能使用。而使用forName方法就不需要,这样对于我们的攻击者来说就十分有利,我们可以加载任意类。

另外,我们经常在一些源码里看到,类名的部分包含 符号,比如 f a s t j s o n 在 c h e c k A u t o T y p e 的时候就会先将“ 符号,比如fastjson在checkAutoType的时候就会先将“ 符号,比如fastjsoncheckAutoType的时候就会先将”替换成“.”

(https://github.com/alibaba/fastjson/blob/fcc9c2a/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java#L1038)。$的作用是查找内部类

例如,Java的普通类C1中编写了内部类C2,在编译的时候,会生成两个文件:C1.class和C1$C2.class,我们可以将他们看作两个无关的类,通过“Class.forName("C1$C2")”即可加载这个内部类

获得类以后,我们可以继续使用反射来获取这个类中的属性、方法,也可以实例化这个类,并调用方法。

—Class.newInstance()

“class.newInstance()”的作用就是调用这个类的无参构造方法,这个比较好理解。不过,我们有时候在写漏洞利用方法的时候,会发现使用newInstance方法总是不成功,这时候原因可能是:

·1、你使用的类没有无参构造方法

·2、你使用的类构造方法是私有的

最最最常见的情况就是java.lang.Runtime,这个类在我们构造命令执行Payload的时候很常见,但我们不能直接这样来执行命令:

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");

你会得到这样一个错误:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原因是Runtime类的构造方法是私有的。

----单例模式

有同学就比较好奇,为什么会有类的构造方法是私有的,难道他不想让用户使用这个类吗?

这其实涉及到很常见的设计模式:“单例模式”(有时候工厂模式也会写成类似)。

比如,对于Web应用来说,数据库连接只需要建立一次,而不是每次用到数据库的时候再新建立一个连接,此时作为开发者你就可以将数据库连接使用的类的构造方法设置为私有,然后编写一个静态方法来获取:

package com.example.demo.Reflect;

public class TrainDB {
  private static TrainDB instance = new TrainDB();

  public static TrainDB getInstance() {
    return instance;
  }

  private TrainDB() { // 建立连接的代码...
  }
}

这样,只有类初始化的时候会执行一次构造方法,后面只能通过getInstance方法来获取这个对象,避免建立多个数据库连接。

回到正题,Runtime类就是单例模式,我们只能通过“Runtime.getRuntime()”来获取到

Runtime对象。我们将上述Payload进行修改即可正常执行命令了:

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");

这里用到了getMethod和invoke方法。

getMethod的作用是通过反射获取一个类的某个特定的公有方法

而学过Java的同学应该清楚,Java中支持类的重载,我们不能仅通过方法名来确定一个方法。

所以,在调用getMethod方法的时候,我们需要传给他需要获取的方法的参数类型列表。

比如这里的Runtime.exec方法有6个重载:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们使用最简单的,也就是第一个,它只有一个参数,类型是String,所以我们使用

getMethod("exec", String.class)

来获取Runtime.exec方法。

invoke方法的作用是执行方法,它的第一个参数是:

·如果这个方法是一个普通方法,那么第一个参数是类对象

·如果这个方法是一个静态方法,那么第一个参数是类

这也比较好理解了,我们正常执行方法是“[1].method([2], [3], [4]...)”,其实在反射里就是“method.invoke([1], [2], [3], [4]...)”。

所以我们将上述命令执行的Payload分解一下就是:

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");

这个就比较好看懂了。

package com.example.demo.Reflect;

import java.lang.reflect.Method;

public class test1 {
    public static void main(String[] args) throws Exception {
//        ref("com.example.demo.Reflect.TouchFile");
//        Class clazz = Class.forName("java.lang.Runtime");
//        clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");
//        Class clazz = Class.forName("java.lang.Runtime");
//        clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");

        Class clazz = Class.forName("java.lang.Runtime");
        Method execMethod = clazz.getMethod("exec", String.class);
        Method getRuntimeMethod = clazz.getMethod("getRuntime");
        Object runtime = getRuntimeMethod.invoke(clazz);
        execMethod.invoke(runtime, "calc.exe");
    }

    public static void ref(String name) throws Exception {
        Class.forName(name);
    }
}

现在存在两个疑问:

1、如果一个类没有无参构造方法,也没有类似单例模式里的静态方法,我们怎样通过反射实例化该类呢?

2、如果一个方法或构造方法是私有方法,我们是否能执行它呢?

—Constructor.newInstance()

对于第一个问题,我们需要用到一个新的反射方法getConstructor方法

和getMethod方法类似,getConstructor方法接收的参数是构造方法列表类型,因为构造方法也支持重载,所以必须用参数列表类型才能确定具体哪个构造方法。

获取到构造方法后,我们使用Constructor的newInstance方法来执行。

比如,我们常用的另一种执行命令的方式ProcessBuilder,我们使用反射来获取其构造方

法,然后调用“start()”来执行命令:

Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();

----ProcessBuilder的两个构造方法

ProcessBuilder有两个构造方法:

public ProcessBuilder(List<String> command) {
public ProcessBuilder(String... command) {

我们上面用到了第一个形式的构造方法,所以我们在调用getConstructor方法的时候传入的是List.class。

但是,我们看到,前面这个Payload用到了Java里的强制类型转换,有时候我们利用漏洞

的时候(在表达式上下文中)是没有这种语法的。所以,我们仍需利用反射来完成这一步。

其实用的就是前面讲过的知识:

Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

通过“getMethod(“start”)”获取到start方法,然后invoke方法执行,invoke方法的第一个参数就是ProcessBuilder Object了。

-----可变长参数String…

那么,如果我们要使用

public ProcessBuilder(String... command) {

这个构造方法,需要怎样用反射执行呢?

这又涉及到Java里的可变长参数(varargs)了。正如其他语言一样,Java也支持可变长参数,就是当你定义方法的时候不确定参数数量的时候,可以使用“…”这样的语法来表示“这个方法的参数个数是可变的”

对于可变长参数,Java其实在编译的时候会编译成一个数组,也就是说,如下这两种写法在底层是等价的(也就是不能重载):

public void hello(String[] names) {}
public void hello(String... names) {}

也正因为如此,如果我们有一个数组,想传给hello方法,只需直接传即可:

String[] names = {"hello", "world"};
hello(names);

那么对于反射来说,如果要获取的目标方法里包含可变长参数,其实我们认为它是数组就行了。

所以,我们将字符串数组的类“String[].class”传给getConstructor方法,获取ProcessBuilder方法的第二种构造方法:

Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getConstructor(String[].class)

在调用Constructor的newInstance方法时,因为Constructor的newInstance方法本身接收的就是可变长参数:

public T newInstance(Object... initargs)
  throws InstantiationException, IllegalAccessException,
      IllegalArgumentException, InvocationTargetException
{

我们传给ProcessBuilder构造方法的也是一个可变长参数,二者叠加为一个二维数组,所以整个Payload如下:

Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();

-----getDeclaredConstructor替换Runtime.getRuntime()

再说到第二个问题,如果一个方法或构造方法是私有方法,我们是否能执行它呢?

这就涉及到getDeclared系列的反射了,与普通的getMethod、getConstructor区别是:

·getMethod系列方法获取的是当前类中所有公共方法,包括从父类继承的方法。

·getDeclaredMethod系列方法获取的是当前类中“声明”的方法,是实在写在这个类里的,包括私有的方法,但从父类里继承来的就不包含了。

getDeclaredMethod的具体用法和getMethod类似,getDeclaredConstructor的具体用法和

getConstructor类似,不再赘述。

举个例子,前文我们说过Runtime这个类的构造方法是私有的,我们需要用

“Runtime.getRuntime()”来获取对象。其实现在我们也可以直接用getDeclaredConstructor来获取这个私有的构造方法来实例化对象,进而执行命令:

Class clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec", String.class).invoke(m.newInstance(), "calc.exe");

可见,这里使用了setAccessible方法,这个是必须的。我们在获取到一个私有方法后,必须用setAccessible方法修改它的作用域,否则仍然不能调用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

–获取类的构造器

先来看

\java\lang\Class.java

*返回类中所有的public构造器集合,默认构造器的下标为0*

public Constructor<?>[] getConstructors() throws SecurityException {

*返回类中所有的构造器,包括私有*

public Constructor<?>[] getDeclaredConstructors() throws SecurityException {

*返回指定的public构造器,参数为构造器参数类型集合*

public Constructor<T> getConstructor(Class<?>... parameterTypes)
  throws NoSuchMethodException, SecurityException {

*返回任意指定的构造器*

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
  throws NoSuchMethodException, SecurityException {

*测试代码*

E:\beifen\java\基础\demo\src\main\java\com\example\demo\Reflect\Role.java

package com.example.demo.Reflect;

public class Role {
    private String name = "默认是不带参数的构造方法";
    private String type = "默认是public的构造方法";
    private int age = 1;
    private static Role instance = null;

    // 不带参数的构造方法
    public Role() {
    }

    // 带参数的构造方法
    public Role(String name) {
        this.name = name;
    }

    // 带2个参数的构造方法,第二个参数为int类型
    public Role(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 私有的带2个参数的构造方法,第二个参数用String类型来区分
    private Role(String name, String type) {
        this.name = name;
        this.type = type;
    }

    private void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        System.out.println(getName() + ":" + getType() + ":age为" + age);
        return this.getName();
    }

    // synchronized与static synchronized的区别
    // synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块
    // 注意这里是“类的当前实例”,类的两个不同实例就没有这种约束了
    // static synchronized是要控制类的所有实例的访问,限制线程同时访问jvm中该类的所有实例同时访问对应的代码块
    public synchronized static Role getInstance() {
        if (instance == null) {
            instance = new Role("带参数的构造方法", "private型的构造方法");
        }
        return instance;
    }

    private void setType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

E:\beifen\java\基础\demo\src\main\java\com\example\demo\Reflect\ReflectTest3.java

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectTest3 {
  public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    // 获取类的构造器
    System.out.println();
    System.out.println("==========反射例子之3==========");
    Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
    try {
      // public Constructor<?>[] getConstructors() 返回类中所有的public构造器集合,默认构造器的下标为0
      // public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造器,包括私有
      cls2.getConstructors();
      cls2.getDeclaredConstructors();
      // public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回指定的public构造器,参数为构造器参数类型集合
      // public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器
      Constructor<Role> constructor1 = cls2.getConstructor(new Class[]{String.class});
      Constructor<Role> constructor2 = cls2.getConstructor(new Class[]{String.class, int.class});Role role3_1 = constructor1.newInstance("public型的带1个参数的构造方法");Role role3_2 = constructor2.newInstance("public型的带2个参数的构造方法", 2);
​      role3_1.toString();
​      role3_2.toString();} catch (SecurityException e) {
​      e.printStackTrace();} catch (NoSuchMethodException e) {
​      e.printStackTrace();} catch (IllegalArgumentException e) {
​      e.printStackTrace();} catch (InvocationTargetException e) {
​      e.printStackTrace();}
  }
}

因为Role.java中,age为private:

private int age = 1;

无法通过System.out.println直接打印

所以****修改下toString方法****:

@Override
public String toString() {
  System.out.println(getName() + ":" + getType() + ":age为" + age);
  return this.getName();
}

*运行结果*外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*调试看下这两行*

cls2.getConstructors();
cls2.getDeclaredConstructors();

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多了一个私有构造方法接着测试下,*获取类的私有构造器*

*只能使用getDecalredConstructor方法,而不能使用getConstructor方法,使用setAccessible方法设置访问权限*

public class ReflectTest4 {
  public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    System.**out**.println();
    System.**out**.println("==========反射例子之4==========");
    Constructor<Role> constructor2;
    Role role4 = null;
    Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
    try {
      // 只能使用getDecalredConstructor方法,而不能使用getConstructor方法
      constructor2 = cls2.getDeclaredConstructor(new Class[]{String.class, String.class});
      // 设置访问权限,私有时,必须设置
      constructor2.setAccessible(true);
      role4 = constructor2.newInstance("带参数的构造方法", "private型的构造方法");
      role4.toString();
    } catch (SecurityException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (IllegalArgumentException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (InvocationTargetException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    }
  }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

–获取类的成员变量

先来看

\java\lang\Class.java

*获取所有的public成员变量*

public Field[] getFields() throws SecurityException {

*获取任意的public成员变量*

public Field getField(String name)
  throws NoSuchFieldException, SecurityException {

*获取任意的所有的成员变量*

public Field[] getDeclaredFields() throws SecurityException {

*获取任意的指定名字的成员变量*

public Field getDeclaredField(String name)
  throws NoSuchFieldException, SecurityException {

*测试代码*

public class ReflectTest5 {
  public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    // 获取类的成员变量
    Constructor<Role> constructor2;
    Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
    // 默认的构造方法
    Role role2 = cls2.newInstance();
    try {
      constructor2 = cls2.getDeclaredConstructor(new Class[]{String.class, String.class});
      constructor2.setAccessible(true);
      Role role4 = constructor2.newInstance("带参数的构造方法", "private型的构造方法");
      // 上边这些都是之前几个测试里的,为了构造一个public一个private来对比// public Field[] getFields() 获取所有的public成员变量// public Field getField(String name) 获取任意的public成员变量// public Field[] getDeclaredFields() 获取任意的所有的成员变量
​      cls2.getDeclaredFields();// public Field getDeclaredField(String name) 获取任意的指定名字的成员变量Field field1 = cls2.getDeclaredField("type");
​      field1.setAccessible(true);// 参数是实例化的类Object object1 = field1.get(role2);// 参数是实例化的类Object object2 = field1.get(role4);System.out.println("==========反射例子之5==========");System.out.println("默认是不带参数的构造方法:" + object1);System.out.println("带参数的构造方法:" + object2);} catch (SecurityException e) {// **TODO Auto-generated catch block****
**      e.printStackTrace();} catch (NoSuchMethodException e) {// **TODO Auto-generated catch block****
**      e.printStackTrace();} catch (NoSuchFieldException e) {// **TODO Auto-generated catch block****
**      e.printStackTrace();} catch (IllegalArgumentException e) {// **TODO Auto-generated catch block****
**      e.printStackTrace();} catch (InvocationTargetException e) {// **TODO Auto-generated catch block****
**      e.printStackTrace();}
  }
}

主要是:

Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
Role role2 = cls2.newInstance();
cls2.getDeclaredFields();
Field field1 = cls2.getDeclaredField("type");
field1.setAccessible(true);
Object object1 = field1.get(role2);

*运行结果*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*调试*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

–获取类的方法

*获取所有的公有方法的集合*

public Method[] getMethods() throws SecurityException {

*获取指定的公有方法*

参数1:方法名 参数2:参数类型集合

public Method getMethod(String name, Class<?>... parameterTypes)
  throws NoSuchMethodException, SecurityException {

*获取所有的方法*

public Method[] getDeclaredMethods() throws SecurityException {

*获取任意指定的方法*

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  throws NoSuchMethodException, SecurityException {

无参的时候我们只要传null就行了

*测试代码*

public class ReflectTest6 {
  public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    // 获取类的方法
    Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
    Role role2 = cls2.newInstance();
    try {
      // public Method[] getMethods() 获取所有的公有方法的集合
      // public Method getMethod(String name, Class<?>... parameterTypes) 获取指定的公有方法
      // 参数1:方法名 参数2:参数类型集合
      // public Method[] getDeclaredMethods() 获取所有的方法
      // public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取任意指定的方法
      Method method1 = cls2.getDeclaredMethod("setName", new Class[]{String.class});
      method1.setAccessible(true);
      method1.invoke(role2, "私有的方法我也可以用");
      // 无参的时候我们只要传null就行了
      Method method2 = cls2.getMethod("getName", null);
      Object invoke = method2.invoke(role2, null);
      System.out.println("==========反射例子之6==========");
      System.out.println(invoke);
    } catch (SecurityException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (IllegalArgumentException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    } catch (InvocationTargetException e) {
      // **TODO Auto-generated catch block****
**      e.printStackTrace();
    }
  }
}

*运行结果*外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对应setName方法和getName方法:

private void setName(String name) {
  this.name = name;
}
public String getName() {
  return name;
}

–静态方法

Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
Method method3 = cls2.getDeclaredMethod("getInstance", null);
method3.setAccessible(true);
Object invoke = method3.invoke(null, null);

完整的****测试代码****:

package com.example.demo.Reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectTest7 {
    public static void main(String[] args) throws ClassNotFoundException,
            InstantiationException, IllegalAccessException {
        // 静态方法
        Class<Role> cls2 = (Class<Role>) Class.forName("com.example.demo.Reflect.Role");
        try {
            Method method3 = cls2.getDeclaredMethod("getInstance", null);
            method3.setAccessible(true);
            // 记住,与普通方法不同,第一个参数是null,也可以传cls2,都可以
//            Object invoke = method3.invoke(cls2, null);
            Object invoke = method3.invoke(null, null);
            if (invoke instanceof Role) {
                System.out.println("实例化成功");
                invoke.toString();
            }
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

*运行结果*外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

–获取类的内部类

java.lang.Class.getDeclaredClasses() 方法****返回反映对象定义的私有,受保护,公共和默认值的Class对象数组,但不包括子类或接口****。

如果类没有声明的类或接口的成员,或如果此Class对象表示一个基本类型,一个数组类或void,此方法返回一个长度为0的数组。

*测试代码*

public class getDeclaredClassesTest {
  public static void main(String[] args) {
    try {
      Class cls = Class.forName("com.example.demo.Reflect.getDeclaredClassesTest");Class[] classes = cls.getDeclaredClasses();for (int i = 0; i < classes.length; i++) {System.out.println("Class = " + classes[i].getName());}} catch (ClassNotFoundException e) {System.out.println(e.toString());}
  }

  public class InnerClass1 {
    public InnerClass1() {
      System.out.println("Inner Class1");
    }
  }

  public class InnerClass2 {
    public InnerClass2() {
      System.out.println("Inner Class2");
    }
  }

  private class InnerPrivateClass {
    public InnerPrivateClass() {
      System.out.println("Inner Private Class");
    }
  }

  protected class InnerProtectedClass {
    public InnerProtectedClass() {
      System.out.println("Inner Protected Class");
    }
  }
}

*运行结果*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一共4个,*包含private和protected的类*

–修改final属性值

E:\beifen\java\基础\demo\src\main\java\com\example\demo\Reflect\Person.java

*Person*

class Person {
public final String name = “Mike”;
}

*利用Field的modifiers来修改final属性*

*测试代码*

package com.example.demo.Reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest8 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.example.demo.Reflect.Person");
        modify(c, "name", "aaa");
    }

    public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {
        Field field = object.getClass().getDeclaredField(fieldName);

        Field modifiersField = Field.class.getDeclaredField("modifiers");
        // Field的modifiers是私有的
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        field.set(object, newFieldValue);
        System.out.println(object);
    }
}

*调试*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

modifiersField.setAccessible(true);

对应:

*“override = true”*外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成功修改为 “aaa”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bulldozer++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值