简体   繁体   English

是否可以使Java可执行文件?

[英]Is it possible to make a Java executable?

To be clear, by executable I do not mean literal bytes ready for the processor. 需要明确的是,可执行文件并不是要为处理器准备好文字字节。 For example a bash script, which is interpreted and not executable, becomes executable when a shebang is added to the top that specifies the script should be run by /bin/bash or /bin/sh or whatever program will be interpreting it. 例如,当将解释器添加到顶部以指定该脚本应由/bin/bash/bin/sh或任何将对其进行解释的程序运行时,被解释且不可执行的bash脚本将变为可执行文件。

I was wondering if it's possible to do with Java, which is not technically a scripting language but is definitely not executable. 我想知道是否可以使用Java,Java从技术上讲不是一种脚本语言,但绝对不是可执行的。 It seems like Java would be hard because the user doesn't actually have the opportunity to add a shebang to the compiled file, and the compiled java cannot come from stdin. 似乎Java很难,因为用户实际上没有机会在编译文件中添加shebang,并且编译后的Java不能来自stdin。

You can certainly create a file: 您当然可以创建一个文件:

#!/any/executable/program args
...input goes here...

You could do it with Java 你可以用Java做到

#!/path/bin/java mainclass
...this is System.in...

Instead of writing lots of code to have Java be executable in source form, you have a few options: 您可以选择几种方法来代替编写大量代码来使Java以源代码形式可执行:

Use Scala! 使用Scala! Did you know that Scala is built off Java? 您知道Scala是基于Java构建的吗? It has an interpreter and compiler. 它具有解释器和编译器。 You can run a script, a shell or compile and run it. 您可以运行脚本,shell或编译并运行它。 Scala and Java work seamlessly together. Scala和Java无缝协作。 Both compile to the same bytecode and run on a JVM. 两者都编译为相同的字节码并在JVM上运行。 Yes, the language would feel weird because Scala is like a cross between Java, R and Python, but most of the core language is unchanged and all Java packages are available. 是的,这种语言会让人觉得很奇怪,因为Scala就像Java,R和Python之间的交叉,但是大多数核心语言都没有改变,并且所有Java包都可用。 Give Scala a try. 试试Scala。 If you are on Linux, you might as well look at Spark too, even for one machine. 如果您使用的是Linux,那么即使对于一台机器,您也可能会查看Spark。

If you insist on using Java only, you can create a hybrid program that does two things (I've done this before): compile code and run it. 如果您坚持只使用Java,则可以创建一个混合程序来完成两件事(之前已经完成):编译代码并运行它。 The executable or even a bash script can do the work of taking source files and making them executable. 可执行文件甚至是bash脚本都可以完成获取源文件并使它们可执行的工作。 If you are looking to make a Java shell, then you need to make a dynamic runtime compiler/loader, but you simply need to use what Java/Oracle already gives us. 如果要制作Java Shell,则需要制作动态运行时编译器/加载器,但您只需要使用Java / Oracle已经提供的功能即可。 Imagine inserting Java syntax from a file where I put a print statement. 想象一下从放置打印语句的文件中插入Java语法。 You could have anything in there you wanted as long as it compiles. 只要可以编译,就可以在其中找到任何想要的东西。 See this example: 请参阅以下示例:

package util.injection;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Compiler {

    static final long t0 = System.currentTimeMillis();

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(64);
        String packageName = "util";
        String className = "HelloWorld";
        sb.append("package util;\n");
        sb.append("public class HelloWorld extends " + Function.class.getName() + " {\n");
        sb.append("    public void test() {\n");
        sb.append("        System.out.println(\"Hello from dynamic function!\");\n");
        sb.append("    }\n");
        sb.append("}\n");
        String code = sb.toString();

        String jarLibraryFile = "target/myprojectname.jar";

        Function dynFunction = code2class(packageName, className, code, jarLibraryFile);
        dynFunction.test();
    }

    public static Function code2class(String packageName, String className, String code, String jarLibraryFile) {
        String wholeClassName = packageName.replace("/", ".") + "." + className;
        String fileName = wholeClassName.replace(".", "/") + ".java";//"testcompile/HelloWorld.java";
        File javaCodeFile = new File(fileName);
        string2file(javaCodeFile, code);
        Function dynFunction = null;
        try {

            boolean success = compile(jarLibraryFile, javaCodeFile);

            /**
             * Load and execute
             * ************************************************************************************************
             */
            System.out.println("Running... " + (System.currentTimeMillis() - t0) + " ms");
            Object obj = load(wholeClassName);
            // Santity check
            if (obj instanceof Function) {
                dynFunction = (Function) obj;
                // Run it 
                //Edit: call dynFunction.test(); to see something
            }
            System.out.println("Finished... " + (System.currentTimeMillis() - t0) + " ms");
        } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
            exp.printStackTrace();
        }
        return dynFunction;
    }

    public static boolean compile(String jarLibraryFile, File javaCodeFile) throws IOException {
        /**
         * Compilation Requirements
         * ********************************************************************************************
         */
        System.out.println("Compiling... " + (System.currentTimeMillis() - t0) + " ms");
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

        // This sets up the class path that the compiler will use.
        // I've added the .jar file that contains the DoStuff interface within in it...
        List<String> optionList = new ArrayList<>(2);
        optionList.add("-classpath");
        optionList.add(System.getProperty("java.class.path") + ";" + jarLibraryFile);

        Iterable<? extends JavaFileObject> compilationUnit
                = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(javaCodeFile));
        JavaCompiler.CompilationTask task = compiler.getTask(
                null,
                fileManager,
                diagnostics,
                optionList,
                null,
                compilationUnit);
        fileManager.close();

        /**
         * *******************************************************************************************
         * Compilation Requirements *
         */
        if (task.call()) {
            return true;
            /**
             * ***********************************************************************************************
             * Load and execute *
             */
        } else {
            for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
                System.out.format("Error on line %d in %s%n",
                        diagnostic.getLineNumber(),
                        diagnostic.getSource().toUri());
                System.out.printf("Code = %s\nMessage = %s\n", diagnostic.getCode(), diagnostic.getMessage(Locale.US));

            }
        }
        return false;
    }

    public static void string2file(File outputFile, String code) {
        if (outputFile.getParentFile().exists() || outputFile.getParentFile().mkdirs()) {

            try {
                Writer writer = null;
                try {
                    writer = new FileWriter(outputFile);
                    writer.write(code);
                    writer.flush();
                } finally {
                    try {
                        writer.close();
                    } catch (Exception e) {
                    }
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }
    }

    public static Object load(String wholeClassName) throws IllegalAccessException, InstantiationException, ClassNotFoundException, MalformedURLException {
        // Create a new custom class loader, pointing to the directory that contains the compiled
        // classes, this should point to the top of the package structure!
        URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()});
        // Load the class from the classloader by name....
        Class<?> loadedClass = classLoader.loadClass(wholeClassName);
        // Create a new instance...
        Object obj = loadedClass.newInstance();
        return obj;
    }

}

