概念
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。(百度百科)
反射机制指的是可以于运行时加载、探知、使用编译期间完全未知的类。已知一个类的地址及名称,就可以使用此类。
反射原理:对象用来表示或封装一些数据。一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中。获取到class对应的Class实例(对象)后,就可以获取该class的所有信息。通过Class实例获取class信息的方法称为反射。
一个类只会被加载一次,因此一个类的Class对象是唯一的,且只能由JVM创建。
反射把java类中的各种结构(方法、属性、构造器、类名)映射成一个个的Java对象。利用反射技术可以对一个类进行解剖,反射是框架设计的灵魂。
使用反射后,如果在编译时无法获取某个类,只通过类名就可使编译通过。
花费我好多心思抄的一张图片。(读书人的事怎么能叫抄呢?)
作用
- 动态加载类、动态获取类的信息(属性、方法、构造器等)
- 动态构造对象
- 动态调用类和对象的任意方法、构造器
- 动态调用和处理信息
- 获取泛型信息
- 处理注解
什么是动态?程序运行时,改变程序结构或变量类型。
获取Class对象的三种方法
- 实例对象.getClass();
- 类.class;
- Class.forName(path);
示例1:
import java.lang.reflect.InvocationTargetException;
/**
* 反射:java类中的各种结构(方法、属性、构造器、类名)映射成一个个的Java对象
* 1、获取Class对象
* 共有三种方式,但最好使用class.forName("包名.类名");
* 因为此方式不要求 在编译时存在类
* 2、可以动态创建对象
* @author dxt
*
*/
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
//1. 三种方式获取Class对象
//1.1 对象.getClass()
Phone p = new Phone();
Class clz = p.getClass();
//1.2 类.class
clz = Phone.class;
//1.3 class.forName("包名.类名");
clz = Class.forName("dxt.basic.Phone");
//2. 动态创建对象
Phone p2 = (Phone)clz.getConstructor(null).newInstance(null);
System.out.println(p2);
}
}
class Phone{
public Phone(){
}
}
示例2:
package dxt.test;
/**
* 测试各种类型对应的java.lang.Class对象的获取方式
* @author dxt
*
*/
@SuppressWarnings("all") //压制警告
public class Demo01 {
public static void main(String[] args){
String path = "dxt.test.bean.User";
try {
Class c = Class.forName(path);
//对象用来表示或封装一些数据。一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中
//通过这个Class对象,我们可以获取对应类的全部信息。
System.out.println(c);
System.out.println(c.hashCode());
Class c2 = Class.forName(path);
System.out.println(c2.hashCode());
Class strClazz = String.class;
Class strClazz2 = path.getClass();
System.out.println(strClazz == strClazz2);
Class intClass = int.class;
//相同类型和维度的数组对应同一个Class对象
int[] arr01 = new int[10];
int[] arr02 = new int[20];
System.out.println(arr01.getClass().hashCode());
System.out.println(arr02.getClass().hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
反射获取类的信息
- 获取类的地址、名字、属性、方法、构造器等信息。
示例:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 使用反射的API,获取类的信息(类的名字、属性、方法、构造器等)
* @author dxt
*
*/
public class Demo02 {
public static void main(String[] args){
String path = "dxt.test.bean.User";
try {
Class c1 = Class.forName(path);
//1.1 获取报名+类名
System.out.println(c1.getName());
//1.2 获取类名
System.out.println(c1.getSimpleName());
//2.1 获取类中公开 属性数组
Field[] fields = c1.getFields(); //只能获取public的属性
System.out.println(fields.length);
//2.2 获取所有的属性
Field[] fields2 = c1.getDeclaredFields();
System.out.println(fields2.length);
//2.3 获取对应名字的属性
Field f = c1.getDeclaredField("name");
System.out.println(f);
//3.1 获取方法信息
Method[] methods = c1.getMethods(); //只能获取public方法
Method[] methods2 = c1.getDeclaredMethods(); //获取所有方法
Method m = c1.getDeclaredMethod("getName", null);
//如果方法由参数,则必须传递参数类型对应的class对象
Method m2 = c1.getDeclaredMethod("setId", int.class);
System.out.println(m2);
//4 获取构造器信息
Constructor[] constructors = c1.getConstructors(); //public
Constructor[] constructors2 = c1.getDeclaredConstructors();
Constructor cscr = c1.getConstructor(null); //无参构造器
System.out.println(cscr);
Constructor cscr2 = c1.getConstructor(int.class, String.class, int.class);
System.out.println(cscr2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
反射操作类的信息
- 操作类的属性、方法、构造器等信息。
示例:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import dxt.test.bean.User;
/**
* 通过反射API动态的操作:构造器、方法、属性
* @author dxt
*
*/
public class Demo03 {
public static void main(String[] args){
String path = "dxt.test.bean.User";
try {
Class<User> c = (Class<User>)Class.forName(path);
//通过动态调用 构造方法,构造对象
//1.1 newInstance()直接构造对象,其实是调用了User的无参构造方法,User必须要有无参构造方法
User u = c.newInstance();
System.out.println(u);
//1.2 先获取对应构造方法,在使用构造器的newInstance()构造对象
Constructor<User> cscr = c.getDeclaredConstructor(int.class, String.class, int.class);
User u2 = cscr.newInstance(1001, "dxt", 20); //有参构造必须要传参数
System.out.println(u2.getName());
//2. 通过反射API调用普通方法
User u3 = c.newInstance();
//u3.setAge(20); //直接调用
Method m = c.getDeclaredMethod("setName", String.class);
m.invoke(u3, "dxt001"); //通过反射调用
System.out.println(u3.getName());
//3. 通过反射操纵属性
User u4 = c.newInstance();
Field f = c.getDeclaredField("id"); //获取对应属性
//设置可以访问private修饰的属性
f.setAccessible(true); //设置这个属性不用做安全检查,可以直接使用。
f.set(u4, 2001); //设置属性值
int id = f.getInt(u4); //获取属性值
System.out.println(id);
} catch (Exception e) {
e.printStackTrace();
}
}
}
反射机制性能问题
- 反射会使性能变差。反射调用耗时有可能是对象直接调用方法耗时的几十倍。
- setAccessible()
启用和禁止安全检查的开关,值为 true 表示反射的对象在使用时应该取消Java语言访问检查;值为 false 表示反射的对象应该实施Java语言访问检查。
禁止安全检查,会提高反射的运行速度。
反射操作泛型
- Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是一旦编译完成,所有和泛型有关的类型全部擦除。即Class类中不包含与泛型有关的类型信息。
- 为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
示例代码
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import dxt.test.bean.User;
/**
* 测试反射操作泛型
* @author dxt
*
*/
public class Demo04 {
public void test01(Map<String, User> map, List<User> list){
System.out.println("Demo04.test01()");
}
public Map<Integer,User> test02(){
System.out.println("Demo04.test02()");
return null;
}
public static void main(String[] args){
try {
//1. 获取指定方法参数的泛型
Method m = Demo04.class.getMethod("test01", Map.class, List.class);
Type[] t = m.getGenericParameterTypes(); //会有多个参数,获取参数类型(包含参数的泛型信息)
for(Type paramType : t){
System.out.println("#"+paramType);
if(paramType instanceof ParameterizedType){
//强转为带泛型的参数,然后获取正真的泛型
Type[] genericTypes = ((ParameterizedType)paramType).getActualTypeArguments();
for(Type genericType : genericTypes){
System.out.println("泛型类型:" + genericType);
}
}
}
//2. 获取指定方法返回值泛型信息
Method m2 = Demo04.class.getMethod("test02", null);
Type returnType = m2.getGenericReturnType(); //获取带泛型的返回类型
if(returnType instanceof ParameterizedType){
Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();
for(Type genericType : genericTypes){
System.out.println("返回值--泛型类型:" + genericType);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果