反射与动态代理

来源:互联网 发布:业务流程优化研究现状 编辑:程序博客网 时间:2024/06/14 08:35

反射:运行时类型信息

反射功能通过Class类和java.lang.reflect包下的实现了Member接口的Filed,Method,Constructor等类来完成,

可以用Constructors类的实例来完成创建新对象,用getset方法来读取和写入Field对象代表的fields,通过invoke方法来调用整体上Method对象代表的方法

所有关于RTTI和反射真正的区别是,编译器在编译期间检查.class文件,而反射是在运行时检查。

Class

public String getName()

String 的形式返回 Class对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

如果此类对象表示的是非数组类型的引用类型,则返回该类的二进制名称Java Language Specification, Second Edition对此作了详细说明。

如果此类对象表示一个基本类型或 void,则返回的名字是一个与该基本类型或void 所对应的Java 语言关键字相同的String

如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '['字符加元素类型名。元素类型名的编码如下: 

Element Type       Encoding  

boolean              Z  

byte                 B  

char                 C  

class or interface       Lclassname;  

double                   D  

float                    F  

int                      I  

long                     J  

short                    S  

类或接口名 classname 是上面指定类的二进制名称。

示例:

 String.class.getName()

     returns "java.lang.String"

 byte.class.getName()

     returns "byte"

 (new Object[3]).getClass().getName()

     returns "[Ljava.lang.Object;"

 (new int[3][4][5][6][7][8][9]).getClass().getName()

     returns "[[[[[[[I"

 

public Class<? super T> getSuperclass()

返回表示此 Class所表示的实体(类、接口、基本类型或 void)的超类的Class。如果此Class 表示Object 类、一个接口、一个基本类型或void,则返回null。如果此对象表示一个数组类,则返回表示该Object 类的Class 对象。

返回:

此对象所表示的类的超类。

 

public Class<?>[] getInterfaces()

确定此对象所表示的类或接口实现的接口。

如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。数组中接口对象顺序与此对象所表示的类的声明的 implements子句中接口名顺序一致

如果此对象表示一个接口,则该数组包含表示该接口扩展(extends)的所有接口的class对象。数组中接口对象顺序与此对象所表示的接口的声明的 extends子句中接口名顺序一致。

如果此对象表示一个不实现任何接口的类或接口,则此方法返回一个长度为 0的数组。

如果此对象表示一个基本类型或 void,则此方法返回一个长度为0 的数组。

 

String[] a=new String[5];

System.out.println(a.getClass());

System.out.println(a.getClass().getSuperclass());

