黑马程序员之----------java 之反射与动态代理模式



-----<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

Java 反射

一、什么是反射:

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

二、什么是Java中的类反射:

Reflection 允许运行中的 Java 程序对自身进行检查,并能直接操作程序的内部属性和方法。
Reflection 是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期 Reflection APIs 取得任何已知名称 class 的內部信息,包括 packagetype parameterssuperclassimplemented interfacesinner classes, outer class, fieldsconstructorsmethodsmodifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods

换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。

 

三、Java类反射中所必须的类:

包括:FieldConstructorMethodClassObject.

Field类封装了反射类的属性,

Constructor类则封装了反射类的构造方法。

Method类封装反射类方法的一个类。

Class类将每个对象被映射为一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

Proxy类 提供动态地生成代理类和类实例的静态方法。

Array类 提供动态地生成和访问JAVA数组的方法。
四、Java的反射类能做什么:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判段任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。

 

获取Class对象有三种方式:

1.通过Object类的getClass()方法。

Class c1 = new String(" ").getClass();

2.通过Class类的静态方法——forName()来实现:

Class c2 = Class.forName("MyObject");

3.如果T是一个已定义的类型的话,在java中,它的.class文件名:T.class就代表了与其匹配的Class对象,例如:

Class c3 = Manager.class;

Class c4 = int.class;

Class c5 = Double[].class;

 

java中,一切皆对象。

java中数组的其实就是某个类,含有相同元素的相同维数的数组还会共同享用同一个Class对象!

Class类中存在以下几个重要的方法:

1.getName()

一个Class对象描述了一个特定类的特定属性,而这个方法就是返回String形式的该类的简要描述。对数组的Class对象调用该方法会产生奇怪的结果。

 

2.newInstance()

该方法可以根据某个Class对象产生其对应类的实例。需要强调的是,它调用的是此类的默认构造方法。例如:

MyObject x = new MyObject();

MyObject y = x.getClass().newInstance();

 

3.getClassLoader()

返回该Class对象对应的类的类加载器。

 

4.getComponentType()

该方法针对数组对象的Class对象,可以得到该数组的组成元素所对应对象的Class对象。

int[] ints = new int[]{1,2,3};

Class class1 = ints.getClass();

Class class2 = class1.getComponentType();

而这里得到的class2对象所对应的就应该是int这个基本类型的Class对象。

 

5.getSuperClass()

返回某子类所对应的直接父类所对应的Class对象。

 

6.isArray()

判定此Class对象所对应的是否是一个数组对象。

 

 

java 代码用于测试的类

public class Person {

public int age;

public String name;

private int add;

 

public Person() {

Log("Constructor");

}

 

Person(int age, String name) {

this.age = age;

this.name = name;

Log("Constructor with parameters");

}

 

public void setAdd(int add1) {

this.add = add1;

}

 

public int getAdd() {

return add;

}

 

private void Log(String name) {

System.out.println("Person" + name);

 

}

}

 

class Studebts extends Person {

public int age;

public String name;

private int add;

 

public Studebts() {

Log("Constructor");

}

 

Studebts(int age, String name) {

this.age = age;

this.name = name;

Log("Constructor with parameters");

}

 

public void setAdd(int add2) {

this.add = add2;

}

 

public int getAdd() {

return add;

}

 

private void Log(String str) {

System.out.println("Students:" + str);

}

}

}

1、获取类的Class对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象方式:

1.调用getClass

public class Test{

public static void main(String[] args){

Boolean age = true;

Class<?> class1= age.getClass();

System.out.println(class1);

}

}

输出:class java.lang.Boolean

2.运用.class 语法

public class Test{

public static void main(String[] args){

Boolean age = true;

Class<?> class1= Boolean.class;

System.out.println(class1);

}

}

输出:class java.lang.Boolean

3.运用static method Class.forName()

public class Test{

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

Boolean age = true;

Class<?> class1= Class.forName("java.lang.Boolean");

System.out.println(class1);

}

}

输出:class java.lang.Boolean

4.运用primitive wrapper classes的TYPE 语法这里返回的是原生类型,和Boolean.class返回的不同

