在用java开发桌面应用程序时(虽然现在很少有人用),如果将项目导出成jar包而项目中包含图片等资源时,可能会无法正确加载这些资源。这主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过 “file:/e:/…/ResourceJar.jar/resource/123.jpg” 这种形式的文件URL来定位 123.jpg 。所以即使是相对路径,也无法定位到jar文件内的图片资源。解决方法有如下两种。
将图片资源分离出来(不打包)
如果你的项目结构图是这样的:
而代码是这样写的:
package com.xiaoli.tank;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class River {
int x,y;
ImageIcon river = new ImageIcon("images/river.gif");
public River(int x,int y){
this.x=x;
this.y=y;
}
public void draw(Graphics g){
g.drawImage(river.getImage(),x, y,null);
}
public Rectangle getRect() {
return new Rectangle(x, y,river.getIconWidth(),river.getIconHeight());
}
}
那么将项目打包后的jar文件和images文件放在同一目录下即可,如图所示:
整体打包
很多时候放置两个文件很变扭,那么我们可以将整个项目进行打包。在项目的 src
目录下新建 package images
,将图片资源放入 images
包下,如下图所示:
这时候对应的代码应该写为:
package com.xiaoli.tank;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class River {
int x,y;
ImageIcon river = new ImageIcon(this.getClass().getClassLoader().getResource("images/river.gif"));
public River(int x,int y){
this.x=x;
this.y=y;
}
public void draw(Graphics g){
g.drawImage(river.getImage(),x, y,null);
}
public Rectangle getRect() {
return new Rectangle(x, y,river.getIconWidth(),river.getIconHeight());
}
}
ClassLoader
是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。可以这样说,当我们调用jar包中的River
类时,JVM加载进River
类,并记录下River
运行时信息(包括River
所在jar包的路径信息)。而ClassLoader
类中的方法可以帮助我们动态的获取这些信息:
●public URL getResource(String name);
查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。
●public InputStream getResourceAsStream(String name);
返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。ClassLoader
是abstract
的,不可能实例化对象,更加不可能通过ClassLoader
调用上面两个方法。所以我们真正写代码的时候,是通过Class
类中的getResource()
和getResourceAsStream()
方法,这两个方法会委托ClassLoader
中的getResource()
和getResourceAsStream()
方法。