Dawn's Blogs

分享技术 记录成长

0%

Java高级 (10) 反射

Java 反射机制

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

反射主要用到的 API:

  • java.lang.Class:代表一个类。
  • java.lang.reflect.Method:类的方法。
  • java.lang.reflect.Field:类的成员变量。
  • java.lang.reflect.Constructor:类的构造器。
  • 等等

Class 类

加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。这个对象就像一面镜子,透过这个镜子看到类的结构。

常用方法

Class 类常用方法如下:

image-20230206105907091

反射的举例:

通过反射,可以访问类的私有结构(构造器、属性、方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 实例化 Class 类对象
String str = "xxx.Person";
Class c = Class.forName(str);
// 获取构造器
Constructor cons = c.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 18);
// 获取 name 属性并修改
Field field = c.getField("name");
field.set(obj, "Dawn");
Obj name = field.get(obj);
System.out.println(name);
// 调用方法
Method show = c.getDeclearedMethod("show");
show.invoke(obj);
// 访问修改私有属性
Field age = c.getDeclearedField("age");
age.setAccessible(true);
age.set(obj, 20);

获取 Class 类实例方法

获取 Class 类实例方法:

  • 类的 class 属性:若已知具体的类,通过类的class属性获取。
  • 实例的 getClass 方法:已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象。
  • Class 类的 forName 方法:已知一个类的全类名,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException 异常。
  • 类加载器 ClassLoader:使用类加载器 ClassLoader 的 loadClass 方法。

创建运行时类的对象

创建运行时类的对象有两种方法:

  • Class 对象的 newInstance 方法:这个方法要求类必须有一个无参数的构造器(InstantiationException),并且类构造器的访问权限足够(IllegalAccessException)。
  • Class 对象的 getDeclaredConstructor(Class parameterTypes) 方法:用这个方法可以获取指定形参类型的构造器
1
2
3
4
5
6
7
// 根据全类名获取对应的Class对象
String name = "dawn.java.Person";
Class c = Class.forName(name);
// 调用指定参数结构的构造器,生成Constructor的实例
Constructor con = c.getConstructor(String.class, Integer.class);
// 通过Constructor实例创建类的对象
Person p = (Person) con.newInstance("Dawn", 24);

调用运行时类的指定结构

Class 类可以获取方法(Method)、构造器(Constructor)、属性(Field)、权限(Modifier)、接口(Interface)、注解(Annotation)等。

  • getXxx:返回 Class 对象中权限为 public 的 Xxx(包括父类中声明的)。

  • getDeclaredXxx:返回 Class 对象中声明的所有 Xxx(包括所有权限,但是不包括父类中的,只包括类本身声明的)。

setAccessible 方法

Method 和 Field、Constructor 对象都有 setAccessible() 方法。参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

  • 使得原本无法访问的私有成员也可以访问

代理设计模式

在之前介绍过代理设计模式,核心就是代理类和被代理类实现同一个接口。代理模式又分为:

  • 静态代理模式。
  • 动态代理模式。

静态代理模式

静态代理模式中,代理类是已经定义好的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.dawn.java;

interface ClothFactory {
void produceCloth();
}

// 代理类
class ProxyClothFactory implements ClothFactory {
private ClothFactory factory;

public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}

@Override
public void produceCloth() {
System.out.println("代理工厂准备工作");
factory.produceCloth();
System.out.println("代理工厂结束工作");
}
}

// 被代理类
class NikeClothFactory implements ClothFactory {

@Override
public void produceCloth() {
System.out.println("Nike 生产ing。。。");
}
}

public class StaticProxyTest {
public static void main(String[] args) {
// 被代理对象
NikeClothFactory nikeClothFactory = new NikeClothFactory();

// 代理对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);

proxyClothFactory.produceCloth();
}

}

动态代理

动态代理借助 Java API java.lang.reflect.Proxy 类完成,Proxy 是专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

实现方法

动态代理的实现步骤

  • 创建一个实现接口 InvocationHandler 的类,它必须实现 invoke 方法,以完成代理的具体操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyInvocationHandler implements InvocationHandler {
private Object obj; // 使用被代理类的对象进行赋值

public void bind(Object obj) {
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...

Object returnVal = method.invoke(obj, args);

// ...

return returnVal;
}
}
  • 定义声明被代理类
  • 利用 Proxy.newProxyInstance 方法,**newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)**,生成一个动态代理对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ProxyFactory {
// 返回一个代理类对象
public static Object getProxyInstance(Object obj) { // obj 被代理类对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.bind(obj); // 绑定被代理对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), myInvocationHandler);
}
}

public class DynamicProxyTest {
public static void main(String[] args) {
// 被代理对象
NikeClothFactory nikeClothFactory = new NikeClothFactory();
// 动态代理对象
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();

}
}

动态代理与 AOP

AOP(Aspect Orient Programming)面向切面编程,主要解决的是在执行目标方法之前、之后插入一些通用处理

在 InvocationHandler 实现类中,重写的 invoke 方法中,在执行被代理对象的目标方法前后,可以加上一些通用的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyInvocationHandler implements InvocationHandler {
private Object obj; // 使用被代理类的对象进行赋值

public void bind(Object obj) {
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通用的处理 ...

Object returnVal = method.invoke(obj, args);

// 通用的处理 ...

return returnVal;
}
}

image-20230206205403624