反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
反射允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先知道运行对象是谁。
1 反射的作用
反射机制可以用来:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
2 反射的优缺点
2.1 反射的优点
- 可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
- 类浏览器和可视化开发环境:一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
- 调试器和测试工具:调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
2.2 反射的缺点
反射机制对于编写系统程序来说极其实用,但是通常不适于编写应用程序。
反射是很脆弱的,即编译器很难帮助人们发现程序中的错误。任何错误只能在运行时才被发现,并导致异常。
- 性能开销:反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
- 安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
- 内部暴露:反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
3 基本运用
反射相关的类一般都在 java.lang.relfect 包里,java.lang.reflect 类库主要包含了以下三个类:
- Field:可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
- Method:可以使用 invoke() 方法调用与 Method 对象关联的方法;
- Constructor:可以用 Constructor 的 newInstance() 创建新的对象。
3.1 获得 Class 对象
获得类对象的方式有三种:
- 类型.class
例如:Class<?> class = String.class - 调用对象getClass()
例如:Class<?> class = “hello”.getClass() - Class.forName()
例如:Class<?> class = Class.forName(“java.lang.String”)
虚拟机为每个类管理一个 Class 对象。因此,可以利用 == 运算符实现两个类对象比较的操作。
3.2 判断是否为某个类的实例
- 一般用 instanceof 关键字来判断是否为某个类的实例。
- 同时也可用反射中 Class 对象的 isInstance() 方法来判断:class.isInstance(obj);
3.3 创建实例
- 调用 Class 类的 newInstance()
Class<?> c = String.class;
Object str = c.newInstance();
- 调用 Constructor 类的 newInstance()
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
3.4 获取方法
获取某个 Class 对象的方法集合
- getDeclaredMethods
返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 - getMethods
返回某个类的所有公用方法,包括超类的公用方法。 - getMethod
返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应 Class 的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
3.5 获取构造器信息
获取某个 Class 对象的构造器集合,与获取方法用法相似
- getDeclaredConstructors
- getConstructors 返回public构造器,包括超类的公有构造器
- getConstructor
3.6 获取类的域
- getFiled、getFileds
返回 public 域,包括超类的公有域 - getDeclaredField、getDeclaredFields
所有已声明的全部域,包括私有和受保护域,但不包括超类的域
3.7 调用方法
利用 Method 类中的 invoke 方法,调用任意方法。
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
3.8 获取修饰符
Modifier 类的静态方法分析 getModifiers 返回的整型数值,isPublish、isPrivate 或 isFinal 判断方法或构造器是否是 public、private 或 final。
3.9 利用反射创建数组
java.lang.reflect 包中的 Array 类允许动态地创建数组。
public static void testArray() throws ClassNotFoundException {
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
//获取某一项的内容
System.out.println(Array.get(array,0));
}