Java的反射机制

一.Java Reflection

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

1.Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理(前边讲interface时有静态代理)

2.反射相关的主要API:

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

3.初识反射

通过反射在运行时构造一个类的对象, 调用方法和成员变量

One More Thing 👇
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
public class TestReflection {
public static void main(String[] args) throws Exception{
//class文件中运行时Person类即为Class类的实例(clazz)
Class<Person> clazz = Person.class;
//1.创建clazz对应的运行时类(Person类)的对象
Person p = clazz.newInstance();
System.out.println(p);
//2.通过反射调用运行时类指定的成员变量
Field f1 = clazz.getField("name");//调用public变量
f1.set(p, "LiuDehua");
Field f2 = clazz.getDeclaredField("age");//调用private变量
f2.setAccessible(true);
f2.set(p, 21);
System.out.println(p);
//3.反射调用运行时类指定的方法
Method m1 = clazz.getMethod("show");//无参方法
m1.invoke(p);
Method m2 = clazz.getMethod("display", String.class);//带参方法
m2.invoke(p, "CHINA");
}
}
class Person{
public String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void show() {
System.out.println("我是一个人");
}
public void display(String nation) {
System.out.println("我的国籍是:"+nation);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

二.反射的源头Class类

  • 源文件经过编译(javac.exe)以后, 得到一个或多个.class文件, .class文件经过运行(java.exe)这步, 就需要进行类的加载(通过JVM的类加载器), 加载到内存的缓存中. 每一个放入缓存中的.class文件就是一个Class的实例!
  • Class的一个对象, 对应着一个运行时类(每一个运行时类只加载一次). 相当于一个运行时类本身充当了Class的一个实例.
  • 每一个对象, 都能通过它的getClass()方法, 得到它对应的运行时类
  • 通过Class的实例得到一个运行时类中的完整结构(属性, 方法, 构造器, 内部类, 父类, 所在包, 异常, 注解, … )

1.创建Class类的对象

  1. 通过运行时类本身的.class属性获取
  2. 通过运行时类对象的getClass()方法获取
  3. 通过Class类的静态方法Class.forName(String path)方法
  4. (了解)通过类加载器(ClassLoader)的对象的loadClass(String path)方法

ClassLoader的应用:

One More Thing 👇
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args){
//法一:
ClassLoader loader = this.getClass().getClassLoader();
InputStream is = loader.getResourceAsStream("com\\crd\\reflection\\jdbc.properties");
//法二:
// FileInputStream is = new FileInputStream(new File("jdbc1.properties"));
Properties pros = new Properties();
pros.load(is);
String name = pros.getProperty("user");
System.out.println(name);
String password = pros.getProperty("password");
System.out.println(password);
}

有了Class的实例后, 可以做什么?

  1. 创建对应的运行时类的对象(重点)
  2. 获取对应运行时类完整的类的结构: 属性、方法、构造器、包、父类、接口、泛型、注解、异常、内部类…
  3. 调用对应的运行时类中指定的结构(某个指定的属性、方法、构造器)(重点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
Method[] m1 = clazz.getMethods();
//获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
Method[] m2 = clazz.getDeclaredMethods();
//获取其他结构调用类似方法即可
//获取父类的泛型(以后会用到)
@Test
public void test1(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type;
Type[] arr = param.getActualTypeArguments();
for(int i = 0;i < arr.length;i ++){
System.out.println((Class)arr[i]);//父类的泛型
}
}

注意: 调用getDeclaredXxx()方法后, 建议要调用一下, setAccessible(true);

2.创建运行时类的对象

  • 使用Class实例的newInstance()方法创建, 该方法默认调用运行时类的空参构造器(该构造器需要足够的权限)
  • 故, 创建类时建议创建一个空参的构造器
  • 也能用指定的构造器创建运行时类的对象
1
2
3
4
5
6
7
8
@Test
public void test() throws Exception{
Class clazz = Person.class;
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);//指定构造器
cons.setAccessible(true);
Person p = (Person)cons.newInstance("Tom",10);
System.out.println(p);
}

三.动态代理

