有反射必然就有正射,而正射就是我们平时写代码的正常逻辑。直接通过类和对象调用其方法或访问其属性,这种方式是静态的,意味着编译时就已经确定了方法的调用以及对象的属性访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package org.example;class MyClass { public void sayHello () { System.out.println("Hello, World!" ); } } public class Main { public static void main (String[] args) { MyClass myClass = new MyClass (); myClass.sayHello(); } }
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.
反射就是一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这是 Java 提供的一种动态机制,它允许程序在运行时获取有关类的信息,并能在不事先知道类或对象类型的情况下,动态地调用它们的方法或访问它们的属性
通过使用反射我们不仅可以获取到任何类的成员方法 (Methods)、成员变量 (Fields)、构造方法 (Constructors) 等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等
反射的基本使用
获取 Class 对象
主要有四种方法:
使用 Class.forName() 静态方法
使用类的 .class 方法
使用实例对象的 getClass() 方法
使用类加载器的 loadClass() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package org.example; public class Main { public static void main (String[] args) throws Exception{ String className = "java.lang.Runtime" ; Class class1 = Class.forName(className); System.out.println(class1.getName()); Class class2 = Runtime.class; System.out.println(class2.getName()); Runtime runtime = Runtime.getRuntime(); Class class3 = runtime.getClass(); System.out.println(class3.getName()); Class class4 = ClassLoader.getSystemClassLoader().loadClass(className); System.out.println(class4.getName()); } }
反射创建对象
主要是通过 Class 和 Constructor 的 newInstance() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package org.example; import java.lang.reflect.Constructor; class MyClass { public void sayHello () { System.out.println("Hello, World!" ); } } public class Main { public static void main (String[] args) throws Exception{ String className = "org.example.MyClass" ; Class class1 = Class.forName(className); MyClass myClass = (MyClass) class1.newInstance(); System.out.println(myClass); Constructor constructor = class1.getDeclaredConstructor(); MyClass myClass1 = (MyClass) constructor.newInstance(); System.out.println(myClass1); } }
反射获取类的构造器
getConstructor() 返回当前类单个 public 构造器,getConstructors() 返回当前类所有的 public 的构造器,是一个数组
getDeclaredConstructor() 返回当前类指定参数类型的单个构造器,无视权限,getDeclaredConstructors() 返回当前类中所有声明的构造器,是一个数组
注意这里是当前类 ,因为与方法不同,构造器不会被继承
反射获取类的方法
方法名
访问权限
范围
返回类型
getMethod()
仅 Public
包含父类/接口
Method
getDeclaredMethod()
所有 (Public/Private/…)
仅当前类
Method
getMethods()
仅 Public
包含父类/接口
Method[]
getDeclaredMethods()
所有 (Public/Private/…)
仅当前类
Method[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package org.example; import java.lang.reflect.Method; class MyClass { public void sayHello () { System.out.println("Hello, World!" ); } } public class Main { public static void main (String[] args) throws Exception{ String className = "org.example.MyClass" ; Class class1 = Class.forName(className); Method[] methods = class1.getMethods(); for (int i = 0 ; i < methods.length; i++) { System.out.println(methods[i]); } } }
这里 org. Example. MyClass 继承了 Object 类中的所有公共方法
1 2 Method method = class1.getMethod("sayHello" ); method.invoke(class1.getDeclaredConstructor().newInstance());
使用 invoke() 调用具体方法,第一个参数必须是类实例对象,如果调用的是 static 方法那么第一个参数值可以传 null
反射获取成员变量
方法名
访问权限
范围
返回类型
getField(String name)
仅 Public
包含父类/接口
Field
getDeclaredField(String name)
所有 (Public/Private/…)
仅当前类
Field
getFields()
仅 Public
包含父类/接口
Field[]
getDeclaredFields()
所有 (Public/Private/…)
仅当前类
Field[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package org.example; import java.lang.reflect.Field; class MyClass { public Integer age; private String email; public void sayHello () { System.out.println("Hello, World!" ); } } public class Main { public static void main (String[] args) throws Exception{ String className = "org.example.MyClass" ; Class class1 = Class.forName(className); Field[] fields = class1.getDeclaredFields(); for (int i = 0 ; i < fields.length; i++) { System.out.println(fields[i]); } } }
1 2 3 Field field1 = class1.getField("age" ); Object value = field1.get(class1.getDeclaredConstructor().newInstance()); System.out.println("Value of age: " + value);
使用 get(类实例对象) 获取具体值
反射 java. Lang. Runtime
java.lang.Runtime 类使用的是单例模式,意味着 Java 设计者为了保证全系统只有一个运行环境实例,将它的构造方法设置为了 private。而 Class.newInstance() 方法只能调用类的 public 无参构造方法,因此我们有两种方式来获取 Runtime 实例
使用标准的单例模式:
通过提供的静态方法 getRuntime() 来获取实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package org.example; import java.lang.reflect.Method; public class Main { public static void main (String[] args) throws Exception{ String className = "java.lang.Runtime" ; Class class1 = Class.forName(className); Method method = class1.getMethod("getRuntime" ); Object runtime = method.invoke(null ); Method execMethod = class1.getMethod("exec" , String.class); execMethod.invoke(runtime, "calc.exe" ); } }
通过强行破坏单例模式,获取构造方法(通过设置 setAccessible(true) 在 Java 9 即以上的版本中会发生报错,主要原因还是 JVM 对权限的检查,防止滥用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package org.example; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class Main { public static void main (String[] args) throws Exception{ String className = "java.lang.Runtime" ; Class class1 = Class.forName(className); Constructor constructor = class1.getDeclaredConstructor(); constructor.setAccessible(true ); Object runtime = constructor.newInstance(); Method execMethod = class1.getDeclaredMethod("exec" , String.class); Process process = (Process) execMethod.invoke(runtime, "calc.exe" ); InputStream inputStream = process.getInputStream(); System.out.println(org.apache.commons.io.IOUtils.toString(inputStream, "UTF-8" )); } }