结冰的离季
本帖最后由 结冰的离季 于 2022-5-10 12:58 编辑

要求是修改对象的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


我的代码是
  1. Field field = spm.getClass().getDeclaredField("fileAssociations");
  2.                         field.setAccessible(true);
  3.                         Field modifiersField = Field.class.getDeclaredField("modifiers");
  4.                         modifiersField.setAccessible(true);
  5.                         modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
复制代码
getDeclaredField("modifiers") 就  NoSuchFieldException 了

咋办,在线等

解决方案
  1. public void test() {
  2.         try {
  3.             Field field = Bukkit.getServer().getClass().getDeclaredField("commandMap");
  4.             field.setAccessible(true);
  5.             SimpleCommandMap map = (SimpleCommandMap) field.get(Bukkit.getServer());
  6.             Field f = Unsafe.class.getDeclaredField("theUnsafe");
  7.             f.setAccessible(true);
  8.             Unsafe unsafe = (Unsafe) f.get(null);
  9.             long offset = unsafe.objectFieldOffset(field);
  10.             //map为替换的对象
  11.             unsafe.putObject(Bukkit.getServer(), offset, map);
  12.         } catch (Exception e) {
  13.             e.printStackTrace();
  14.         }
  15.     }
复制代码



凉鱼
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段。

对于 static final 字段的修改对于代码是(很有可能)不可见的。

结冰的离季
凉鱼 发表于 2022-5-7 20:26
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段 ...

我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码

凉鱼
结冰的离季 发表于 2022-5-7 20:30
我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码 ...

您应该考虑用其他方式实现功能,例如修改后重新编译第三方代码

一意孤行的话可以使用 sun.misc.Unsafe,但是对于这个字段的修改仍然很可能不可见

teddyxlandlee
首先,你的需求是什么?写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,这太小题大做了。

结冰的离季
本帖最后由 结冰的离季 于 2022-5-9 12:33 编辑
teddyxlandlee 发表于 2022-5-9 09:44
首先,你的需求是什么?写Mod?写插件?写附属?
如果是上述需求,你不应该用反射去掉final。
Fabric有acce ...

写插件,存在即合理,我要修改服务端 CraftServer类 的 private final SimpleCommandMap commandMap 替换为自己的类型,目的是修改它的一个方法。
bukkit插件的性质决定它不能使用mixin这些工具,我又是单纯的插件,不通过反射我要怎么通过插件来修改代码。
那我改不了类的方法,我通过继承类型来覆写方法 再反射把对象设置回去有问题吗



下一页 最后一页