本文介绍了Java - 在另一个进程中启动另一个类'main的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一种干净的方法来启动带有GUI的Java程序的许多实例,我想以编程方式执行它。我想运行的程序只是一个.class文件(一个带有main方法的已编译的.java文件),它应该显示一个GUI并独立于其他文件运行(作为它自己的进程)。我还需要传递一些参数。

I need a clean way to start many instances of a Java program with a GUI, and I want to do it programmatically. The "program" i want to run is just a .class file (a compiled .java file with a main method), it should show a GUI and run independently from the others (as its own process). I also need to pass that program some arguments.

检查EDIT5是否有完整的工作解决方案代码。

Check EDIT5 for the complete working solution code.

这里是应该启动许多进程的类

Here's the class that's supposed to start the many processes

package startothermain;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Starter {

    public static void main(String[] args) {
        int starts = 4;

        for (int i = 0; i < starts; ++i) {
            System.out.println("Starting an app");
            ProcessBuilder pb = new ProcessBuilder("java.exe", "-cp", "bin", "Started", "arg0");
            try {
                pb.start();
            } catch (IOException ex) {
                Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

这个是应该启动并显示GUI的类

This is the class that's supposed to be started and display a GUI

package startothermain;

import javax.swing.JOptionPane;

public class Started {

    public static void main(String[] args) {
        JOptionPane.showMessageDialog(null, args[0]);
    }
}

我尝试了ProcessBuilder和Runtime.getRuntime()建议我在其他答案中找到了,但它们似乎没有用。我总是得到某种未找到的错误,比如这个

I tried both ProcessBuilder and Runtime.getRuntime() suggestions I found in other answers, but they don't seem to work. I always get some kind of "not found" error, like this one

SEVERE: null
java.io.IOException: Cannot run program "java.exe": error=2, No such file or directory
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1041)
    at startothermain.Starter.main(Starter.java:35)
Caused by: java.io.IOException: error=2, No such file or directory
    at java.lang.UNIXProcess.forkAndExec(Native Method)
    at java.lang.UNIXProcess.<init>(UNIXProcess.java:135)

我在工作NetBeans并单击运行按钮,因此.java文件应该正确编译并位于同一文件夹中。我希望这对IDE创建的src / build文件夹没有任何问题。

I'm working from NetBeans and clicking the Run button, so the .java files should be compiled properly and in the same folder. I hope this is no side problem with the src/build folders created by the IDE.

编辑:我试图找到java.exe,但我在Linux操作系统。叹。我改为java,但我仍然有同样的问题。应设置路径变量。此外,我担心,如果我给它一个完整的路径,它将变得不可移植。

I was trying to find the java.exe, but I'm on Linux. Sigh. I changed it for "java", but I still have the same problem. The path variable should be set. Moreover, I'm worried that, if I give it a full path, it will become unportable.

试图在Linux上获得JAVA_HOME

Trying to get the JAVA_HOME on Linux

System.getenv("JAVA_HOME"); // returns null
System.getProperty("java.home"); // returns /usr/lib/jvm/java-7-openjdk-amd64/jre

Windows

System.getenv("JAVA_HOME"); // returns C:\Program Files\Java\jdk1.7.0_51
System.getProperty("java.home"); // returns C:\Program Files\Java\jdk1.7.0_51\jre

EDIT2:这个新代码不会产生错误,但也不会打开任何GUI。我在Windows和Linux上都试过这个,结果相同。

This new code does NOT spawn errors, but does not open any GUI either. I tried this on both on Windows and Linux, with the same results.

String javaHome = System.getProperty("java.home");
ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", "bin", "Started", "arg0");

EDIT3:我重定向了流程构建器的错误和输出流(不是创建过程的流)并且它事实证明,Java ClassLoader无法找到该类。我想问题不是JAVA_HOME,而是路径问题。

I redirected the error and output streams of the processbuilder (not of the crerated processes) and it turns out that the Java ClassLoader can't find the class. I guess the problem is not JAVA_HOME, but rather a path problem.

这是新代码

package startothermain;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Starter {

    public static void main(String[] args) {
        int starts = 4;
        System.out.println(System.getProperty("java.home")); // prints C:\Program Files\Java\jdk1.7.0_51\jre
        System.out.println(System.getenv("JAVA_HOME")); // prints C:\Program Files\Java\jdk1.7.0_51

        System.out.println("Working Directory = "
                + System.getProperty("user.dir")); // prints C:\Users\Agostino\Documents\NetBeansProjects\StartOtherMain

        for (int i = 0; i < starts; ++i) {
            System.out.println("Starting an app");
            ProcessBuilder pb = new ProcessBuilder("java", "build.classes.startothermain.Started");
            File log = new File("log");
            pb.redirectOutput(log);
            pb.redirectError(log);
            try {
                pb.start();
            } catch (IOException ex) {
                Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

In日志文件我发现此错误

In the log file I find this error

java.lang.NoClassDefFoundError: build/classes/startothermain/Started (wrong name: startothermain/Started)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)
Exception in thread "main"

EDIT4:现在代码有效,但如果我尝试使用a。找不到启动类中的jar库。

now the code works, but if I try to use a .jar library in the Started class, it can't be found.

请参阅此Started类,它使用通过项目库添加​​到项目库的.jar库(JavaTuples) NetBeans

See this Started class that uses a .jar lib (JavaTuples) that is added to the project libraries through NetBeans

package startothermain;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Starter {

    public static void main(String[] args) {
        int starts = 1;

        for (int i = 0; i < starts; ++i) {
            System.out.println("Starting an app");
            ProcessBuilder pb = new ProcessBuilder();

            String fullClassName = Started.class.getName();
            pb.command("java", "-cp", "./build/classes", fullClassName, "myArg");

            File log = new File("log");
            pb.redirectOutput(log);
            pb.redirectError(log);
            try {
                pb.start();
            } catch (IOException ex) {
                Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

修正入门级

package startothermain;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Starter {

    public static void main(String[] args) {
        int starts = 1;

        for (int i = 0; i < starts; ++i) {
            System.out.println("Starting an app");
            ProcessBuilder pb = new ProcessBuilder();

            String fullClassName = Started.class.getName();
            pb.command("java", "-cp", "./build/classes", fullClassName, "myArg");

            File log = new File("log");
            pb.redirectOutput(log);
            pb.redirectError(log);
            try {
                pb.start();
            } catch (IOException ex) {
                Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

ProcessBuilder日志中的错误

The error in the ProcessBuilder log

Exception in thread "main" java.lang.NoClassDefFoundError: org/javatuples/Pair
    at startothermain.Started.main(Started.java:28)
Caused by: java.lang.ClassNotFoundException: org.javatuples.Pair
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 1 more

EDIT5:工作代码

Working code

入门级

package startothermain;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Starter {

    public static void main(String[] args) {
        int starts = 1;

        for (int i = 0; i < starts; ++i) {
            System.out.println("Starting an app");
            ProcessBuilder pb = new ProcessBuilder();

            String fullClassName = Started.class.getName();
            String pathToClassFiles = new File("./build/classes").getPath();
            String pathSeparator = File.pathSeparator; // ":" on Linux, ";" on Windows
            String pathToLib = new File("./lib/javatuples-1.2.jar").getPath();
            pb.command("java", "-cp", pathToLib + pathSeparator + pathToClassFiles, fullClassName, "myArg");

            File log = new File("log" + i + ".txt"); //debug log for started process
            try {
                pb.redirectOutput(log);
                pb.redirectError(log);
                pb.start();
            } catch (IOException ex) {
                Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

开始class

package startothermain;

import javax.swing.JOptionPane;
import org.javatuples.Pair;

public class Started {

    public static void main(String[] args) {
        Pair<String, Integer> pair = Pair.with("One", 1);
        JOptionPane.showMessageDialog(null, args[0] + " " + pair);
    }
}


推荐答案

As根据您的要求,我在答案中总结了我的评论。

As on your request I summarize my comments in an answer.

您的第一个问题是您尝试调用 java.exe 在Linux或Mac机上,不遵守将文件类型包含在名称中的Microsoft惯例。因此,Linux和Mac通常使用 java

Your first problem was that you tried to invoke java.exe on a Linux or Mac box, which does not stick to the Microsoft convention of including the file type into the name. Therefore Linux and Mac usually use java instead.

可执行文件的路径也可能因计算机而异。因此,可能最通用的方法是使用 JAVA_HOME 环境变量来定位java可执行文件,该变量可以通过 System.getenv(JAVA_HOME)在Java中获取); 或通过 System.getProperty(java.home); 但请注意,它们可能无法明确指向所需的目录。特别是一些Linux发行版将所有二进制文件放在 / bin / usr / bin 中,因此 $ {JAVA_HOME} / bin / java 可能无法使用。在这种情况下,您应该创建一个指向可执行文件的(硬)链接。

The path to the executable can vary also from computer to computer. Therefore the probably most generic way is to locate the java executable using the JAVA_HOME environment variable which can be fetched in Java via System.getenv("JAVA_HOME"); or via System.getProperty("java.home"); Note however that they might not obviously point to the desired directory. Especially some Linux distributions place all binary files inside /bin or /usr/bin and therefore ${JAVA_HOME}/bin/java might not be available. In that case you should create a (hard)link to the executable.

如果未设置路径,您可以在正在执行的控制台会话中手动设置它您的应用程序(在Windows上在Linux上设置JAVA_HOME = C:\Program Files \ Java (或在较新的Windows版本上设置) export JAVA_HOME = /选择/ JAVA )。这也可以通过

If the path is not set you can set it manually in the session of your console you are executing your application (on Windows set JAVA_HOME=C:\Program Files\Java (or setx on newer Windows versions) on Linux export JAVA_HOME=/opt/java). This can also be done with user-priviledges

强烈建议。链接的文章深入解释了为什么要处理它 - 不仅要捕获被调用进程抛出的异常。

It is further strongly recommended to take care of in- and output-streams when dealing with processes. The linked article explains in depth why you should take care of it - not only to catch exceptions thrown by the invoked process.

关于调用进程(特别是Java可执行文件)你有两个选择:
你可以直接调用Java进程(假设Java在你的PATH上),用新的ProcessBuilder(java, - cp,path / to / your / binaries,package.ClassName); 或者你可以通过shell调用Java可执行文件 - 在Linux上你也可以使用新的ProcessBuilder(/ bin / bash, - c,java -cp path / to / your / binaries package.ClassName); (虽然引物明显更可取。)

On invoking a process (especially with the Java executable) you have a couple of options:You can either invoke the Java process directly (assuming Java is on your PATH) with new ProcessBuilder("java", "-cp", "path/to/your/binaries", "package.ClassName"); or you can invoke the Java executable via a shell - on Linux f.e. you could also use new ProcessBuilder("/bin/bash", "-c", "java -cp path/to/your/binaries package.ClassName"); (though the primer one is obviously preferable).

在动态加载类/库时,必须使用完全限定的类名。因此,如果您在项目的根目录中调用Java进程,并且您的构建工具在 ./ build / classes 内生成类文件,并且您的类测试位于包 testpackage 中,您将得到以下集合: ./ build /类/ testpackage /的Test.class 。要启动一个新的Java进程来调用 Test.class 中包含的main方法,你必须使用以下命令: java -cp ./build / classes testpackage.Test 否则Java将无法找到 Test 的类定义并执行main方法。

On loading classes/libraries dynamically you have to use the fully qualified class name. So, if you invoke the Java process in the root-directory of your project and your building-tool generates the class-files inside of ./build/classes and your class Test is located in package testpackage you will end up with the following set: ./build/classes/testpackage/Test.class. To start a new Java process which invokes the main method included in your Test.class you have to use the following command: java -cp ./build/classes testpackage.Test else Java will not be able to find the class definition of Test and execute the main-method.

必须将缺少的依赖项添加到调用Java命令的类路径( -cp ... )段中。 F.e: java -cp lib / jar1.jar; lib / jar2.jar; build / classes / * package.ClassName 。多个归档或目录由; 分隔,星号 * 也可以包含目录中的所有内容。

Missing dependencies have to be added to the classpath (-cp ...) segment of the invoking Java command. F.e: java -cp lib/jar1.jar;lib/jar2.jar;build/classes/* package.ClassName. Multiple archives or directories are separated by a ; and an asterisk * is also possible to include everything within a directory.

剩下的一个注意事项:如果您尝试发送应用程序,则需要将此代码改编为更通用的版本(属性文件fe),因为这可能是由于路径可能完全不同,故障概率很高。

One note left: if you ever try to ship your "application" you will need to adapt this code to a more generic version (property file f.e) as this will probably fail with high probability as the path might be totally different.

如果我忘记了什么,请告诉我。

If I have something forgotten, please let me know.

这篇关于Java - 在另一个进程中启动另一个类'main的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 05:45
查看更多