class [Ljava.lang.String;

class java.lang.Object

 

public T newInstance()

              throws InstantiationException,

                     IllegalAccessException

创建此 Class对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new表达式实例化该类。如果该类尚未初始化,则初始化这个类。

注意,此方法传播 null构造方法所抛出的任何异常,包括已检查的异常。使用此方法可以有效地绕过编译时的异常检查,而在其他情况下编译器都会执行该检查。Constructor.newInstance 方法将该构造方法所抛出的任何异常包装在一个(已检查的)InvocationTargetException 中,从而避免了这一问题。

 

Constructor<T> getConstructor(Class<?>... parameterTypes)

          返回一个 Constructor对象,它反映此 Class对象所表示的类的指定公共构造方法。

 Constructor<?>[] getConstructors()

          返回一个包含某些 Constructor对象的数组,这些对象反映此 Class对象所表示的类的所有公共构造方法。

 Annotation[] getDeclaredAnnotations()

          返回直接存在于此元素上的所有注释。

 Class<?>[] getDeclaredClasses()

          返回 Class对象的一个数组,这些对象反映声明为此 Class对象所表示的类的成员的所有类和接口。

 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

          返回一个 Constructor对象,该对象反映此 Class对象所表示的类或接口的指定构造方法。

 Constructor<?>[] getDeclaredConstructors()

          返回 Constructor对象的一个数组,这些对象反映此 Class对象表示的类声明的所有构造方法。

 Field getDeclaredField(String name)

          返回一个 Field对象,该对象反映此 Class对象所表示的类或接口的指定已声明字段。

 Field[] getDeclaredFields()

          返回 Field对象的一个数组,这些对象反映此 Class对象所表示的类或接口所声明的所有字段。

 Method getDeclaredMethod(String name, Class<?>... parameterTypes)

          返回一个 Method对象,该对象反映此 Class对象所表示的类或接口的指定已声明方法。

 Method[] getDeclaredMethods()

          返回 Method对象的一个数组,这些对象反映此 Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 

 Field getField(String name)

          返回一个 Field对象,它反映此 Class对象所表示的类或接口的指定公共成员字段。

 Field[] getFields()

          返回一个包含某些 Field对象的数组,这些对象反映此 Class对象所表示的类或接口的所有可访问公共字段。

Method getMethod(String name, Class<?>... parameterTypes)

          返回一个 Method对象,它反映此 Class对象所表示的类或接口的指定公共成员方法。

 Method[] getMethods()

          返回一个包含某些 Method对象的数组,这些对象反映此 Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共member 方法。

public Class<?> getComponentType()

返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。

 

Member

public interface Member {

    public static final int PUBLIC = 0;

    public static final int DECLARED = 1;

    public Class<?> getDeclaringClass();

           //返回表示声明由此 Member 表示的成员或构造方法的类或接口的 Class 对象

    public String getName();

           // 返回此 Member 表示的底层成员或构造方法的简单名称。

    public int getModifiers();

          //作为整数返回由此 Member 所表示的成员或构造方法的 Java 语言修饰符。

    public boolean isSynthetic();

          // 如果此成员是编译器引入的,则返回 true;否则,返回 false。

}

 

AccessibleObject

java.lang.reflect.AccessibleObject

public class AccessibleObject extendsObject

implements AnnotatedElement

AccessibleObject 类是 FieldMethodConstructor对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用FieldMethodConstructor对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization或其他持久性机制)以某种通常禁止使用的方式来操作对象。

<T extends Annotation>  T

 getAnnotation(Class<T> annotationClass)

          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null

 Annotation[] getAnnotations()

          返回此元素上存在的所有注释。

 Annotation[] getDeclaredAnnotations()

          返回直接存在于此元素上的所有注释。

 boolean isAccessible()

          获取此对象的 accessible标志的值。

 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

          如果指定类型的注释存在于此元素上,则返回 true,否则返回false

static void setAccessible(AccessibleObject[] array, boolean flag)

          使用单一安全性检查(为了提高效率)为一组对象设置 accessible标志的便捷方法。

 void setAccessible(boolean flag)

          将此对象的 accessible标志设置为指示的布尔值。

 

Constructor

java.lang.reflect.Constructor<T>

public final class Constructor<T> extends AccessibleObject

implements GenericDeclaration, Member

 

public Class<?>[] getParameterTypes()

按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。

public T newInstance(Object... initargs)

              throws InstantiationException,

                     IllegalAccessException,

                     IllegalArgumentException,

                     InvocationTargetException

使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。

如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。

如果构造方法的声明类是非静态上下文的内部类,则构造方法的第一个参数需要是封闭实例;请参阅Java 语言规范  15.9.3 节。

如果所需的访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类。

如果构造方法正常完成,则返回新创建且已初始化的实例。

 

Field

java.lang.reflect.Field

public final class Fieldextends AccessibleObject implements Member

 

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

Array 允许在执行 getset 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个IllegalArgumentException

public String getName()

返回此 Field 对象表示的字段的名称。

 

public Class<?> getType()

返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

 

public Object get(Object obj)

           throws IllegalArgumentException,

                  IllegalAccessException

返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。

底层字段的值是按以下方式获得的:

如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。

否则,底层字段是一个实例字段。如果指定的 obj 变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException

