0x00 Java反射概述
Java Reflection
Reflection
(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API
取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class
类型的对象(一个 类只有一个Class
对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射。
- 正常方式: 引入需要的“包类”名称–>通过
new
实例化–>取得实例化对象 - 反射方式: 实例化对象–>
getClass()
方法–>得到完整的“包类”名称
Java
反射机制研究及应用
Java
反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器- ……
0x01 Class类
Class
类概述
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接 口。对于每个类而言,JRE
都为其保留一个不变的Class
类型的对象。一个Class
对象包含 了特定某个结构(class/interface/enum/annotation/primitive type/void/[])
的有关信息。
Class
本身也是一个类Class
对象只能由系统建立对象- 一个加载的类在
JVM
中只会有一个Class
实例 - 一个
Class
对象对应的是一个加载到JVM
中的一个.class
文件 - 每个类的实例都会记得自己是由哪个
Class
实例所生成 - 通过
Class
可以完整地得到一个类中的所有被加载的结构 Class
类是Reflection
的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class
对象
获取Class
类的实例
- 前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠, 程序性能最高
Class class = String.class;
- 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class class = "www.fauns.com".getClass();
- 前提:已知一个类的全类名,且该类在类路径下,可通过
Class
类的静态方 法forName()
获取,可能抛出ClassNotFoundException
Class class = Class.forName("java.lang.String");
- 其他方式
ClassLoader cl = this.getClass().getClassLoader();
Class class = cl.loadClass("类的全类名");
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class sql {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 获得Driver类型的对象
Driver driver = new Driver();
// 通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
}
}
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Collection_Test {
public static void main(String[] args) {
try {
// 1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
Class<Person> personClass = Person.class;
// 2.通过实例对象,调用该实例的getClass()方法获取Class对象
Class<?> aClass = Class.forName("bh.test1.Person");
Object m = aClass.newInstance();
Class<?> aClass1 = m.getClass();
// 3.使用全类名配合forName()方法获得class对象
Class<?> aClass2 = Class.forName("bh.test1.Person");
// 默认调用的是无参数的构造方法
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
Person hehe = constructor.newInstance("hehe", 28);
System.out.println(hehe);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
Class
类的常用方法
方法名 | 功能说明 |
static Class forName(Stringname) | 返回指定类名name 的Class 对象 |
ObjectnewInstance() | 调用缺省构造函数,返回该Class 对象的一个实例 |
getName() | 返回此Class 对象所表示的实体(类、接口、数组类、基本类型 或void )名称 |
ClassgetSuperClass() | 返回当前Class 对象的父类的Class 对象 |
Class []getInterfaces() | 获取当前Class 对象的接口 |
ClassLoadergetClassLoader() | 返回该类的类加载器 |
ClassgetSuperclass() | 返回表示此Class 所表示的实体的超类的Class |
Constructor[]getConstructors() | 返回一个包含某些Constructor 对象的数组 |
Field[]getDeclaredFields() | 返回Field 对象的一个数组 |
Method getMethod(String name,Class …paramTypes) | 返回一个Method 对象,此对象的形参类型为paramType |
import bh.test1.Person;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {/*
* 通过Class类中的forName方法,
* 首先封装一个clasetAccessibless类型的对象,
* 然后通过调用NewInstance方法实例化生成初始想要生成的对象
* */
/*
* 假设现在的类名是一个可控的变量,
* 则可以通过控制类名来实现实例化特定的对象
* */
Class<?> person1 = Class.forName("bh.test1.Person");
System.out.println("返回指定类名 name 的 Class 对象: " + person1);
System.out.println("调用缺省构造函数,返回该Class对象的一个实例: " + person1.newInstance());
System.out.println("返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称: " + person1.getName());
System.out.println("返回当前Class对象的父类的Class对象: " + person1.getSuperclass());
System.out.println("获取当前Class对象的接口: " + person1.getInterfaces());
// 获得接口的名称
Class<?>[] interfaces = person1.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
ClassLoader classLoader = person1.getClassLoader();
System.out.println("返回该类的类加载器: " + classLoader);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {
Class<?> person1 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors = person1.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors));
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
for (Constructor<?> constructor : constructors) {
// 获得构造方法所需参数的个数
// System.out.println(constructor.getParameters().length);
if (constructor.getParameters().length == 2) {
Object hehe = constructor.newInstance("hehe", 20);
System.out.println(hehe);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
1.1. getConstructors
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
// Person person = new Person("haha", 1999);
// System.out.println(person.getClass());
try {
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
Class<?> person2 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors1 = person2.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors1);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
for (Constructor<?> constructor : constructors1) {
if (constructor.getParameters().length == 2) {
Object asdfasdf = constructor.newInstance("asdfasdf", 999);
System.out.println(asdfasdf);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
1.2. getConstructor
与getConstructors
区别
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {
/*
* 通过getConstructor获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* 如果不想使用getConstructors获得多个构造器,
* 此时可以使用getConstructor来获得特定的构造器,
* 其参数为构造器需要使用的参数加上.class
* */
Class<?> person1 = Class.forName("bh.test1.Person");
Constructor<?> constructor1 = person1.getConstructor(String.class, int.class);
Object nihao = constructor1.newInstance("nihao", 29);
System.out.println("nihao: " + nihao);
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
Class<?> person2 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors1 = person2.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors1);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
for (Constructor<?> constructor : constructors1) {
if (constructor.getParameters().length == 2) {
Object asdfasdf = constructor.newInstance("asdfasdf", 999);
System.out.println(asdfasdf);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
1.3. getMethod
import bh.test1.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 使用getMethod获得的方法的修饰符需要为public
Method setName = aClass.getMethod("setName", String.class);
setName.invoke(o, "nuew_name");
String name = ((Person) o).getName();
System.out.println(name);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
import bh.test1.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 使用getMethod获得的方法的修饰符需要为public
Method setName = aClass.getMethod("classSayName");
setName.invoke(o);
setName.invoke(Person.class);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
1.4. getDeclaredFields
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
// 返回Field对象的一个数组
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 返回Field对象的一个数组
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
for (Field declaredField : declaredFields) {
// System.out.println(declaredField.getName());
if (declaredField.getName() == "namePublic") {
declaredField.set(o, "helloworld");
} else if (declaredField.getName() == "agePublic") {
declaredField.set(o, 10);
}
}
System.out.println(o);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
1.5. getDeclaredField
与getDeclaredFields
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
Field namePublic = aClass.getDeclaredField("namePublic");
namePublic.set(o, "hahahaha");
/*for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}*/
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
if (declaredField.getName() == "namePublic") {
declaredField.set(o, "asdlkajsdlkjasdlkajsd");
} else if (declaredField.getName() == "agePublic") {
declaredField.set(o, 9999999);
}
}
System.out.println(o);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
1.6. getModifiers
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
public class Collection_Test {
public static void main(String[] args) {
try {
// 1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
Class<Person> personClass = Person.class;
// 2.通过实例对象,调用该实例的getClass()方法获取Class对象
// Class<?> aClass = Class.forName("bh.test1.Person");
// Object m = aClass.newInstance();
// Class<?> aClass1 = m.getClass();
// 3.使用全类名配合forName()方法获得class对象
// Class<?> aClass2 = Class.forName("bh.test1.Person");
// 默认调用的是无参数的构造方法
Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class, int.class);
// Person hehe = constructor.newInstance("hehe", 28);
// System.out.println(hehe);
int modifiers = constructor.getModifiers();
System.out.println(modifiers);
/*
* 通过Modifier类中的isPublic,isprotected,isPrivate方法
* 可以判断某个方法是否是public,protected还是private
* */
if (Modifier.isPublic(modifiers)) {
System.out.println("now constructor is public");
} else if (Modifier.isProtected(modifiers)) {
System.out.println("now constructor is protected");
} else if (Modifier.isPrivate(modifiers)) {
System.out.println("now constructor is private");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
0x02 反射的常用方法
以下方法基本上涵盖了java
安全中各种和反射有关的payload
forName
作用:动态地加载类,这个方法是java.lang.Class
类的一部分,用于加载具有指定名字的类。
在正常情况下,除了系统类,想获得一个类,需要先 import
。而使用forName
就不需要,这对于我们攻击者来说十分有利,可以加载任意类。
forName
的两个重载
- 常见的获取class的方式,可以理解为第二种形式的一个封装
Class<?> forName(String name)
Class<?> forName(String name, **boolean** initialize, ClassLoader loader)
- 第一个参数为类名,第二个参数为是否初始化,第三个参数为
ClassLoader
Class.forName(className) <==> Class.forName(className, true, currentLoader)
第三个参数ClassLoader
是一个加载器,告诉Java
虚拟机如何加载这个类,后面再详细介绍漏洞利用方法。Java
默认的ClassLoader
就是根据类名来加载类,这个类名是完整路径,如java.lang.Runtime
。
然而对于第二个参数initialize
来说是作为初始化,那么初始化指的是哪部分?
可以将这个初始化
理解为类的初始化
public class Person {
{
System.out.printf("Empty block initial %s\n", this.getClass());
}
static {
System.out.printf("Static initial %s\n", TrainPrint.class);
}
public Person() {
System.out.printf("Initial %s\n", this.getClass());
}
}
上面这个类首先调用的是static{}
,其次是{}
,最后是构造函数
。其中static{}
就是再类初始化的时候调用的,而{}
中的代码会放在构造函数的super()
后面,但在当前构造函数内容的前面。
所以forName
中的initialize=true
其实就是告诉Java
虚拟机是否执行类初始化
。
那么假设又如下函数,其中函数的参数name可控
public void ref(String name) throws Exception {
Class.forName(name);
}
就可以编写一个恶意类,将恶意代码放置在static{}
中,从而执行,这个恶意类如何带入目标机器中,涉及到ClassLoader的一些利用方法。
package com.example;
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
newInstance
作用:实例化对象的方法,动态地创建类的实例,调用类的无参构造函数。这个方法是java.lang.Class
类的一部分,用于创建给定类的新实例。
如使用class.newInstance()
可能会失败,原因是以下问题导致的
- 使用的类没有无参构造函数
- 使用的类构造函数是私有的
package org.example;
public class test {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("java.lang.Runtime");
aClass.getMethod("exec", String.class).invoke(aClass.newInstance(), "id");
}
}
以下报错是因为Runtime类的构造方法是私有的
这是由于Runtime
使用到了“单例模式”,只有类初始化的时候会执行一次构造函数,只能通过Runtime.getRuntime()
来获取到Runtime
对象。
Java单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的特点如下:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 单例模式可以确保某个类只有一个实例,并且提供全局访问点。
修改上面代码,如下
package org.example;
public class test {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("java.lang.Runtime");
// aClass.getMethod("exec", String.class).invoke(aClass.newInstance(), "id");
aClass.getMethod("exec", String.class).invoke(aClass.getMethod("getRuntime").invoke(aClass), "calc");
}
}
getMethod
作用:通过反射动态地获取一个类的某个特定的公有方法。这个方法是java.lang.Class
类的一部分,用于获取给定类中定义的方法。
Java中支持类的重载无法通过函数名来确定一个函数,在调用getMethod时,依据需要获取的函数的参数来判断,如Runtime.getRuntime().exec
的方法重载
最简单是使用getMethod("exec", String.class)
来获取Runtime.exec
方法,一个参数,类型String
invoke
作用:动态地调用类的方法,执行方法。这个方法是java.lang.Class
类的一部分,用于调用一个对象的方法或构造函数。需要两个参数:一个对象(如果调用的是非静态方法),一个方法名和一个参数列表。
参数:
如果这个方法是一个普通方法,那么第一个参数是类对象
如果这个方法是一个静态方法,那么第一个参数是类
正常执行方法是[1].method([2], [3], [4]...)
反射里就是method.invoke([1], [2], [3], [4]...)
静态方法:因为它直接与类Person关联;普通方法:因为它们与类的实例关联。
package org.example;
public class Person {
private String name;
// 静态方法,因为它直接与类Person关联
public static int getMax(int a, int b) {
return Math.max(a, b);
}
// 普通方法,因为它们与类的实例关联。
public String getName() {
return name;
}
}
将上面命令执行payload
分解后如下
package org.example;
import java.lang.reflect.Method;
public class test {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("java.lang.Runtime");
aClass.getMethod("exec", String.class).invoke(aClass.getMethod("getRuntime").invoke(aClass), "calc");
Method exec = aClass.getMethod("exec", String.class);
Method getRuntime = aClass.getMethod("getRuntime");
Object invoke = getRuntime.invoke(aClass);
exec.invoke(invoke, "calc");
}
}
0x03 思考
一个类没有无参构造方法,也没有类似单例模式里的静态方法,改如何通过反射实例化该类?
getConstructor
使用新的反射方法getConstructor
,获取一个类的指定构造函数
接受的参数是构造函数列表类型,因为构造函数也支持重载,所以必须用参数列表类型才能确定唯一一个构造函数,与getMethod("exec", String.class)
类似,获取到构造函数,然后使用newInstance
来实例化对象。
(List<String> command)
如另一种执行命令的方式ProcessBuilder
,使用反射来获取构造函数,然后调用start()
来执行命令
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
public class testcc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
}
}
ProcessBuilder
有两个构造函数,上面使用的是第一种构造函数,所以传参是List.class
public ProcessBuilder(List<String> command) {
if (command == null)
throw new NullPointerException();
this.command = command;
}
public ProcessBuilder(String... command) {
this.command = new ArrayList<>(command.length);
for (String arg : command)
this.command.add(arg);
}
构造反射-(List<String> command)
上面payload使用了Java强制类型转换,一般来说在利用漏洞时(表达式上下文中)没有这种语法,所以需要利用反射来完成这一步。
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
public class testcc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
// ((ProcessBuilder) aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
aClass.getMethod("start").invoke(aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));
}
}
通过getMethod("start")
获取到start
方法,然后invoke
执行,invoke
第一个参数就是ProcessBuilder Object
(String... command)
当定义函数时不确定参数数量,可以使用...
来表示这个函数的参数个数是可变的
对于可变长参数,Java会将其编译成一个数组,下面两种写法在底层是等价的(无法重载)
public void hello(String[] names) {}
public void hello(String... names) {}
对于反射来说,如果要获取的目标函数里包含可变长参数,我们可以默认它是数组
将字符串数组的类String[].class
传给getConstructor
,获取ProcessBuilder
的第二种构造函数
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class testcc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();
}
}
构造反射-(String... command)
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class testcc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
// ((ProcessBuilder) aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();
aClass.getMethod("start").invoke(aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}}));
}
}
一个方法或构造方法是私有方法,改如何执行?
getDeclared
使用getDeclared
系列的反射
getMethod
系列方法获取的是当前类中所有公共方法,包括从父类继承的方法getDeclaredMethod
系列方法获取的是当前类中“声明”的方法,是存在当前这个类里,包括私有的方法,(但从父类里继承来的不包含?)
getDeclaredMethod
的具体用法和getMethod
类似,getDeclaredConstructor
的具体用法和getConstructor
类似
Java
反射API
中的方法
getMethod
和getDeclaredMethod
:这两个方法用于获取类的特定方法。
getMethod
返回指定类中声明的公共方法(包括从父类继承的方法)。如果方法不存在,它返回null
。getDeclaredMethod
返回指定类中声明的任意方法(包括从父类继承的方法)。如果方法不存在,它抛出NoSuchMethodException
。
getConstructor
和getDeclaredConstructor
:这两个方法用于获取类的特定构造函数。
getConstructor
返回指定类中的公共构造函数(包括从父类继承的构造函数)。如果构造函数不存在,它返回null
。getDeclaredConstructor
返回指定类中声明的任意构造函数(包括从父类继承的构造函数)。如果构造函数不存在,它抛出NoSuchMethodException
。
简单来说,如果你想获取公共方法或公共构造函数,可以使用 getMethod
或 getConstructor
。如果你想获取类中声明的任意方法或任意构造函数(包括从父类继承的),你可以使用 getDeclaredMethod
或 getDeclaredConstructor
。
getDeclaredConstructor
Runtime
类的构造函数是私有的,需要用Runtime.getRuntime()
来获取对象。现在也可以使用getDeclaredConstructor
来获取这个私有的构造方法来进行实例化对象,然后执行命令
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class testcc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class<?> aClass = Class.forName("java.lang.Runtime");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
aClass.getMethod("exec", String.class).invoke(declaredConstructor.newInstance(), "calc.exe");
}
}
setAccessible
方法:在获取到一个私有方法后,必须用setAccessible
修改它的作用域,否则无法调用