一、概述
根据虚拟机的工作原理,一般情况下,类需要经过:加载->验证->准备->解析->初始化->使用->卸载这个过程,如果需要反射的类没有在内存中,那么首先会经过加载这个过程,并在在内存中生成一个class对象,有了这个class对象的引用,就可以发挥开发者的想象力,做自己想做的事情了。
反射的作用
- 需要访问隐藏属性或者调用方法改变程序原来的逻辑,这个在开发中很常见的,由于一些原因,系统并没有开放一些接口出来,这个时候利用反射是一个有效的解决方法
- 自定义注解,注解就是在运行时利用反射机制来获取的。
- 在开发中动态加载类,比如在Android中的动态加载解决65k问题等等,模块化和插件化都离不开反射,离开了反射寸步难行。
二、具体API
1、java.lang.Class 类
java.lang.Class 类是Java中的反射中心。
Class类的一个对象表示运行时程序中的一个类。 我们可以使用 Class 类在运行时查找类的信息。
我们可以通过以下方式获取类的Class对象的引用:
- 使用类文字
Class<Test> testClass = Test.class;
复制代码
int.class 和 Integer.TYPE 指的是同一个类对象。
Class c = boolean.class;
c = Boolean.TYPE;
复制代码
- 使用 Object 类的 getClass() 方法
class Test{
}
public class Main {
public static void main(String[] args) {
Test testRef = new Test();
Class testClass = testRef.getClass();
}
}
复制代码
- 使用Class类的forName()方法
//源码:
Class<?> forName(String className)
Class<?> forName(String name, boolean initialize, ClassLoader loader)
//示例
class MyClass {
static {
System.out.println("Loading class MyClass...");
}
}
public class Main {
public static void main(String[] args) {
try {
String className = "MyClass";
boolean initialize = false;
ClassLoader cLoader = Main.class.getClassLoader();
Class c = Class.forName(className, initialize, cLoader);
className = "MyClass";
System.out.println("about to load");
// Will load and initialize the class
c = Class.forName(className);
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
}
}
复制代码
2、Java类反射
我们可以使用Java反射来获取关于类的信息,例如作为其包名称,其访问修饰符等,详细如下:
- 获取类名
String simpleName = boolean.class.getSimpleName();
复制代码
- 获取类的修饰符
类的修饰符是关键字之前的关键字类在类声明中,如 abstract , public Class 中的 getModifiers()方法返回类的所有修饰符 getModifiers()方法返回一个整数。我们必须调用 java.lang.reflect.Modifier.toString(int modifiers)以获得修饰符的文本形式(待写代码测试)
- 要获取超类(父类)的名称
请使用 Class 中的 getSuperclass()方法。 如果对Object类调用getSuperclass()方法,它将返回null,因为它没有超类。
- 要获取类实现的所有接口的名称
Class[] interfaces = c.getInterfaces();
复制代码
3、Java字段反射
我们可以使用java.lang.reflect.Field类来获取关于类中的字段的信息。在Class类返回关于字段的 Field 对象有以下四种:
- getFields()方法返回所有可访问的公共字段在类中声明或继承自超类。
Field[] getFields()
复制代码
- getDeclaredFields()方法返回所有字段只出现在类的声明中(不是从继承的字段)。
Field[] getDeclaredFields()
复制代码
- getField(String name)通过字段名获取 Field 对象,包括父类以及实现的接口类。
Field getField(String name)
复制代码
- getDeclaredField(String name)通过(本类中)字段名获取 Field 对象。
Field getDeclaredField(String name)
复制代码
4、Java 方法反射
方法反射关键字如下:
- 方法:java.lang.reflect.Method 类的实例表示一个
方法
,继承自一个通用的抽象超类可执行
—— Executable - 参数:方法中的
参数
由 Parameter 类的对象表示
getParameters()方法获取所有参数作为 Parameter 的数组 getTypeParameters()方法返回一个TypeVariable数组,该数组表示通用方法或构造函数的类型参数
- 修饰符:方法中的
修饰符
getModifiers()方法将修饰符作为int返回
5、Java 构造函数反射
Class 类获取有关构造函数的信息有如下四种:
- getConstructors()方法返回当前和超类的所有公共构造函数。
Constructor[] getConstructors()
复制代码
- getDeclaredConstructors()方法返回当前类的所有声明的构造函数。
Constructor[] getDeclaredConstructors()
复制代码
- getConstructor(Class ... parameterTypes)通过参数类型获取构造函数对象。
Constructor<T> getConstructor(Class... parameterTypes)
复制代码
- getDeclaredConstructor(Class ... parameterTypes)通过参数类型获取构造函数对象。
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
复制代码
6、Java反射对象创建
我们可以使用反射动态创建类的对象。通过调用其中一个构造函数。然后我们可以访问对象的字段的值,设置它们的值,并调用它们的方法。
- 创建反射对象有两种方法:
- 使用no-args构造函数
- 使用带参数的构造函数
- 无参数构造函数
如果你有一个 Class 对象的引用,你可以创建一个对象该类对Class类使用 newInstance()方法。此方法不使用参数,并且是等效的使用new运算符的类的no-args构造函数。
String s = String.class.newInstance();
复制代码
- 带参数的构造函数
通过调用特定的构造函数使用反射创建对象。示例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class MyClass {
public MyClass(int i, String s) {
System.out.println("called");
System.out.println(i);
System.out.println(s);
}
}
public class Main {
public static void main(String[] args) {
//第一步
Class<MyClass> myClass = MyClass.class;
try {
//第二步
Constructor<MyClass> cons = myClass.getConstructor(int.class,
String.class);
//第三步
MyClass chris = cons.newInstance(1, "abc");
System.out.println(chris);
} catch (NoSuchMethodException | SecurityException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
System.out.println(e.getMessage());
}
}
}
复制代码
- 调用方法
通过上面的构造方法拿到对象之后,就可以开始调用方法了,直接看示例:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class MyClass {
public MyClass() {
}
public void setName(String n) {
System.out.println(n);
}
}
public class Main {
public static void main(String[] args) {
//第一步
Class<MyClass> myClass = MyClass.class;
try {
//第二步
MyClass p = myClass.newInstance();
//第三步
Method setName = myClass.getMethod("setName", String.class);
//第四步,调用方法(静态方法第一个参数可为null,待测试)
setName.invoke(p, "abc");
} catch (InstantiationException | IllegalAccessException
| NoSuchMethodException | SecurityException | IllegalArgumentException
| InvocationTargetException e) {
System.out.println(e.getMessage());
}
}
}
复制代码
7、Java 反射字段访问
反射字段访问分为读取字段的值和设置字段的值,而且字段访问不分静态和非静态(都一样),具体看示例:
import java.lang.reflect.Field;
class MyClass {
public String name = "Unknown";
public MyClass() {
}
public String toString() {
return "name=" + this.name;
}
}
public class Main {
public static void main(String[] args) {
//第一步
Class<MyClass> ppClass = MyClass.class;
try {
//第二步
MyClass p = ppClass.newInstance();
//第三步 获取字段
Field name = ppClass.getField("name");
//如果是私有字段,需要添加下面注释的这一步
//nameField.setAccessible(true);
//第四步 取字段的值
String nameValue = (String) name.get(p);
System.out.println("Current name is " + nameValue);
//第四步 设置字段的值
name.set(p, "abc");
nameValue = (String) name.get(p);
System.out.println("New name is " + nameValue);
} catch (InstantiationException | IllegalAccessException
| NoSuchFieldException | SecurityException | IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
复制代码
8、Java数组反射
- 通过反射创建数组并操作器元素
import java.lang.reflect.Array;
public class Main {
public static void main(String[] args) {
try {
//int[] my = new int[2];
Object my = Array.newInstance(int.class, 2);
int n1 = Array.getInt(my, 0);
int n2 = Array.getInt(my, 1);
System.out.println("n1 = " + n1 + ", n2=" + n2);
Array.set(my, 0, 11);
Array.set(my, 1, 12);
n1 = Array.getInt(my, 0);
n2 = Array.getInt(my, 1);
System.out.println("n1 = " + n1 + ", n2=" + n2);
} catch (NegativeArraySizeException | IllegalArgumentException
| ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
}
}
}
复制代码
- 通过反射获取数组的维度,如三维数组
public class Main {
public static void main(String[] args) {
int[][][] intArray = new int[1][2][3];
System.out.println("int[][][] dimension is " + getArrayDimension(intArray));
}
public static int getArrayDimension(Object array) {
int dimension = 0;
Class c = array.getClass();
if (!c.isArray()) {
throw new IllegalArgumentException("Object is not an array");
}
while (c.isArray()) {
dimension++;
//数组元素的类型
c = c.getComponentType();
}
//最终的数组维度
return dimension;
}
}
复制代码
- 通过反射动态扩展数组
Java数组是一个固定长度的数据结构。要放大数组,我们可以创建一个更大尺寸的数组,并将旧数组元素复制到新数组元素。
import java.lang.reflect.Array;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ids = new int[2];
System.out.println(ids.length);
System.out.println(Arrays.toString(ids));
ids = (int[]) expandBy(ids, 2);
ids[2] = 3;
System.out.println(ids.length);
System.out.println(Arrays.toString(ids));
}
public static Object expandBy(Object oldArray, int increment) {
Object newArray = null;
int oldLength = Array.getLength(oldArray);
int newLength = oldLength + increment;
Class<?> c = oldArray.getClass();
newArray = Array.newInstance(c.getComponentType(), newLength);
System.arraycopy(oldArray, 0, newArray, 0, oldLength);
return newArray;
}
}
复制代码
三、推荐反射工具类(源码为kotlin)
四、结束语
本文为学习笔记,全文来自Java反射
文章图片以及概述来自Java反射以及在Android中的特殊应用
最后感谢阅读!