[英]Java - start another class' main in a different process
我需要一種干凈的方法來啟動帶有GUI的Java程序的許多實例,我想以編程方式執行它。 我想運行的“程序”只是一個.class文件(一個帶有main方法的已編譯的.java文件),它應該顯示一個GUI並獨立於其他文件運行(作為它自己的進程)。 我還需要傳遞一些參數。
檢查EDIT5以獲取完整的工作解決方案代碼。
這是應該啟動許多進程的類
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的類
package startothermain;
import javax.swing.JOptionPane;
public class Started {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, args[0]);
}
}
我嘗試了在其他答案中找到的ProcessBuilder和Runtime.getRuntime()建議,但它們似乎不起作用。 我總是得到某種“未找到”的錯誤,就像這個一樣
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文件夾沒有任何相關問題。
編輯:我試圖找到java.exe,但我在Linux上。 嘆。 我改為“java”,但我仍然有同樣的問題。 應設置路徑變量。 而且,我擔心,如果我給它一個完整的路徑,它將變得不可移植。
試圖在Linux上獲取JAVA_HOME
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
編輯2:這個新代碼不會產生錯誤,但也不會打開任何GUI。 我在Windows和Linux上都試過這個,結果相同。
String javaHome = System.getProperty("java.home");
ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", "bin", "Started", "arg0");
EDIT3:我重定向了錯誤並輸出了流程構建器的流(而不是創建的流程),結果發現Java ClassLoader找不到類。 我想問題不是JAVA_HOME,而是路徑問題。
這是新代碼
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);
}
}
}
}
在日志文件中,我發現此錯誤
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"
編輯4:現在代碼工作,但如果我嘗試在Started類中使用.jar庫,則無法找到它。
請參閱此Started類,該類使用通過NetBeans添加到項目庫的.jar lib(JavaTuples)
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);
}
}
}
}
修復了Starter類
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日志中的錯誤
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:工作代碼
入門課程
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);
}
}
}
}
上課
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);
}
}
根據您的要求,我在答案中總結了我的意見。
您的第一個問題是您嘗試在Linux或Mac上調用java.exe
,這不符合將文件類型包含在名稱中的Microsoft約定。 因此Linux和Mac通常使用java
代替。
可執行文件的路徑也可能因計算機而異。 因此,可能最通用的方法是使用JAVA_HOME
環境變量來定位java可執行文件,該變量可以通過System.getenv("JAVA_HOME");
在Java中獲取System.getenv("JAVA_HOME");
或通過System.getProperty("java.home");
但請注意,它們可能無法明確指向所需的目錄。 特別是一些Linux發行版將所有二進制文件放在/bin
或/usr/bin
,因此${JAVA_HOME}/bin/java
可能不可用。 在這種情況下,您應該創建一個指向可執行文件的(硬)鏈接。
如果未設置路徑,您可以在執行應用程序的控制台會話中手動設置它(在Windows上set JAVA_HOME=C:\\Program Files\\Java
(或在較新的Windows版本上export JAVA_HOME=/opt/java
)在Linux上export JAVA_HOME=/opt/java
)。 這也可以通過用戶權限來完成
強烈建議在處理流程時處理輸入和輸出流 。 鏈接的文章深入解釋了為什么要處理它 - 不僅要捕獲被調用進程拋出的異常。
在調用進程(特別是使用Java可執行文件)時,您有幾個選項:您可以使用new ProcessBuilder("java", "-cp", "path/to/your/binaries", "package.ClassName");
直接調用Java進程(假設Java在您的PATH上) new ProcessBuilder("java", "-cp", "path/to/your/binaries", "package.ClassName");
或者你可以通過shell調用Java可執行文件 - 在Linux上你也可以使用new ProcessBuilder("/bin/bash", "-c", "java -cp path/to/your/binaries package.ClassName");
(雖然引物一明顯是優選的)。
在動態加載類/庫時,您必須使用完全限定的類名。 因此,如果您在項目的根目錄中調用Java進程,並且您的構建工具在./build/classes
生成類文件,並且您的類Test
位於包testpackage
您將最終得到以下集合: ./build/classes/testpackage/Test.class
。 要啟動一個調用Test.class
包含的main方法的新Java進程,必須使用以下命令: java -cp ./build/classes testpackage.Test
else Java將無法找到Test
的類定義和執行main方法。
必須將缺少的依賴項添加到調用Java命令的類路徑( -cp ...
)段中。 Fe: java -cp lib/jar1.jar;lib/jar2.jar;build/classes/* package.ClassName
。 多個檔案或目錄由a分隔;
並且星號*
也可以包含目錄中的所有內容。
剩下的一個注意事項:如果您嘗試發送“應用程序”,則需要將此代碼調整為更通用的版本(屬性文件fe),因為路徑可能完全不同,因此很可能會失敗。
如果我忘記了什么,請告訴我。
您是否嘗試從命令提示符運行java.exe,如果它在那里不起作用,您需要設置Java路徑到JAVA安裝這可以通過在系統變量中設置變量JAVA_PATH來完成,這應該指向您的Jdk bin文件夾。
如果這不起作用,那么我認為你需要提供JAVA.exe的完整路徑,因為程序試圖找到這個文件並且它無法找到該文件並且它給出了錯誤
相反,您可以嘗試制作更好的解決方案,因為線程使用的資源更少,效率也更高
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.