个人总结笔记,不具备指导作用,怕说错了说漏了带坏其他同学,有错误的地方希望热心的同学可以指出,感谢
进入正片
我们在使用反射的时候,最主要的目的自认为是可以去操作一些原本设计不想要被外部操作的一些方法、属性,从而达到某种效果。
所以在这里主要记录一下想要通过反射操作原本不能被操作的方式属性的流程。
一、Class的获取
首先是获取Class对象,分为三种方式:
1、通过Object.getClass()去获取
通过实例化一个对象,调用getClass即可获取到该对象的class。
public class Main {
public static void main(String[] args) {
Father father = new Father();
System.out.println(father.getClass());
}
}
//控制台
class Father
2、通过 .class 去获取
public class Main {
public static void main(String[] args) {
System.out.println(Father.class);
}
}
//控制台
class Father
通过这种方式可以避免没必要的实例的创建
3、通过 Class.forName() 去获取
public class Main {
public static void main(String[] args) {
try {
Class<?> father = Class.forName("com.Father");
System.out.println(father);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//控制台
class Father
通过上面三种方式获取到Class对象后,我们最主要的目的还是获取其中的属性、方法以及构造方法
二、属性、方法的获取
获取属性
获取属性的方法有两个
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
@CallerSensitive
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
我们可以看到在getField方法中出现了一个Member.PUBLIC,首先推测这个方法可以返回出public修饰符修饰的属性。
接下来我们创建son和father两个类并且让son继承father,并且两个方法都包含三种常用修饰符(public、private、protected)
//Father.class
package com;
public class Father {
public String name;
public String wifeName = "xxx";
private int income;
protected String blackHistory;
}
//Son.class
package com;
public class Son extends Father {
public String name;
protected String blackHistory;
private String girlfriendName = "Rachel Green";
}
我们分别用这两个方法去打印一下看看
public class Main {
public static void main(String[] args) {
Class<Son> sonClass = Son.class;
//通过getField获取son中的public对象name
try {
System.out.println("getField:"+sonClass.getField("name")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getFiledError:"+e.getMessage()+"\n");
}
//通过getField获取son中的protected对象fatherBlackHistory
try {
System.out.println("getField:"+sonClass.getField("fatherBlackHistory")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getFiledError:"+e.getMessage()+"\n");
}
//通过getField获取son中的private对象girlfriendName
try {
System.out.println("getField:"+sonClass.getField("girlfriendName")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getFiledError:"+e.getMessage()+"\n");
}
//通过getField获取father中的public对象wifeName
try {
System.out.println("getField:"+sonClass.getField("wifeName")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getFiledError:"+e.getMessage()+"\n");
}
//通过getDeclaredField获取son中的public对象name
try {
System.out.println("getDeclaredField:"+sonClass.getDeclaredField("name")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getDeclaredFieldError:"+e.getMessage()+"\n");
}
//通过getDeclaredField获取son中的protected对象fatherBlackHistory
try {
System.out.println("getDeclaredField:"+sonClass.getDeclaredField("fatherBlackHistory")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getDeclaredFieldError:"+e.getMessage()+"\n");
}
//通过getDeclaredField获取son中的private对象girlfriendName
try {
System.out.println("getDeclaredField:"+sonClass.getDeclaredField("girlfriendName")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getDeclaredFieldError:"+e.getMessage()+"\n");
}
//通过getDeclaredField获取father中的public对象wifeName
try {
System.out.println("getDeclaredField:"+sonClass.getDeclaredField("wifeName")+"\n");
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("getDeclaredFieldError:"+e.getMessage()+"\n");
}
}
}
//控制台
getField:public java.lang.String com.Son.name
getFiledError:fatherBlackHistory
getFiledError:girlfriendName
getField:public java.lang.String com.Father.wifeName
getDeclaredField:public java.lang.String com.Son.name
getDeclaredField:protected java.lang.String com.Son.fatherBlackHistory
getDeclaredField:private java.lang.String com.Son.girlfriendName
getDeclaredFieldError:wifeName
通过输出我们可以发现Son.class.getFiled方法可以获取到Son.class中的public属性(private、protected均不能获取),不仅如此,getFiled还可以获取到父类的public属性。与此同时,Son.class.getDeclaredField方法只能获取到Son.class中所有声明的属性(Declared)。
说完getFiled、getDeclaredField,Class中还能通过getFields()、getDeclaredFields()获取到属性数组,同样我们来打印一下。
public class Main {
public static void main(String[] args) {
Class<Son> sonClass = Son.class;
Field[] fields = sonClass.getFields();
for (Field f:fields){
System.out.println("getFields:"+f);
}
System.out.println();
Field[] declaredFields = sonClass.getDeclaredFields();
for (Field f:declaredFields){
System.out.println("getDeclaredFields:"+f);
}
}
}
//控制台
getFields:public java.lang.String com.Son.name
getFields:public java.lang.String com.Father.name
getFields:public java.lang.String com.Father.wifeName
getDeclaredFields:public java.lang.String com.Son.name
getDeclaredFields:protected java.lang.String com.Son.fatherBlackHistory
getDeclaredFields:private java.lang.String com.Son.girlfriendName
看到getFields()、getDeclaredFields()与getFiled()、getDeclaredField()的取值范围没有发生变化。
属性操控
获取到Field对象后如何获取其中对应的内容:
public class Main {
public static void main(String[] args) {
Son s = new Son();
s.name = "wzj";
Class<?> sonClass = s.getClass();
Field[] fields = sonClass.getFields();
for (Field f:fields){
try {
System.out.println(f.get(s));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println();
Field[] declaredFields = sonClass.getDeclaredFields();
for (Field f:declaredFields){
try {
System.out.println(f.get(s));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
//控制台
wzj
null
xxx
wzj
java.lang.IllegalAccessException: Class Main can not access a member of class com.Son with modifiers "protected"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.get(Field.java:390)
at Main.main(Main.java:23)
java.lang.IllegalAccessException: Class Main can not access a member of class com.Son with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.get(Field.java:390)
at Main.main(Main.java:23)
我们发现使用getFiled对public对象进行操作没有问题,但对private、protected对象进行操作会出现不合法的访问异常IllegalAccessException。因此如果我们想要访问private、protected对象需要开启权限。
public static void main(String[] args) {
Son s = new Son();
s.name = "wzj";
Class<?> sonClass = s.getClass();
try {
Field girlfriendName = sonClass.getDeclaredField("girlfriendName");
girlfriendName.setAccessible(true);//开启访问权限
System.out.println("女友名字:"+girlfriendName.get(s));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
//控制台
女友名字:Rachel Green
修改也可以直接调用girlfriendName.set(s,"Monica");
获取方法
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
@CallerSensitive
public Method[] getMethods() throws SecurityException
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException
getMethod、getMethods、getDeclaredMethod、getDeclareMethods这几个方法跟获取属性的方法没有什么差别,这边就不想细说了。
操控方法
我们首先在Son.class中新建两个私有方法
public class Son extends Father {
public String name;
protected String fatherBlackHistory;
private String girlfriendName = "Rachel Green";
private String exposeFatherBlackHistory(String fBH){
System.out.println("Son expose:"+fBH);
return fBH;
}
private static String exposeFatherBlackHistory(){
System.out.println("Son expose:凭空捏造");
return null;
}
}
之后在Main.class中通过反射进行调用
public static void main(String[] args) {
Son s = new Son();
Class<?> sonClass = s.getClass();
try {
Method method = sonClass.getDeclaredMethod("exposeFatherBlackHistory", String.class);
method.setAccessible(true);
method.invoke(s,"偷藏私房钱");
Method methodWithoutParam = sonClass.getDeclaredMethod("exposeFatherBlackHistory");
methodWithoutParam.setAccessible(true);
methodWithoutParam.invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
//控制台
Son expose:偷藏私房钱
Son expose:凭空捏造
我们通过invoke方法调用对应的method,第一个参数填入我们具体调用的实例化对象,如果是静态方法的话,第一个参数为null,虽然填入null或者实例化对象都可以成功,但不清楚静态方法的invoke中传入实例化对象会造成什么问题,以后在了解后会再补上,目前从效果上没有差别,猜测有可能造成内存泄漏的问题?
第二个参数就是传入不限定个数的类型,这个参数存在的原因自然不用说,是为了解决重载出现的同名不同参的方法。
获取构造方法
在反射中实例化对象有两种方式,一种是A.class.newInstance();可以看着newInstance方法是一个无参方法,因此它只能加载默认的无参构造函数。另一种是
Constructor<A> constructor = A.class.getConstructor(Class<?>... parameterTypes);
constructor.newInstance(Object ... initargs);
由此可以开出通过Constructor实例化更加灵活,我们通过代码实现一下
在Son类中创建构造方法
public class Son extends Father {
public String name;
protected String fatherBlackHistory;
private String girlfriendName = "Rachel Green";
public Son() {
name = "wzj";
}
public Son(String name){
this.name = name;
}
private String exposeFatherBlackHistory(String fBH){
System.out.println("Son expose:"+fBH);
return fBH;
}
private static String exposeFatherBlackHistory(){
System.out.println("Son expose:凭空捏造");
return null;
}
@Override
public String toString() {
return "Son{" +
"name='" + name + '\'' +
'}';
}
}
之后在Main.class中通过反射调用
public static void main(String[] args) {
try {
Son son = Son.class.newInstance();
System.out.println(son.toString());
Constructor<Son> constructor = Son.class.getConstructor(String.class);
Son s = constructor.newInstance("cjsq Wzj");
System.out.println(s.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
//控制台
Son{name='wzj'}
Son{name='cjsq Wzj'}
总结
简单写了一下Java反射中常用的获取操作属性、方法、构造函数的做法,反射还有可以完成的其他功能的方法没有讲到,主要是自己接触的比较少,目前仅限于使用基本的方法,还需要再详细了解了解再来补充,目前先写到这。