深入理解Java反射机制:动态加载类与操作对象的方法
反射相关的概念

反射是Java的特征之一。它是间接操作目标对象的机制。核心是JVM仅在运行时动态加载该类。对于任何班级,您都可以知道此类的所有属性和方法以及任何类别。对象可以调用其方法/访问属性。动态获取信息和动态调用对象方法的这种功能已成为Java语言的反射机制。通过使用反射,我们不仅可以获取任何类的信息,例如任何类的成员方法,成员变量和构造函数,而且还可以动态创建Java类实例,调用任何类方法并修改任何类成员。可变值等
(或可以直接移动)
类对象获取
对于普通用户,我们可以使用以下方法来创建一个实例:
Person test = new Person();
但是,当我们创建类的实例对象时,我们无法使用上述方法,并且在运行它时会抛出错误。
Class test = new Class();
因为其构造函数是私有的,所以只有JVM才能创建类对象
获取方法
1。类属性
第一个是我们可以通过类属性类获得的最简单和最清晰的方法。
Class c1=ReflectDemo.class;
2。getClass()实例化对象的方法
第二种方式,我们可以先实例化对象,然后调用getClass()方法。
ReflectDemo demo2= new ReflectDemo();
Class c2 = demo2.getClass();
3。class.forname(字符串className):动态加载类
第三种类型是调用类类中的前字符方法,将字节码文件加载到内存中,然后返回类对象。
Class c3 = Class.forName("reflectdemo.ReflectDemo");
笔记
我们通常使用第三种方法通过class.forname方法动态加载类。并且使用Forname不需要导入其他类,我们可以加载我们的任何类。
使用类。类属性需要导入类软件包,这太可靠了,很容易在大型项目中丢弃汇编错误;
使用实例化对象的GetClass()方法需要创建对象本身,而对象本身无需使用反射机制。
因此,当我们获取类对象时,我们通常使用类。Forname方法获取它。
需要特别注意以获取数组类型的类对象,并且需要使用Java类型的描述方法,如下所示:
Class<?> doubleArray = Class.forName("[D");//相当于double[].class
Class<?> cStringArray = Class.forName("[[Ljava.lang.String;");// 相当于String[][].class
获取运行时类类对象代码段:
String className = "java.lang.Runtime";
Class runtimeClass1 = Class.forName(className);
Class runtimeClass2 = java.lang.Runtime.class;
Class runtimeClass3 = ClassLoader.getSystemClassLoader().loadClass(className);
您可以通过上述任何方法获得Java.lang.runtime类的类对象。反映对内类的呼叫时,您需要使用$。例如,com.anbai.test类具有一个名为Hello的内部类,因此在您进入过程时称其为:com.anbai.test $ hello。
找成员
获得上一个类对象后,您可以通过以下方法获得成员变量字段:
field [] getfields():获取所有公共修改的成员变量
field [] getDeclaredFields()获取所有成员变量,而无需考虑修饰符
field getfield(字符串名称)获取指定名称的公共修改成员变量
字段getDeclaredField(字符串名称)获取指定的成员变量
成员方法,构造函数相似。
获得Java.lang.reflect.Method对象后,我们可以通过该方法的Invoke方法调用类方法。
Java反射不仅可以获取类的所有成员变量的名称,而且还可以修改相应的值,而不管权限修改器的任何内容。
获取成员变量值:
Object obj = field.get(类实例对象);
修改成员变量值:
field.set(类实例对象, 修改后的值);
当我们没有修改成员变量权限时,我们可以使用该方法:field.setAccessible(true)将其修改为访问成员变量访问权限。
如果我们需要修改最终关键字修改的成员变量,则需要先修改方法
// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");
// 设置modifiers修改权限
modifiers.setAccessible(true);
// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// 修改成员变量值
field.set(类实例对象, 修改后的值);
反射创建类对象
通常,我们使用类对象的NewInstance()方法来创建类对象。
使用的方法也很简单。您只需要在通过Forname方法获得的类对象中创建NewInstance方法。
Class c = Class.forName("com.reflect.MethodTest"); // 创建Class对象Object m1 = c.newInstance(); // 创建类对象
例如:
import java.lang.reflect.Method;
public class ReflectTest {
public void reflectMethod() {
System.out.println("反射测试成功!!!");
}
public static void main(String[] args) {
try {
Class c = Class.forName("com.reflect.ReflectTest"); // 创建Class对象
Object m = c.newInstance(); // 创建类实例对象
Method method = c.getMethod("reflectMethod"); // 获取reflectMethod方法
method.invoke(m); // 调用类实例对象方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
笔记
public Object invoke(Object obj, Object... args)
Invoke方法的第一个参数未固定:
如果调用此方法是一种普通方法,则第一个参数是类对象。
如果此方法称为静态方法,则第一个参数是类。
反射java.lang.runtime
java.lang.runtime具有可以执行本地命令的EXEC方法,因此在许多有效负载中,我们可以看到反射调用运行时类以执行本地系统命令。
反射运行时执行本地命令代码段:
// 获取Runtime类对象
Class runtimeClass1 = Class.forName("java.lang.Runtime");
// 获取构造方法
Constructor constructor = runtimeClass1.getDeclaredConstructor();constructor.setAccessible(true);
// 创建Runtime类示例,等价于
Runtime rt = new Runtime();Object runtimeInstance = constructor.newInstance();
// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClass1.getMethod("exec", String.class);
// 调用exec方法,等价于
rt.exec(cmd);Process process = (Process) runtimeMethod.invoke(runtimeInstance, cmd);
// 获取命令执行结果
InputStream in = process.getInputStream();
// 输出命令执行结果
System.out.println(IOUtils.toString(in, "UTF-8"));
为什么设置可访问?
Java中的任何类都必须具有一个或多个构造函数。如果在代码中未创建构造函数,则在编译类时将自动创建无参数构造函数。
public class Runtime {
/** Don't let anyone else instantiate this class */
private Runtime() {}
}
从上面的运行时类代码注释中,我们看到它不希望除本身以外的任何人创建此类的实例,因为这是一个私人类构造函数,因此我们无法进入新的Runtime类实例,也就不可能使用运行时RT = new Runtime();创建一个运行时对象,但借助反射机制,修改方法访问权限,以便可以间接创建运行时对象。
runtimeclass1.getDeclaredConstructor和runtimeclass1.getConstructor都可以获取类构造函数。区别在于,后者无法获得私人方法,因此通常在获得某个类别的构造函数时,我们将使用前者来获取构造函数。如果构造函数具有一个或多个参数,则在获取构造函数时,我们应该在相应的参数类型数组中传递,例如:clazz.getDeclaredConstructor(String.Class,string.class)。
如果我们想获取类的所有构造方法,我们可以使用:clazz.getDeclaredConstructors获取构造函数数组。
获得构造函数后,我们可以通过constructor.newinstance()创建类实例。同样,如果有参数,我们应该传递相应的参数值,例如:construptor.newinstance(“ admin”,“ 123456”)。如果我们无法访问构造函数方法的权限,则应调用constructor.setAccessible(true)以修改访问权限以成功创建类实例。
如果您不执行私人方法?
Runtime.getRuntime().exec(String command)//重载Runtime.exec