文章目录
一、反射概述
1.1 反射是什么
反射式 Java 被视为动态语言的关键,反射机制允许程序在执行期借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型 的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的内部结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为反射。
1.2 反射的作用
1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时判断任意一个类所具有的成员变量和方法
4、在运行时调用任意一个对象的成员变量和方法
5、在运行时生成注解
二、对 Class 类的理解
2.1 初步认识
Class 类是用来描述类的类,就是说 Class 类的对象就是其他类(而不是其他类的对象),如:
Person person = new Person(); //这是创建了一个 Person 对象,person 的类型是 Person
Class clazz = Class.forName("Person"); //这是创建了一个 Class 对象,Person.class 是把 Person 类加载到内存中,并没有创建 Person 的实例。
Person person2 = clazz.newInstance(); //使用 newInstance() 方法才创建了 Person 的实例。
2.2 深入理解
1、Class 本身也是一个类(可以有 Class clazz = Class.class)的操作
2、Class 对象只能由系统建立
3、一个加载的类在 JVMA中只会有一个 Class 实例
- 例:使用 javac.exe HelloWorld.java 生成一个 HelloWorld.c 文件,然后使用 java.exe HelloWorld 把 HelloWorld.c 文件加载到内存中,在 JVM 中就只有这一个 HelloWorld.c 。
4、每个类的实例都会记得自己是由哪个 Class 实例生成。
5、通过 Class 可以完整地得到一个类中所有被加载的结构。
6、Class 类是反射的根源,针对任何想动态加载、运行的类,唯有先获得相应的 Class 对象。
三、反射与封装性的个人理解
3.1 反射和封装性是站在不同的层面的
首先,分装性是对类的对象来说的,我们在类的外部创建了一个类的对象,那么通过这个对象不能访问 private的方法和属性,如下面代码,在类的外部创建了 Person 的对象 zhanSan,那么通过 zhangSan 是不可以访问到 private 属性的;
class Person {
private String name;
private int age;
public String getName() {
return this.name;
}
public int age getAge() {
return this.age;
}
}
class Test {
public static void main(String[] args) {
Person zhanSan = new Person();
}
}
而反射创建的是 Class 的对象,而不是 Person 的对象,Class 是用来描述类的,它获得 Person 的方法与成员变量的方式是通过 Class 中的方法获得的,而不是通过 Person 的对象获得的。
public class ReflectTest {
@Test
public void test1() throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
Class clazz = Class.forName("reflectTest.Person");
Person p2 = (Person)clazz.newInstance();
Field name = clazz.getDeclaredField("name"); //通过 Class 对象的方法来获得 Person 的私有属性
name.setAccessible(true);
name.set(p2, "liSi");
}
}
3.2 更深层次的理解封装性
1、封装的意思不是禁止,而是不建议,如果一个类中声明了 private 的属性或方法,那么说明这个类会有更好用的 public 方法来提供对应的功能,我们只需要用 public 方法就可以了,private 声明的方法只在类内部有用,它是用来实现 public 方法的基础,这种方法在类的外部使用是没有意义的,就好比说:
一辆汽车,对外提供的 public 方法是踩油门,踩油门可以实现汽车加速,为了实现这个public 方法汽车内部可能有 private 方法用来气缸加温,点火等,我们作为使用者只需要踩油门就可以了,知道怎么给气缸加温毫无意义。
2、反射是可以访问私有成员和方法,但是我们并不建议这么做。
四、反射的应用——动态代理
4.1 代理设计模式的原理
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
听起来比较抽象,举个例子来理解吧,比如说张三有一套房子要卖,那么他可以直接把自己的电话和房子信息放到网上,买家直接和张三进行对接,这种方式就是没有代理的;但是现在张三很忙,所以需要顾一个中介来帮张三,中介知道这个房子的信息以及张三这个房子想要卖多少钱,中介会负责与买家进行沟通,这就是使用了代理模式。
4.2 静态代理
静态代理就是指定了被代理的类形(即接口)。
interface SellHouse {
void sell();
}
class Customer implements SellHouse {
private String name;
public Customer(String name) {
this.name = name;
}
@Override
public void sell() {
System.out.println("我是客户,我要卖房子");
}
}
class HouseProxy implements SellHouse {
private SellHousePeople customer;
public HouseProxy(SellHousePeople customer) {
this.customer = customer;
}
@Override
public void sell() {
System.out.println("我是房产中介,我帮人卖房子......");
System.out.println("中介帮客户办手续,带领买家看房子......");
customer.sell();
System.out.println("中介卖完了房子。");
}
}
public class StaticProxy {
public static void main(String[] args) {
SellHouse houseProxy = new HouseProxy(new Customer("张三"));
houseProxy.sell();
}
}
4.3 动态代理
4.1 动态代理的引入
静态代理的逻辑清晰,代码易读,但它存在两个问题:
1、编译期间就必须指定被代理的类,而我们在一个程序中可能有多个类需要被代理,那么就需要写多个代理类,这会使代码量过大,所以我们为了关注业务本身,在程序运行中创建代理对象则方便了很多。
2、程序在可能在运行期间才确定要代理的类,那么这种情况下就必须使用动态代理了
4.2 动态代理的实现
想要实现动态代理,首先要解决两个问题:
1、如何根据被代理类动态地创建代理类
2、如何通过调用代理类的方法来调用到被代理类的方法
解决问题一:
class ProxyFactory {
//该方法返回一个代理类对象
public static Object getProxyInstance(Object obj) {
//参数分别是被代理类,被代理类实习的接口,第三个参数 null 稍后再讨论
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), null); //通过调用该方法,就能根据被代理类返回一个代理类的对象
}
}
解决问题二:
//在上述的代码上添加一些东西
class ProxyFactory {
//该方法返回一个代理类对象
public static Object getProxyInstance(Object obj) {
//这两行代码实现通过调用代理类的方法来调用到被代理类的方法
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
//这里把上面的 null 换成了 handler
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
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 {
//returnValue 就是方法的返回值,注意:void 也是一种返回类型
Object returnValue = method.invoke(obj, args);
return returnValue;
}
}
现在问题已经全部解决了,我们来演示一个实例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//定义接口:行驶
interface Driving {
void drive();
}
//汽车实现行驶接口
class Car implements Driving {
@Override
public void drive() {
System.out.println("燃烧汽油行驶");
}
}
//定义接口:飞翔
interface Flying {
String fly();
}
//鸟实现飞翔接口
class Bird implements Flying {
@Override
public String fly() {
System.out.println("小鸟飞翔");
return "小鸟飞翔";
}
}
//代理类
class ProxyFactory {
//该方法返回一个代理类对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
//负责调用被代理类的方法
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 {
//代理负责做第一件事
System.out.println("-----------------通用方法一-----------------");
//调用函数的返回值类型
Object returnValue = method.invoke(obj, args);
//代理负责做第二件事
System.out.println("-----------------通用方法二-----------------");
return returnValue;
}
}
public class DynamicProxy {
public static void main(String[] args) {
Car car = new Car();
Driving carProxy = (Driving) ProxyFactory.getProxyInstance(car);
carProxy.drive();
System.out.println("\n-----------------------------------------------------\n");
Bird bird = new Bird();
Flying birdProxy = (Flying) ProxyFactory.getProxyInstance(bird);
birdProxy.fly();
}
}