问题描述
我需要一种干净的方法来启动带有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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!