public class Test{

public static void main(String[] args) {

Boolean age = true;

Class<?> class1= Boolean.TYPE;

System.out.println(class1);

}

}

输出:boolean

 

2、获取类的Fields

可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。JAVA 的Class<T>类提供了几个方法获取类的属性。

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

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

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

public Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字

1.代码

public class Test{

public static void main(String[] args) {

boolean age = true;

Class<?> class1 = Students.class;

        

// 使用getFields获取属性

Field[] fields = class1.getFields();

for (Field f : fields)

{

    System.out.println(f);

}

 

System.out.println();

             

// 使用getDeclaredFields获取属性

fields = class1.getDeclaredFields();

for (Field f : fields)

{

    System.out.println(f);

}

}

}
输出:

public int xsd.Students.age

public java.lang.String xsd.Students.name

public int xsd.Person.age

public java.lang.String xsd.Person.name

 

public int xsd.Students.age

public java.lang.String xsd.Students.name

private int xsd.Students.add

可见getFields和getDeclaredFields区别:

getFields返回的是申明为public的属性,包括父类中定义,

getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。

3、获取类的Method

通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法

Class<T>类提供了几个方法获取类的方法。

public MethodgetMethod(String name,Class<?>... parameterTypes)  返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法

public Method[] getMethods()    返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口

        (包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法

public MethodgetDeclaredMethod(Stringname,Class<?>... parameterTypes)

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

public Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,

包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

代码:

public class Test{

public static void main(String[] args) {

boolean age = true;

// 使用getMethods获取函数 

Class<?> classType = Students.class;

Method[] methods = classType.getMethods();

for (Method m : methods)

{

    System.out.println(m);

}

 

System.out.println();

 

// 使用getDeclaredMethods获取函数 

methods = classType.getDeclaredMethods();

for (Method m : methods)

{

    System.out.println(m);

}

}

}

输出:

public void xsd.Students.setAdd(int)

public int xsd.Students.getAdd()

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

public native int java.lang.Object.hashCode()

public final native java.lang.Class java.lang.Object.getClass()

public boolean java.lang.Object.equals(java.lang.Object)

public java.lang.String java.lang.Object.toString()

public final native void java.lang.Object.notify()

public final native void java.lang.Object.notifyAll()

 

private void xsd.Students.Log(java.lang.String)

public void xsd.Students.setAdd(int)

public int xsd.Students.getAdd() 

4、获取类的Constructor

通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 

Class<T>类提供了几个方法获取类的构造器。

public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法

public Constructor<?>[] getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的 所有公共构造方法

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法

public Constructor<?>[] getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 它们是公共、保护、默认(包)访问和私有构造方法

public class Test {

public static void main(String[] args) {

boolean age = true;

// 使用getMethods获取函数

Class<?> classType = Students.class;

Method[] methods = classType.getMethods();

for (Method m : methods) {

System.out.println(m);

}

 

System.out.println();

 

// 使用getDeclaredMethods获取函数

methods = classType.getDeclaredMethods();

for (Method m : methods) {

System.out.println(m);

}

// 使用getConstructors获取构造器

Constructor<?>[] constructors = classType.getConstructors();

for (Constructor<?> m : constructors) {

System.out.println(m);

}

 

System.out.println();

 

// 使用getDeclaredConstructors获取构造器

constructors = classType.getDeclaredConstructors();

for (Constructor<?> m : constructors) {

System.out.println(m);

}

}

}

输出:

public void xsd.Students.setAdd(int)

public int xsd.Students.getAdd()

public static java.lang.reflect.Constructor[] xsd.Person.getConstructors()

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

public native int java.lang.Object.hashCode()

public final native java.lang.Class java.lang.Object.getClass()

public boolean java.lang.Object.equals(java.lang.Object)

public java.lang.String java.lang.Object.toString()

public final native void java.lang.Object.notify()

public final native void java.lang.Object.notifyAll()

private void xsd.Students.Log(java.lang.String)

public void xsd.Students.setAdd(int)

public int xsd.Students.getAdd()

public xsd.Students()

public xsd.Students()

xsd.Students(int,java.lang.String)

5、新建类的实例

通过反射机制创建新类的实例,有几种方法可以创建

调用无自变量ctor

1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败.

public class Test {

public static void main(String[] args) throws InstantiationException, IllegalAccessException {

Class<?> classType = Students.class;

Object inst = classType.newInstance();

System.out.println(inst);

}

}

输出:

PersonConstructor

Students:Constructor

xsd.Students@c3c749

2、调用默认Constructor对象的newInstance方法

public class Test {

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

Class<?> classType = Students.class;

Constructor<?> constructor1 = classType.getConstructor();

Object inst = constructor1.newInstance();

System.out.println(inst);

}

}

