java---反射机制
java语言允许通过程序化的方式间接对Class进行操作,Class文件由类加载器装载后,在JVM中形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如
构造函数、属性和方法等。
java允许用户借由这个Class相关的元信息对对象间接调用Class对象的功能。
11.1、什么是反射?
反射机制是在运行状态中:
对于任意的一个类,都能够知道这个类的所有属性和方法
对于任意的一个对象,都能够调用它的任意一个方法和属性。
11.2、反射提供的功能有:
1)、在运行时判断任意一个所属的类
2)、在运行时构造任意一个类的对象
3)、在运行时判断任意一个类所具有的成员变量和方法
4)、在运行时调用任意一个对象的方法
5)、生成动态代理
11.3、获取反射对象的三种方式
第一种、Class.forName(全类名),推荐使用,可以捕获异常ClassNotFoundException
package com.cn.fanshe;
public class Test { public static void main(String[] args) { //获取反射对象(反射入口):: /** * 获取反射对象有3种方式: * 第一种、Class.forName(全类名) * 第二种、类名.class * 第三种、对象.getClass() * */ try { Class<?> userClazz=Class.forName("com.cn.fanshe.User"); System.out.println(userClazz); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } |
第二种、类名.class
Class<?> userClazz=User.class; System.out.println(userClazz); |
第三种、对象.getClass()
//1.创建对象 User user=new User(); Class<?> userClazz=user.getClass(); System.out.println(userClazz); |
11.4、获取反射类中的所有公共方法
package com.cn.fanshe;
import java.lang.reflect.Method; /** * 1、获得所有的公共方法(本类、父类以及接口中的所有方法,符合访问修饰符的public方法,但不包括private方法) * */ public class Test { public static void getAllMethod(){ //1.获得反射对象 Class<?> userClazz=null; try { userClazz=Class.forName("com.cn.fanshe.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //2.获得所有的公共方法 Method[] methods=userClazz.getMethods(); //3.遍历所有的公共方法 for(Method method:methods){ System.out.println(method); } }
public static void main(String[] args) { Test.getAllMethod(); } } |
11.5、获得反射类中的所有接口
//1.获得反射对象 Class<?> userClazz=null; try { userClazz=Class.forName("com.cn.fanshe.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Class<?>[] userInterfaces=userClazz.getInterfaces(); for(Class<?> inter:userInterfaces){ System.out.println(inter); } |
11.6、获得所有的父类,父类只有1个
Class<?> superClass=userClazz.getSuperclass(); System.out.println(superClass); |
11.7、获得所有的构造方法
Constructor<?>[] constructors=userClazz.getConstructors(); for(Constructor<?> con:constructors){ System.out.println(con); } |
11.8、获取所有的公共属性
Field[] fields=userClazz.getFields(); for(Field fie:fields){ System.out.println(fie); } |
注:获取所有公共属性和方法,则修改为如下:
Field[] fields=userClazz.getDeclaredFields(); |
11.9、获取对象的实例,并操作对象
package com.cn.fanshe;
import java.lang.reflect.Field; /** * 1、获得所有的公共方法(本类、父类以及接口中的所有方法,符合访问修饰符的public方法,但不包括private方法) * */ public class Test { public static void test() throws InstantiationException, IllegalAccessException{ //1.获得反射对象 Class<?> userClazz=null; try { userClazz=Class.forName("com.cn.fanshe.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //2.获取对象实例 User user=(User) userClazz.newInstance(); user.setName("Jerry"); user.setAge(21); System.out.println(user.getName()+","+user.getAge()); }
public static void main(String[] args) throws InstantiationException, IllegalAccessException { Test.test(); } }
|
11.10、创建对象实例并操作对象属性
//1.获得反射对象 Class<?> userClazz=null; try { userClazz=Class.forName("com.cn.fanshe.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //2.获取对象实例 User user=(User) userClazz.newInstance(); Field nameField=userClazz.getDeclaredField("name"); nameField.set(user, "张三"); System.out.println(user.getName()); |
此时会报异常,如下:
Exception in thread "main" java.lang.IllegalAccessException: Class com.cn.fanshe.Test can not access a member of class com.cn.fanshe.User with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:95) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253) at java.lang.reflect.Field.doSecurityCheck(Field.java:983) at java.lang.reflect.Field.getFieldAccessor(Field.java:927) at java.lang.reflect.Field.set(Field.java:680) at com.cn.fanshe.Test.test(Test.java:19) at com.cn.fanshe.Test.main(Test.java:24)
|
原因是访问的是私有的name,需要修改属性的访问权限,如果因为访问修饰符限制造成的异常,可以通过属性或方法.setAccessible(true),暂时性的打开权限。
如下:
//2.获取对象实例 User user=(User) userClazz.newInstance(); Field nameField=userClazz.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(user, "张三"); System.out.println(user.getName()); |
注:方法的操作类似属性,但是方法的调用用invoke()
如下:
//2.获取对象实例 User user=(User) userClazz.newInstance(); //interfaceMethod2是方法名,null是参数类型 Method method=userClazz.getDeclaredMethod("interfaceMethod2", null); method.invoke(user, null); //常规方法调用如user.say(参数) |
11.11、反射的适用场景?
1)、java的反射机制在做基础框架时十分有用,即反射机制是很多java框架的基石。
最经典的就是在XML文件或Properties里面写好配置,然后在java类里面解析XML或properties里面的内容,得到一个字符串,然后用反射机制,根据这个字符串获得某个类的Class实例,这样就可以动态配置一些东西,不用每次都要在代码里面去new或者做其它事情,以后要改的话直接修改配置文件,代码维护起来就方便了。
2)、java类里面不一定能直接调用另外的方法,这时可以通过反射机制来实现。
3)、在编码阶段不知道那个类名,要在运行期从配置文件读取类名,这时就没办法硬编码new ClassName();而必须用到反射才能创建这个对象,反射的目的就是为了扩展未知的应用。
11.12、反射的性能问题
反射机制是一种程序自我分析的能力,用于获取一个类的变量、构造函数、方法、修饰符
优点:运行期类型的判断,动态类加载,动态代理使用反射
缺点:性能是一个问题,反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的java代码要慢很多。
11.13、程序员在自己的业务开发中应该尽量远离反射
反射:在流行的库如hibernate和spring中,反射自然有其用武之地。不过内省业务代码在很多时候都不是一件好事,原因有很多,总之不建议使用反射。
首先是代码可读性和工具支持,打开IDE,寻找你的java代码的内部依赖,很容易吧。
现在使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。