Java基础---反射

来源:互联网 发布:telnet 80端口 编辑:程序博客网 时间:2024/06/05 22:50

 

反射原理

 一、反射的概念

             反射就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。

             JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

            要想解剖一个类,必须先要获取到该类的字节码文件对象而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象,有三种获取Class对象的方式,例如
//方式1:           Person p = new Person();           Class c = p.getClass();//方式2:           Class c2 = Person.class;//任意数据类型都具备一个class静态属性,看上去要比第一种方式简单。//方式3:           Class c3 = Class.forName("Person");//将类名作为字符串传递给Class类中的静态方法forName          /*第三种和前两种的区别:前两种你必须明确Person类型,后面是你给我这种类型的字符串就行。这种扩展更强,我不需要知道你的类,我只提供字符串,按照配置文件加载就可以了。*/

二、通过反射获取构造方法并使用

1、获取构造方法        

        getConstructors;getDeclaredConstructors//前者获取公共构造方法,后者获取全部,包括私有构造方法

2、创建对象

        newInstance();con.newInstance(“zhangsan", 25);//无参或带参初始化实例

【案例】

import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/* * 需求:通过反射获取私有构造方法和公共构造方法并使用 * private Human(String name){} * public Human(String name, int age, String address) {} *  * 不用反射之前只能获取公共构造方法: * Human p = new Human("德艺双馨",25,"中国·广东"); * p.show(); */public class ReflectDemo {public static void main(String[] args) {try {// 获取字节码文件对象Class c = Class.forName("Human");// Constructor con1=c.getConstructor(String.class);//NoSuchMethodException// 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。Constructor con = c.getDeclaredConstructor(String.class);// 用该私有构造方法创建对象出现IllegalAccessException,通过以下方式暴力访问con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。Object obj = con.newInstance("德艺双馨");// 通过私有构造方法初始化对象((Human) obj).show();// 德艺双馨:0——未填写Constructor con2 = c.getConstructor(String.class,int.class, String.class);// 这个是公共构造方法Object obj2 = con2.newInstance("德艺双馨", 25, "中国·广东");//通过公共构造方法初始化((Human) obj2).show();// 德艺双馨:25——中国·广东} catch (Exception e) {e.printStackTrace();}}}//创建一个Human类class Human {private String name;private int age = 0;private String address = "未填写";private Human(String name) {this.name = name;}public Human(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public void show() {System.out.println(name + ":" + age + "——" + address);}}

三、通过反射获取成员变量并使用

1、获取所有成员 

        getFields,getDeclaredFields;//同理,前者获取所有公共成员变量,后者获取或有包括私有的成员变量

2、获取单个成员

     getField;getDeclaredField; //后者能获取私有变量而前者不能 

3、修改成员的值

      set(Object obj,Objectvalue);//将指定对象变量上此Field对象表示的字段设置为指定的新值 

【案例】

import java.lang.reflect.Constructor;import java.lang.reflect.Field;public class ReflectField {public static void main(String[] args) {Class clazz = Human.class;// 获取字节码文件对象// 获取所有的成员变量// Field[] fields = c.getFields();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field);/*输出:  * private java.lang.String Human.name  * private int Human.age * private java.lang.String Human.address */}// 获取单个的成员变量并赋值try {// 通过带参构造方法创建对象Constructor con = clazz.getConstructor(String.class, int.class,String.class);// 这个是公共构造方法Object obj = con.newInstance("德艺双馨", 25, "中国·广东");// 获取address并对其赋值// Field addressField = clazz.getField("address");//NoSuchFieldExceptionField addressField = clazz.getDeclaredField("address");// public void set(Object obj,Object value)// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。addressField.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"// 获取name并对其赋值Field nameField = clazz.getDeclaredField("name");nameField.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问nameField.set(obj, "index80");// 获取age并对其赋值Field ageField = clazz.getDeclaredField("age");ageField.setAccessible(true);ageField.set(obj, 25);((Human) obj).show();// index80:25——北京} catch (Exception e) {e.printStackTrace();}}}


四、通过反射获取成员方法并使用

1、获取所有方法

     getMethods;getDeclaredMethods;

2、获取单个方法

     getMethod;getDeclaredMethod;

3、暴力访问

     method.setAccessible(true);//取消java语法检查

【案例】

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectMethod {public static void main(String[] args) {Class klass = Human.class;// 获取字节码文件对象// 用反射的方式调用方法try {// 通过带参构造方法创建对象Constructor con = klass.getDeclaredConstructor(String.class);// 这个是私有构造方法con.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问Object obj = con.newInstance("德艺双馨");Method m = klass.getMethod("show");// 获取show()方法m.invoke(obj);// 调用obj对象的方法,输出 德艺双馨:0——未填写} catch (Exception e) {e.printStackTrace();}}}

五、反射的应用——动态代理

1、Javajava.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。

2、Proxy类中的方法创建动态代理类对象

     public static Object newProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandler h);

     //最终会调用InvocationHandler的方法。

3、InvocationHandler

      Object invoke(Object proxy,Methodmethod,Object[]args)

【案例】

(1)学生接口

public interface StudentDao {public abstract void login();public abstract void regist();}

(2)用户操作接口

/* * 用户操作接口 */public interface UserDao {public abstract void add();public abstract void delete();public abstract void update();public abstract void find();}

(3)学生接口实现类

public class StudentDaoImpl implements StudentDao {@Overridepublic void login() {System.out.println("登录功能");}@Overridepublic void regist() {System.out.println("注册功能");}}

(4)用户接口实现

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 find() {System.out.println("查找功能");}}

(5)实现InvocationHandler接口

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("权限校验");Object result = method.invoke(target, args);System.out.println("日志记录");return result; // 返回的是代理对象}}

(6)测试类

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.find();System.out.println("-----------");// 我们要创建一个动态代理对象// Proxy类中有一个方法可以创建动态代理对象// public static Object newProxyInstance(ClassLoader loader,Class<?>[]// interfaces,InvocationHandler h)// 我准备对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.find();System.out.println("-----------");StudentDao sd = new StudentDaoImpl();MyInvocationHandler handler2 = new MyInvocationHandler(sd);StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass().getClassLoader(), sd.getClass().getInterfaces(), handler2);proxy2.login();proxy2.regist();}}


总结:

       Proxy类中创建动态代理对象的方法的三个参数:

                ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

                Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

                InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

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

       InvocationHandler接口中invoke方法的三个参数:

               proxy:代表动态代理对象

               method:代表正在执行的方法

               args:代表调用目标方法时传入的实参

       Proxy.newProxyInstance

               创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,$开头,proxy为中,最后一个数字表示对象的标号。

System.out.println(u.getClass().getName());

 

 

0 0