我在这里有一个相当有趣的场景。假设我有一段存储在Java String中的C代码。我需要在我自己的Java程序中运行这段代码。
案例1
class Main{
public static void main(String[] args) {
String cCode = "printf(\"Hello World\n\");"
// I need to run the cCode here.
// We are allowed to call a method with params.
}
}
我认为我应该做的是。
案例2
我正在考虑执行上述过程,因为如果源代码是预定义的,我知道如何使用JNI执行此操作。
class Main{
static {
System.loadLibrary("Main"); // Load native library at runtime
}
private native void sayHello();
public static void main(String[] args) {
new Main().sayHello();
}
}
在预先编写的C代码的情况下。我们做的是。
javac Main.java
javah -jni Main
gcc -share -I/path/to/jni -I/path/to/jni_md -o Main.so
java Main
有人能告诉我,我是否采取了正确的路径(案例1 )?或者有更好的方法吗?
**注意:这里的关键点是,我只允许编译一次java代码。 (开头)。**
编辑:在检查了@Dúthomhas的评论和回答后,我想我应该再解释一下。我为机器学习项目做这个的原因。已经确定数值计算部分具有瓶颈并且使用C作为上述方法值得尝试它的风险。所以安全性现在已经不在了。我们只需将此作为实验。
答案 0 :(得分:3)
非答案答案:不要这样做。
你要求做的是一个非常糟糕的主意,有几个原因,两个主要原因是:
要求嵌入完全不同的语言意味着添加和链接库和大量代码以同步库,以及执行静态分析和沙箱代码的代码。换句话说,您要求在已有的语言之上实现整个语言。
可以认为C无论如何都是基本系统,甚至可以实现JVM(通常是),但这不是重点。问题不在于C库,它是 C编译器/解释器,就简单的解释编程语言而言,它是一个相当复杂的代码库。
建议:使用Java
ToolProvider
类专门为您提供Java代码的动态编译。看看它。
确保使用SecurityManager
类正确地沙箱化代码。 (并且,如果可能的话,在一个单独的受限制的JVM中运行它。)
建议:使用JavaScript / ECMAScript
ScriptEngine
类正是为此而设计的。再次,谷歌周围的例子。而且,再次,不要忘记安全。
建议:使用现有库
但我真的想/必须使用C
唉。 可以使用C,但只是非常困难。 Google围绕“嵌入式c解释器”提供小型C语言解释器,您可能能够集成到您的源中。 (祝你好运!)
答案 1 :(得分:1)
让我澄清你的两个案例。
案例1 是指在运行过程中运行C 程序。这不是大多数人认为的"从java" 调用本机方法。您无法在Main中创建本机字段。实际上,Java没有原生字段,只有原生方法。但是在案例1 中,您也不会使用本机方法。您可以编译和运行任何用C语言或任何其他语言编写的程序(前提是运行时环境中有编译器)。为此,您使用shell命令(Runtime.exec()
)来编译程序并运行它。 C程序的参数可以在命令行上传递'如Runtime.exec()
中所述。它可以与您选择的任何IPC的Java进程通信。通常,我们只是使用管道来读取子进程的标准输出。
案例2 使用JNI(Java Native Interface)在进程中运行本机代码。您在此处描述的流程是正确的,但您可以修改它以使用在运行时构建的共享库。首先,删除本机方法,并将loadLibrary()
删除到单独的类。确保只有在Main类运行gcc -o libMain.so
命令(使用与案例1 中相同的Runtime.exec()
)后,类加载器才会加载此类。这样,静态构造函数将加载新构建的库。
在 Cases 中,您不需要重新编译Java。您不需要运行 javah 来构建libMain.so
- 这只是一个方便的步骤,以保证C标头与Java类同步。但在您的情况下,Java类不会更改 - 因此本机方法签名也不会更改。
请注意案例1 更容易,尤其是如果您必须运行不同的已编译C'字符串',但案例2 可以提供更好的性能,如果你必须多次调用本机方法。