是否可以在 Java 中在运行时创建变量?

Posted

技术标签:

【中文标题】是否可以在 Java 中在运行时创建变量?【英文标题】:Is it possible to create variables at runtime in Java? 【发布时间】:2011-11-20 16:27:36 【问题描述】:

例如,假设我想将String[] fruits = "Pear", "Banana", "Apple";“提取”成三个单独的变量,例如:

for (int i=0; i != fruits.length; ++i) 
    // of course there's no eval in Java
    eval("String fruit + i = " + fruits[i] + ";"); 


// ie: code that creates something equivalent to the following declarations:
String fruit0 = "Pear";
String fruit1 = "Banana";
String fruit2 = "Apple";

我怎么能这样做,忽略“你为什么要这样做?”你可能会被敦促问我的问题。

以前曾多次提出类似的问题,但从未给出真正的答案,因为 OP 真正需要的是使用不同的方法。这很好,但这可能吗?

我查看了反射,似乎没有任何方法可以让我向实例添加额外的字段,更不用说动态创建局部变量了。

【问题讨论】:

我能想到的唯一可能的方法是使用运行时字节码注入。据我所知,我们无法在编译前创建动态变量。 让我猜猜,您主要是 php 程序员,不是吗?我接近了吗? 我认为java.lang.reflect.Proxy 是您可以去的最接近的地方。即使那样你也只能实现在编译时定义的东西。 是的,但您将无法在通过该机制创建的代码之外使用它们(除非界面一致)。 @HovercraftFullOfEels 我从来没有在 PHP/Python/JS 的实际代码中使用过它(在堆栈上创建新的局部变量),但事实是,如果我需要它,它就在那里。另一个特性是动态地向实例添加成员,但更有用。 【参考方案1】:

是否可以在 Java 中在运行时创建变量?

简单的答案是否定的。

Java 是一种静态语言,不支持将新的变量声明注入到现有的编译程序中。有替代方案(按有用性/难度增加的顺序):

Map 中将“变量”表示为名称/值对。或者想出其他一些不需要需要真实动态变量的设计。 使用在 JVM 上运行并可从 Java 调用的脚本语言。 使用某种模板机制生成包含声明的新源代码,并动态编译和加载它。 使用字节码操作库(例如 BCEL)动态创建类文件,然后动态加载它们。

第一种方法是最好的。 Java 是一种静态语言,如果你不反对它,效果最好。如果这对您来说是个问题,可能是您使用了错误的语言。

最后两个是困难/复杂的,并且具有显着的性能成本。他们几乎肯定不会提供帮助......

【讨论】:

【参考方案2】:

问题不是你为什么要这样做,而是“你打算用它做什么?”。所以假设在运行时变量名为fruits2 神奇地出现在你的方法的堆栈上。怎么办?您必须在编译时知道它的名称才能利用它。反射不会帮助您访问局部变量。

无论如何,如果您描述更详细的用例,我会很感兴趣。

【讨论】:

@Alex,如果你曾经使用过 javascript 的 eval,你会看到这个功能实际上是多么强大和有用。您可以根据各种因素即时构建整个代码段并执行它们,等等。如果您从未*以这种方式思考过,我意识到很难看到它的用途,但是一旦您了解了这些可能性,当您转向不支持它的语言时,您真的会错过它(即大多数)。 @Dr.Dredel 我完全同意你的看法。然而,Java 不是一种动态语言,只要我们的讨论范围仅限于 Java(问题是),我们就必须处理它。我肯定会在 Java 中找到类似 javascript 的 eval 的良好用途,但它(遗憾的是)不存在。【参考方案3】:

您提出问题的方式,人们不会理解您在问什么。我相信(如果我理解的话)您的问题的答案(应该表述为:“是否可以在运行时动态创建变量”)是“不像您所提出的那样”。

你说得对,Java 中没有类似的 javascript(非常强大,但缓慢且充满危险的“eval”函数),而这正是你需要做的事情才能完成你希望做的事情做。

存在的最接近的是哈希图(实际上非​​常接近),您可以在运行时指定键,然后设置值。它的用途相当广泛,因为您可以拥有一个允许在字段中存储任何类型的地图。

【讨论】:

【参考方案4】:

您将无法修改已加载到 JVM 中的类。但是,您可以想象使用 ASM http://asm.ow2.org/> 或 BCEL http://commons.apache.org/bcel/> 来动态生成具有动态定义字段的新类。

麻烦多于它的价值。说真的,只需使用 HashMap!

【讨论】:

【参考方案5】:

Janino 对你有用吗?

这里有一些代码。我认为它接近你想要的,但我不确定。

package misc;

import java.lang.reflect.InvocationTargetException;

import org.codehaus.janino.CompileException;
import org.codehaus.janino.ScriptEvaluator;
import org.codehaus.janino.Parser.ParseException;
import org.codehaus.janino.Scanner.ScanException;

public class JaninoExample 
public static void main(String[] args) 
    String in = " \"Pear\", \"Banana\", \"Apple\";";
    try 
        ScriptEvaluator se = new ScriptEvaluator("return new String[]"+in,String[].class);
        try 
            String[] fruits = (String[])se.evaluate(new Object[]);
            for(String fruit:fruits)
                System.out.println(fruit);
            
         catch (InvocationTargetException e) 
            e.printStackTrace();
        
     catch (CompileException e) 
        e.printStackTrace();
     catch (ParseException e) 
        e.printStackTrace();
     catch (ScanException e) 
        e.printStackTrace();
    


【讨论】:

【参考方案6】:

您能否详细说明,不确定您在此处所做的不同之处。当然,您可以创建三个不同的字符串。但是我相信 java 中的语法是 string xx = new string("DDFD");

编辑:

我的意思是,你想在这里改变什么。您可以动态分配内存,因此您可以动态创建“变量”。但是,您不能以原始方式创建“变量”,例如“int x = 0;”在运行时,您可以在运行时将节点添加到链表、调整数组大小等。

【讨论】:

我相信我有一个模糊的想法,我相信有人提到了“字节注入”,不确定这是否是我正在考虑的,因为我对此有最小的概念。但是,如果您可以找出散列内存和堆栈内存中的内存地址位置,则可以通过超出参数等范围来直接将变量名称与其相关联。虽然我对此知之甚少,但这就像缓冲区溢出的整个概念黑客攻击。我认为这可能是您想要研究的方向?

以上是关于是否可以在 Java 中在运行时创建变量?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中在运行时创建 pdf 文件 [关闭]

是否有在单个过程中在运行时初始化可变数量的对象的过程?

在 Java 中在运行时扩展或添加新类

在 Scala 中在运行时获取类型的字符串表示

在片段中在运行时更改方向时更改布局而不重新创建视图

如何在 Android M 或更高版本中在运行时更改权限时防止重新创建 Activity