मैं एक नया ClassLoader
बना देता हूं और इसे एक नया Class
परिभाषित करता हूं, जिसका अर्थ है कि नई कक्षा एक नए नामस्थान में होनी चाहिए, जो यह है, AFAIK। अजीब बात यह है कि, जब मैं नई कक्षा पर Class.getPackage
पर कॉल करता हूं, तो यह मेरे मुख्य नामस्थान में किसी भी अन्य वर्ग पर getPackage
पर कॉल करके लौटाए गए सटीक वही ऑब्जेक्ट देता है।कक्षा.getPackage अलग-अलग पैकेजों से कक्षाओं के लिए एक ही पैकेज क्यों लौटाता है?
:
एक वर्ग के क्रम पैकेज या इंटरफ़ेस पैकेज नाम और वर्ग या अंतरफलक के निर्णायक वर्ग लोडर से निर्धारित होता है।
तो दूसरे शब्दों में, यदि आपके पास एक ही पैकेज में दो कक्षाएं हैं, लेकिन अलग-अलग क्लासलोडर्स द्वारा लोड की जाती हैं, तो उन्हें विभिन्न पैकेजों में माना जाता है। (इसे नीचे दिए गए मेरे टेस्ट केस में प्रतिबिंब के माध्यम से "पुष्टि" भी किया जा सकता है।)
तो जब मैं ऐसा करता हूं तो मुझे कैसे परिणाम मिलता है मुझे दोनों कक्षाओं पर getPackage
से एक ही परिणाम मिलता है?
package pkg;
import java.io.*;
// Yes, you can try commenting this class, you'll get the same result.
class LoadedClass {
LoadedClass() {
System.out.println("LoadedClass init");
}
}
class MyClassLoader extends ClassLoader {
Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
class Main {
public static void main(String[] args) throws Exception {
MyClassLoader mcl = new MyClassLoader();
// load compiled class from file
FileInputStream fileinputstream = new FileInputStream(
"/home/me/test/pkg/LoadedClass.class" /* <- point to whever these classes
* are being compiled to. */
);
int numberBytes = fileinputstream.available();
byte classBytes[] = new byte[numberBytes];
fileinputstream.read(classBytes);
fileinputstream.close();
Class<?> lc = mcl.defineClass("pkg.LoadedClass", classBytes);
Package myPackage = Main.class.getPackage();
Package lcPackage = lc.getPackage();
System.out.println("lc package: " + lcPackage);
System.out.println("my package: " + myPackage);
System.out.println("lc ClassLoader: " + lc.getClassLoader());
System.out.println("lc ClassLoader parent: " +
lc.getClassLoader().getParent());
System.out.println("my ClassLoader: " + Main.class.getClassLoader());
System.out.println("are they equal? " + (lcPackage == myPackage));
if (lcPackage == myPackage) {
System.out.println("okay... we should be able to instantiate " +
"the package if that's true, lets try");
lc.newInstance(); // boom as expected
}
}
}
यह आउटपुट:
lc package: package pkg
my package: package pkg
lc ClassLoader: [email protected]
lc ClassLoader parent: [email protected]
my ClassLoader: [email protected]
are they equal? true
okay... we should be able to instantiate the package if that's true, lets try
Exception in thread "main" java.lang.IllegalAccessException: Class pkg.Main can not access a member of class pkg.LoadedClass with modifiers ""
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.Class.newInstance0(Class.java:349)
at java.lang.Class.newInstance(Class.java:308)
at pkg.Main.main(Main.java:42)
जैसी उम्मीद थी, आप सामान्य रूप से प्रतिबिंब के माध्यम से इस भरी हुई वर्ग का दृष्टांत नहीं कर सकते, क्योंकि पैकेज-निजी और यह एक में है
यहाँ मेरी परीक्षा है अलग पैकेज (समान नाम, अलग नामस्थान), जो सही AFAIK है, क्योंकि यह प्रकार की सुरक्षा लागू कर रहा है।
बस सोच रहा है क्योंकि मैं पिछले कुछ दिनों में जेवीएम और सुरक्षा वास्तुकला का अध्ययन कर रहा हूं और इस तरह की छोटी सूक्ष्मताएं ढूंढ रहा हूं इसलिए इसका कारण बनाना मुश्किल है।
हाँ, मूल क्लासलोडर को बुलाया जा रहा था। "लेकिन क्या यह वास्तव में कोई फर्क पड़ता है कि एक पैकेज ऑब्जेक्ट या एकाधिक पैकेज ऑब्जेक्ट्स हैं?"मुझे लगता है कि यह एक दुर्लभ उपयोग-मामला है, लेकिन मैंने जो ओपनजेडके कार्यान्वयन किया है, वह पाठ्यक्रम के प्रतिबिंब में पैकेज चेक करता है, और यह पता चला है कि यह 'GetPackage' –
I को कॉल करने के बजाय क्लासलोडर और पूरी तरह से योग्य नाम की तुलना करता है। अनुमान लगाएं कि यह एक कारण है कि ओएसजीआई लोग 'स्प्लिट पैकेज' (संकुल जो जार फाइलों, बंडलों, क्लासलोडर्स में फैले हुए हैं) से नफरत करते हैं। – Thilo
@ लोंगपोक - मेरा * "क्या यह वास्तव में कोई फर्क पड़ता है ..." * बिंदु यह है कि एक बार जब आप इस विसंगति को समझें कि आप इसके चारों ओर प्रोग्राम कर सकते हैं, जैसे कोड आपको मिला है। –