package scratch;
import java.io.*;
public class Main extends ClassLoader
{
public Main()
{
super(Main.class.getClassLoader());
}
public static void main(String[] args)
throws Throwable
{
Main m=new Main();
File f=null;
f = new File("/usr/home/plos/obj/scratch/Main.class");
int size = (int)f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
Class klass=m.defineClass("scratch.Main",buff,
0,buff.length);
Main m2=(Main) klass.newInstance();
}
}
I get:
Exception in thread "main" java.lang.ClassCastException: scratch.Main
cannot be cast to scratch.Main
at scratch.Main.main(Main.java:29)
Notes:
This is distilled from attempting to write a class loader and every
attempt ends with the same error (even cutting and pasting web and
junit examples of class loaders and calling them instead of mine).
Side point: why initialize f to null? You throw away the value immediately,
so why use it at all?
> f = new File("/usr/home/plos/obj/scratch/Main.class");
> int size = (int)f.length();
Why not "long size = ..."?
> byte buff[] = new byte[size];
> FileInputStream fis = new FileInputStream(f);
> DataInputStream dis = new DataInputStream(fis);
> dis.readFully(buff);
> dis.close();
>
> Class klass=m.defineClass("scratch.Main",buff,
> 0,buff.length);
>
> Main m2=(Main) klass.newInstance();
> }
> }
>
> I get:
> Exception in thread "main" java.lang.ClassCastException: scratch.Main
> cannot be cast to scratch.Main
> at scratch.Main.main(Main.java:29)
Looks to me like the two classes are loaded from different class loaders. The
cast uses Main from the "normal" class loader to cast a Main from the custom
class loader, thus not compatible.
I'm very inexperienced with ClassLoader idioms so I am far from sure of this
analysis.
Maybe you'd be better off using a ClassLoader that isn't the class already
loaded in order to run the ClassLoader that it itself is.
--
Lew
The "same" class loaded by a different class loader is considered a
different class. Think of it as an invisible runtime namespace.
<customClassLoader>.scratch.Main cannot be cast to
<systemClassLoader>.scratch.Main.
Remember, if you're trying to dynamically load/unload/reload a class,
you can't have any reference to that class in the system class loader.
You also can't expect non-reflective communication between your
dynamically loaded class and the rest of your application, unless the
class implements/extends an interface/class which is loaded in the
system class loader. And then you can only rely on the methods in your
base class/interface.
HTH.
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Fun exceptions happen when working with class loaders.
The short answer:
Multiple class loaders don't mix. One can change the default class
loader through some java -D option; I don't remember what it is however.
The long answer:
A class is uniquely determined by its instantiating class loader and its
fully-qualified name.
This is an example of a working loadClass function. I did not write this
myself, and it is based off of a very old version of Java (discussion of
custom class loaders is almost nil), so I do not know which parts are
irrelevant.
public final Class<?> loadClass(String name, boolean resolve) throws
ClassNotFoundException {
// Don't bother loading Java classes
if (name.startsWith("java.") || name.startsWith("javax."))
return super.loadClass(name, resolve);
// Did we already define it?
Class result = findLoadedClass(name);
if (result == null) {
String internalName = name.replace('.', '/') + ".class";
InputStream is = getParent().getResourceAsStream(internalName);
if (is != null) {
try {
// Load the raw bytes.
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b;
while ((b=is.read())!=-1)
bos.write((byte)b);
byte[] bytes = bos.toByteArray();
result = defineClass(name, bytes, 0, bytes.length);
} catch (IOException ex) {
throw new ClassNotFoundException(name +
" could not be loaded", ex);
}
}
}
if (result != null) {
if (resolve) {
resolveClass(result);
}
} else {
// Let someone else find it.
result = super.loadClass(name, resolve);
}
return result;
}
Even here, it is still incompatible with system-default loaded classes.
--
Beware of bugs in the above code; I have only proved it correct, not
tried it. -- Donald E. Knuth
>Main m2=(Main) klass.newInstance();
It would help people help you if you told them the exception occurred
in this line and there was no other stack trace.
see:
http://mindprod.com/jgloss/runerrormessages.html#CLASSCASTEXCEPTION
Hint: class X is not the same as class X if they were loaded with
different class loaders.
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com