Java 为什么显示图像的代码会给出一个"&引用;“错误”&引用;何时构建到jar中?

Java 为什么显示图像的代码会给出一个"&引用;“错误”&引用;何时构建到jar中?,java,image,jar,bufferedimage,embedded-resource,Java,Image,Jar,Bufferedimage,Embedded Resource,我想通过在JLabel中绘制BuffereImage来显示图像 x/yOffset是在JLabel的中间绘制一个较小的图像 如果我在IDE中运行代码,它可以正常工作,并在JFrame上显示图像 如果我现在将该类构建到一个jar文件中,它将不再工作 我尝试将图像设置为JLabel的图标,而不使用BuffereImage,但这不是我想要做的 下面是我的图像类的代码: public class ImageHQ extends JLabel { BufferedImage img; int xOff

我想通过在JLabel中绘制BuffereImage来显示图像

x/yOffset是在JLabel的中间绘制一个较小的图像

如果我在IDE中运行代码,它可以正常工作,并在JFrame上显示图像

如果我现在将该类构建到一个jar文件中,它将不再工作

我尝试将图像设置为JLabel的图标,而不使用BuffereImage,但这不是我想要做的

下面是我的图像类的代码:

public class ImageHQ extends JLabel {

BufferedImage img;

int xOffset=0;
int yOffset=0;


public ImageHQ(String path, int xOffset, int yOffset) {
    try {
        try {
            img = ImageIO.read(new File(getClass().getResource(path).toURI()));
        } catch (URISyntaxException ex) {
            Logger.getLogger(ImageHQ.class.getName()).log(Level.SEVERE, null, ex);
            errorMsg(ex.getMessage());
        }
    } catch (IOException ex) {
        Logger.getLogger(ImageHQ.class.getName()).log(Level.SEVERE, null, ex);
        errorMsg(ex.getMessage());
    }
    
    this.xOffset = xOffset;
    this.yOffset = yOffset;

}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g;

    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g.drawImage(img, 0+xOffset, 0+yOffset, null);

    repaint();
}

public void errorMsg(String msg) {
    JOptionPane.showMessageDialog(null, msg, "Fehler", JOptionPane.ERROR_MESSAGE);
}
}

PS:errorMsg方法也不会给我错误。

问题在于:

new File(getClass().getResource(path).toURI())
应用程序资源不能保证是单独的文件。jar条目只是压缩存档的一部分。它不是硬盘上的单独文件。这就是为什么不能使用文件来读取它

读取资源的正确方法是根本不尝试将其转换为文件
getResource
返回一个URL;您可以直接将该URL传递给:

请注意使用了类文本,
ImageHQ.class
,而不是getClass()。这保证了您的资源是相对于您自己的类来读取的,而不是一个子类,它可能位于不同的包或不同的包中

从输入流读取 一般来说,可能存在URL不够的情况。您还可以使用获取从资源读取的开放InputStream。在您的情况下,您可以:

try (InputStream stream = ImageHQ.class.getResource(path)) {
    img = ImageIO.read(stream);
}
但这将是次优的,因为URL可以提供InputStream无法提供的信息,如文件名、内容类型,以及图像数据长度的预先知识

资源路径 传递给
getResource
getResourceAsStream
的字符串参数实际上不是文件名。它是相对URL的路径部分。这意味着以C:\开头的参数将始终失败

因为参数是URL,所以在所有平台上,它总是使用前斜杠(
/
)来分隔路径组件。通常,它是针对调用其getResource*方法的类对象的包进行解析的;因此,如果
ImageHQ
com.example
包中,则此代码:

ImageHQ.class.getResource("logo.png")
将在.jar文件中查找com/example/logo.png

您可以选择以斜杠开始字符串参数,这将强制它相对于.jar文件的根。上述内容可以写成:

ImageHQ.class.getResource("/com/example/logo.png")
ClassLoader中也有getResource*方法,但不应使用这些方法。始终使用Class.getResource或Class.getResourceAsStream。ClassLoader方法在Java 8和更早版本中的功能类似,但在Java 9中,Class.getResource在模块化程序中更安全,因为它不会与模块封装冲突。(ClassLoader.getResource不允许在其字符串参数的开头使用
/
,并且始终假定该参数相对于.jar文件的根。)

空返回值 如果path参数没有命名.jar文件中实际存在的资源(或者资源位于不允许读取的模块中),则所有getResource*方法都将返回
null
。NullPointerException或IllegalArgumentException是这种情况的常见症状。例如,如果没有
logo.png
与.jar文件中的ImageHQ类在同一个包中,则getResource将返回null,并将该null传递给
ImageIO.read
将导致IllegalArgumentException,如ImageIO.read文档中所述

如果出现这种情况,可以通过列出.jar文件的内容来对其进行故障排除。有很多方法可以做到这一点:

  • 每个IDE的文件浏览器或文件树都可以检查.jar文件的内容
  • 如果您的JDK位于shell的路径中,那么只需执行
    jartf/Path/to/myapplication.jar
  • 在Unix和Linux中,
    unzip-v/path/to/myapplication.jar
    也可以工作,因为.jar文件实际上是一个包含一些特定于Java的条目的zip文件
  • 在Windows中,您可以创建.jar文件的副本,将副本的扩展名更改为.zip,并使用任何zip工具(包括Windows文件资源管理器)打开它
回到示例,如果您的类位于
com.example
包中,并且您的代码正在执行
ImageHQ.class.getResource(“logo.png”)
,那么您将检查.jar文件的内容,以查找
com/example/logo.png
条目。如果不存在,getResource方法将返回null

关于打印错误消息 将
ex.getMessage()
替换为
ex.toString()
。通常情况下,异常消息本身没有意义。您还应该添加
ex.printStackTrace()
到每个
catch
块(或添加记录堆栈跟踪的日志语句),这样您就可以确切地知道问题发生的位置

关于绘画
切勿从paintComponent方法调用
repaint()
。这将创建一个无限循环,因为
repaint()
将强制Swing绘制系统再次调用
paintComponent

整个自定义组件是不必要的。抗锯齿渲染提示对图像没有任何影响,它们与文本和绘制的线条等相关。可以使用
EmptyBorder
实现偏移。还请注意(当实际需要自定义绘制图像时),每个
JComponent
都是
ImageObserver
so
g.drawImage(…null)
最好是
g.drawImage(…这个)。至于反对票,我同意这是令人迷惑的。也可能是投票结束的人(原因对我来说同样神秘),但谁能肯定呢?忽略我所说的“不必要的”。我只看了代码,没有看描述,在那里你记录了更多的东西
ImageHQ.class.getResource("/com/example/logo.png")