反射相关API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
Class类
在Object类中定义了public final Class getClass()方法,此方法返回值的类型是一个Class类,此类是Java反射的源头,通过对象反射求出类的名称。
获取Class对象的方式:
- 调用类的属性:.class
- 调用类的对象:
getClass() - 调用Class的静态方法:
forName(String classPath)
当获取到Class对象,会在内存中缓存一定的时间,在此时间用不同的方式获取都是同一个Class对象。
以下类型有Class对象:
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组(数组的元素类型、维度一样,就是同一个Class对象)
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
创建Class类的对象
newInstance():创建对应的Class类的对象,内部调用了Class类的空参构造器
调用Class类的指定方法
-
getMethod(String name,Class…parameterTypes):获得一个Method对象,并设置此方法操作时所需要的参数类型。 -
Object invoke(Object obj, Object...args):返回原方法返回值- obj:方法调用者,若原方法若为静态方法,可为null
- args:具体参数
setAccessible():参数值为true,则指示反射的对象在使用时取消Java语言访问检查
类的初始化过程与ClassLoader
没学 JVM 之前不用深究,做个简单了解即可
什么时候开始类的初始化:
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
- Load:将class文件字节码内容加载到内存中,生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器(ClassLoader)参与。
- Link:正式为类变量(static)分配内存并设置类变量默认初始值的阶段。
- Initialize:执行类构造器()方法的过程,构造类信息,给类变量赋值。跟对象构造器不是同一个东西。
ClassLoader:
系统类加载器(加载自定义类)常用方法:
ClassLoader classloader = ClassLoader.getSystemClassLoader():获取一个系统类加载器classloader = classloader.getParent():获取系统类加载器的父类加载器,即扩展类加载器classloader = classloader.getParent():获取扩展类加载器的父类加载器,即引导类加载器
代理
代理设计模式:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。简单来说就是对原始对象的加强。
静态代理
PA
特点:
- 静态代理要求原始对象和代理对象实现同一个业务接口。代理对象中的核心功能是由原始对象来完成,代理对象负责增强功能
- 代理对象在编译时就已经确定
EC
需求:有个明星(原始对象),档期很满,我们想约这个明星来学校表演。我们只能通过他的助理来约他,助理就是(代理对象)。具体的时间、地点、场合、费用(功能增强)都只能跟助理来谈。助理完全负责明星的所有行程,并且安排明星来表演。
实现代码如下:
- 定义业务接口
//描述:定义服务层业务接口--->谁实现接口谁就唱歌
public interface Service {
//表演唱歌的
void sing();
}
- 定义原始对象
public class SuperStarLiu implements Service {
@Override
public void sing() {
System.out.println("我是刘德华,我正在唱歌...");
}
}
- 定义代理对象
//明星唱歌表演的代理对象,完成歌手唱歌表演之外的其它业务
public class Agent implements Service {
//1.类中的成员变量设计为接口
public Service target; //目标对象
//2.方法的形参设计为接口
public Agent(Service target) { //传入目标对象
this.target = target;
}
@Override
public void sing() {
System.out.println("预订时间");
System.out.println("预订场地");
//3.调用时接口指向实现类
target.sing();//目标对象开始唱歌(谁来谁唱歌)
System.out.println("结算费用");
}
}
- main 方法调用
public static void main(String[] args) {
Service agent = new Agent();
agent.sing();
}
动态代理
动态代理是指客户通过代理类来调用目标对象的方法,并在程序运行时根据需要动态创建目标类的代理对象,这种情况下,代理类并不是在Java代码中定义的。
对比静态代理的优点:
- 不用定义代理类
- 不用关心目标对象,一个动态代理工厂就能生成任何目标对象的代理对象
相关 API
Proxy :专门完成代理的操作类,是所有动态代理类的父类,作用是为一个或多个接口动态地生成实现类
-
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创建一个动态代理类所对应的 Class 对象 -
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):创建一个动态代理对象- loader:类加载器
- interfaces:目标类实现的全部接口
- h:InvocationHandler 接口的实现类对象
InvocationHandler:动态代理机制中的一个接口,在实现类的 invoke() 中编写增强逻辑,当代理对象调用方法时,会自动调用方法 invoke()
Object invoke(Object proxy, Method method, Object[] args):
- proxy:代理对象
- method:目标对象中要调用的方法
- args:方法调用时传递的参数
代码实现
需要解决的两个重难点:
- 如何根据加载到内存中的目标类,动态的创建一个代理类及其对象
- 通过代理类的对象调用方法时,如何动态的去调用目标类中的同名方法
代码执行步骤如下:
- 调用动态代理工厂生成目标对象的代理对象
- 代理对象调用方法时,会走
invoke()方法,里面可以调用目标对象的方法,也能写增强逻辑 - 代理对象调用方法返回值是
invoke()的返回值
具体代码如下:
- 定义接口:
interface Human {
String getBelief();
void eat(String food);
}
- 定义目标类
class SuperMan implements Human {
@Override
public String getBelief() {
return "我相信我会飞";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
- 定义动态代理工厂
class ProxyFactory{
//调用此方法返回一个代理对象,解决重难点1
public static Object getProxyInstance(Object obj){ //obj:目标对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
handler);
}
}
- 定义 MyInvocationHandler
class MyInvocationHandler implements InvocationHandler{
private Object obj;
public void bind(Object obj){ //对目标对象进行赋值
this.obj = obj;
}
//代理类的对象调用方法时,会自动调用如下方法 invoke
//将目标类执行的方法功能声明在 invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标对象调用的方法,解决重难点2
Object returnValue = method.invoke(obj, args);
//目标对象调用方法的返回值作为当前方法的返回值
return returnValue;
}
}
- 编写 main 调用
public static void main(String[] args) {
//传入目标对象
SuperMan superMan = new SuperMan();
//proxyInstance:代理对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理对象调用方法时,会调用目标类中同名方法(因为第四步的注释"解决重难点2")
System.out.println(proxyInstance.getBelief());
proxyInstance.eat("西红柿");
}