java反射机制

来源:互联网 发布:互联网大数据论文题目 编辑:程序博客网 时间:2024/06/08 04:13

一.反射的概念及作用

1.概念:通过Java反射机制,可以访问程序中已加载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。在java.lang.reflect包中提供了对该功能的支持。

2.作用:

1.反编译:.class-->.java

        2.通过反射机制访问java对象的属性,方法,构造方法等;

3.sun为我们提供了那些反射机制中的类:

java.lang.Class;    

java.lang.reflect.Constructor; 

java.lang.reflect.Field;        

java.lang.reflect.Method;

java.lang.reflect.Modifier;

面试题:

如何获取类的字节码文件对象,并且有几种方式呢?

1)Object类中的getClass()方法,表示正在运行的那个类:Class类

2)数据类型的class属性  举例:String.class,Student.class

3)Class类中的特有方法:forName(String className):(重点,数据库加载驱动:Drivers)

public static Class<?> forName(String className):获取字节码文件对象(参数是一个字符串...,字符串的内容是一个类的全路径名称)

最常用的方式是方式3)

package reflect;public class ReflectDemo {/** * @param args * @throws Exception  */public static void main(String[] args) throws Exception {//方式一:创建Person对象Person p1 = new Person();Class c1 = p1.getClass();//创建对象Person p2 = new Person();Class c2 = p2.getClass();System.out.println(p1==p2);//falseSystem.out.println(c1==c2);//true//方式二:通过数据类型的Class属性Class c3 = Person.class ;System.out.println(c3==c1);//true//方式三:参数不是一个类名,而是类的全路径名称// 获取一个类中的构造方法,首先找到这个类的class文件对象,通过反射Class c4 = Class.forName("reflect.Person");System.out.println("c4:"+c4); //c4:class reflect.PersonSystem.out.println(c4==c1);}}
4.通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 

1)获取构造器

package reflect;import java.lang.reflect.Constructor;public class ReflectDemo2 {public static void main(String[] args) throws Exception {//获取一个类中的构造方法,首先找到这个类的class文件对象,通过反射Class c = Class.forName("reflect.Person");//1.public Constructor<?>[] getDeclaredConstructors():获取的是当前字节码文件对象中所有的构造方法Constructor[] con = c.getDeclaredConstructors();for(Constructor constructor:con){System.out.println(constructor);}System.out.println("-----------");/** * 输出:private reflect.Person(java.lang.String,int,java.lang.String) * reflect.Person(java.lang.String,int) *public reflect.Person() *  * *///2.public Constructor<?>[] getConstructors():获取的是当前字节码文件对象中所有的公共的构造方法Constructor[] publicCon= c.getConstructors();for(Constructor constructor:publicCon){System.out.println(constructor);}System.out.println("-----------");/** * public reflect.Person() * */}}
2)获取单个的构造器并创建一个实例

package reflect;import java.lang.reflect.Constructor;/** * 之前创建对象: * 给对象的成员属性赋值 * Person p = new Person("韩庚",20,"北京") * System.out.println(p) ; *  *  下面通过反射获取构造方法并且给成员属性赋值 *2017年12月13日  */public class ReflectDemo3 {public static void main(String[] args) throws Exception {// 获取Person类的字节码文件对象Class c = Class.forName("reflect.Person");//通过反射获取指定的构造方法getConstructor(Class ...parameterTyps)Constructor con = c.getConstructor(String.class,int.class,String.class);//创建构造器的实例对象,来给他指定的字节码文件对象里面的成员变量赋值Object obj = con.newInstance("韩庚",20,"北京");//实际参数-->通过反射--->Person.class---->getConstructor() System.out.println(obj);}}
3).获取单个的私有构造器并创建实例

在给私有构造器创建实例之前要先调用public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性),参数的值为true则表示反射的对象在使用时应该取消java语言的访问检查。

package reflect;import java.lang.reflect.Constructor;/** *通过反射获取私有构造器并赋值 * *2017年12月14日  */public class ReflectDemo4 {public static void main(String[] args) throws Exception {// 获取Person类的字节码文件对象Class c1 = Class.forName("reflect.Person");//通过反射获取指定的构造方法getConstructor(Class ...parameterTyps)Constructor con = c1.getDeclaredConstructor(String.class);//public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性)//参数的值为true则表示反射的对象在使用时应该取消java语言的访问检查con.setAccessible(true);//在给构造器创建实例对象前应该取消检查//为构造器创建实例对象Object obj = con.newInstance("韩庚");System.out.println(obj);}}
4.通过反射机制得到某个类的成员变量并使用


1)获取字节码文件对象(forName())

2)获取构造器对象(getConstructor())通过无参构造

3)创建构造器实例(newInstance)

4)通过反射获取单个成员变量并赋值(c.getField("address"))

