![]() |
|
|
|||||||
![]() |
Java - What is the difference between Class.forName and ClassLoader.defineClass |
|
|
Thread Tools | Search this Thread |
|
|
#1 |
|
Dear all,
I have three ways of reading in a class in a dynamic way: Instantiating the class works with two of them, with the third this results in an exception: Exception in thread "main" java.lang.IllegalAccessError: tried to access class JSHOP2_Method0 from class domainpresentationplanner >From the class description, I cannot find out why defineClass does not work. The three ways are: // (1) does not work File file = new File(full_path_to_file, "domainpresentationplanner.class"); classCode = getBytesFromFile(file); domainClass = defineClass("domainpresentationplanner", classCode, 0, classCode.length); domain = (Domain) domainClass.newInstance(); // the same in all three cases // (2) works domainClass = loadClass("domainpresentationplanner"); domain = (Domain) domainClass.newInstance(); // (3) works domainClass = Class.forName("domainpresentationplanner"); domain = (Domain) domainClass.newInstance(); getBytesFromFile is taken from http://javaalmanac.com/egs/java.io/File2ByteArray.html I should mention that domainpresentationplanner.java, the java file whose compilation produced domainpresentation.class, is comprised of several classes, one of them being JSHOP2_Method0. All resulting class files are in the same directory as domainpresentation.class. Why does version (1) not work but (2) and (3) do? Thank you, Uli Ulrich Scholz |
|
|
|
|
#2 |
|
Posts: n/a
|
Hi,
defineClass is protected, then that means that your are overriding a ClassLoader. check then for the findClass() method (log calls) I don't known if this is a "simplified" version of your code, but that's not the usual way to dynamically load classes using define class BR |
|
|
|
#3 |
|
Posts: n/a
|
Ulrich Scholz schrieb:
> I have three ways of reading in a class in a dynamic way: Instantiating > the class works with two of them, with the third this results in an > exception: > > Exception in thread "main" java.lang.IllegalAccessError: tried to > access class JSHOP2_Method0 from class domainpresentationplanner > >>From the class description, I cannot find out why defineClass does not > work. The three ways are: > > // (1) does not work > File file = new File(full_path_to_file, > "domainpresentationplanner.class"); > classCode = getBytesFromFile(file); > domainClass = defineClass("domainpresentationplanner", classCode, 0, > classCode.length); > domain = (Domain) domainClass.newInstance(); // the same in all three > cases As described in <http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html> all the defineClass(...) methods are all declared protected. As such they can only be called from *within* ClassLoader and its subclasses. You would need to fiddle with defineClass(...) *only* if you write your own subclass of ClassLoader. You never call defineClass(...) from outside, but instead let it be called from inside the ClassLoader base class. > > // (2) works > domainClass = loadClass("domainpresentationplanner"); > domain = (Domain) domainClass.newInstance(); As described in <http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String)> ClassLoader's method loadClass(String) is called by the JVM to load classes. Therefore I think it is not meant to be used by applications (at least not by "normal" applications). Don't ask me why it is public, though. private would have been enough, because the JVM is able to call even private methods. > > // (3) works > domainClass = Class.forName("domainpresentationplanner"); > domain = (Domain) domainClass.newInstance(); Class.forName(String) and Class.forName(String,boolean,ClassLoader) is the most high-level method of your 3 ways, and therefore the most usual way to load classes. I would recommend to restrict your application to use only Class.forName, and try to avoid explicit calls to ClassLoader methods as much as you can. BTW: Class.forName(...) and newInstance() only work if the class is public and has a public no-argument constructor. > > > getBytesFromFile is taken from > http://javaalmanac.com/egs/java.io/File2ByteArray.html > > I should mention that domainpresentationplanner.java, the java file > whose compilation produced domainpresentation.class, is comprised of > several classes, one of them being JSHOP2_Method0. All resulting class > files are in the same directory as domainpresentation.class. > > Why does version (1) not work but (2) and (3) do? -- "Thomas:Fritsch$ops:de".replace(':','.').replace(' $','@') |
|
|
|
#4 |
|
Posts: n/a
|
> As described in
> <http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html#lo...)> > ClassLoader's method loadClass(String) is called by the JVM to load > classes. I should have mentioned that I did try the three ways in a subclass of ClassLoader. |
|
|
|
#5 |
|
Posts: n/a
|
On 6 Dec 2005 08:30:56 -0800, "Ulrich Scholz" <> wrote,
quoted or indirectly quoted someone who said : > domainClass = defineClass("domainpresentationplanner", classCode, 0, >classCode.length); I gather this code is hidden inside your own classloader? But you hard coded a name? I think we need to see more of your program to figure out what you did. -- Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching. |
|
|
|
#6 |
|
Posts: n/a
|
Ulrich Scholz <> wrote:
> > As described in > > <http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html#lo...)> > > ClassLoader's method loadClass(String) is called by the JVM to load > > classes. > > I should have mentioned that I did try the three ways in a subclass of > ClassLoader. Okay. The difference, then, is that Class.forName and ClassLoader.loadClass both use the normal class loading scheme. ClassLoader.defineClass skips that normal scheme and defines the class directly in this class loader. The normal scheme is to do the following: 1. First, ask the parent class loader for the class. 2. If the parent doesn't have it, load it in this class loader. 3. If this class loader can't find it, throw an exception. So if you're seeing the behavior you describe, it must be because Class.forName and ClassLoader.loadClass are actually finding the class in the parent. However, defineClass doesn't look in the parent first, so it loads the class in the current class loader. So why does this cause IllegalAccessError? You're attempting to access a package-access class from its package. However, when your class domainpresentationplanner uses that other class, the other class *is* loaded by the default scheme described above, and ends up loaded from the parent class loader. So you end up with domainpresentationplanner loaded from one class loader, and JSHOP2_Method0 loaded from a different class loader. Even though their packages have the same name, a package can belong to one and only one class loader... so they are actually different packages. The package-access level doesn't permit this, and the error is thrown. Confused? You should be. Basically, you should never call defineClass in a class loader except in the following circumstances: A. You may call defineClass from findClass, if you are writing a class loader that respects the delegation model. B. You may call defineClass from loadClass(String,boolean) if you are writing a class loader that does *not* respect the delegation model. However, you *must* use delegation consistently throughout your class loader. Pick A or B, not both (and implement B consistently, if that's your choice). -- www.designacourse.com The Easiest Way To Train Anyone... Anywhere. Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation |
|
|
|
#7 |
|
Posts: n/a
|
Roedy Green schreef:
> On 6 Dec 2005 08:30:56 -0800, "Ulrich Scholz" <> wrote, > quoted or indirectly quoted someone who said : > > >>domainClass = defineClass("domainpresentationplanner", classCode, 0, >>classCode.length); > > I gather this code is hidden inside your own classloader? But you > hard coded a name? I think we need to see more of your program to > figure out what you did. And how about starting class names with an uppercase letter? H. -- Hendrik Maryns ================== www.lieverleven.be http://aouw.org |
|
|
|
#8 |
|
Posts: n/a
|
Ulrich Scholz wrote:
> // (1) does not work > File file = new File(full_path_to_file, > "domainpresentationplanner.class"); > classCode = getBytesFromFile(file); > domainClass = defineClass("domainpresentationplanner", classCode, 0, > classCode.length); > domain = (Domain) domainClass.newInstance(); // the same in all three > cases [This is only emphasing a point made in the other replies] You should not use defineClass() to create class objects /except/ as one private step in the overall process of loading a class (as controlled by the JVM). Application code should never create a class object that way, it should always use one of the forms of Class.forName(). -- chris |
|
|
|
#9 |
|
Posts: n/a
|
> Application code should never create a class object [with defineClass()],
OK, I agree > it should always use one of the forms of Class.forName(). Here, I have my doubts. From my current (in the last two day constantly growing) knowledge about the class loading mechanism of Java (or, at least what I believe to know about it), it follows that I have to create new subclasses of ClassLoader because I dynamically create and use a class file of the same name. For each new version I need a different ClassLoader. As I need to write a sublcass of MyClassLoader ClassLoader anyway, where's the difference of using Class.forName/3 with an instance of MyClassLoader and of using MyClassLoader.loadClass/2 directly? Uli |
|
|
|
#10 |
|
Posts: n/a
|
Ulrich Scholz wrote:
> As I need to write a sublcass of MyClassLoader ClassLoader anyway, > where's the difference of using Class.forName/3 with an instance of > MyClassLoader and of using MyClassLoader.loadClass/2 directly? Calling loadClass() directly side-steps some of the stuff that the JVM does with the new class after it has been loaded and before it is returned by Class.forName(). E.g. try getting the class object for MyClass[]; Class.forName("[LMyClass;", true, aClassloader) works (or whatever the exact String should be -- I can't remember off-hand) but trying the same thing via a classloader does not, since it is the JVM that "manages" array classes. There /may/ be other processing that it side-steps too, I don't know for sure, and the specs are not clear. -- chris |
|