是否可以通过 java 中的反射更改默认的初始字段值?

Is it possible to change the default initial field value via reflection in java?

假设我们有一个带有字段的 class,它有一个默认的初始值,并且不会被构造函数更改,例如

public class Server {
  private int pingFrequency = 500;

  public Server() {
  }
}

现在我不想将默认初始值更改为另一个值在构造对象之前。原因是这个 class 被库使用并隐藏了对象实例。所以我只能控制何时构造对象,而不能控制构造对象的位置和方式。

我尝试通过反射获取字段,但我没有看到任何更改默认值的方法

Field pingFrequency =  Class.forName("Server").getDeclaredField("pingFrequency")

我想我必须在 classloader 中改变一些东西,但我不知道是什么以及如何。

谢谢

当你声明一个class喜欢

public class Server {
  private int pingFrequency = 500;

  public Server() {
  }
}

没有区别
public class Server {
  private int pingFrequency;

  public Server() {
    pingFrequency = 500;
  }
}

public class Server {
  private int pingFrequency;

  {
    pingFrequency = 500;
  }
  public Server() {
  }
}

事实上,所有三个变体都被编译成相同的字节码。所有字段初始化程序和实例初始化程序块的代码都被复制到此 class 的每个构造函数¹,就在超级构造函数调用和构造函数的其余部分之间。 [JLS §12.5]
¹ 不委托给此 class

的另一个构造函数

更改赋值的唯一方法是修改所有构造函数的代码,以更改赋值。这不能通过反射来完成,只能通过字节码操作工具来完成。

注意当字段已经声明时final,like

public class Server {
  private final int pingFrequency = 500;

  public Server() {
  }
}

字节码中会有一个属性,报告常量值[JVMS §4.7.2], additionally to the assignment. However, for such a compile-time constant, every ordinary read access will be replaced with the constant value at compile-time[JLS §13.1], so even changing the assignment would have no effect then (neither would changing the attribute)[JLS §13.4.9]。试图替换该字段的实际用途会引发一个问题,即您无法将它们与常量 500.

的其他用途区分开来

如果字段是 staticfinal,则根本没有赋值,常量值属性将用于初始化字段,但是,更改它的效果与对于常量实例字段,因为字段访问仍被替换为旧的常量值。