1、概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.
2、获取Class对象
这里先构造一个测试类
package com.example.demo2;
public class People {
public int age;
public String name;
private double acount;
public People(){
}
public People(int age, String name, double acount) {
this.age = age;
this.name = name;
this.acount = acount;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private double getAcount() {
return acount;
}
private void setAcount(double acount) {
this.acount = acount;
}
}
- 通过Class类的forName方法,这是一个静态方法,可能抛出异常ClassNotFoundException的异常,同时需要注意的是,传入的参数是包名+类名。
Class clz = Class.forName("com.example.demo2.People");
- 直接通过类名得到
Class plz = People.class;
还可以通过某一个具体的对象来构建Class对象,这样就可以达到修改该对象的属性或者调用方法。
People xudong = new People();
xudong.setName("张三");
xudong.setAge(25);
Class peopleclz = xudong.getClass();
3、获取构造方法
pulic类型的构造函数直接通过
plz.getConstructor()
私有类构造函数通过
plz.getDeclaredConstructor()
也可以获取构造方法的数组
// 获取全部构造函数
Constructor[] constructors=plz.getConstructors();
System.out.println("全部构造函数");
for(Constructor con:constructors){
System.out.println(con.getName());
Parameter[] parameters=con.getParameters();
for(Parameter parameter:parameters){
System.out.println("\t"+parameter.getName()+" 类型: "+parameter.getType());
}
}
输出结果
4、获取全部成员和方法
// 获取全部成员
Field[] fields = plz.getDeclaredFields();
System.out.println("全部成员");
for(Field field:fields){
System.out.println("\t"+field.getName());
}
// 获取全部函数
Method[] methods = plz.getDeclaredMethods();
System.out.println("全部函数");
for (Method method:methods){
System.out.println("\t"+method.getName());
}
5、获取私有方法并调用
// 获取私有方法
Method setAccount = plz.getDeclaredMethod("setAcount", double.class);
// 非常重要,破解private
setAccount.setAccessible(true);
setAccount.invoke(people,10000.0);
私有成员,私有方法都可以通过getDeclared***方法获得,如果下一步需要对其进行调用,就调用setAccessiable方法,让其变得可访问便可以对其调用了。
6、注意事项
- 如果方法参数是double,int 之类的包装类型,那么也要通过double.class, int,class去获得该方法。
- 对于private类型的成员或者方法,通过setAccessible(true);改变其访问性。
- 通过反射调用方法时,注意参数的类型。
7、动态代理
通过动态代理,我们可以在不改动某个类的前提下,为其增加某些功能,比如增加日志记录的功能等,spring中就大量运用了动态代理,这里我们实现一个最简单的动态代理,需要用到InvocationHandler接口和Proxy类。
interface Subject{
public void dosomething();
public String sayHello();
}
// 需要被代理的类
class SubjectImpl implements Subject{
public SubjectImpl() {
}
@Override
public void dosomething() {
System.out.println("I am doing something");
}
@Override
public String sayHello() {
return "Hello world!";
}
}
// 代理类
public class SubjectProxy implements InvocationHandler {
private Subject subject;
public SubjectProxy(Subject sub){
subject=sub;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前,做一些比如日志记录等功能");
// 实现方法的调用
Object obj = method.invoke(subject,args);
System.out.println("代理后");
return obj;
}
public static void main(String[] args){
Subject sub=new SubjectImpl();
SubjectProxy subjectProxy=new SubjectProxy(sub);
Subject subProxy = (Subject) Proxy.newProxyInstance(
sub.getClass().getClassLoader(),
sub.getClass().getInterfaces(),
subjectProxy
);
subProxy.dosomething();
System.out.println(subProxy.sayHello());
}
}