.. ..

package util.injection;

public class Function {

    private static final long serialVersionUID = 7526472295622776147L;

    public void test() {
                System.out.println("Hello from original Function!");
    }

    public int getID() {
        return -1;
    }

    public void apply(float[] img, int x, int y) {

    }

    public double dot(double[] x, double[] y) {
            return 0;
    }
}

No. It's not possible to put a she bang on any script and it will execute. 不可以。在任何脚本上都不能放她,它会执行。 Bash relies on the fact that the file with the shebang ignores lines starting with # . Bash依赖于以下事实:带有shebang的文件会忽略以#开头的行。 Hence any scripting language or byte code that can ignore the first line with the shebang will work. 因此,任何可以忽略shebang第一行的脚本语言或字节代码都将起作用。

If your language doesn't support # as comment or ignores the first line needs to go through another scripting language that ignores that. 如果您的语言不支持#作为注释或忽略它,则第一行需要使用另一种脚本语言来忽略它。

That being said you can have bash scripts that have binary blobs inline that can be called. 话虽这么说,您可以拥有bash脚本,这些脚本具有可以被调用的内联二进制blob。 Game installers do so. 游戏安装程序会这样做。

After two and a half years I have stumbled upon a more complete answer than was given in 2016. The Java binaries can be embedded within the executable, contrary to John Hascall's answer. 两年半之后,我偶然发现了比2016年更完整的答案。Java二进制文件可以嵌入到可执行文件中,这与John Hascall的答案相反。 This article explains how this can be done in linux and unix like systems by adding a binary payload to a shell script . 本文介绍了如何通过在shell脚本中添加二进制有效负载来在linux和unix之类的系统中完成此操作。

I will offer a short summary of the procedure. 我将提供该过程的简短摘要。

Given an executable jar named any_java_executable.jar 给定一个名为any_java_executable.jar的可执行jar
Given you want to create an executable named my_executable 鉴于您要创建一个名为my_executable的可执行文件
Given a script file named basis.sh with the following contents 给定一个名为basis.sh的脚本文件,其内容如下

#!/bin/sh
MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
    java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$@"
exit 1

The native executable can be created by running the following two commands. 可以通过运行以下两个命令来创建本机可执行文件。

cat basis.sh any_java_executable.jar > my_executable;
chmod +x my_executable;

Then my_executable is a native executable capable of running the java program without depending on the location of the jar file. 然后my_executable是一个本机可执行文件,能够运行Java程序而无需依赖jar文件的位置。 It can be executed by running 可以通过运行来执行

./my_executable [arg1 [arg2 [arg3...]]]

and can be used anywhere as a CLI tool if placed in /usr/local/bin . 如果放在/usr/local/bin ,则可以在任何地方用作CLI工具。

Since JDK11 you can do it directly with source code: 从JDK11开始,您可以直接使用源代码执行此操作:

#!/usr/lib/jvm/jdk-11/bin/java --source 8

public class Oneliner {
  public static void main(String[] args){
    System.out.println("ok");
  }
}

Note, that --source parameter is mandatory if file extension is not .java . 注意,如果文件扩展名不是.java ,则--source参数是必需的。 Values 6-11 are supported, but 6 is marked as deprecated. 支持值6-11,但将6标记为已弃用。

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

相关问题 使Java库可执行 - Make a java library executable 是否可以通过简单地调用其名称(不带“ java ..”)来使Java程序可执行? - Is it possible to make a java program executable by simply calling its name (without “java ..”)? 是否可以使 Spring MVC Web 应用程序作为嵌入 Java 和 Tomcat 的“独立可执行文件”运行? - Is it possible to make a Spring MVC web app run as a “standalone executable” with Java and Tomcat embedded? 将 Android 可执行文件 (.apk) 转换为 Java 可执行文件在技术上是否可行? (。罐) - Would it be technically possible to convert a Android executable (.apk) to a Java executable? (.jar) 将基于文本的游戏制作成可执行的jar - Java - Make a text based game into an executable jar - Java 如何使用sqlite制作可执行的java应用程序 - how to make executable java app with sqlite Java Netbeans GUI构建器使可执行jar - Java Netbeans GUI builder make executable jar 建立SSH连接并以Java运行可执行文件 - Make SSH Connection and Run An Executable in Java 如何使Java运行可执行应用程序? - How to make Java run executable application? 是否可以在不先安装Java的情况下在计算机上运行可执行jar文件? - Is it possible to run an executable jar file on a machine without installing java first?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM