反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取状态信息以及调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
基本的反射技术
Class类
在反射中只要我们获取了一个类的Class对象,我们就可以获取该类的所有信息,在Java中获取Class对象的方法有三种:
1.对象名.getClass(); 通过实例化对象的getClass()方法进行获取。
String str = new String();
Class cls = str.getClass();
2.类名.class; 用过类名的class方法进行获取
Class cls = String.class;
3.Class.forName();通过Class的forName()方法进行获取,参数为类的完全限定名
Class cls = Class.forName("java.lang.String");
调用构造方法
为了能够任意调用类的构造方法,Java的反射API提供了Construcor对象,该对象包含了一个构造方法的所有信息,但在调用构造方法之前,我们首先得获取该类的构造方法,部分实现代码如下:
public class Main {
public static void main(String[] args) throws Exception {
//获取String类的Class对象
Class cls = Class.forName("java.lang.String");
// 获取构造方法String(String):
Constructor cons = cls.getConstructor(String.class);
// 调用构造方法:
String str = (String) cons.newInstance("hello world");
System.out.println(str);
}
}
获取继承关系
当我们获取到某个Class对象时,那么我们就可以通过下列方法获取该类的继承关系,部分实现代码如下:
Class cls = Class.forName("java.lang.String");//获取String类的Class对象
Class superClass = cls.getSuperclass();//获取该类父类类型
Class[] interfaces = cls.getInterfaces();//获取该类的所有接口
访问字段
对于一个类中的字段,我们不仅可以通过Class对象获取该字段值,还可以对字段值进行修改,部分实现代码如下:
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
//通过Person类中的get方法获取字段值
Person person = new Person("Xiao Ming");
System.out.println(person.getName()); // "Xiao Ming"
//通过反射获取字段值
Class cls = p.getClass();
Field filed = cls.getDeclaredField("name");
//由于name字段被private修饰,因此需要通过setAccessible(true)将name字段设置为允许访问
filed.setAccessible(true);
filed.set(person, "Xiao Hong");
System.out.println(person.getName()); // "Xiao Hong"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
调用方法
当我们获取到某个Class对象时,那么我们就可以通过下列方法实现方法的调用,部分实现代码如下:
public class Main {
public static void main(String[] args) throws Exception {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
}
}
反射的功能
在运行期构造一个类的对象;
获取方法、属性等类中的基本信息;
调用类对象的方法;生成动态代理。
动态代理
动态代理是代理模式
的一种实现方式,代理模式除了动态代理还有 静态代理
,只不过静态代理能够在编译时期确定类的执行对象,而动态代理只有在运行时才能够确定执行对象是谁。代理可以看作是对最终调用目标的一个封装,我们能够通过操作代理对象来调用目标类,这样就可以实现调用者和目标对象的解耦合。
代理模式所涉及的角色有三个:
(1)、抽象主题角色(Subject):声明了真实主题和代理主题的公共接口,这样一来在使用真实主题的任何地方都可以使用代理主题。
(2)、代理主题角色(Proxy):代理主题角色内部含有对真实主题的引用,从而可以操作真实主题对象;代理主题角色负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递到真实主题之前或之后,都要执行一些其他的操作,而不是单纯地将调用传递给真实主题对象。
(3)、真实主题角色(RealSubject):定义了代理角色所代表的真实对象。
附:在WCF或者WebService的开发过程中,我们在客户端添加服务引用的时候,在客户程序中会添加一些额外的类,在客户端生成的类扮演着代理主题角色,我们客户端也是直接调用这些代理角色来访问远程服务提供的操作。这个是远程代理的一个典型例子。
动态代理在mybatis中的应用
在项目中配置Mybatis时,我们仅创建了映射器接口映射文件,并未创建映射器接口的实现类,但当我们使用Sqlsesion的getMapper()方法时,Mybatis却自动帮我们实现了映射器接口的实现类,从而可以使用映射器接口中的方法。