输出:

PersonConstructor

Students:Constructor

xsd.Students@c3c749

调用带参数ctor

3、调用带参数Constructor对象的newInstance方法

public class Test {

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

Class<?> classType = Students.class;

Constructor<?> constructor2 =classType.getDeclaredConstructor(int.class, String.class);

Object inst = constructor2.newInstance(1, "123");

System.out.println(inst);

}

}

输出:

PersonConstructor

Students:Constructor with parameters

xsd.Students@1bc4459 

6、调用类的函数

通过反射获取类Method对象,调用Field的Invoke方法调用函数。

public class Test {

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

Class<?> classType = Students.class;

Object inst = classType.newInstance();

Method logMethod = classType.getDeclaredMethod("Log", String.class);

logMethod.invoke(inst, "test");

}

} 

输出:

PersonConstructor

Students:Constructor

Exception in thread "main" java.lang.IllegalAccessException

上面失败是由于没有权限调用private函数,这里需要设置Accessibletrue;

public class Test {

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

Class<?> classType = Students.class;

Object inst = classType.newInstance();

Method logMethod = classType.getDeclaredMethod("Log", String.class);

logMethod.setAccessible(true);

logMethod.invoke(inst, "test");

}

}

输出:

PersonConstructor

Students:Constructor

Students:test

 

7、设置/获取类的属性值

通过反射获取类的Field对象,调用Field方法设置或获取值

public class Test {

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

Class<?> classType = Students.class;

Object inst = classType.newInstance();

Field intField = classType.getField("age");

intField.setInt(inst, 100);

int value = intField.getInt(inst);

 

}

}

输出:

PersonConstructor

Students:Constructor

四、动态创建代理类

相对于静态代理的代理类在编译时生成(.class文件),动态代理与其的区别是:动态代理类在运行时在JVM中生成。Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类(实现了InvocationHandler接口)对象,便能动态地获得代理类,避免了静态代理中代理类的急剧膨胀问题。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。

代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。

代理模式的角色:

抽象角色:声明真实对象和代理对象的共同接口

代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

动态代理:

java.lang.reflect.Proxy Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类

——protected Proxy(InvocationHandler h)

构造函数,用于给内部的InvocationHandler类型的属性h赋值。 参数h即我们自定义的调用处理器(实现了InvocationHandler接口)的对象。

——public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

获得一个代理类(类对应的Class对象)。loader指定类的加载器。interfaces是代理类要实现的接口,一般是被代理类所拥有的全部接口的数组。

—— public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

返回代理类的一个实例。返回后的代理类可以当作被代理类使用。参数java.lang.ClassLoader:这是类加载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。

Proxy 静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。

每次生成动态代理类对象时都需要指定一个类加载器对象。interfaces即代理类所要实现的接口,一般是被代理类所实现的全部接口的数组。h即我们传入的调用处理器对象(实现了InvocationHandler接口)。

封装的方法

——public static boolean isProxyClass(Class<?> cl) 判断指定的类是否是一个动态代理类。参数cl即类对应的Class对象。

 

——public static InvocationHandler getInvocationHandler(Object proxy) 获取指定代理对象所关联的调用处理器。参数proxy即代理类实例

 

InvocationHandler 是代理实例的调用处理程序 实现的接口,每个代理实例都具有一个关联的调用处理程序。

——public Object invoke(Object proxy, Method method, Object[] args)

第一个参数proxy一般是指代理类,method是指被代理的方法的Method对象,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