5)调用set()方法赋值

package reflectfield;import java.lang.reflect.Constructor;import java.lang.reflect.Field;/** * 通过反射获取成员变量并使用 *成员变量--->Field *2017年12月14日  */public class ReflectDemo {public static void main(String[] args) throws Exception {// 1)通过反射获取字节码文件对象Class c = Class.forName("reflectfield.Person");/*//2)获取所有的公共的成员变量public Field[] getFields():所有的公共的可访问的字段,返回的是Field对象数组Field[] fields = c.getFields();//Field[] fields = c.getDeclaredFields() ;for(Field f : fields){System.out.println(f);}*//** * 结果:public java.lang.String reflectfield.Person.address * *///获取到这个Field对象之前,还获取构造器对象(通过无参构造创建Person类的实例)Constructor con = c.getConstructor() ;//创建构造器实例Object obj = con.newInstance() ;//Person的实例对象//System.out.println(obj);//通过反射获取单个成员变量并赋值Field addressFiled = c.getField("address");addressFiled.set(obj, "北京");System.out.println(obj);     //Person [name=null, age=0, address=北京]System.out.println("----------------");//给name赋值并使用(private)Field nameField = c.getDeclaredField("name");//给name属性设置值nameField.setAccessible(true);nameField.set(obj, "韩庚");System.out.println(obj);System.out.println("----------------");//给age赋值并使用(default)Field ageField = c.getDeclaredField("age");//ageField.setAccessible(true);ageField.set(obj, 18);System.out.println(obj);}}
5.通过反射得到某个类的成员方法并使用


package reflectmethod;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class ReflectDemo {public static void main(String[] args) throws Exception {//反射获取字节码文件对象Class c = Class.forName("reflectmethod.Person");//获取成员方法(Method)//public Method[] getMethods():获取当前该字节码文件对象(Person.class)中自己本身以及它父类中所有的公共成员方法//public Method[] getDeclaredMethods():获取当前字节码文件对象本身所有的成员方法Method[] methods = c.getDeclaredMethods();for(Method m : methods){System.out.println(m);}System.out.println("-------------------------");//获取构造器对象,通过构造器创建当前字节码文件的实例对象Constructor con = c.getConstructor();Object obj = con.newInstance();   //Person对象//获取单个成员方法Method m1 = c.getMethod("show");/** * public Object invoke(Object obj, Object... args) * 参数1:表示当前针对哪个实例对象进行方法的调用 * 参数2:当前调用该方法的时候里面传递的实际参数 */m1.invoke(obj) ;System.out.println("-------------------------");//调用getString()方法Method m2 = c.getMethod("getString", String.class,int.class);Object objString = (String)m2.invoke(obj, "hello",100) ;System.out.println(objString);System.out.println("-------------------------");//调用私有的fun()Method m3 = c.getDeclaredMethod("fun");m3.setAccessible(true);//取消Java语言的访问检查m3.invoke(obj);}}
练习 给ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据

已经给定了对象,直接用对象代调用getClass()方法获取字节码文件对象,用字节码文件对象调用方法,不用获取构造器并且通过构造器创建当前字节码文件的实例对象

package reflectest;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;public class ArrayListTest {/** * 给ArrayList<String>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢? *  * 反射的应用: * 1)在集合中应用 * 2)用来反射区读取配置文件来加载里面的内容(MySQL,Oracle等等,SQLServer,MangoDB) * @param args * @throws SecurityException  * @throws NoSuchMethodException  * @throws InvocationTargetException  * @throws IllegalArgumentException  * @throws IllegalAccessException  */public static void main(String[] args) throws NoSuchMethodException,SecurityException,IllegalAccessException,IllegalArgumentException,InvocationTargetException {//创建ArrayList对象ArrayList<String> arry = new ArrayList<String>();//利用反射类给集合中添加字符串//获取ArrayList类的字节码文件Class c = arry.getClass();//通过反射获取公共访问的方法Method m = c.getMethod("add", Object.class);//调用底层invoke(当前字节码文件对象的实例,实际参数)m.invoke(arry, "hello");m.invoke(arry, "world");m.invoke(arry, "java");System.out.println(arry);}}

6.反射的应用-设置配置文件

需要创建一个.txt文件,以键值对的方式写内容

package reflectest;import java.io.FileReader;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Properties;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException {//文本文件是一种键值对象的形式//要将文本文件中的内容加载到属性集合类中//创建属性集合类对象Properties prop = new Properties();FileReader fr = new FileReader("class.txt");//属性集合类的加载方法:load()prop.load(fr);fr.close();//通过键获得值String className = prop.getProperty("className");String methodName = prop.getProperty("methodName");//将反射应用到配置文件中Class c = Class.forName(className);//通过反射获取构造器对象,并通过构造器对象创建该类的实例Constructor con = c.getConstructor();Object obj = con.newInstance();//获取成员方法Method m = c.getMethod(methodName);m.invoke(obj);}}


package reflectest;public class Student {public void show(){System.out.println("student study!");}}

练习:1:写一个方法, public void setProperty(Object obj, String propertyName, Object value){}, 此方法可将obj对象中名为propertyName的属性的值设置为value

package reflectest;import java.lang.reflect.Field;/** * 1:写一个方法, * public void setProperty(Object obj, String propertyName, Object value){}, * 此方法可将obj对象中名为propertyName的属性的值设置为value*/public class Tool {public void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{//获取类的字节码文件对象Class c = obj.getClass();//获取Field对象Field field = c.getDeclaredField(propertyName);////防止出现IllegalAccessException:设置Java语言取消访问检查field.setAccessible(true);field.set(obj, value);}}

package reflectest;public class ToolDemo {public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {//创建Person类对象Person p = new Person();// 创建工具类对象Tool tool = new Tool();//设置name属性tool.setProperty(p, "name", "韩庚");tool.setProperty(p, "age", 18);System.out.println(p);}}class Person{private String name;private int age;@Overridepublic String toString() {return name + "----" + age ;}}
二.java的动态代理机制

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler(interface)、另一个则是Proxy(class),这一个类和接口是实现我们动态代理所必须用到的。下面是javaAPI文档对这两者的描述:

InvocationHandler:

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

先了解完Proxy,下面可以通过实例加深了解

Proxy:

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

上面介绍完了有关的接口和类,以下通过实例加深对动态代理的理解
首先定义一个UserDao的接口,声明四种方法
package reflectHander;public interface UserDao {//定义四个方法//增public abstract void add() ;//删public abstract void delete() ;//update:修改public abstract void update() ;//查public abstract void serach() ;}
接着,定义一个类来实现这个接口,这个类就是我们真实的对象
package reflectHander;public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("增加功能");}@Overridepublic void delete() {System.out.println("删除功能");}@Overridepublic void update() {System.out.println("修改功能");}@Overridepublic void serach() {System.out.println("查询功能");}}
下一步,我们就定义一个动态代理类,前面说过每一个动态代理必须实现InvocationHandler 这个接口
package reflectHander;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler{//这个是我们要代理的真实对象private Object target;//构造方法:给我们要代理的对象赋初值public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 权限校验System.out.println("权限校验");//当代理对象调用真实对象的方法时,其自动的跳转到代理对象关联的handler对象的invoke方法来进行调用Object result = method.invoke(target, args) ;//日志记录System.out.println("日志记录");return result; //返回值就是代理对象}}
最后来看一下测试类

package reflectHander;import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) {// 创建用户操作的对象(要代理的真实对象)UserDao ud = new UserDaoImpl();//ud.add();//ud.delete();//ud.update();//ud.serach();System.out.println("--------");//给ud设置代理对象//要代理哪个真实对象,就将该对象作为参数传进去,最后是通过该真实对象来调用其方法的MyInvocationHandler handler = new MyInvocationHandler(ud) ;UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(),handler);proxy.add();proxy.delete();proxy.update();proxy.serach();}}

原创粉丝点击