Java - Class对象、反射、动态代理学习

         Java是一个动态链接语言。Java中的类在需要时才会被加载,这个类我们可以视为一个Class对象(xxx.class)。管理这些Class对象的类则就是Class类。

         这里有点拗口,对于初学者来说(比如:笔者),需要仔细的理解一下。我们知道.java文件在编译之后会生成一个.class文件。比如Student.java,该文件里存放着Student类的定义。编译之后会生成一个Student.class文件。在JVM中,可以简单的理解Student.class就是一个Class对象(不是指那个文件,而是JVM中的一个数据对象)。它管理着Student这个类的全部信息。

         我们直接看Class的源码。在 java.lang中。本文主要介绍几个常用的方法,以及它们使用的使用方法。

         首先是构造器:Class的构造器是私有化的,私有化构造器一般用于禁止用户直接进行创建对象。

    /**
     * 构造器私有化,参数为类加载器
     * 禁止外界直接进行创建对象
     * */
    private Class(ClassLoader loader) {
        classLoader = loader;
    }

那么Class对象该怎么创建呢?

       在解答这个问题之前,我们先看一个静态方法:Class.forName(String s);用Java连接过数据库的同学应该对此并不陌生。遗憾的是,该方法的底层实现使用native方法 forName0实现的。forName0的参数有四个:String name, boolean initialize,ClassLoader loader, Class<?> caller。其中ClassLoader是类加载器,用于加载一个类。

       其使用方法和功能如下:

    /*******************************************************
     *  得到类名为className的一个Class对象的引用 xxx.class
     *  如果找不到会抛出异常 ClassNotFoundException
     *
     *  调用该方法的主要目的是,className.class 如果没有被加载,
     *  则会被加载,从而执行className.class的静态子句等。
     *
     *  Class c = Class.forName("java.util.Random");
     *
     *  String driver = "oracle.jdbc.driver.OracleDriver"; //orale 驱动
     *  Class.forName(driver);                              //注册驱动,使用类加载
     ******************************************************/  
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

   private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
   throws ClassNotFoundException;

 

      现在我们来解答一下,如何对Class进行实例化,得到一个xxxx.class。

     1、通过静态方法.Class.forName(String s) 适用于知道类名(People)的情况:

      Class c= Class.forName("People");  // c就是 People.class
 

     2,我们还可以通过对象来获得Class对象,适用于知道实例对象(people)的情况。

     Class c=people.getClass();

     getClass(),方法是Object类中的方法,可以返回people的Class对象。

 

通过Class对象得到一个实例

    得到Class对象之后,我们就可以使用Class中的成员方法了。而不是只能使用静态方法。

     还有另一个常用的方法。叫newInstance();该方法,是用来得到一个Class对象(比如上面的People.class)的一个实例。

     People people = c.newInstance();

     通常情况下,我们会写成      Class.forName("People").newInstance();

 

Class对象的属性

     看到这里,我们应该对Class对象有了一定的了解。下面我们来看看Class对象的属性。

     Class对象是用于管理类信息的,包括类的方法,属性,类型,名称,加载器等等。(比如String.class 管理String类的全部信息)。比如c.getClassLoader() ,就可以得到People的类加载器。

     Class对象的存在,使得我们可以访问类信息。

 

反射:由对象得到类属性和方法信息。

      Class类和java.lang.reflect类库一起提供了反射的支持。reflect中包含了三个重要的类:Field,Method,Constructor。见名知义,分别管理字段,方法,构造器。

     Class的getMethods()以及getConstructor()方法,分别返回Method对象数组和Constructor对象数组。这两个类,都提供了深层方法,来解析对象所代表的方法、并获取其名字、参数、返回值等信息。

      这是我们下面动态代理能实现的基础。通过对象obj 得到Object.class,再得到Object的各个方法Method。

 

动态代理

     动态代理:产生一个对象obj的代理对象Proxy。代理对象存在的价值主要在于拦截对真实业务对象obj的访问。(代理对象:经纪人,业务对象:明星)。代理对象应该具有和业务对象相同的方法。最后真正执行的,依然是业务对象的方法。

     在这里需要介绍一下Proxy类的一个静态方法

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

     用于生成一个代理实例。

/******************************************
 *           代理类:
 * 用于生成一个代理对象,通常使用静态方法:Proxy.newProxyInstance( , , )。共有三个参数:
 * ClassLoader loader用来指明生成代理对象使用哪个类装载器,
 * Class<?>[] interfaces用来指明生成哪个对象(接口)的代理对象,通过接口指定,
 * InvocationHandler h用来指明产生的这个代理对象要做什么事情。
 * 所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。
 *****************************************/
public class Proxy implements java.io.Serializable {
     ...
     public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
        ...
     }
     ...
}

      这里又有一个新的类型,InvocationHandler h ,该类型是一个接口。里面只有一个方法invoke()。动态代理实现invoke()用于通知业务对象进行执行相应的操作。    

    下面,我们用一个例子来说明动态代理具体是怎么实现的。

1、先规定一个接口,代理想要做的操作

