【问题标题】:How Do I Compile/Run a java program from a Java Program (Input failure?)如何从 Java 程序编译/运行 Java 程序(输入失败?)
【发布时间】:2015-11-22 17:44:59
【问题描述】:

背景信息:

我是一名高中生,目前正在学习 Java,所以如果我的代码有明显的缺陷/我不小心用代码重新发明了轮子,我深表歉意。

最近我一直致力于编写一种深奥的语言,并决定将其编写为一个解释器,将代码翻译成 Java,然后运行代码。我迈出的第一步是尝试创建一个可以编译和运行 java 程序的小程序。其中的大部分代码都是从另一篇文章中收集的,这是我看过的第三或第四篇文章: how to compile & run java program in another java program?

我在该线程上使用了第三个答案中的代码,最初认为它有效。不幸的是,当我尝试使用类的文件名运行代码以使程序在其自身内编译和运行时,程序失败了。

这是修改后的代码:

 /**
 *Functions printLines, Run, and parts of main came from stacks overflow 
 *originaly but modifications have been made
 *https://stackoverflow.com/questions/4842684/how-to-compile-run-java-program-in-another-java-program
 */
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Scanner;

public class JTest
{
        private static void printLines(String name, InputStream ins) throws Exception 
    {
            String line = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(ins));
            while ((line = in.readLine()) != null) 
            {
                //System.out.println(name + " " + line);
                System.out.println(line);
            }
    }
private static int run(String command) throws Exception 
    {
        System.out.println(command);//prints command
            Process pro = Runtime.getRuntime().exec(command);
            printLines(command, pro.getInputStream());
            printLines(command + " stderr:", pro.getErrorStream());
            pro.waitFor();
        // System.out.println(command + " exitValue() " + pro.exitValue());
            return pro.exitValue();
    }
    public static void main(String args[]) 
    {
    System.out.println("Enter the name of the file you want to run: ");
    Scanner cin = new Scanner(System.in);  
        String jFileName = cin.nextLine();  

        try 
        {
            int k =  run("javac " + jFileName + ".java");
            if (k==0)
            k=run("java " + jFileName);
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }  
}

我还使用了另一个类:

public class Cout
{
    public static void main(String args [])
    {
        System.out.println("Hello World");
    }
}

在我的初始测试中...

输出:

Enter the name of the file you want to run:

输入:

Cout

输出:

javac Cout.java
java Cout
Hello World

这是我尝试从 JTest 运行 JTest 时发生的情况...

输出:

Enter the name of the file you want to run:

输入:

JTest

输出:

javac JTest.java
java JTest
Enter the name of the file you want to run:

输入:

Cout

在我输入此内容后,终端窗口上没有输出任何内容,这导致了我的主要问题:

为什么我的代码没有运行 Cout 类,我该如何解决? (最好以使我的代码与 linux 和 windows 兼容的方式)或者有人可以指出我的资源吗?

【问题讨论】:

  • 请不要用[ANSWERED]标记标题在答案旁边使用复选标记以接受正确的答案,您和回答者将获得一些代表点,并将向社区显示您找到了一个回答
  • 我的错。我混淆了在这个论坛和另一个论坛之间标记问题的过程。

标签: java compilation


【解决方案1】:

您的主要问题是理解输入和输出流。

每个进程都有三个标准流:标准输入、标准输出和标准错误。

当您通常从命令外壳运行程序时,无论是 Windows CMD 还是 Linux 终端/控制台,标准输入附加到终端的输入流,标准输出和错误附加到控制台输出。

当您在 Java 中运行进程时,尤其是当您使用 Runtime.exec 而不是使用 ProcessBuilder 时,标准流从两个调用程序通过管道传输。

您在“前台”程序中键入的内容不会自动转到“后台”程序。 “返回”程序在System.in 上的扫描仪上调用nextLine。它的System.in 通过Process.getOutputStream() 重定向到“前端”程序。它正在等待从该管道中流出的东西。但是您的“前端”程序不会向该流写入任何内容。它处理的唯一流是标准输出和标准错误——从“前”程序的角度输入的“后”程序的输出。

所以“后退”程序会坐下来等待,什么也不做。而你在这个阶段的“前端”程序正试图读取它的输出。在“返回”程序终止或关闭其标准输出之前,它不会停止读取它。这当然不行。

