What is the difference between Class.forName and ClassLoader.defineClass

U

Ulrich Scholz

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
 
O

oulan bator

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
 
T

Thomas Fritsch

Ulrich said:
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

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.
 
R

Roedy Green

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.
 
C

Chris Smith

Ulrich Scholz said:
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
 
H

Hendrik Maryns

Roedy Green schreef:
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
 
C

Chris Uppal

Ulrich said:
// (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
 
U

Ulrich Scholz

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
 
C

Chris Uppal

Ulrich said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top