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); // 填写邀约。
}
}