ฉันต้องการทราบวิธีที่ดีที่สุดในการโหลดทรัพยากรใน Java:
this.getClass().getResource() (or getResourceAsStream())
,Thread.currentThread().getContextClassLoader().getResource(name)
,System.class.getResource(name)
.
ฉันต้องการทราบวิธีที่ดีที่สุดในการโหลดทรัพยากรใน Java:
this.getClass().getResource() (or getResourceAsStream())
,Thread.currentThread().getContextClassLoader().getResource(name)
,System.class.getResource(name)
.คำตอบ:
หาทางออกตามสิ่งที่คุณต้องการ ...
มีสองสิ่งที่getResource
/ getResourceAsStream()
จะได้รับจากคลาสที่เรียกว่า ...
ดังนั้นถ้าคุณทำ
this.getClass().getResource("foo.txt");
มันจะพยายามโหลด foo.txt จากแพ็กเกจเดียวกันกับคลาส "this" และด้วยตัวโหลดคลาสของคลาส "this" หากคุณใส่ "/" ไว้ข้างหน้าแสดงว่าคุณกำลังอ้างถึงทรัพยากรอย่างแน่นอน
this.getClass().getResource("/x/y/z/foo.txt")
จะโหลดทรัพยากรจากตัวโหลดคลาสของ "this" และจากแพ็คเกจ xyz (จะต้องอยู่ในไดเร็กทอรีเดียวกับคลาสในแพ็คเกจนั้น)
Thread.currentThread().getContextClassLoader().getResource(name)
จะโหลดด้วยตัวโหลดคลาสบริบท แต่จะไม่แก้ไขชื่อตามแพ็คเกจใด ๆ (ต้องอ้างอิงอย่างแน่นอน)
System.class.getResource(name)
จะโหลดทรัพยากรด้วยตัวโหลดคลาสของระบบ (จะต้องมีการอ้างอิงอย่างแน่นอนเช่นกันเนื่องจากคุณจะไม่สามารถใส่อะไรลงในแพ็คเกจ java.lang (แพ็คเกจของ System) ได้
เพียงแค่ดูที่มา ยังระบุว่า getResourceAsStream เรียกใช้ "openStream" บน URL ที่ส่งคืนจาก getResource และส่งกลับ
Thread#setContextClassLoader
ที่จะมีการเปลี่ยนแปลงที่รันไทม์ผ่าน สิ่งนี้มีประโยชน์หากคุณต้องการแก้ไข classpath ในขณะที่โปรแกรมกำลังทำงาน
ดีก็ส่วนหนึ่งขึ้นอยู่กับสิ่งที่คุณต้องการให้เกิดขึ้นถ้าคุณเป็นจริงในชั้นเรียนมา
ตัวอย่างเช่นสมมติว่าSuperClass
อยู่ใน A.jar และSubClass
อยู่ใน B.jar และคุณกำลังเรียกใช้โค้ดในวิธีการอินสแตนซ์ที่ประกาศไว้SuperClass
แต่this
อ้างถึงอินสแตนซ์ของSubClass
. ถ้าคุณใช้this.getClass().getResource()
มันจะดูสัมพันธ์กับSubClass
ใน B. jar ฉันสงสัยว่ามักจะไม่ใช่สิ่งที่จำเป็น
โดยส่วนตัวแล้วฉันอาจใช้Foo.class.getResourceAsStream(name)
บ่อยที่สุด - ถ้าคุณรู้จักชื่อของทรัพยากรที่คุณตามมาแล้วและคุณแน่ใจว่าเกี่ยวข้องกับที่Foo
ใดนั่นเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการทำ IMO
แน่นอนว่ามีหลายครั้งที่ไม่ใช่สิ่งที่คุณต้องการเช่นกัน: ตัดสินแต่ละกรณีด้วยข้อดีของมัน เป็นเพียง "ฉันรู้ว่าทรัพยากรนี้รวมอยู่ในชั้นเรียนนี้" เป็นทรัพยากรที่พบบ่อยที่สุดที่ฉันพบ
this.getClass()
พิมพ์ออกมา สร้างอินสแตนซ์ของคลาสย่อยและเรียกใช้เมธอด ... มันจะพิมพ์ชื่อของคลาสย่อยไม่ใช่ซูเปอร์คลาส
ฉันค้นหาสถานที่สามแห่งตามที่แสดงด้านล่าง ยินดีต้อนรับความคิดเห็น
public URL getResource(String resource){
URL url ;
//Try with the Thread Context Loader.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader != null){
url = classLoader.getResource(resource);
if(url != null){
return url;
}
}
//Let's now try with the classloader that loaded this class.
classLoader = Loader.class.getClassLoader();
if(classLoader != null){
url = classLoader.getResource(resource);
if(url != null){
return url;
}
}
//Last ditch attempt. Get the resource from the classpath.
return ClassLoader.getSystemResource(resource);
}
ฉันรู้ว่ามันสายไปแล้วสำหรับคำตอบอื่น แต่ฉันแค่อยากแบ่งปันสิ่งที่ช่วยฉันในตอนท้าย นอกจากนี้ยังโหลดทรัพยากร / ไฟล์จากพา ธ สัมบูรณ์ของระบบไฟล์ (ไม่ใช่เฉพาะคลาสพา ธ เท่านั้น)
public class ResourceLoader {
public static URL getResource(String resource) {
final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
classLoaders.add(Thread.currentThread().getContextClassLoader());
classLoaders.add(ResourceLoader.class.getClassLoader());
for (ClassLoader classLoader : classLoaders) {
final URL url = getResourceWith(classLoader, resource);
if (url != null) {
return url;
}
}
final URL systemResource = ClassLoader.getSystemResource(resource);
if (systemResource != null) {
return systemResource;
} else {
try {
return new File(resource).toURI().toURL();
} catch (MalformedURLException e) {
return null;
}
}
}
private static URL getResourceWith(ClassLoader classLoader, String resource) {
if (classLoader != null) {
return classLoader.getResource(resource);
}
return null;
}
}
ฉันลองใช้หลายวิธีและฟังก์ชั่นที่แนะนำข้างต้น แต่ไม่ได้ผลในโครงการของฉัน อย่างไรก็ตามฉันได้พบวิธีแก้ปัญหาแล้วและนี่คือ:
try {
InputStream path = this.getClass().getClassLoader().getResourceAsStream("img/left-hand.png");
img = ImageIO.read(path);
} catch (IOException e) {
e.printStackTrace();
}
this.getClass().getResourceAsStream()
ในกรณีนี้จะดีกว่า หากคุณดูที่มาของgetResourceAsStream
วิธีการคุณจะสังเกตเห็นว่ามันทำสิ่งเดียวกันกับคุณ แต่จะฉลาดกว่า (ทางเลือกอื่นหากไม่ClassLoader
พบในคลาส) นอกจากนี้ยังแสดงให้เห็นว่าคุณสามารถพบศักยภาพnull
ในgetClassLoader
ในรหัสของคุณ ...
this.getClass().getResourceAsStream()
ไม่ได้สำหรับฉันดังนั้นฉันจึงใช้มันได้ผล ฉันคิดว่ามีบางคนที่สามารถเผชิญกับปัญหาเช่นเดียวกับฉัน