所以这两个进程是死锁的。他们每个人都在等待来自另一个进程的东西。

事实上,您处理流的方式可能存在另一个问题。例如,如果程序有错误,这些错误将被放置在标准错误流中。如果程序终止,很好。但如果没有,您将永远无法阅读标准错误,因为您仍将无休止地等待该程序的“标准输出”,而这可能根本不存在。


所有这一切的一个可能解决方案是让单独的线程处理每个流。

  • 一个线程需要读取控制台输入(“front”程序System.in),并将它读取的任何内容传递给getOutputStream()(“back”程序的标准输入)。
  • 一个线程需要读取“返回”程序的标准输出 (getInputStream()),并将所有内容发送到它自己的 System.out
  • 一个线程需要对错误流和System.err执行相同的操作。

但复杂之处在于,当“返回”程序终止时,您需要让这些线程停止,以便您可以再次读取自己的 System.in 并运行另一个命令。输出处理线程相对容易——当进程终止时,它们会看到“文件结束”,然后就可以终止了。但是“输入”读取线程需要有一种机制,在“返回”程序终止时中断它。

顺便说一句,如果您使用ProcessBuilder 来构建您的流程,您将可以更好地控制输入和输出的重定向。您可以让您的程序直接将其输出和错误消息写入控制台。您仍然需要正确设计输入 - 用于“前”程序的行不应被“后”程序错误地使用,因此您不能不重定向输入。

【讨论】:

    【解决方案2】:

    在 Fedora 23 下它适用于我。

    这是我的输出:

    $ java JTest 
    Enter the name of the file you want to run: 
    Cout
    javac Cout.java
    java Cout
    Hello World
    

    当我运行它们时,当前目录中有 JTest.java 和 Cout.java。

    【讨论】:

    • 如果你首先运行JTest并让它自己编译然后(在新编译的JTest的运行中)编译Cout,就会出现问题。
    【解决方案3】:

    看了上面的答案后,我意识到我忘记了我可以调用 main 方法来创建一些解决方法。因此,虽然我需要在某些时候创建一个变量字符串,但这里是代码及其输入和输出。

    类 JTest

    /**
    *Functions printLines, Run, and parts of main came from stacks overflow 
    *originaly but modifications have been made
    *http://stackoverflow.com/questions/4842684/how-to-compile-run-java-program-in-another-java-program
    */
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Scanner;
    
    public class JTest
    {
        private static void printLines(String name, InputStream ins) throws Exception 
        {
            String line = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(ins));
            while ((line = in.readLine()) != null) 
            {
                System.out.println(line);
            }
        }
        private static int run(String command) throws Exception 
        {
            Process pro = Runtime.getRuntime().exec(command);
            printLines(command, pro.getInputStream());
            printLines(command + " stderr:", pro.getErrorStream());
            pro.waitFor();
            return pro.exitValue();
        }
    
    
    
    
    
        public static void main(String args[]) 
        {
        System.out.println("Enter the name of the file you want to run: ");
        Scanner cin = new Scanner(System.in);  
            String jFileName = cin.nextLine();  
    
            try 
            {
            String arg[] = { "" } ;
            int binary = cin.nextInt();
                int k =  run("javac " + jFileName + ".java");
                if (k == 0)
                if (binary == 1)
                    JTest.main(arg);
                else
                    Foo.main(arg);
            } 
            catch (Exception e) 
            {
                e.printStackTrace();
            }
        }  
    }
    

    类 Foo

    import java.util.Scanner;
    public class Foo
    {
        public static void main(String args [])
        {
            Scanner cin = new Scanner(System.in);  
            int bar = cin.nextInt();
            System.out.println("Your number times 2 is: " + (bar * 2));
        }
    
    }
    

    输入输出对话

    输出:

    Enter the name of the file you want to run:
    

    输入:

    JTest
    1
    

    输出:

    Enter the name of the file you want to run:
    

    输入:

    JTest
    1
    

    输出:

    Enter the name of the file you want to run:
    

    输入

    Foo
    0
    4
    

    输出:

    Your number times 2 is: 4
    

    正如程序所示,输入和输出都可以正常工作。

    【讨论】:

      猜你喜欢
      • 2021-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      相关资源
      最近更新 更多