本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188
反射和代理机制是JDK5.0提供的java新特性,反射的出现打破了java一些常规的规则,如,私有变量不可访问。但反射和代理在学习过程中也是一个比较难理解的知识点。本人曾经学过一段时间的反射和代理,但好长时间不用好像有点生疏了,当时学的时候就理解的不是很透彻,这次总结算是重新学习一遍吧,如果有什么错误,请大家拍砖:
先看一下,Java 反射机制主要提供了以下功能:
•在运行时判断任意一个对象所属的类。
•在运行时构造任意一个类的对象。
•在运行时判断任意一个类所具有的成员变量和方法。
•在运行时调用任意一个对象的方法
一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语
API简介
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
–Class类:代表一个类。
–Field 类:代表类的成员变量(成员变量也称为类的属性)。
–Method类:代表类的方法。
–Constructor 类:代表类的构造方法。
–Array类:提供了动态创建数组,以及访问数组的元素的静态方法
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。
Class类是Reflection API 中的核心类,它有以下方法
–getName():获得类的完整名字。
–getFields():获得类的public类型的属性。
–getDeclaredFields():获得类的所有属性。
–getMethods():获得类的public类型的方法。
–getDeclaredMethods():获得类的所有方法。
-getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
-getConstructors():获得类的public类型的构造方法。
•getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
•newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
•(2)通过默认构造方法创建一个新对象:
•Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
•以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
•(3)获得对象的所有属性:
•Field fields[]=classType.getDeclaredFields();
•Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性
(4)Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回
(5)Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。 要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
获取某个类或某个对象所对应的Class对象的常用的3种方式:
a) 使用Class类的静态方法forName:Class.forName(“java.lang.String”);
b) 使用类的.class语法:String.class;
c) 使用对象的getClass()方法:String s = “aa”; Class<?> clazz = s.getClass();
下面写一个程序来用一下这些API吧:
- //获得MethodInvoke类对应的一个clas对象
- Class<?> MethodInvok=MethodInvoke.class;
- //获得一个MethodInvoke类对应的对象实例
- Object MethodInvo=MethodInvok.newInstance();
- //获得MethodInvo对象对应的add方法对应的一个对象实例
- 1):Method method=MethodInvok.getMethod("add", int.class,int.class);
- //调用MethodInvo对象对应的add方法对应的一个对象(MethodInvo)实例所代表的方法,并获得结果
- 2)Object result= method.invoke(MethodInvo, 1,2);
- System.out.println(result);
- System.out.println("--------------------------------------");
- Method method1=MethodInvok.getMethod("print",String.class);
- Object Result1=method1.invoke(MethodInvo, "tom");
- System.out.println(Result1);
注:1)处的int.class,int.class可以写为new Class[]{int.class,int.class}
原因在于getMethod方法的第二个参数是一个可变参数。
2)处的1,2可以写为new int【】{1,2},原因如1);
4.若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
a) 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:
Class<?> classType = String.class;
Object obj = classType.newInstance();
b) 先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
注:
4. 若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{“hello”, 3});
代码示例:
- // 该方法实现对Customer对象的拷贝操作
- public Object copy(Object object) throws Exception
- {
- Class<?> classType = object.getClass();
- //先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用
- Constructor对象的newInstance()方法构造一个实例。
- Object objectCopy = classType.getConstructor(new Class[] {})
- .newInstance(new Object[] {});
- // Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默
- 认和private访问级别的属性
- Field[] fields = classType.getDeclaredFields();
- for (Field field : fields)
- {
- String name = field.getName();
- // 将属性的首字母转换为大写
- String firstLetter = name.substring(0, 1).toUpperCase(); String getMethodName = "get" + firstLetter + name.substring(1);
- String setMethodName = "set" + firstLetter + name.substring(1);
- Method getMethod = classType.getMethod(getMethodName,
- new Class[] {});
- Method setMethod = classType.getMethod(setMethodName,
- new Class[] { field.getType() });
- Object value = getMethod.invoke(object, new Object[] {});
- setMethod.invoke(objectCopy, new Object[] { value });
- }
- // 以上两行代码等价于下面一行
- // Object obj2 = classType.newInstance();
- // System.out.println(obj);
- return objectCopy;
- }
5.Integer.TYPE返回的是int,而Integer.class返回的是Integer类所对应的Class对象。
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法
一维数组的简单创建,设值,取值
Object array = Array.newInstance(classType, 10);
Array.set(array, 5, "hello");
String str = (String)Array.get(array, 5);
二维数组的简单创建,设值,取值
- //创建一个设值数组维度的数组
- int[] dims = new int[] { 5, 10, 15 };
- //利用Array.newInstance创建一个数组对象,第一个参数指定数组的类型,第
- 二个参数设值数组的维度,下面是创建一个长宽高为:5,10,15的三维数组
- Object array = Array.newInstance(Integer.TYPE, dims);
- System.out.println(array instanceof int[][][]);
- //获取三维数组的索引为3的一个二维数组
- Object arrayObj = Array.get(array, 3);
- //获取二维数组的索引为5的一个一维数组
- arrayObj = Array.get(arrayObj, 5);
- //设值一维数组arrayObj下标为10的值设为37
- Array.setInt(arrayObj, 10, 37);
- int[][][] arrayCast = (int[][][]) array;
- System.out.println(arrayCast[3][5][10]);
利用反射访问类的私有方法:
代码示例:
- Private p = new Private();
- Class<?> classType = p.getClass();
- Method method = classType.getDeclaredMethod("sayHello",
- new Class[] { String.class });
- method.setAccessible(true);//压制Java的访问控制检查,使允许访问private方法
- String str = (String)method.invoke(p, new Object[]{"zhangsan"});
- System.out.println(str);
利用反射访问类的私有变量:
- Private2 p = new Private2();
- Class<?> classType = p.getClass();
- Field field = classType.getDeclaredField("name");
- field.setAccessible(true);//压制Java对访问修饰符的检查
- field.set(p, "lisi");
- System.out.println(p.getName());
代理
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
•在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
代理模式一般涉及到的角色有
–抽象角色:声明真实对象和代理对象的共同接口
–代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
–真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
•(1)Interface InvocationHandler:该接口中仅定义了一个方法
–public object invoke(Object obj,Method method, Object[] args)
•在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
•(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容
•protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
•static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
•static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓Dynamic Proxy(动态代理)是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作
在使用动态代理类时,我们必须实现InvocationHandler接口
通过代理的方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系
动态代理是指客户通过代理类来调用其它对象的方法
•动态代理使用场合:
•调试
•远程方法调用(RMI)
动态代理的步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
4.通过代理调用方法