package proxy;
/**
 * @ClassName: Subscribe
 * @Description: 邀约
 * @author: 西瓜shine
 * @date: 2018-12-28 下午7:19:02
 *
 */
public interface Subscribe {
    void confirmTime(String s);
    void confirmPlace(String s);
}

  2、业务对象:老板

package proxy;
/**
 * @ClassName: Boss
 * @Description: 老板,业务对象类。
 * @author: 西瓜shine
 * @date: 2018-12-28 下午7:19:02
 */
public class Boss implements Subscribe{
    private String name;
    public Boss(String name) {
        this.name =name;
    }

    @Override
    public void confirmTime(String s) {
        System.out.println("Boss:Time is no problem:"+s);
    }

    @Override
    public void confirmPlace(String s) {
        System.out.println("Boss:Place is no problem:"+s);
    }
}

3、代理对象:秘书 

     这里需要实现一个接口InvocationHandler中的invoke()。参数和作用,在上文已经见过。

     该方法内可以对请求进行初步处理,通过判断客户端发来的请求方法(一个代理可以实现多个方法)method的名称(method.getName)来判断,可在调用业务对象的方法之前,进行简单的处理。之后调用业务对象的接口方法来实现

具体的操作:   method.invoke(boss,args); 该语句的意思是:调用boss的method方法,参数为args。

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @ClassName: Secretary
 * @Description: 这个代理类负责生成Boss对象的代理:秘书
 * @author: 西瓜shine
 * @date: 2018-12-28 下午7:19:02
 *
 */
public class Secretary implements InvocationHandler {
    private Boss boss ;              // 每个秘书只有一个老板
    private String name;
    public Secretary(String name,String boss) {
        this.name = name;
        this.boss = new Boss(boss);
    }

    public Secretary(String boss) {
        this.name = "Secretary of"+boss;
        this.boss = new Boss(boss);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("confirmTime"))
            System.out.println("Secretary: Just a minute,I will ask my boss ,Is the time ok?");
        if(method.getName().equals("confirmPlace"))
            System.out.println("Secretary: Just a minute,I will ask my boss, Is the place ok?");
        return method.invoke(boss,args);
    }

}

 4、测试。

package proxy;

import java.lang.reflect.Proxy;
/**
 * @ClassName:  Interview
 * @Description: 模拟访客先老板预约,只知道老板的姓名“Ma Yun”
 * @author: 西瓜shine
 * @date: 2018-12-28 下午7:19:02
 *
 */
public class Interview {
    public static void require(Subscribe proxy){
        proxy.confirmTime("tomorrow");
        proxy.confirmPlace("meeting room");
    }

    public static void main(String[] args){
        Secretary secretary = new Secretary("Ma Yun");   // 得到一个马云的秘书的联系方式
        Subscribe proxy = (Subscribe) Proxy.newProxyInstance(  // 得到预约的代理
                Subscribe.class.getClassLoader(),
                new Class[] {Subscribe.class},
                secretary
        );
        require(proxy);
    }
}

运行结果:

Secretary: Just a minute,I will ask my boss ,Is the time ok?
Boss:Time is no problem:tomorrow
Secretary: Just a minute,I will ask my boss, Is the place ok?
Boss:Place is no problem:meeting room

 

此时运行已经正确。

但是,获取代理的方法

Subscribe proxy = (Subscribe) Proxy.newProxyInstance(  // 得到预约的代理
                Subscribe.class.getClassLoader(),
                new Class[] {Subscribe.class},
                secretary
        );

放在客户端有些不太符合面向对象设计,而且每个客户都要重复该代码。所以对Subscribe类和客户端进行的修改。

public class Secretary implements InvocationHandler {
    private Boss boss ;              // 每个秘书只有一个老板。实际
    private String name;
    public Secretary(String name,String boss) {
        this.name = name;
        this.boss = new Boss(boss);
    }

    public Secretary(String boss) {
        this.name = "Secretary of"+boss;
        this.boss = new Boss(boss);
    }

    public Subscribe getProxy(){
        return (Subscribe) Proxy.newProxyInstance(  // 得到预约的代理
                Subscribe.class.getClassLoader(),
                new Class[] {Subscribe.class},
                this
        );
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("confirmTime"))
            System.out.println("Secretary: Just a minute,I will ask my boss ,Is the time ok?");
        if(method.getName().equals("confirmPlace"))
            System.out.println("Secretary: Just a minute,I will ask my boss, Is the place ok?");
        return method.invoke(boss,args);
    }
}
package proxy;

/**
 * @ClassName:  Interview
 * @Description: 模拟访客先老板预约,只知道老板的姓名“Ma Yun”
 * @author: 西瓜shine
 * @date: 2018-12-28 下午7:19:02
 *
 */
public class Interview {
    public static void require(Subscribe proxy){
        proxy.confirmTime("tomorrow");
        proxy.confirmPlace("meeting room");
    }

    public static void main(String[] args){
        Secretary secretary = new Secretary("Ma Yun");   // 得到一个马云的秘书的联系方式
        Subscribe proxy = secretary.getProxy();                // 从秘书那边得到一个邀约方法(可视为邀约信的格式)
        require(proxy);                                        // 填写邀约。
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值