反射的概念
java 的放射机制:在程序运行时,程序有能力获取一个类的所有方法和属性;并且对于任意一个对象,可以调用它的任意方法或者获取其属性
通俗解析:java 文件需要编译成. class 文件才能被 jvm 加载使用, 对象的. class 数据在 jvm 里就是 Class;我们如果能拿到这个 Class对象,就能获取该 Class对应的对象类型,及在该类型声明的方法和属性值;还可以根据 Class创建相应的类型对象,通过 Field,Method 反过来操作对象
java 相关类介绍
获取 Class 的三种方法
1 通过已知的类型获取 classpublicClassgetExample(){ Classclazz=Example.class;returnclazz; }//根据Example获取Class=》Example.class
2 通过实例对象获取 class
Exampleexample=newExample(); // getClass是Object类里面的方法;《?》是通配符 Classclazz=example.getClass();return(Class)clazz; }publicClassgetExampleByInstance(){
3 通过 Class.forName 获取全路径指定类名的 class
\* 1 className 是个类名 2 initialize 是否延迟加载 3 loader 加载器 \*/ privatestaticnativeClassforName0(StringclassName,booleaninitialize, ClassLoaderloader,Classcaller)throwsClassNotFoundException; publicstaticClassforName(StringclassName)throwsClassNotFoundException{ Classcaller=Reflection.getCallerClass(); returnforName0(className,true,ClassLoader.getClassLoader(caller),caller); } //两个forName方法最终都会调用forName0方法去加载class publicstaticClassforName(Stringname, booleaninitialize,ClassLoaderloader)throwsClassNotFoundException{ .... returnforName0(name,initialize,loader,caller); }/\*\*forName0本地方法,C++实现,jvm调用
publicClassgetInteger()throwsClassNotFoundException{ Classclazz=Class.forName("java.lang.Integer");return(Class)clazz; }//示例:通过java.lang.Integer
JAVA 反射 API
Class 常用操作方法publicConstructor\[\]getDeclaredConstructors() //获取特定的构造方法/privatepublic publicConstructorgetDeclaredConstructor(Class...parameterTypes) //获取类的父类 publicnativeClassgetSuperclass() //获取类实现的接口 privateClass\[\]getInterfaces(booleancloneArray) //获取在类内定义的内部类或接口 publicClass\[\]getDeclaredClasses() //获取所有的方法 publicMethod\[\]getDeclaredMethods()throwsSecurityException //根据方法名和参数获得特定的方法 publicMethodgetDeclaredMethod(Stringname,Class...parameterTypes) //获取类型的定义的所有属性 publicField\[\]getFields()throwsSecurityException //根据属性命名获得特定的Field publicFieldgetField(Stringname)//获取所有的构造方法/privatepublic
Method 常用的操作方法
publicClassgetReturnType() //获得方法的传入参数类型 publicClass\[\]getParameterTypes() //obj是实例对象,args是方法,反过来由Method控制对象的方法调用 publicObjectinvoke(Objectobj,Object...args)//获得方法的放回类型
Field 常用的操作方法
publicbooleanequals(Objectobj) //获得obj中对应的属性值 publicObjectget(Objectobj) //设置obj中对应属性值 publicvoidset(Objectobj,Objectvalue)//属性与obj相等则返回true
Constructor
publicTnewInstance(Object...initargs)//根据传递的参数创建类的对象:initargs构造方法参数
1 根据 class 创建对象
Classclazz=Example.class; Exampleexample=clazz.newInstance(); //方式二先获取再由Constructor:clazz.getConstructors()/getConstructor(...) //再由Constructor.newInstance方法构造对象 ----------------------------------------- publicclassExample{ privateintvalue; publicExample(){}//如果只声明有参构造函数,clazz.newInstance()会报错 publicExample(Integervalue){this.value=value;} staticpublicvoidmain(String\[\]args)throwsException{ Classclazz=Example.class; //根据指定构造函数参数获取Constructor Constructorconstructor=clazz.getConstructor(Integer.class); Exampleexample=constructor.newInstance(100); System.out.println(example.value); } }//方式一clazz.newInstance()
2 由 class 获取 Field,并操作实例的属性
privateintvalue,count; staticpublicvoidmain(String\[\]args)throwsException{ Classclazz=Example.class; //获取所有的属性,getField只能获取public的属性 Field\[\]fs=clazz.getDeclaredFields(); //根据名称获取指定Field Fieldvalue=clazz.getDeclaredField("value"); Exampleexample=clazz.newInstance(); //使用反射机制可以打破封装性,导致了java对象的属性不安全 value.setAccessible(true);//setAccessible(true)让private的参数可赋值操作 //由Field反过去设置example的值 value.set(example,100); System.out.println(example.value); } }publicclassExample{
3 由 class 获取 Method,并反射调用实例方法
publicstaticvoidmain(String\[\]args)throwsException{ Classclazz=Example.class; Exampleexample=clazz.newInstance(); Method\[\]methods=clazz.getDeclaredMethods(); //getDeclaredMethod和getMethod是:getMethod只能返回public的方法 Methodmethod=clazz.getDeclaredMethod("hello",String.class); method.setAccessible(true); method.invoke(example,"cscw"); } privatevoidhello(Stringname){System.out.println(name+"Hello!");} } ----- cscwHello!publicclassExample{
反射机制应用的场景
1 动态拓展:假设有同一组类是实现相同的接口,并且类的加载方式不限制。当我们需要那种具体类实现的功能时,只需加载. class 文件,并获取对应的 Class对象。可以由 Class 或者 Constructor 实例化对象 instance;根据接口定义,可以获取 Class里的某一方法 Method,并配合 instance 反射调用功能方法
2 Spring 的 IOC 就是基于反射机制实现
3 JDK 的动态代理
反射和 JDK 动态代理
在 Java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口。通过这个类和接口可以生成 JDK 动态代理类或动态代理对象//所有方法都会调用此代理方法 Objectinvoke(Objectvar1,Methodvar2,Object\[\]var3)throwsThrowable; } publicclassProxyimplementsSerializable{ ... //根据interfaces和InvocationHandler生成代理对象 publicstaticObjectnewProxyInstance(ClassLoaderloader, Class\[\]interfaces,InvocationHandlerh) ... }publicinterfaceInvocationHandler{
JDK 的动态代理由 Proxy 和 InvocationHandler 实现;而被代理对象必须实现一个接口。代理对象由 Proxy 生成,可转为接口 interface 的实现类对象 OBJ。当调用 OBJ 的方法时,则会触发 InvocationHandler.invoke,参数依次为「代理对象」,「Method 对象」, 和「方法 Method 所需的参数」。在 invoke 方法可以加入拓展的逻辑,如日志记录操作;「并可以在 invoke 里利用反射的技术调用被代理对象方法」
示例
privateTtarget;//被代理对象 publicTbind(Tobj){ target=obj;return(T)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this); } /\*\*Objecto是代理对象;o的方法调用->ExampleFactory.invoke \*invoke(...)->在invoke方法里面反射调用代理对象方法+增强逻辑 \*/ @Override publicObjectinvoke(Objecto,Methodmethod,Object\[\]objects)throwsThrowable{ //增强逻辑 System.out.println("logstart"); //反射调用被代理对象方法 Objectresult=method.invoke(target,objects); System.out.println("logend");returnresult; } } ----------- publicinterfaceFace{ voidhello(Stringname); } --------- //被代理对象必须实现一个接口,并由接口方法对方提供功能 publicclassExampleimplementsFace{ publicvoidhello(Stringname){ System.out.println(name+"Hello!"); } publicstaticvoidmain(String\[\]args){ //ExampleFactory相当于一个中介人 ExampleFactoryfactory=newExampleFactory<>(); //example是代理对象 Faceexample=exampleProxy.bind(newExample()); example.hello("思婷"); } } -----logstart 思婷Hello!logendpublicclassExampleFactoryimplementsInvocationHandler{