反射与动态代理
1、反射
1.1什么是反射
这个问题我也不是很清楚,所以就从官网粘来一段,具体如下:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.2 反射能做什么
就如官方解释中说的那样:对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
说是这么说,我们来看看具体的实现吧:
反射获取对象或类的全类名
这里先上代码:
@Test
public void reflect1(){
JButton btn = new JButton();
String name = btn.getClass().getName();
System.out.println(name);
System.out.println(JButton.class.getName());
}
运行结果我就不展示了,之后的代码片段也请自行尝试,毕竟,看懂和会写是两码事。
注意看两次获取类名的区别:
- 第一次是创建对象后获取的类名:对象名.getClass().getName();
- 第二次是直接用类名获取全类名: 类名.class.getName();
如果你们自己敲了一遍代码的话,就会发现调用getName()方法的对象类型为Class类型。
一些人可能会说:这看着也没啥用呀。
对于这部分人,我的建议是:先别管它有没有用,简单记一下它能做什么就够了。
获取Class对象的三种方法
- 方法一:Class.forName(“全类名”)
- 方法二:类名.class
- 方法三:对象.getClass()
具体实现如下
@Test
public void reflect2() throws ClassNotFoundException {
//通过“全类名”获得Class
Class c1 = Class.forName("Test1");//连接数据库时
//Class
Class c2 = Test1.class;
//getClass
Class c3 = new Test1().getClass();
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c3.getName());
}
获取父类与接口
可以使用一个已知的类或对象获取该类的所有父类与接口信息
获取父类信息
Class对象.getSuperclass()
获取实现的接口
Class对象.getInterfaces();
千万别忘记,java语言只能实现单继承;但可以实现多个接口!!
测试代码如下:
@Test
public void reflect3() throws Exception{
Class c1 = JButton.class;
//获得父类
Class superclass = c1.getSuperclass();
String name = superclass.getName();
System.out.println(name);
//获得实现的接口
Class[] interfaces = c1.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(anInterface.getName());
}
}
获取类的所有构造方法以及方法中的参数类型
获取构造方法
Class对象.getConstructors();
获取方法中的参数类型
方法对象.getParameterTypes();
构造方法可以有多个,构造方法中的参数也可以有多个。
测试代码如下:
@Test
public void reflect4() throws Exception{
Class c1 = Book.class;
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
Class[] parameterTypes = constructor.getParameterTypes();
System.out.print(constructor.getName()+"(");
for (Class parameterType : parameterTypes) {
System.out.print(parameterType.getName()+",");
}
System.out.println(")");
}
}
以上代码是打印成我们常见的构造方法的形式,想直接看效果的话可以自行修改,最主要的方法就是获取构造方法和参数类型的方法。
根据获取的构造方法创建对象
我们都知道,构造方法是用来创建对象的,既然用反射获取了类的构造方法,那能不能用获取到的构造方法来创建这个类的对象呢?
答案是可以!
使用到的方法
构造方法对象.newInstance();
该方法默认创建一个Object类型的对象,但可以强转成许需要类型的对象。
代码如下:
@Test
public void reflect5() throws Exception{
Class c1 = Book.class;
Constructor[] constructors = c1.getConstructors();
Book book1 = (Book) c1.newInstance();
}
获取类的全部属性
获取所有非私有属性
Class对象.getFields()
获取指定非私有属性
Class对象.getField(String 属性名)
获取所有属性(包括私有)
Class对象.getDeclaredFields()
获取指定的属性(包括私有)
Class对象.getDeclaredField(String 属性名)
Field类这里可以理解为属性类,可以用它做如下操作:
获取属性的类型
Field对象.getType()
获取属性的名字
Field对象.getName()
获取属性的访问修饰符对应的数字
Field对象.getModifiers()
//说明:一个类的访问修饰符是以整型的数字存储的
将获取到的访问修饰符数字转换为对应访问修饰符的名字
Field.toString(int 访问修饰符对应的整型数字)
设置类的属性
设置属性值
可以使用上面的获取属性的方法获得Field类对象(属性对象),使用Field与该类的对象可以设置属性值。
Field对象.set(Object 本类的对象, Object 属性的值)
得到属性值
Field对象.get(Object 本类的对象)
如果要设置的属性为私有属性,需要先设置成允许访问
Field对象.setAccessible(true);
Field对象.set(Object 本类的对象, Object 属性的值);
测试代码如下:
@Test
public void reflect8() throws Exception{
Class c1 = Book.class;
Object o = c1.newInstance();
Field author = c1.getDeclaredField("author");
//如果设置的属性的访问修饰符为private,可以调用方法setAccessible(true),
//true 允许访问 ; false 不允许
author.setAccessible(true);
author.set(o,"吴承恩");
System.out.println(author.get(o));
}
获取类的方法
获取类的所有方法(不包括私有)
Class对象.getMethods()
获取指定方法(不包括私有)
Class对象.getMethod(String 方法名, … 方法参数类型)
获取类的所有方法(包括私有)
Class对象.getDeclaredMethods()
获取指定方法(包括私有)
Class对象.getDeclaredMethod(String 方法名, … 方法参数类型)
执行获取的方法
通过上述操作可以获取到Method对象(方法对象),使用该对象可以执行获取到的方法。
Method对象.invoke(Object 对象名, Object … 参数名)
2、动态代理
说实话,这个我也不是特别理解,所以直接上代码:
主要的实现
用代理工厂将我们创建的对象转换为代理对象,然后用代理对象调用方法;
代理对象与我们创建的对象本质上属于同一种类;
调用的invoke方法为我们重写后的invoke方法
创建一个用于测试的接口Student接口:
public interface Student {
void write();
void copy();
}
实现上面的接口
public class StudentImpl implements Student {
@Override
public void write() {
System.out.println("自己写作业");
}
@Override
public void copy() {
System.out.println("抄别人的作业");
}
}
实现InvocationHandler接口,重写Invoke()方法
public class HomeWorkHandler implements InvocationHandler {
private Object target;
public HomeWorkHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打开作业本");
Object object = method.invoke(target);
System.out.println("合上作业本");
return object;
}
}
创建代理工厂,用于生成代理对象
用到的方法:
public class ProxyFactory {
public static Object getProxy(Object target){
HomeWorkHandler homeWorkHandler = new HomeWorkHandler(target);
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
homeWorkHandler);
return object;
}
}
进行测试
@Test
public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Student target = new StudentImpl();
Object student = ProxyFactory.getProxy(target);
Method copy =
student.getClass().getMethod("copy");
copy.invoke(student);
}
代理对象调用类中的方法使用的是反射中的调用方法,区别在于代理对象走的是重写后的invoke方法,反射走的是未被重写的invoke方法。