一、Java反射机制具体是什么( 三 )


3.访问并操作类的私有变量和方法 调用私有方法
package com.qjy.javabasicpractice.string.bean;public class GetMethodTestClass {private String MSG = "GetMethodTestClass";private void privateMethod(String head , int tail){System.out.print(head + tail+"is Reflection");}public String getMsg(){return MSG;}}
private static void getPrivateMethod(Class mClass) throws Exception{//1. 获取私有方法//第一个参数为要获取的私有方法的名称//第二个为要获取方法的参数的类型,参数为 Class...,没有参数就是null//方法参数也可这么写 :new Class[]{String.class , int.class}Method privateMethod = mClass.getDeclaredMethod("privateMethod", String.class, int.class);//2. 开始操作方法if (privateMethod != null) {//获取私有方法的访问权//只是获取访问权,并不是修改实际权限privateMethod.setAccessible(true);//使用 invoke 反射调用私有方法//privateMethod 是获取到的私有方法//testClass 要操作的对象//后面两个参数传实参privateMethod.invoke(mClass.getConstructor().newInstance(), "Java Reflect ", 666);}}
需要注意的是对象的第一个参数一定要是实例化的对象,我这里是通过调用构造器实例化出了对象,如果直接第一个参数填写,则会报java.lang.tion的异常 。
访问和修改私有变量
如同上文,访问私有方法一样,访问和修改私有变量,无非就是两步曲
private static void modifyPrivateFiled() throws Exception {//1. 获取 Class 类实例GetTestClass testClass = new GetTestClass();Class mClass = testClass.getClass();//2. 获取私有变量Field privateField = mClass.getDeclaredField("MSG");//3. 操作私有变量if (privateField != null) {//获取私有变量的访问权privateField.setAccessible(true);//修改私有变量,并输出以测试System.out.println("Before Modify:MSG = " + testClass.getMsg());//调用 set(object , value) 修改变量的值//privateField 是获取到的私有变量//testClass 要操作的对象//"Modified" 为要修改成的值privateField.set(testClass, "Modified");System.out.println("After Modify:MSG = " + testClass.getMsg());}}
修改私有常量
常量,在Java中通常是指final关键字修饰的一经赋值便不可再被修改的变量 。
那么通过反射机制,我们可以针对已赋值的常量,进行修改吗;修改常量的值,能够在使用到常量的地方生效吗 。
首先我们要补充和了解使用JVM虚拟机是如何去使用常量的
Java 虚拟机(JVM)在编译 .java 文件得到 .class 文件时,会优化我们的代码以提升效率 。其中一个优化就是:JVM 在编译阶段会把引用常量的代码替换成具体的常量值 。

编译前的 .java 文件:
//注意是 String类型的值private final String FINAL_VALUE = "http://www.kingceram.com/post/hello";if(FINAL_VALUE.equals("world")){//do something}
复制代码编译后得到的 .class 文件:
private final String FINAL_VALUE = "http://www.kingceram.com/post/hello";//替换为"hello"if("hello".equals("world")){//do something}
从中我们可以看到:JVM在使用常量时,会对部分常量在使用到的地方对其直接进行优化,这里的部分常量是指:
对于 int 、long 、 以及这些基本类型 JVM 会优化,而对于、Long 、 这种包装类型,或者其他诸如 Date 、 类型则不会被优化 。
所以我们可以这么理解:
我们在程序运行时刻依然可以使用反射修改常量的值(后面会代码验证),但是 JVM 在编译阶段得到的 .class 文件已经将常量优化为具体的值,在运行阶段就直接使用具体的值了,所以即使修改了常量的值也已经毫无意义了 。