利用反射对修饰符为final的成员变量进行修改

假设咱们有以下一个类,咱们要利用反射来对其成员变量就行修改:java

class Entity {
	public int i = 1;
}

通常咱们会这么作:spa

try {
	Entity e = new Entity();
	System.out.println("before: " + e.i);
		
	Field f = Entity.class.getDeclaredField("i");
	f.setInt(e, 2);
	// 或者这样
	//f.set(e, 2);// java会自动装箱拆箱
			
	System.out.println("after: " + e.i);
} catch (Exception e) {
	e.printStackTrace();
}

结果输出:code

before: 1
after: 2

可是若是咱们的成员变量是final呢?
对象

class Entity {
	public final int i = 1;
}

咱们再用上面的方式来进行修改值,发现出异常了:get

java.lang.IllegalAccessException: Can not set final int field com.test.Entity.i to (int)2

那咱们该怎么办呢?咱们能够修改为员变量的final修饰符,使其变为public int i = 1;具体方法以下:
源码

try {
	Entity e = new Entity();
	System.out.println("before: " + e.i);
			
	Field f = Entity.class.getDeclaredField("i");
			
	Field modifiersField = Field.class.getDeclaredField("modifiers");
	modifiersField.setAccessible(true);
        
        // 输出17:表示修饰符为:public final
        System.out.println(f.getModifiers());
        
        /* 这里就是要修改修饰符了,至于为何是f.getModifiers() & ~Modifier.FINAL,你们看一下Modifier的源码就知道了*/
	modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
	
	// 输出1:表示修饰符已经被修改成:public
	System.out.println(f.getModifiers());

	f.setAccessible(true);
			
	f.setInt(e, 2);
	//f.set(e, 3);
			
	System.out.println("after: " + e.i);
} catch (Exception e) {
	e.printStackTrace();
}

没有抛出异常了,可是确没有出现咱们想要的结果,输出为:虚拟机

before: 1
17
1
after: 1

这是为何呢?咱们稍微修改下Entity的代码以下:it

class Entity {
	public final Integer i = 1;
}

而后再执行,发现结果已经被修改了:
io

before: 1
17
1
after: 3

说明我能够对对象数据类型进行修改,而不能对基本数据类型进行修改class

咱们再来试试String类型的:

class Entity {
	public final String s = "a";
}
try {
	Entity e = new Entity();
	System.out.println("before: " + e.s);
			
	Field f = Entity.class.getDeclaredField("s");
			
	Field modifiersField = Field.class.getDeclaredField("modifiers");
	modifiersField.setAccessible(true);

	System.out.println(f.getModifiers());
		
	modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
			
	System.out.println(f.getModifiers());

	f.setAccessible(true);
			
	//f.setInt(e, 2);
	f.set(e, "b");
			
	System.out.println("after: " + e.s);
} catch (Exception e) {
	e.printStackTrace();
}

结果输出:

before: a
17
1
after: a

咦?居然木有改变?

咱们再对Entity稍微改动下:

class Entity {
	public final String s = new String("a");
}

结果输出:

before: a
17
1
after: b

修改为功,至于为何会这样,那得问java虚拟机了


好了,总结一下,对于final修改的成员变量,基本数据以及public final String s = "a";这种方式不可被修改

而对于对象数据类型是能够突破final限制进行修改的,可是通常咱们也不多会这么用

相关文章
相关标签/搜索