我们在使用动态代理时要自定义调用处理器InvocationHandlerImpl实现该接口,通过对invoke方法的实现处理对被代理对象的方法访问的控制。

InvocationHandlerImpl中包含被代理的对象的引用。

 

对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 

动态Proxy是这样的一种类:

它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

动态代理过程

1,类之间的关系

 

2.使用动态代理的步骤:

 

(1)通过实现 InvocationHandler 接口创建自己的调用处理器(持有被代理类实例的引用);

(2)通过为 Proxy 类的静态方法getProxyClass根据指定的 ClassLoader 对象和一组 interface 来创建动态代理类;

(3)通过反射机制获得动态代理类的构造函数的Constructor对象,其唯一参数类型是调用处理器接口类型;

(4)通过反射机制由构造函数的Constructor对象创建动态代理类的实例,构造时调用处理器对象作为参数被传入(在这之前需创建被代理类及调用处理器的实例)。

(5)通过代理对象调用方法。

    //步骤1: InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

   // 其内部通常包含指向委托类(即被代理类)实例的引用,用于真正执行分派转发过来的方法调用

   InvocationHandler handler = new InvocationHandlerImpl(..); //可以将被代理类对象作为参数传入

 

   // 步骤2:通过 Proxy 为包括 InterfaceX 接口在内的一组接口动态创建动态代理类

   Class clazz = Proxy.getProxyClass(classLoader, new Class[] { InterfaceX.class, ... }); 

 

   //步骤3: 通过反射由生成的动态代理类的Class对象获得其构造函数的Constructor对象

   Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 

 

   //步骤4: 通过构造函数对象创建动态代理类对象

   InterfaceX proxy = (InterfaceX)constructor.newInstance(new Object[] { handler }); 

 

   //步骤5:通过代理调用方法,此方法即代理方法(假设为request())

   proxy.request();

3.简化的步骤

Proxy的静态方法    

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

将步骤2、3、4封装起来了,我们可以更方便地使用动态代理:

(1)通过实现 InvocationHandler 接口创建自己的调用处理器(持有被代理类实例的引用);

(2)通过Proxy类的静态方法newProxyInstance(...)创建代理类的实例。

(3)通过代理类对象调用方法。

      //步骤1: 自定义调用处理器类InvocationHandlerImpl(实现 InvocationHandler接口,且持有被代理类的引用)

      InvocationHandler handler = new InvocationHandlerImpl(..); 

 

      //步骤2: 通过 Proxy 的静态方法newProxyInstance(..)直接创建动态代理类实例

      InterfaceX proxy = (InterfaceX)Proxy.newProxyInstance( classLoader,interfaces, handler ); 

      

     //步骤3:通过代理调用方法,此方法即代理方法(假设为request())

      proxy.request();

 

四、动态代理示例

1.抽象角色Subject

public interface Subject{

public void request();

}

2、定义真实角色即被代理的类RealSubject

public class RealSubject  implements Subject{

public void request(){

System.out.println("from real subject");

}

}

3、定义代理角色

public class MyInvocationHandler implements InvocationHandler{

//持有被代理类的引用

private Subject real;

public MyInvocationHandler(Subject real){

this.real=real;

}

public Object invoke(Object proxy, java.lang.reflect.Method method,

Object[] args) throws Throwable{

System.out.println("before");//可以附加操作控制对真实对象方法的访问

//System.out.println(proxy.getClass().getName()+"---"+proxy.getClass().getSuperclass().getName()+"---"+method.getName());

Object obj=method.invoke(real, args);//执行被代理对象的方法

System.out.println("after");

        return obj;//obj是method方法返回的数据

}

}

4、通过Proxy.newProxyInstance构建代理对象

public class DynamicTest{

public static void main(String[] args){

  Subject real=new RealSubject();

  InvocationHandler h=new MyInvocationHandler(real);

  

  //获得被代理类所实现的所有接口的数组,在这里数组中只有Subject.class一个元素

  Class[] interfaces= real.getClass().getInterfaces();

  

  //获得类加载器

  ClassLoader loader=h.getClass().getClassLoader();

  

  //获得动态代理类的实例

      Object s=java.lang.reflect.Proxy.newProxyInstance(loader,interfaces, h);

      

      //通过代理类对象调用方法

      Subject sub=(Subject)s;

      sub.request();

}

}

