使用反射操作类的私有属性(Field)和私有方法(Method)
2017-02-24 13:29
459 查看
刚刚了解了反射,看到了使用反射操作类的私有属性,在操作私有变量时,需要使用Field类,我们来看一个例子:
下面说一下,使用反射调用类的私有方法(Method).
我们先来写一个类,类里面有两个公开方法和一个私有方法.
代码很简单,写好之后,我们开始使用反射:
第一步.我们获取类的class对象:
也可以这样获取:
或者这样获取:
java提供了三种方法获取类的class对象,上面三种就是,我们通过类的class对象来操作程序.
获取到类的class对象之后,我们通过class对象来获取类的实例:
我们需要明白在JAVA中任何class都要装载在虚拟机上才能运行,而forName就是装载类用的,这和new不一样,要分清楚哦。
第三步,我们通过cls获取类的共有方法:
还记得第一步我们写的那个类吗?去看一下吧,我们获取到print方法,print方法有一个参数,这个参数是String类型的,getMethod方法,要求我们传入方法的名称(String类型),和方法需要传入的参数的class对象,我们来看一下源码:
第二参数要我们传入参数,允许传入多个参数,但是他抛出了异常,因为我们可能是非法操作类的方法,然后,该方法调用另一个getMethod方法,返回匹配指定的方法对象:
代码做了一些判断,包括方法名是否为空,参数是否为空,最后因该是判断此方法是私有还是公有方法,这里recursivePublicMethods 默认是为true的,也就是说,我们调用getMethod方法时,就已经确定了调用的是公有方法.
我们使用Method对象来接受返回的数据,因为最终返回的是method类型,也就是方法,然后调用invoke来执行方法,看一下源码中invoke方法的解释:
我们把多余注释去掉,再看一下:
*看看每个参数的意思:
第一个代表了(调用方法的对象),我认为谁的方法谁调用
第二个参数代表:需要的参数列表,逗号分隔.
所以我们再使用method调用类的方法时,需要传入类的实例和参数列表!*
公有方法说完了,说一下私有方法.
前面说到:在调用公共方法时:
使用类的class对象调用getMethod方法,这个方法默认是公共方法,但是访问私有方法时不是这样的,我们需要调用另一个方法:
虽然方法名称不同,但是他们的代码中却调用了同一个方法,就是getMethod方法,千万不要误会,这个getMethod方法是一个重载方法。与我们直接调用的是同名不同参的方法.
在调用公有方法时,在上面getMethod方法的第三个参数,源码中默认填写了true,但是此时却是false,因为我们此时调用的是一个私有方法。
同时我们需要调用:
来设置可访问性。我们来看一下此方法解释:
如果我们设置true,表示我们访问私有方法时尽量不受到权限检查,如果设置为false,代表我们要接受java权限检查,所以我们填写true,不接受权限检查。
在调用getDeclaredMethod方法时,和调用getMethod方法一样,我们需要传入:方法名,每个参数对应的class对象。
最后执行invoke方法,传入类的实例,和真正的参数!
// field访问私有变量 private void useFeildOfReflect() throws Exception { //获取类的class对象 Class<?> cls = Class.forName("com.xiao.reflectclass.User"); //获取User类的实例 User user = (User) cls.newInstance(); //使用Field中的方法获取User的私有属性 Field fieldName = cls.getDeclaredField("userName");//Field访问私有变量 fieldName.setAccessible(true);//允许访问私有变量 user.setUserName("yxs");//设置私有变量的值 Field fieldAge = cls.getDeclaredField("age");//Feild访问私有变量 fieldAge.setAccessible(true); user.setAge(25); Field fieldSex = cls.getDeclaredField("sex");//Field访问私有变量 fieldSex.setAccessible(true); user.setSex("男"); LogSystem.print(user.toString() + "\n私有属性操作"); } User类如下: public class User implements Serializable{ public User() { super(); } public User(String userName, int age, String sex) { this.userName = userName; this.age = age; this.sex = sex; } private String userName; private int age; private String sex; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "年龄"+age+"\n"+"姓名"+userName+"\n"+"性别"+sex; } }
下面说一下,使用反射调用类的私有方法(Method).
我们先来写一个类,类里面有两个公开方法和一个私有方法.
public class LogSystem { public static String Tag = "yxs"; public static void print(String msg){ Log.i(Tag,msg); } public void cal(int f,int s){ print((f+s)+"结果"); } private void cal(int f,int s,int t){ print((f*s*t)+"私有结果"); } }
代码很简单,写好之后,我们开始使用反射:
第一步.我们获取类的class对象:
Class<?> cls = Class.forName("com.xiao.reflectclass.LogSystem");
也可以这样获取:
Class<?> cls = new LogSystem().getClass();
或者这样获取:
Class<?> cls3 = LogSystem.class;
java提供了三种方法获取类的class对象,上面三种就是,我们通过类的class对象来操作程序.
获取到类的class对象之后,我们通过class对象来获取类的实例:
LogSystem ls = (LogSystem) cls.newInstance();//获得类的实例
我们需要明白在JAVA中任何class都要装载在虚拟机上才能运行,而forName就是装载类用的,这和new不一样,要分清楚哦。
A a = (A)Class.forName(“package.A”).newInstance(); 和 A a = new A;两行代码是等价的。
第三步,我们通过cls获取类的共有方法:
//公有方法1 Method method = cls.getMethod("print", String.class); method.invoke(ls, "hahahahah");
还记得第一步我们写的那个类吗?去看一下吧,我们获取到print方法,print方法有一个参数,这个参数是String类型的,getMethod方法,要求我们传入方法的名称(String类型),和方法需要传入的参数的class对象,我们来看一下源码:
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { return getMethod(name, parameterTypes, true); }
第二参数要我们传入参数,允许传入多个参数,但是他抛出了异常,因为我们可能是非法操作类的方法,然后,该方法调用另一个getMethod方法,返回匹配指定的方法对象:
private Method getMethod(String name, Class<?>[] parameterTypes, boolean recursivePublicMethods) throws NoSuchMethodException { if (name == null) { throw new NullPointerException("name == null"); } if (parameterTypes == null) { parameterTypes = EmptyArray.CLASS; } for (Class<?> c : parameterTypes) { if (c == null) { throw new NoSuchMethodException("parameter type is null"); } } Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes) : getDeclaredMethodInternal(name, parameterTypes); // Fail if we didn't find the method or it was expected to be public. if (result == null || (recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) { throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes)); } return result; }
代码做了一些判断,包括方法名是否为空,参数是否为空,最后因该是判断此方法是私有还是公有方法,这里recursivePublicMethods 默认是为true的,也就是说,我们调用getMethod方法时,就已经确定了调用的是公有方法.
我们使用Method对象来接受返回的数据,因为最终返回的是method类型,也就是方法,然后调用invoke来执行方法,看一下源码中invoke方法的解释:
/** * Invokes the underlying method represented by this {@code Method} * object, on the specified object with the specified parameters. * Individual parameters are automatically unwrapped to match * primitive formal parameters, and both primitive and reference * parameters are subject to method invocation conversions as * necessary. * * <p>If the underlying method is static, then the specified {@code obj} * argument is ignored. It may be null. * * <p>If the number of formal parameters required by the underlying method is * 0, the supplied {@code args} array may be of length 0 or null. * * <p>If the underlying method is an instance method, it is invoked * using dynamic method lookup as documented in The Java Language * Specification, Second Edition, section 15.12.4.4; in particular, * overriding based on the runtime type of the target object will occur. * * <p>If the underlying method is static, the class that declared * the method is initialized if it has not already been initialized. * * <p>If the method completes normally, the value it returns is * returned to the caller of invoke; if the value has a primitive * type, it is first appropriately wrapped in an object. However, * if the value has the type of an array of a primitive type, the * elements of the array are <i>not</i> wrapped in objects; in * other words, an array of primitive type is returned. If the * underlying method return type is void, the invocation returns * null. * * @param receiver the object the underlying method is invoked from * @param args the arguments used for the method call * @return the result of dispatching the method represented by * this object on {@code obj} with parameters * {@code args} * * @exception IllegalAccessException if this {@code Method} object * is enforcing Java language access control and the underlying * method is inaccessible. * @exception IllegalArgumentException if the method is an * instance method and the specified object argument * is not an instance of the class or interface * declaring the underlying method (or of a subclass * or implementor thereof); if the number of actual * and formal parameters differ; if an unwrapping * conversion for primitive arguments fails; or if, * after possible unwrapping, a parameter value * cannot be converted to the corresponding formal * parameter type by a method invocation conversion. * @exception InvocationTargetException if the underlying method * throws an exception. * @exception NullPointerException if the specified object is null * and the method is an instance method. * @exception ExceptionInInitializerError if the initialization * provoked by this method fails. */ public native Object invoke(Object receiver, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
我们把多余注释去掉,再看一下:
/* @param receiver the object the underlying method is invoked from * @param args the arguments used for the method call * @return the result of dispatching the method represented by * this object on {@code obj} with parameters */ public native Object invoke(Object receiver, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
*看看每个参数的意思:
第一个代表了(调用方法的对象),我认为谁的方法谁调用
第二个参数代表:需要的参数列表,逗号分隔.
所以我们再使用method调用类的方法时,需要传入类的实例和参数列表!*
公有方法说完了,说一下私有方法.
前面说到:在调用公共方法时:
//公有方法1 Method method = cls.getMethod("print", String.class); method.invoke(ls, "hahahahah");
使用类的class对象调用getMethod方法,这个方法默认是公共方法,但是访问私有方法时不是这样的,我们需要调用另一个方法:
//执行私有方法 method = cls.getDeclaredMethod("cal", int.class, int.class, int.class); method.setAccessible(true); method.invoke(ls, 2, 3, 4);
虽然方法名称不同,但是他们的代码中却调用了同一个方法,就是getMethod方法,千万不要误会,这个getMethod方法是一个重载方法。与我们直接调用的是同名不同参的方法.
//源码: public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { return getMethod(name, parameterTypes, false); }
在调用公有方法时,在上面getMethod方法的第三个参数,源码中默认填写了true,但是此时却是false,因为我们此时调用的是一个私有方法。
同时我们需要调用:
method.setAccessible(true);
来设置可访问性。我们来看一下此方法解释:
Set the accessible flag for this object to the indicated boolean value. A value of true indicates that the reflected object should suppress Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks. 将此对象的可访问标志设置为所指示的布尔值。true值表明反射的对象应当压制java语言访问检查时。false表示反映的对象应执行java语言访问检查。
如果我们设置true,表示我们访问私有方法时尽量不受到权限检查,如果设置为false,代表我们要接受java权限检查,所以我们填写true,不接受权限检查。
在调用getDeclaredMethod方法时,和调用getMethod方法一样,我们需要传入:方法名,每个参数对应的class对象。
最后执行invoke方法,传入类的实例,和真正的参数!
method.invoke(ls, 2, 3, 4);
相关文章推荐
- Java访问私有属性(不使用getter方法和反射)
- 使用反射调用私有属性和方法
- 使用反射操作私有(Private)方法和属性
- 利用反射对私有属性/方法进行设置/调用(转自己贴)
- 利用反射对私有属性/方法进行设置/调用
- 反射的基础(三):属性类(Field)的使用
- 使用typeof方法反射属性和方法
- 黑马程序员--Java基础加强--15.利用反射操作泛型IV【通过反射Method解析泛型方法思路】【通过Method对四种Type子接口类型进行解剖】【使用递归对任意复合泛型类型进行彻底解剖】【个人
- 使用JAVA的反射机制反射带有数组参数的私有方法
- Java反射之访问私有属性或方法
- 第2条:使用私有构造方法强化singleton属性
- 使用java基础反射访问私有域、方法和构造函数
- 使用 反射 改变私有成员属性的值
- 访问操作类私有属性和方法的两种方式
- Java反射之访问私有属性或方法
- 使用反射、泛型和委托,动态调用对象的属性和方法——性能和灵活性兼备的方法
- 反射:使用字符串访问类、方法和属性
- 使用反射调用类型成员 方法,字段,属性
- 利用反射调用私有方法、访问私有属性
- java 使用反射机制来获取私有方法和成员变量