简体   繁体   English

java内联最终不可变常量

[英]java inlining final immutable constants

Is it possible to forbid inlining of final Strings? 是否可以禁止内联最终字符串? Probably that is a weird requirement but I need to change final field value in runtime. 可能这是一个奇怪的要求,但是我需要在运行时更改最终字段的值。 It is necessary for unit testing. 这是单元测试所必需的。

For example: 例如:

class asd {
    public static final String value = "sdfmsdkofl";

    public String getValue() {
        return value;
    }
}

I need somehow to avoid inlining of value variable. 我需要某种方式来避免value变量的内联。 And I can not change source code :(. That means I can not change way of field is accessed, add getters and setters. 而且我无法更改源代码:(。这意味着我无法更改访问字段的方式,请添加getter和setter。

Some magical parameters that would switch off ALL optimisations, even such simple. 一些神奇的参数会关闭ALL优化,即使如此简单。

You can't. 你不能 The only possible workaround is to make the constant not final , which makes it, well, not a constant. 唯一可能的解决方法是使常数不是final ,这使它成为常数。 What you should be doing is reorganizing your code so it can be tested without this sort of madness. 应该做的是重新组织代码,以便可以在没有这种疯狂的情况下对其进行测试。

Hm. First caevat: this is a BAD thing to do in production code. 首先,在生产代码中这是一件很糟糕的事情。 I've done it in tests (to switch jdbc driver) but it is not a good idea. 我已经在测试中完成了(切换jdbc驱动程序),但这不是一个好主意。 But: this is probably doable if you do it early enough. 但是:如果您尽早这样做,这可能是可行的。 JavaC (assuming you use the Oracle one) does no optimization, it's all done by the the JIT. JavaC(假设您使用的是Oracle)没有进行优化,而所有操作均由JIT完成。 So, if you change it before the JIT does its magic then it should be fine. 因此,如果您在JIT执行其魔术之前更改了它,那应该没问题。 In theory,you should be able to change the value after run-time optimization as well, since the JIT:ed code should be marked as no longer valid once you change the string but here I'm skating on very thin ice indeed. 从理论上讲,您还应该能够在运行时优化后更改该值,因为一旦更改了字符串,应将JIT:ed代码标记为不再有效,但实际上,我确实滑得很滑。

This is (parts) of my test code. 这是我的测试代码的一部分。

I need to change the driver in this class: 我需要在此类中更改驱动程序:

class MysqlConnection {
   private static final String DRIVER_NAME = "com.mysql.jdbc.Driver";

    protected Connection instantiateNewConnection() {
    try {
        // The newInstance() call is a work around for some 
        // broken Java implementations

        Class.forName(DRIVER_NAME).newInstance();
    } catch (Exception ex) {
        // handle the error 
        LOG.info("Class Exception: " + ex);
    }
}
}

And I do it like this: 我这样做是这样的:

class DBOperation {
    static {
        Field f = MysqlConnection.class.getDeclaredField("DRIVER_NAME");
        f.setAccessible(true);
        f.set(f, LocalFlipper.HSQLDB_DRIVER);
    }
}

This works. 这可行。 It IS possible to change final fields in java, it's just not a good idea. 可以在java中更改final字段,但这不是一个好主意。 First I modify the field in question, then I instantiate an instance and the DRIVET_NAME field contains the jdbc driver I want. 首先,我修改有问题的字段,然后实例化一个实例,并且DRIVET_NAME字段包含我想要的jdbc驱动程序。

There seems to be some doubts about wether this will work or not, but I can assure you it does, try it out for yourselves. 似乎有人怀疑这是否行得通,但我可以向您保证,请自己尝试一下。

@LouisWasserman: I've gone and javap:ed parts of the code: @LouisWasserman:我已经去掉了javap:ed部分代码:

Class.forName(DRIVER_NAME).newInstance();

corresponds to 对应于

   28:  ldc     #16; //String com.mysql.jdbc.Driver
   30:  invokestatic    #17; //Method  java/lang/Class.forName(Ljava/lang/String;)Ljava/lang/Class;
   33:  invokevirtual   #18; //Method java/lang/Class.newInstance:()Ljava/lang/Object;

As you can see, the String is not inlined. 如您所见,该字符串未内联。 Besides, how do you inline an object (or complex type)? 此外,如何内联对象(或复杂类型)? You can inline a reference (naturally). 您可以内联引用(自然)。 I agree that if we have code like 我同意,如果我们有类似的代码

Class.forName("com.mysql.jdbc.Driver");

Then there is no way to access that string since we can't get a reference to it. 然后,由于我们无法获得对该字符串的引用,因此无法访问该字符串。

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

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