一、反射介绍
反射机制:Java反射机制在运行状态,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
(面试讲:通过反射,类对我们来说都是完成透明的,可以动态的获取类的属性和方法,可以动态的调用对象的属性和方法)
加载概述:当程序要使用某个类时,若类还没有加载到内存时,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。
二、3种方式反射获取字节码文件对象
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
通过反射获取各个时期的字节码文件对象
- Person.java源文件 (Class clazz = Class.forName("全限定类名"),获取字节码文件对象)
- Person.class字节码文件 ( Class clazz = Person.class ,获取字节码文件对象)
- Person p = new Person()实例对象 ( Person clazz = p.getClass(),获取字节码文件对象)
三、反射功能
下面讲解的都是对于Student对象的操作,通过Student字节码对象clazz来进行操作。
public class Student {
public Student() {}
public Student(String username, String sex) {
this.username = username;
this.sex = sex;
}
private String username;
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [username=" + username + ", sex=" + sex + "]";
}
}
1、通过反射对构造方法操作
/*反射对构造方法操作*/
Class clazz = Class.forName("com.xue.test.Student");
//无参构造方法
Student stu = (Student) clazz.newInstance(); //通过反射实例化Student对象
stu.setSex("女");
stu.setUsername("美丽");
System.out.println(stu);
//有参构造方法
Constructor myCon = clazz.getConstructor(String.class, String.class);//获取指定有参构造
Student stu2 = (Student)myCon.newInstance("张三","男");
System.out.println(stu2);
2、通过反射对属性操作
/*反射对属性进行操作*/
Field field = clazz.getDeclaredField("sex"); //暴力反射对私有变量sex
field.setAccessible(true); //去除私有权限
field.set(stu2, "女");
System.out.println(stu2);
3、通过反射对方法进行操作
/*反射对方法进行操作*/
//Method method = clazz.getMethod("sex"); //针对无参方法
Method method = clazz.getMethod("setSex", String.class); //针对有参方法
method.invoke(stu2, "男");
System.out.println(stu2);
我在这介绍了三种必须掌握并能使用的操作反射方法。还有更多的方法(获取类的接口,指定类的输出流等等)基本上比较少使用,我就不作多于的解释。
四、反射应用案例
1、ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现?(泛型擦除,泛型反射)
private static void demo1()
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
//泛型只在编译期有效,在运行期会被擦除掉
Class clazz = Class.forName("java.util.ArrayList");//获取字节码对象
Method m = clazz.getMethod("add", Object.class); //通过字节码对象获取add()方法
m.invoke(list, "aaa"); //通过反射对list添加字符串"aaa"
System.out.println(list);
}
2、此方法可以将obj对象中propertyName的属性的值设置为value,public void setProperty(Object obj, String propertyName, Object value){}
public static void setProperty(Object obj, String propertyName, Object value) throws Exception, SecurityException {
Class clazz = obj.getClass(); //获取传进来的字节码对象
Field f = clazz.getDeclaredField(propertyName); //通过字节码对象获取propertyName属性
f.setAccessible(true); //将其属性设置为可见(private-public)
f.set(obj, value); //将obj对象propertyName属性值设置为value
}
3、动态代理
代理:本来自己应该做的事情,请了别人来做,被请的人就是代理对象
java.lang.reglect包下提供了一个Proxy类和InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,只能对接口做代理
(1)、实现InvocatiionHandler接口类
public class MyInvocation implements InvocationHandler {
private Object obj;
public MyInvocation(Object obj) {
this.obj = obj;
}
public Object createProxy() {
Object ob = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
return ob;
}
//反射中调用方法得调用invoke,对这个方法重写,当调用别的方法时,写执行下列重写方法,在重写方法里面在选择合适时机调用别的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
System.out.println("准备");
result = method.invoke(obj, args);
System.out.println("完事");
return result ;
}
}
(2)、接口与实现类
//定义接口
interface Animal {
void eat();
void sleep();
}
//实现接口
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃饭");
}
@Override
public void sleep() {
System.out.println("狗睡觉");
}
}
(3)、使用动态代理
package com.xue.test;
/**
* 动态代理Dog类
* @author
*
*/
public class Test15 {
public static void main(String[] args) {
Animal dog = new Dog();
Animal an = (Animal)new MyInvocation(dog).createProxy();
an.eat();
an.sleep();
}
}
五、总结
这些都是反射机制的基础,虽然在SE中体现不了多大的用处,但到了EE学习各种框架时,如Spring动态代理就是其中一个运用到反射的机制,掌握反射能让你更轻松学习各类框架。而我先前对反射了解不多(先学了EE),现在算是反过来对反射进行了一个系统的学习。对于将要学习EE的朋友们,这个知识点也算是SE的尽头,希望你们重视这一个知识点的学习,不要懈怠。