  • 代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上
  • 之前提到过静态代理, 特征是代理类和被代理类都是在编译期间确定下来, 不利于程序的扩展. 同时, 每一个代理类只能为一个接口服务, 这样一来程序开发中必然产生过多的代理.
  • 最好可以通过一个代理类完成全部的代理功能

静态代理: 要求被代理类和代理类同时实现相应的一套接口; 通过代理类的对象调用重写接口的方法时, 实际上执行的是被代理类的同样的方法的调用.

One More Thing 👇
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
//创建接口
interface ClothFactory{
void productCloth();
}
//创建被代理类
class NikeClothFactory implements ClothFactory{
public void productCloth(){
System.out.println("Nike工厂生产衣服");
}
}
//创建代理类
class ProxyFactory implements ClothFactory{
ClothFactory cf;
public ProxyFactory(ClothFactory cf){
this.cf = cf;
}
public void productCloth(){
System.out.println("代理类开始代理");
cf.productCloth();
}
}
public class TestFactory{
public static void main(String [] args){
NikeClothFactory nike = new NikeClothFactory();//创建被代理类
ProxyFactory proxy = new ProxyFactory(nike);//创建代理类
proxy.productCloth();
}
}

动态代理: 在程序运行时, 根据被代理类及其实现的接口, 动态的创建一个代理类. 当调用代理类的实现的抽象方法时, 就发起对被代理类同样方法的调用.

One More Thing 👇
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
//动态代理的使用, 反射是动态语言的关键
interface Subject {
void action();
}
//被代理类
class RealSubject implements Subject {
public void action() {
System.out.println("我是被代理类, 记得要执行我哦!么么~~");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;//实现了接口的被代理类的对象的声明
//①给被代理的对象实例化②返回一个代理类的对象
public Object blind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
//当通过代理类的对象发起对被重写的方法的调用时, 都会转换为对如下的invoke方法的调用
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//method方法的返回值时returnVal
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
public class TestProxy {
public static void main(String[] args) {
//1.被代理类的对象
RealSubject real = new RealSubject();
//2.创建一个实现了InvacationHandler接口的类的对象
MyInvocationHandler handler = new MyInvocationHandler();
//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。
Object obj = handler.blind(real);
Subject sub = (Subject)obj;//此时sub就是代理类的对象
sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
//再举一例
NikeClothFactory nike = new NikeClothFactory();
ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);//proxyCloth即为代理类的对象
proxyCloth.productCloth();
}
}

1.动态代理与AOP

One More Thing 👇
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
interface Human {
void info();
void fly();
}
// 被代理类
class SuperMan implements Human {
public void info() {
System.out.println("我是超人!我怕谁!");
}
public void fly() {
System.out.println("I believe I can fly!");
}
}
class HumanUtil {
public void method1() {
System.out.println("=======方法一=======");
}
public void method2() {
System.out.println("=======方法二=======");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;// 被代理类对象的声明
public void setObject(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
HumanUtil h = new HumanUtil();
h.method1();
Object returnVal = method.invoke(obj, args);
h.method2();
return returnVal;
}
}
class MyProxy {
// 动态的创建一个代理类的对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.setObject(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), handler);
}
}
public class TestAOP {
public static void main(String[] args) {
SuperMan man = new SuperMan();//创建一个被代理类的对象
Object obj = MyProxy.getProxyInstance(man);//返回一个代理类的对象
Human hu = (Human)obj;
hu.info();//通过代理类的对象调用重写的抽象方法
System.out.println();
hu.fly();
//*********
NikeClothFactory nike = new NikeClothFactory();
Object obj1 = MyProxy.getProxyInstance(nike);
ClothFactory cloth = (ClothFactory)obj1;
cloth.productCloth();
}
}

评论