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

时间:2018-08-29 12:31:04

标签: java reflection field

让我们说我们有一个带有字段的类,它具有默认的初始值,例如,构造函数不会更改它的初始值

public class Server {
  private int pingFrequency = 500;

  public Server() {
  }
}

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

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

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

我认为我必须在类加载器中进行某些更改,但我不知道该如何更改。

谢谢

1 个答案:

答案 0 :(得分:1)

当您声明类似的类

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() {
  }
}

实际上,所有三个变体都被编译为相同的字节码。所有字段初始化器和实例初始化器块的代码都将复制到此类的每个构造器¹,恰好在超级构造器调用与其余构造器之间。 [JLS §12.5]
¹不会委托给该类的另一个构造函数

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

请注意,当字段声明为final时,例如

public class Server {
  private final int pingFrequency = 500;

  public Server() {
  }
}

字节码中将有一个属性,报告常量值[JVMS §4.7.2]。但是,对于这样的编译时常量,每个普通的读取访问都将在编译时被常量值替换[JLS §13.1],因此,即使更改赋值也不会生效(更改属性也不会) [JLS §13.4.9]。尝试替换该字段的实际用法会引起以下问题:您无法将其与常数500的其他用法区分开。

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