简体   繁体   English

更改静态变量可以使用原始包装,但不能使用原始类型

[英]Changing static variable works Primitive Wrapper but not with Primitive type

I have a situation where I have to change java constant. 我有一种情况,我必须改变java常量。

I have below code working 我有以下代码工作

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Main {
    public static final Integer FLAG = 44;

    static void setFinalStatic(Class<?> clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException {
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        Field modifiers = field.getClass().getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

    public static void main(String... args) throws Exception {
        System.out.printf("Everything is %s%n", FLAG);
        setFinalStatic(Main.class, "FLAG", 33);
        System.out.printf("Everything is %s%n", FLAG);
    }
}

If I run above , I get following output: 如果我在上面运行,我得到以下输出:

Everything is 44
Everything is 33

But if I change FLAG variable to int ie 但是如果我将FLAG变量更改为int ie

public static final int FLAG = 44;

It does not work. 这是行不通的。 The output is : 输出是:

Everything is 44
Everything is 44

Is there any other way to make it work with Primitive Type int . 有没有其他方法可以使它与Primitive Type int

From jls-4.12.4 jls-4.12.4

A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28) , is called a constant variable . 原始类型或类型String的变量是final,并使用编译时常量表达式(第15.28节)初始化,称为constant variable

Also section 13.1 says (emphasis mine) 13.1节也说(强调我的)

3.. References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted . 3 .. 对常量变量字段(第4.12.4节)的引用在编译时被解析为表示的常量值 No reference to such a field should be present in the code in a binary file (except in the class or interface containing the field, which will have code to initialize it). 二进制文件中的代码中不应存在对此类字段的引用(包含该字段的类或接口除外,该字段将具有初始化它的代码)。 Such a field must always appear to have been initialized (§12.4.2); 这样的字段必须总是看似已经初始化(§12.4.2); the default initial value for the type of such a field must never be observed. 绝不能遵守此类字段类型的默认初始值。

It means that compile-time constant expression from constant variables will be put directly in code by compiler (it will be inlined) not read from final reference at runtime. 这意味着来自常量变量的 编译 时常量 表达式将由编译器直接放入代码中(它将被内联),而不是在运行时从最终引用中读取。

For instance if you execute main method from Bar class 例如,如果您从Bar类执行main方法

class Foo{
    static{
        System.out.println("test if class will be loaded");
    }
    public static final int x = 42;
}

class Bar{
    public static void main(String [] args){
        System.out.println(Foo.x);
    }
}

you will see no output from static block of Foo class which means Foo class hasn't been loaded, which means that value of Foo.x didn't come from this class. 您将看不到Foo类的静态块的输出,这意味着Foo类尚未加载,这意味着Foo.x值不是来自此类。 In fact Bar was compiled this way 实际上Bar就是这样编译的

class Bar{
    public static void main(String [] args){
        System.out.println(42); // reference Foo.x will be removed by compiler 
                                // and replaced with actual value because
                                // compiler assumes that value can't/shouldn't
                                // change at runtime
    }
}

So even changing value of Foo.x at runtime will not affect value printed in main method in Bar class. 因此,即使在运行时更改Foo.x值也不会影响在Bar类的main方法中打印的值。

You can't change that mechanism. 你无法改变这种机制。


Possible way around would be initializing your final fields with values created at runtime time (they wouldn't exist at compile time, which will prevent them from being compile-time constant expressions ). 可能的方法是使用在运行时创建的值初始化最终字段(它们在编译时不存在,这将阻止它们成为编译时常量表达式 )。 So instead of 而不是

public static final String x = "foo";

try 尝试

public static final String x = new String("foo");

or in case of primitive types use Unboxing like instead of 或者在原始类型的情况下使用取消装箱而不是

public static final int x = 42;

use 使用

public static final int x = new Integer(42);
  1. Primitive types get inlined. 原始类型内联。

  2. In fact even non-primitive constants, when imported into other classes, will be copied, and the import forgotten. 事实上,甚至非原始常量,当导入到其他类时,将被复制,并且导入被遗忘。 So there it will not work either. 所以它也不会起作用。 Only for constant caches, like the string pool, and Integer (Integer.valueOf(13)) caches you may overwrite their values. 仅对于常量高速缓存(如字符串池)和Integer(Integer.valueOf(13))高速缓存,您可以覆盖它们的值。

This happens because static final fields of primitive or String type get inlined during compile time. 这是因为在编译期间内联或String类型的静态final字段被内联。 The main method will looks something like this after compilation and de-compilation 在编译和反编译之后,main方法看起来像这样

  public static void main(String... args) throws Exception {
        System.out.printf("Everything is %s%n", 44);
        setFinalStatic(Main.class, "FLAG", 33);
        System.out.printf("Everything is %s%n", 44);
    }

because FLAG gets replaced with actual value in compile-time. 因为FLAG在编译时被实际值替换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM