反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
Oracle官方对反射的解释是
Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.
简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,
Java反射框架主要提供以下功能:
- 1.在运行时判断任意一个对象所属的类;
- 2.在运行时构造任意一个类的对象;
- 3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 4.在运行时调用任意一个对象的方法
下面来简单实现一下这几个功能。
1.运行时判断任意一个对象所属的类。
Date date = new Date(); System.out.println(date.getClass().getName());
输出:java.util.Date
可以用这个来做很多的判断。特别是某些同质化类的区分处理。
2.在运行时构造任意一个类的对象;
一般来说,我们构造一个类都是用new的。
但是也可以使用这种方式:
Class temp = Class.forName("java.util.Date"); System.out.println(temp.getMethod("getTime").invoke(temp.newInstance()));
输出:1532439915439
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
4.在运行时调用任意一个对象的方法
我们先创建一个user类,
public class User { private String username; private String phone; private String address; public String getUsername() { return username; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public void setUsername(String username) { this.username = username; } }
然后尝试使用反射的方法获取这个user的属性,方法。
先初始化一个user.
User user = new User(); user.setAddress("广东省广州市"); user.setPhone("15800000000"); user.setUsername("田二妞");
//获取user类的属性 Field[] fields = user.getClass().getDeclaredFields(); for(Field f:fields) System.out.println(f.getName());
输出:
username
phone
address
可以看到这时候可以获取到对应的参数了。
然后尝试调用对应的方法。
System.out.println( user.getClass().getMethod("getUsername").invoke(user));
输出:田二妞。
这里重点介绍一下invoke方法。
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
对带有指定参数的指定对象调用由此 Method
对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj
参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args
数组长度可以为 0 或 null。
如果底层方法是实例方法,则使用动态方法查找来调用它,在发生基于目标对象的运行时类型的重写时更应该这样做。
如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
参数:
obj
- 从中调用底层方法的对象
args
- 用于方法调用的参数
返回:
使用参数 args
在 obj
上指派该对象所表示方法的结果
抛出:
IllegalAccessException
- 如果此 Method
对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。
IllegalArgumentException
- 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
InvocationTargetException
- 如果底层方法抛出异常。
NullPointerException
- 如果指定对象为 null,且该方法是一个实例方法。
ExceptionInInitializerError
- 如果由此方法引起的初始化失败。
反射的应用很广泛,比如在公共的日志模块,控制器模块,ioc模块,DI,等等都运用很多。掌握和了解反射能够优化代码,减少代码量。