java反射机制
Ø 反射技术的由来?
反射技术可以:通过配置文件的形式,配置具体dao的实现类,当调用这些方法的时候,可以读取配置文件,通过反射技术类来生成实例,以后需要变更的时候,直接的修改配置文件,大大的扩展了程序的可扩展性。
目的就是为了让你不修改源码,直接扩展你的程序,这就是反射技术的由来。
Ø 什么是反射?
对于一个java的可执行文件class,可以对这个文件进行解剖,直接从class文件中获取构造方法,成员变量,成员方法,有了这些方法和变量,那就可以运行这些方法。
反射技术是在运行期间执行的,而非编译期间。
Ø 解析class字节码文件的方法?
java是一个面向对象的语言,一切都是对象,可执行文件class也是一个对象,既然class文件是一个对象,有没有描述这个对象类呢? java.lang.Class。
1、解析class对象的类:java.lang.Class
2、解析class文件中内容的类: java.lang.reflect
一个class文件中,包含有构造方法,成员变量,成员方法。
描述构造方法的类:
java.lang.reflect.Constructor
描述成员变量的类:
java.lang.reflect.Field
描述成员方法的类:
java.lang.reflect.Method
3、获取class字节码文件对象的3中方式?
第一种方式,通过类的对象获取
Person ps = new Person();
Classc1 = ps.getClass();
第二种方式,通过类名直接获取
Class c2 = Person.class;
第三种获取字节码文件对象的方式,使用是Class类的中一个静态方法 forName
staticClass<?> forName(String className)
Class c3 = Class.forName("cn.itcast.reflect.Person");
重点推荐使用第三种方式,获取一个类的字节码文件对象。原因是:类名,是一个字符串的表现形式,可以采用参数传递的方式来获取灵活性,更高。
Ø 反射的步骤
第一步:加载class字节码文件;
第二步:通过java.lang.reflect反射类中的方法,反射出字节码文件中的内容。
1 访问构造方法
获取class字节码对象 Class clazz =Class.forName("cn.itcast.reflect.Person");
// 有了字节码文件对象,可以获取其中的构造方法,查找找Class类中的方法 // 获取一个构造方法,即可,区分构造方法的条件是:构造方法中的参数。
1,访问不带参数公共的构造方法 Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor con =clazz.getConstructor(); Object obj = con.newInstance();//实例化
2,访问带参数公共的构造方法 Constructor con = clazz.getConstructor(int.class); con.newInstance(20);
3,获取私有权限的构造方法 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 Constructor描述构造方法的类,有一个父类, AccessibleObject void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。 constructor.setAccessible(true); // 取消访问检查,暴力访问
Constructor constructor = clazz.getDeclaredConstructor(String.class); constructor.setAccessible(true); // 取消访问检查,暴力访问 constructor.newInstance("张三");
//返回所有的构造方法(了解) Constructor [] constructors =clazz.getDeclaredConstructors(); for(int i = 0 ; i < constructors.length ; i++){ Constructor c = constructors[i]; System.out.println(c); Class []type = c.getParameterTypes(); //获取参数类型 for(Class cs:type){ System.out.println(cs.getName()); } }
|
2 获取成员变量,并修改值
// 获取class字节码对象 Class clazz = Class.forName("cn.itcast.reflect.Person");
Field[] getFields() 1 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 Field[] fields = clazz.getFields(); for(Field fd:fields){ System.out.println(fd.getName()); }
2 获取私有的属性 Field field = clazz.getDeclaredField("name"); //获取指定字段的值 field.setAccessible(true); //取消该字段的访问权限 Object obj = clazz.newInstance();//Class类中的方法 newInstanse()保证类有空参数的,权限是public的构造方法 // void set(Object obj, Object value) //将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 //改变该字段的值 field.set(obj, "zhangsan"); System.out.println(obj);
|
3 访问成员方法,并执行
// 获取class字节码对象 Class clazz = Class.forName("cn.itcast.reflect.Person"); //获取到得是公共的,和继承的 Method[] methods = clazz.getMethods(); for(Method m:methods){ System.out.println(m); } //获取指定名称带参数的方法 Method method = clazz.getMethod("show",String.class,int.class); //获取到,运行的方法的 method.invoke(clazz.newInstance(), "字符串",200); // 获取静态方法 Method method = clazz.getMethod("testStatic"); method.invoke(clazz.newInstance());
// 获取私有的方法 Method method = clazz.getDeclaredMethod("method"); //取消权限控制 method.setAccessible(true); //执行 method.invoke(clazz.newInstance());
|
4 通过反射,破坏单例设计模式
/** *单例 */ publicclass Single { private Single() {} privatestaticfinal Single single = new Single(); publicstatic Single getInstance() { returnsingle; } }
/** *反射破坏单例模式 */ publicclass Demo5 { publicstaticvoid main(String[] args) throws Exception { //获取Single类的字节码文件对象 Class clazz = Single.class; // 获取构造方法 Constructor con = clazz.getDeclaredConstructor(); // 取消权限控制 con.setAccessible(true); //实例化 System.out.println(con.newInstance()); System.out.println(con.newInstance()); System.out.println(con.newInstance()); System.out.println(con.newInstance()); System.out.println(con.newInstance());
} } |
注意:
一般情况下访问私有的成员变量、成员方法和构造方法,都是getDeclaredXX();
直接使用clazz.newInstance()的时候,必须被反射的类中又一个公共的的空的构造方法。