如果此 Field 对象强制实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。

否则,从底层实例字段或静态字段中获取该值。如果该字段是一个基本类型字段,则在返回前将该值包装在一个对象中,否则照原样返回。

如果字段隐藏在 obj 的类型中,则根据前面的规则获得字段的值。

参数: 

obj - 从中提取所表示字段的值的对象

 

 

public void set(Object obj,

                Object value)

         throws IllegalArgumentException,

                IllegalAccessException

将指定对象变量上此 Field 对象表示的字段设置为指定的新值。如果底层字段的类型为基本类型,则对新值进行自动解包。

进行此操作的方式如下:

如果底层字段是静态字段,则忽略 obj 变量;它可能为 null。

否则底层字段是一个实例字段。如果指定对象变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象变量不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException

如果此 Field 对象实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException

如果底层字段为 final 字段,则该方法将抛出一个 IllegalAccessException,除非 setAccessible(true) 已经继承该字段并且该字段是一个非静态字段。在通过程序的其他部分可以访问类的实例之前,只有使用空白 final 字段反序列化或重构类的实例期间,以这种方式设置 final 字段才有意义。在其他任何上下文中使用该方法都可能会有不可预知的结果,包括程序的其他部分继续使用该字段的原始值的情况。

如果底层字段的类型为某一基本类型,则可以尝试使用解包转换将新值转换为基本类型的值。如果该尝试失败,则此方法将抛出一个 IllegalArgumentException

如果在进行可能的解包之后,无法通过某一标识或扩展转换将新值转换为底层字段的类型,则此方法将抛出一个 IllegalArgumentException

如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。

字段被设置为可能已解包并扩大的新值。

如果字段隐藏在 obj 的类型中,则根据前面的规则设置字段的值。

参数: 

obj - 应该修改其字段的对象

value - 正被修改的 obj 的字段的新值

 

Method

java.lang.reflect.Method

public final class Method extends AccessibleObject

implements GenericDeclaration, Member

 

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException

public String getName()

String 形式返回此 Method 对象表示的方法名称。

public Class<?> getReturnType()

返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。

public Class<?>[] getParameterTypes()

按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果底层方法不带参数,则返回长度为 0 的数组。

 

public Class<?>[] getExceptionTypes()

返回 Class 对象的数组,这些对象描述了声明将此 Method 对象表示的底层方法抛出的异常类型。如果此方法没有在其 throws 子句中声明异常,则返回长度为 0 的数组。

 

 

 

public Object invoke(Object obj,

                     Object... args)

              throws IllegalAccessException,

                     IllegalArgumentException,

                     InvocationTargetException

对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。

如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。

如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。

如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。

如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。

如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。

参数: 

obj - 从中调用底层方法的对象

args - 用于方法调用的参数

 

Modifier

public class Modifier

extends Object

Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。修饰符集被表示为整数,用不同的位位置(bit position) 表示不同的修饰符。表示修饰符的常量值取自于The JavaTM Virtual Machine Specification, Second edition 的表4.14.44.5 4.7

 

public static String toString(int mod)

返回描述指定修饰符中的访问修饰符标志的字符串。例如:

    public final synchronized strictfp

 

返回的修饰符名称的顺序与The Java Language Specification, Second Edition §8.1.1§8.3.1§8.4.3§8.8.3 §9.1.1 中给出的建议修饰符排序是一致的。此方法使用的完整修饰符排序是:

public protected private abstract static final transient volatile synchronized native strictfp interface

这个类中讨论的 interface 修饰符不是真正的 Java 语言修饰符,它将出现在此方法列出的其他所有修饰符的后面。此方法可能返回一个不是有效 Java 实体修饰符的修饰符;换句话说,它没有对该输入表示的可能有效的组合修饰符进行检查。

 

Array

java.lang.reflect.Array

public final class Arrayextends Object

Array 类提供了动态创建和访问 Java 数组的方法。