五、关于动态代理类

 

1.包:

如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.pattern.proxy 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.pattern.proxy),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;

验证方式:将Subject的public修饰符去掉,在调用处理器MyInvocationHandler的invoke()方法中使用proxy.getClass().getPackage()获得动态代理类的包进行验证。

 

2.类修饰符:

该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;

验证方式:在调用处理器MyInvocationHandler的invoke()方法中使用proxy.getClass().getModifiers()获得类的修饰符字段值,然后与帮助文档中的常量字段表对比(或使用Modifier类的相关方法判断)。

 

3.类名:

格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。

查看Proxy类的源代码中关于类名格式的信息,可以通过相同方式生成两个动态代理类,打印两代理类对象的hashCode值判断是否重复生成代理类。

 

.动态代理类的继承关系:

 

   (1)与Proxy:

         可以看出,Proxy是动态代理类的父类,动态代理类可以调用Proxy中的方法。每个动态代理实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler 去获得代理类实例的调用处理器对象。

 

   (2)接口组的限制:

动态代理类最多实现65535个接口,这是由JVM限制的,在Proxy的源代码中可以很容易发现,当接口数超过65535时,就会throw new IllegalArgumentException("interface limit exceeded");

要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。

这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败。(使用Class.forName()进行可见性判断)

需被代理的所有非 public 的接口必须在同一个包中,否则代理类生成也会失败。(在上面的包的说明中已经能证实到,因为一个类只能位于一个包下)

 

3代理的方法

 

因为动态代理类$ProxyN是Proxy的子类,Proxy又继承于Object,所以$ProxyN可以调用Proxy、Object、它所实现的所有接口中的方法。

 

(1)使用动态代理类的实例调用Proxy中的方法时,不会对这些方法(Proxy的这些方法都是static的)进行代理(即不会委托到调用处理器的invoke上处理)

 

(2)使用动态代理类的实例调用从Object类继承下来的方法时,会对toString()、equals()以及hashCode()这三个方法进行代理(委托到invoke()方法上反射执行),对于其他方法则不代理。

 

(3)使用动态代理类的实例调用从接口(创建代理实例时传入的接口数组)中实现的方法时,这些方法都会被代理,动态代理有意义的地方就在于此。但是需要注意的是当代理的一组接口有重复声明的方法且该方法被调用时,代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,因为在代理类内部无法区分其当前的被引用类型

例如:接口数组      Class[] interfaces= new Class[]{Jia.class,Subject.class,};,并且这两个接口中都声明了同样的方法request(),那么当 Subject sub=(Subject)s;sub.request();时并不是调用的Subject中声明的方法,而是Jia中声明的方法(虽然当前的引用类型是Subject,但是Jia的顺序在Subject的前面)。

 

4异常处理

我们必须遵守一个继承原则是:即子类覆盖父类或实现父接口的方法时,抛出的异常必须在原方法支持的异常列表之内。同样在此处,动态代理类抛出的异常也要在其父类或父接口方法支持的异常列表中。但是如果invoke()方法抛出的异常不在代理类的父类或父接口方法支持的异常列表中,那么将会抛出 UndeclaredThrowableException 异常。这个异常是一个 RuntimeException 类型,所以不会引起编译错误。通过该异常的 getCause 方法,还可以获得原来那个不受支持的异常对象,以便于错误诊断。

验证方式:在上例中的invoke()方法中抛出异常:throw new ClassNotFoundException();

不足----Proxy仅支持interface的代理,这是由java单继承的特点所造成的

Proxy 的重要静态变量

// 映射表:用于维护类装载器对象到其对应的代理类缓存

private static Map loaderToCache = new WeakHashMap();  

// 标记:用于标记一个动态代理类正在被创建中

private static Object pendingGenerationMarker = new Object();  

// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());  

// 关联的调用处理器引用protected InvocationHandler h;

Proxy 构造方法

// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用

private Proxy() {}  

// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用

protected Proxy(InvocationHandler h) {this.h = h;}

 

 

-----<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值