1.什么是反射
反射首先是能够获取到Java中要反射类的字节码 ,然后将字节码中的方法,变量,构造函数等映射成相应的 Method、 Filed、 Constructor 等类,而已让我们间接的调用。
2、常用API
先创建一个类用于测试
public class Order {
public String oid = "00001";//订单的编号
double total;//该订单的总金额
private int state = 1;//表示支付状态1表示字符 0 表示没有支付
public Order(String oid, double total, int state) {
this.oid = oid;
this.total = total;
this.state = state;
}
public Order() {
}
private Order(double total) {
this.total = total;
}
public String getOid() {
return oid;
}
private void setOid(String oid) {
this.oid = oid;
}
public double getTotal() {
return total;
}
private void setTotal(double total) {
this.total = total;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
需要获取相应类的Class对象,也就是得到他们的字节码文件对象
获取方式有3种
1、通过类 Order.class
2、通过对象 实例化一个对象之后 用引用名.class
3、通过Class.forName()方法
(1)通过字节码对象获取成员变量
getDeclaredField获得全部的成员变量
getField获取public成员变量
Field[] fields = aClass.getFields();
System.out.println("========获取public成员变量=========");
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=========获取所有成员变量==========");
Field[] field2 = aClass.getDeclaredFields();
for (Field field : field2) {
System.out.println(field);
}
打印:
========获取public成员变量=========
public java.lang.String proxy.Order.oid
=========获取所有成员变量==========
public java.lang.String proxy.Order.oid
double proxy.Order.total
private int proxy.Order.state
(2)通过字节码对象 对成员变量进行赋值
System.out.println("=============获取某个公共成员变量的值=================");
Field oid = aClass.getField("oid");
//如果是public的成员变量 能直接获取值
String id = (String) oid.get(o);
System.out.println("oid=====>"+id);
System.out.println("=============获取私有成员变量的值=================");
Field state = aClass.getDeclaredField("state");
//需要开启访问 不然报错IllegalAccessException
state.setAccessible(true);
int stat = (int) state.get(o);
System.out.println("state=======>"+stat);
(3)通过字节码对象获取方法的信息
System.out.println("=========获取所有public成员方法========");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("========获取所有成员方法===========");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
输出:
=========获取所有public成员方法========
public int proxy.Order.getState()
public double proxy.Order.getTotal()
public java.lang.String proxy.Order.getOid()
public void proxy.Order.setOid(java.lang.String)
public void proxy.Order.setTotal(double)
public void proxy.Order.setState(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
========获取所有成员方法===========
public int proxy.Order.getState()
public double proxy.Order.getTotal()
public java.lang.String proxy.Order.getOid()
public void proxy.Order.setOid(java.lang.String)
public void proxy.Order.setTotal(double)
public void proxy.Order.setState(int)
我可以居然也会获取父类Object当中的方法,所以需要测试一下能否获得父类的非共有方法,非共有变量,创建一个父类
public class OrderFather {
private int fatherint ;
private String fatherstring;
int fatherdefault;
public int fatherpublic;
private int getFatherint() {
return fatherint;
}
public String getFatherstring() {
return fatherstring;
}
protected int getFatherdefault() {
return fatherdefault;
}
public int getFatherpublic() {
return fatherpublic;
}
}
让Order继承FatherOrder,再次执行下面代码
Field[] fields = aClass.getFields();
System.out.println("========获取public成员变量=========");
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=========获取所有成员变量==========");
Field[] field2 = aClass.getDeclaredFields();
for (Field field : field2) {
System.out.println(field);
}
System.out.println("=========获取所有public成员方法========");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("========获取所有成员方法===========");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
输出:
========获取public成员变量=========
public java.lang.String proxy.Order.oid
public int proxy.OrderFather.fatherpublic
=========获取所有成员变量==========
public java.lang.String proxy.Order.oid
double proxy.Order.total
private int proxy.Order.state
=========获取所有public成员方法========
public int proxy.Order.getState()
public double proxy.Order.getTotal()
public void proxy.Order.setState(int)
public java.lang.String proxy.Order.getOid()
public java.lang.String proxy.OrderFather.getFatherstring()
public int proxy.OrderFather.getFatherpublic()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
========获取所有成员方法===========
public int proxy.Order.getState()
public double proxy.Order.getTotal()
private void proxy.Order.setOid(java.lang.String)
public void proxy.Order.setState(int)
public java.lang.String proxy.Order.getOid()
private void proxy.Order.setTotal(double)
我们可以发现 ,通过子类,只能获取到父类的public的方法和属性。通过getDeclaredXXX
也无法获取,那么怎么通过子类的字节码文件获取父类的非共有成员变量或者方法呢。我们只需要调用一个api就可以
System.out.println("=======================获取父类的信息====================");
System.out.println("===============父类所有方法===============");
Method[] declaredMethods1 = aClass.getSuperclass().getDeclaredMethods();
for (Method method : declaredMethods1) {
System.out.println(method);
}
System.out.println("===============父类所有成员变量===============");
Field[] fields1 = aClass.getSuperclass().getDeclaredFields();
for (Field field : fields1) {
System.out.println(field);
}
输出:
System.out.println("=======================获取父类的信息====================");
System.out.println("===============父类所有方法===============");
Method[] declaredMethods1 = aClass.getSuperclass().getDeclaredMethods();
for (Method method : declaredMethods1) {
System.out.println(method);
}
System.out.println("===============父类所有成员变量===============");
Field[] fields1 = aClass.getSuperclass().getDeclaredFields();
for (Field field : fields1) {
System.out.println(field);
}
通过getSuperclass
方法便获取到了父类的字节码对象,在通过调用getDeclaredXXX
方法,就可以获取到私有的成员变量和属性。
(4)通过反射调用方法
1、调用无参方法,首先在Order类中自己添加一个测试方法
public void happy(){
System.out.println("我很开心");
}
通过名字获取到对应的方法 aClass.getMethod(“xxxx”)
method.invoke(实例对象,参数);
Method happy = aClass.getMethod("happy");
happy.invoke(aClass.newInstance());
输出:我很开心
2、带有参数的方法
那么如果调用的方法是带有参数的呢。如下:
public void happy(int a){
System.out.println("我很开心第"+a+"次");
}
通过下面操作就可以:在获取method的时候,传入参数的class。然后再调用invoke的时候,再把我们实参设置进去
Method happy = aClass.getMethod("happy",int.class);
happy.invoke(aClass.newInstance(),100);
输出:我很开心第100次
3、私有方法
那么尝试一下是不是能安装上面的讨论调用私有方法呢,新增加一个私有方法在Order类中
private void cry(){
System.out.println("我要哭了");
}
然后用getDeclaredMethod方法 获取cry方法。再执行invoke,以为会像我们预期的那样输出
Method cry = aClass.getDeclaredMethod("cry");
cry.invoke(aClass.newInstance());
结果:
Exception in thread "main" java.lang.IllegalAccessException: Class proxy.ReflectionTest can not access a member of class proxy.Order with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:101)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:295)
报错啦,分析得知,不能被Access
,所以修改代码如下:
Method cry = aClass.getDeclaredMethod("cry");
cry.setAccessible(true);
cry.invoke(aClass.newInstance());
这样之后,就正常输出啦。
总结
反射有什么作用呢,其实反射就是用底部原理,对对象进行操作,获取对应的属性啊 ,调用方法。这在动态代理,和各种框架的实现,都到了重要的作用。
我们是使用原生反射的时候需要注意:
1、子类不能直接获取到父类的非共有方法 和属性。但是可以间接获取,通过调用getSuperclass()
方法
2、如果对私有的方法或属性进行操作的时候 需要 cry.setAccessible(true);