Array 允许在执行 get set 操作期间进行扩展转换,但如果发生收缩转换,则抛出IllegalArgumentException

 

public static int getLength(Object array)

                     throwsIllegalArgumentException

int 形式返回指定数组对象的长度。

public static Object get(Object array,

                         int index)

                  throws IllegalArgumentException,

                         ArrayIndexOutOfBoundsException

返回指定数组对象中索引组件的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。

public static Object newInstance(Class<?> componentType,

                                 int length)

                          throwsNegativeArraySizeException

创建一个具有指定的组件类型和长度的新数组。调用此方法等效于创建如下数组:

 int[] x = {length};

 Array.newInstance(componentType, x);

 

public static void set(Object array,

                       int index,

                       Object value)

                throws IllegalArgumentException,

                       ArrayIndexOutOfBoundsException

将指定数组对象中索引组件的值设置为指定的新值。如果数组的类型为基本组件类型,则新值第一个被自动解包。

 

例子

public class Test {

  public static void main(String[]args) throws Exception {

        ArrayList<Integer>list = new ArrayList<Integer>();

        Method method = list.getClass().getMethod("add", Object.class);

        method.invoke(list,"Java反射机制实例。");

        System.out.println(list.get(0));

    }

}

通过反射修改数组大小

 

    int[]temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

      int[]newTemp = (int[])arrayInc(temp, 15);

  //修改数组大小

    public static Object arrayInc(Objectobj, int len) {

        Class<?> arr = obj.getClass().getComponentType();

        Object newArr = Array.newInstance(arr,len);

        int co = Array.getLength(obj);

        System.arraycopy(obj, 0,newArr, 0, co);

        return newArr;

    }

 

将反射机制应用于工厂模式

 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。当我们添加很多的子类的时候,会很麻烦。

 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。

 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成

 

interface fruit {

public abstract void eat();

}

 

class Appleimplements fruit {

public void eat() {

System.out.println("Apple");

}

}

 

class Orangeimplements fruit {

public void eat() {

System.out.println("Orange");

}

}

 

class Factory {

public static fruit getInstance(StringClassName) {

fruit f = null;

try {

f = (fruit) Class.forName(ClassName).newInstance();

} catch (Exceptione) {

e.printStackTrace();

}

return f;

}

}


动态代理

Proxy是一个基本设计模式,代理对象代替了真实对象来提供额外的或不同的操作,这通常包含了与真实对象的通信。

在任何时间你想要把额外的操作放在另外一个地方,代理都是有帮助的。特别是当你想要可以在是否使用这些额外的操作上来回转换时。

java的动态代理把代理的想法更推进了一步,它可以动态地创建代理对象,可以动态地处理对被代理对象的方法调用

所以对代理对象的方法调用被转移到一个单一的invocation handler,它的工作是发现调用了哪个方法,并决定怎么处理它

 

java.lang.reflect

public interface InvocationHandler {

    public Object invoke(Objectproxy, Method method, Object[] args)

        throws Throwable;

}

 

class DynamicProxyHandlerimplements InvocationHandler {

private Objectproxied;

public DynamicProxyHandler(Objectproxied) {

this.proxied =proxied;

}

 

public Object invoke(Objectproxy, Method method, Object[] args) throws Throwable {

System.out.println("**** proxy: " +proxy.getClass() + ", method: " + method + ", args: " + args);

if (args !=null)

for (Objectarg : args)

System.out.println(" " +arg);

return method.invoke(proxied,args);

}

}

 

创建Foo接口的代理对象

   InvocationHandler handler =new MyInvocationHandler(...);

Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),new Class[] { Foo.class });

Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class })

.newInstance(new Object[] {handler });

 

或者

    

Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);

 

public static Object newProxyInstance(ClassLoader loader,

            Class<?>[] interfaces,

            InvocationHandler h)

     throws IllegalArgumentException

 


原创粉丝点击