目录java
反射机制:将类的各个组成部分封装成其余对象,在运行状态中,能够动态获取类信息和调用类对象的方法。程序员
java语言有着”一处编译,到处运行“的优良特色,可以很好地适应不一样的平台,过程大体以下:数组
javac
命令将.java
文件编译,会在磁盘上生成不面向平台的字节码文件.class
。.class
文件加载进内存中。java.lang.Class
的对象,该对象包含类的信息,做为程序从方法区访问各类数据的接口。这个Class对象包含类的全部信息,也就是说,咱们得到了这个Class类的对象,就能够访问到JVM中的这个类。那么,如何获取呢?主要有如下三种方式。安全
经过Class的静态方法forName("全类名")
,返回和全类名对应类的Class对象。数据结构
//Class.forName("全类名"); Class cls1 = Class.forName("com.my.base.Person"); System.out.println(cls1);
经过类名的class属性
获取,返回该类对应的Class对象。性能
//类名.class; Class cls2 = Person.class; System.out.println(cls2);
getClass()
方法在Object中定义,返回该对象所属类对应得Class对象。code
//对象.getClass(); Person p = new Person(); Class cls3 = p.getClass(); System.out.println(cls3);
同一个字节码文件.class
在一次程序运行过程当中,只会被加载一次,不论经过哪一种方式获取的Class对象都是同一个。视频
// == 比较三个对象,都是true System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); System.out.println(cls3 == cls2);
Class对象包含着类的全部信息,而这些信息被封装在java.lang.reflect
包下,成了一个个的类,不彻底统计以下:对象
Method | Field | Annotation | Constructor | Package | Modifier | Parameter |
---|---|---|---|---|---|---|
方法类 | 字段类 | 注解类 | 构造器类 | 包类 | 修饰符类 | 方法参数类 |
Class类中提供了相应的方法,获取这些信息,因为方法众多,具体的还须要参看JDK官方文档。接口
我在这里总结几点通用的:
调用对应的getXxx
一般是得到对应public修饰的单个信息,getDeclaredXxx
则不考虑修饰符,找不到,则往超类找。
调用对应的getXxxs
一般得到对应的public修饰信息的数组,getDeclaredXxxs
则不考虑修饰符,找不到,则往超类找。
"参数名"
。"方法名",参数类型对应的类
。"参数对应的类"
。Object get(Object obj)
,也有设置值的方法:void set(Object obj, Object value)
。Object invoke(Object obj, Object... args)
。Constructor类能够建立实例:T newInstance(Object... initargs)
,若是类中有无参构造器,能够直接利用Class类中的newInstance()方法建立实例。
xxx.setAccessible(true)
暴力破解。判断修饰符时,getModifiers()
返回的时int值,是各个修饰符表示数的和,能够用位运算判断,(xx.getModifiers()&Modifier.STATIC)!=0
表示存在static修饰符。
ps:确定有遗漏的API,到时候用的时候,翻翻API就完事了。
package com.my.reflect.practice; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /** * @auther Summerday */ @SuppressWarnings("unchecked") public class ImplementClone { public Object clone(Object o) throws Exception{ //获取对象的实际类型 Class<Object> clz = (Class<Object>) o.getClass(); //获取全部构造方法 Constructor<Object>[] cs = (Constructor<Object>[]) clz.getDeclaredConstructors(); //任取一个构造方法 Constructor<Object> c = cs[0]; //防止取出构造方法为私有 c.setAccessible(true); //该构造方法参数有or无? //获取参数类型 Class[] ps = c.getParameterTypes(); //存储参数的数组 Object[] os = new Object[ps.length]; for(int i = 0;i<ps.length;i++){ //判断是否为基本类型 if(ps[i].isPrimitive()){ if(ps[i] == boolean.class) os[i] = false; else if(ps[i] == char.class) os[i] = '\u0000'; else os[i] = 0; }else{ os[i] = null; } } //执行构造方法建立对象 Object obj = c.newInstance(os); //获取属性数组 Field[] fs = clz.getDeclaredFields(); for (Field f : fs){ //若是final修饰则返回,没法修改 if((f.getModifiers()&Modifier.FINAL)!=0){ continue; } //暴力破解 f.setAccessible(true); //取出原属性值 Object value = f.get(o); //将取出的属性值赋值给新对象的属性 f.set(obj, value); } return obj; } }
package com.my.reflect.practice; /** * @auther Summerday */ import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /** * * 参考黑马程序员教学视频 * * 实现:一、配置文件 二、反射 * * 1、将须要建立的对象的全类名和须要执行的方法定义在配置文件中 * * 2、在程序中加载读取配置文件 * * 3、使用反射来加载类文件进内存 * * 4、建立对象 * * 5、执行方法 * */ public class ReflectTest { public static void main(String[] args) throws Exception { //一、加载配置文件 //1.1 建立Properties对象 Properties pro = new Properties(); //1.2 加载配置文件,转换为一个集合 //1.2.1获取class目录下的配置文件 //建立类加载器 ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties"); pro.load(resourceAsStream); //二、获取配置文件中定义的数据 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //三、加载该类进内存 Class cls = Class.forName(className); //四、建立对象 Object obj = cls.newInstance(); //五、获取方法 Method method = cls.getMethod(methodName); //六、执行方法 method.invoke(obj); } }
之后会有愈来愈多真实的场景须要用到反射这项灵魂技术,总之,好好看,好好学。