本帖最后由 结冰的离季 于 2022-5-10 12:58 编辑
要求是修改对象的private final 修饰的成员变量
但是从 java 17 开始
官方强制 封装 JDK 内部类 的改动之后就G了 https://openjdk.java.net/jeps/403
我的代码是
复制代码getDeclaredField("modifiers") 就 NoSuchFieldException 了
咋办,在线等
解决方案
复制代码
要求是修改对象的private final 修饰的成员变量
但是从 java 17 开始
官方强制 封装 JDK 内部类 的改动之后就G了 https://openjdk.java.net/jeps/403
JEP 396 moved from default of relaxed strong encapsulation to a default of strong encapsulation, but JDK 9 through 16 is possible to relax it. With JDK 17, the use of the launcher option is no longer possible.
Code that uses reflection to access private fields of exported java.* APIs will no longer work. For example,
var ks = java.security.KeyStore.getInstance("jceks");
var f = ks.getClass().getDeclaredField("keyStoreSpi");
f.setAccessible(true);
will fail with an exception of the form
Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
Unable to make field private java.security.KeyStoreSpi
java.security.KeyStore.keyStoreSpi accessible: module java.base does
not "opens java.security" to unnamed module @6e2c634b
我的代码是
- Field field = spm.getClass().getDeclaredField("fileAssociations");
- field.setAccessible(true);
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- modifiersField.setAccessible(true);
- modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
咋办,在线等
解决方案
- public void test() {
- try {
- Field field = Bukkit.getServer().getClass().getDeclaredField("commandMap");
- field.setAccessible(true);
- SimpleCommandMap map = (SimpleCommandMap) field.get(Bukkit.getServer());
- Field f = Unsafe.class.getDeclaredField("theUnsafe");
- f.setAccessible(true);
- Unsafe unsafe = (Unsafe) f.get(null);
- long offset = unsafe.objectFieldOffset(field);
- //map为替换的对象
- unsafe.putObject(Bukkit.getServer(), offset, map);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段。
对于 static final 字段的修改对于代码是(很有可能)不可见的。
对于 static final 字段的修改对于代码是(很有可能)不可见的。
凉鱼 发表于 2022-5-7 20:26
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段 ...
我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码
结冰的离季 发表于 2022-5-7 20:30
我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码 ...
您应该考虑用其他方式实现功能,例如修改后重新编译第三方代码
一意孤行的话可以使用 sun.misc.Unsafe,但是对于这个字段的修改仍然很可能不可见
首先,你的需求是什么?写Mod?写插件?写附属?
如果是上述需求,你不应该用反射去掉final。
Fabric有access-widener,Forge有access-transformer,都可以临时去掉Minecraft或其它Mod的任何final。
这是因为Fabric和Forge都重写了ClassLoader,加载Minecraft和所有Mod的类时,都会处理好所有的ASM、access操作。
再说了,去修改JDK内部的类、字段、方法本身就是个非常XX的行为(个人观点)
JVM应当是个安全的环境,不是用来肆意修改的。这也是Java 9加入jigsaw的一大初衷。
况且为了删一个外部的final,你去干java.lang.reflect.Field,这太小题大做了。
如果是上述需求,你不应该用反射去掉final。
Fabric有access-widener,Forge有access-transformer,都可以临时去掉Minecraft或其它Mod的任何final。
这是因为Fabric和Forge都重写了ClassLoader,加载Minecraft和所有Mod的类时,都会处理好所有的ASM、access操作。
再说了,去修改JDK内部的类、字段、方法本身就是个非常XX的行为(个人观点)
JVM应当是个安全的环境,不是用来肆意修改的。这也是Java 9加入jigsaw的一大初衷。
况且为了删一个外部的final,你去干java.lang.reflect.Field,这太小题大做了。