【问题标题】:a Java application works fine on Eclipse but it doesn't work properly after being deployed as a runnable jarJava 应用程序在 Eclipse 上运行良好,但在部署为可运行 jar 后无法正常运行
【发布时间】:2012-07-19 07:43:19
【问题描述】:

正如图块所说,我的 Java 应用程序无法正常工作,因为它在 Eclipse 上开发时工作正常。

我完成了在 Eclipse 上构建和测试我的应用程序。正如我所料,它工作得很好。我需要将它部署为一个可运行的 jar,这样我的客户就不能在他们的系统上使用它。我通过在 Eclipse 上导出它使其成为一个可运行的 jar。 当我运行可运行 jar 时,应用程序开始正常工作一段时间并停止读取文件。我在 Eclipse 上的相同代码上没有任何问题。

似乎不起作用的源sn-p如下。

            sfis = new SmbFileInputStream(sFile);
            in = new BufferedInputStream(sfis);

            byte buf[] = new byte[(int)sFile.length()];
            int pos = 0;
            int size = 10;
            int temp;

            while((size=in.read(buf, pos, size)) > 0){
                pos += size; 
                temp = buf.length - pos;
                if(temp < 10){
                    size = temp;
                }
            }

在 Eclipse 上它不会导致任何问题。它完美地从 SMB 连接读取数据并完成其工作。但是从 jar 应用程序来看,它似乎在某个时候停止从 while 循环内的输入流中读取。 这是一个我从未经历过的非常奇怪的案例。我找不到任何解决方案。

这是因为我的代码或 Eclipse 可能错误制作的 jar 文件吗?

--------- 附加信息 -------

在 Edmondo1984 的大力帮助下,我找到了 jar 程序停止的地方。 当它从 SmbFile 进入 inputstream 时,会创建一个新线程 jcifs.util.transport.Transport,该线程被简单地阻塞,应用程序创建另一个 jcifs.util.transport.Transport,依此类推。创建8个或9个jcifs.util.transport.Transport线程后,卡住了,什么也不做。

相同的代码,相同的 jcifs 库。但是在 Eclipse 上运行和在本地机器上作为可运行 jar 运行时的工作方式不同。 我不知道为什么会这样。

【问题讨论】:

  • Eclipse 可能无法将正确的 jar 放入“打包的 jar”中,但这通常会导致类加载错误。你在测试同一个文件吗?
  • 你必须给自己一些关于正在发生的事情的可见指标。即在 try catch 中输出某种关于所发生事件的有意义的信息。上面的错误消息对于调试正在发生的事情很有用。
  • @Edmondo1984 正如您所说,如果 Eclipse 未能添加应用程序所需的一些库,它将无法启动。但它开始运行。哦,是的,我正在测试同一个文件。
  • @Davos555 正如我所解释的,它在 Eclipse 上运行良好。没有错误被抛出。我实际上更喜欢让应用程序出错。这些代码在 catch 块内。我上次所做的只是将 sysout 放在 while 循环中。它会打印一段时间并变慢。然后它就停止了。应用程序仍在运行,但没有错误。
  • 当 Jconsole 卡住时,您能否将其附加到您的应用程序,以便查看应用程序挂起的位置?

标签: java jar inputstream


【解决方案1】:

“流中没有更多内容”的返回值为-1(请参阅Javadoc)。

想象一下,如果您尝试 in.read(buf, pos, size) 但底层仍然不可用,会发生什么。然后调用立即返回,返回值为0(零)。这样,循环条件评估为假并退出循环。所以我认为你应该和!= -1比较。

另一个可能的问题:您还可以考虑如果size = 0(例如由于上述情况)和temp &gt;= 10 会发生什么。因为您直接将size 的值反馈给in.read(buf, pos, size) 作为要读取的最大字节数,所以假设您已经更正了与!= -1 的比较,您可能会陷入无限循环。

【讨论】:

  • 感谢您的回复,但您的解决方案也不起作用。正如我所解释的,它在 Eclipse 上运行良好。如果你说的是一个真正的问题,应用程序也不会在 Eclipse 上运行。
  • 只要read() 总是返回至少一个字节读取(这绝对不能保证),你就不会遇到任何麻烦。我看到您的代码和文档的假设不匹配。由此,我推测如果不满足假设会发生什么(如上所述)。正如 Tiago 在他的回答中指出的那样,Eclipse 和 JAR 之间可能存在不同的运行时行为。
【解决方案2】:

在处理流时,这种行为很常见。小的算法错误可能会在不同的环境中导致不同的症状。

您可以将“调试”信息打印到控制台,以更好地了解您的代码在哪里以及为什么会停止。我很确定它在 read 方法上。

你的代码有几个问题,最臭名昭著的是块:

temp = buf.length - pos;
if(temp < 10){
  size = temp;
}

为什么?当剩下 10 个或更少字节时,您将尝试在下一次 read() 中读取它们,否则您可能会尝试读取太多(再次读取整个文件大小)。在 Eclipse 中运行,文件可能在第一次 read() 中被读取,而 Eclipse 显然不是。 尝试将您的代码更改为以下内容:

FileInputStream sfis = new FileInputStream(sFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buf[] = new byte[4096];
int read = sfis.read(buf);
while(read>0){
  baos.write(buf, 0, read);
  read = sfis.read(buf);
}
sfis.close();

还可以考虑使用Apache's Java IO utilities 代码,而不是总是编写这种类型的代码。 Java 中的 IO 需要大量样板文件,优秀的程序员有更好的事情要做。

附带说明:将文件读入内存通常是个坏主意。除非您打算以某种方式将其数据作为一个整体进行转换,否则流式传输总是更好。我不知道您的程序是做什么用的,但请记住这一点:如果您尝试读取 10GB 文件会发生什么?还是几个?

编码愉快,

【讨论】:

  • 感谢您的回复。我在您提到的部分上这样做的原因是为了使文件读取速度更快一些。我知道这没什么用,但是应用程序读取的文件通常小于 30 MB,所以我需要那种算法。至于你的代码,我也试过了,但同样的事情发生了,而且这次发生的比以前更早。
【解决方案3】:

Java 在任何方面都对大写字母敏感。例如,如果您有名为 image.PNG 的文件,并且在代码中将位置设置为“/image.png”(而不是“/image.PNG”),它将在 eclipse 中正常工作,但在导出可运行的 jar 文件后,java VM 会找不到那个文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-08
    • 2018-11-03
    • 1970-01-01
    相关资源
    最近更新 更多