1.反射概念
对于任意一个类,能够动态获取这个类中的属性和方法;对于任意一个对象,都能够任意调用它的属性和方法。这种动态获取类的信息以及动态调用对象方法的功能称为Java的反射机制。
2.反射的实现方式
方法一:直接通过一个class的静态变量class获取
Class cls = String.class;
方法二:通过该实例变量提供的getClass()方法获取
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
Class cls = Class.forName("java.lang.String");
因为Class实例(正在运行的 Java 应用程序中的类和接口)在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例
Class cls1 = String.class;
String s = "Hello";
Class cls2 = s.getClass();
boolean sameClass = cls1 == cls2; // true
3.Class对象操作
3.1字段操作
public class Apple {
private String color;
private Float weight;
public Apple(String color, Float weight) {
super();
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Float getWeight() {
return weight;
}
public void setWeight(Float weight) {
this.weight = weight;
}
public String toString() {
return "[" + color + "," + weight + "]";
}
private String toString(String name) {
return name + " like the [" + color + "," + weight + "] apple";
}
}
Apple
public class ReflectField {
public static void main(String[] args) {
try {
Apple apple = new Apple("red",12f);
Class appleClass = Class.forName("com.ryj.java8.lambda.apple.Apple");
//获取所有public的field(包括父类)
Field[] fields = appleClass.getFields();
System.out.println("public field num :" + fields.length);
//根据字段名获取当前类的某个field(不包括父类)
Field colorField = appleClass.getDeclaredField("color");
colorField.setAccessible(true);
colorField.set(apple, "green");
System.out.println("apple color :" + apple.getColor());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* public field num :0
apple color :green
*/
Class类提供了以下几个方法来获取字段:
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
3.2方法操作
public class ReflectMethod {
public static void main(String[] args) {
try {
Apple apple = new Apple("red", 12f);
Class appleClass = Class.forName("com.ryj.java8.lambda.apple.Apple");
Method[] methods = appleClass.getDeclaredMethods();
for (Integer i = 0; i < methods.length; i++) {
System.out.println(methods[i].getName());
}
System.out.println("---------------");
Method method = appleClass.getMethod("toString");
System.out.println(method.invoke(apple));
method = appleClass.getDeclaredMethod("toString", String.class);
method.setAccessible(true);
System.out.println(method.invoke(apple, "Tom"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* toString
toString
setColor
getWeight
setWeight
getColor
---------------
[red,12.0]
Tom like the [red,12.0] apple
*/
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);
通过设置setAccessible(true)来访问非public方法;
通过反射调用方法时,仍然遵循多态原则。
4.动态代理
4.1代理模式
代理模式主要为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,好比你将一些繁琐的事情交给第三方去管理,那么第三方就是你的代理,其他人只会去找这个代理。AOP本身就是基于动态代理实现的.
代理模式中涉及到的角色:
- 抽象角色:为真实对象和代理对象提供一个共同的接口,一般是抽象类或者接口。
- 代理角色:代理角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对象。同时,代理对象可以在执行真实对象的操作时,附加其他操作,相当于对真实对象的功能进行拓展。
- 真实角色:最终引用的对象。
4.2静态代理
代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理
public interface HelloInterface {
void sayHello(String name);
}
public class Hello implements HelloInterface{
@Override
public void sayHello(String name) {
System.out.println("Hello " + name +"!");
}
}
public class HelloProxy implements HelloInterface {
private HelloInterface helloInterface = new Hello();
@Override
public void sayHello(String name) {
System.out.println("Before invoke sayHello");
helloInterface.sayHello(name);
System.out.println("After invoke sayHello");
}
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy();
helloProxy.sayHello("Tom");
}
}
/*
* Before invoke sayHello
Hello Tom!
After invoke sayHello
* */
静态代理的缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护
4.2动态代理
目标对象实现了接口,采用JDK的动态代理,如果目标对象没有实现接口,必须采用cglib动态代理.
public class HelloProxy2 {
public static void main(String[] args) {
HelloInterface hello = new Hello();
InvocationHandler handle = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke sayHello");
method.invoke(hello, args);
System.out.println("After invoke sayHello");
return null;
}
};
HelloInterface helloProxy = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handle);
helloProxy.sayHello("Tom");
}
}
/*
* Before invoke sayHello
Hello Tom!
After invoke sayHello
*/
在运行期动态创建一个interface实例的方法如下:
- 定义一个InvocationHandler实例,它负责实现接口的方法调用;
- 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
使用的ClassLoader,通常就是接口类的ClassLoader;
需要实现的接口数组,至少需要传入一个接口进去;
用来处理接口方法调用的InvocationHandler实例。
- 将返回的Object强制转型为接口。