I want to define a custom class loader to load my own class at run-time.
However, Class.newInstance
always fails even if I defined a zero-argument constructor.
The exception message is:
java.lang.IllegalAccessException: Class Hello can not access a member of class Test with modifiers "public"
Why?
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
class CustomClassLoader extends ClassLoader {
private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
public String toString() {
return CustomClassLoader.class.getName();
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
return classes.get(name);
}
byte[] classData;
try {
classData = loadClassData(name);
} catch (IOException e) {
throw new ClassNotFoundException("Class [" + name
+ "] could not be found", e);
}
Class<?> c = defineClass(name, classData, 0, classData.length);
resolveClass(c);
classes.put(name, c);
return c;
}
private byte[] loadClassData(String name) throws IOException {
BufferedInputStream in = new BufferedInputStream(
ClassLoader.getSystemResourceAsStream(name.replace(".", "/")
+ ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
in.close();
byte[] classData = out.toByteArray();
out.close();
return classData;
}
}
class Test
{
public Test()
{}
public void Hello()
{}
}
public class Hello {
public static void main(String[] args)
{
try {
CustomClassLoader loader = new CustomClassLoader();
Class<?> c = loader.findClass("Test"); // OK!
Object o = c.newInstance(); // ALWAYS FAIL!
}
catch (Exception e)
{
String s = e.getMessage();
// s is "java.lang.IllegalAccessException: Class Hello can not access"
// " a member of class Test with modifiers "public""
}
}
}
The problem is that your Test
class is declared with default (package) scope, but at runtime, your Hello
and Test
classes are in different packages.
The name (unique identifier) of a class in Java is a combination of the fully-qualified class name and the class loader that loaded it:
At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader. Each such class or interface belongs to a single run-time package. The run-time package of a class or interface is determined by the package name and defining class loader of the class or interface. (JVMS 5.3)
In this case, the Test
class is being loaded by a downstream class loader, and so its (default) package is not considered to be the same (default) package that Hello
(loaded by the system class loader) is in. Therefore, you don't have access to the constructor since the class itself is not public.
This example will work if you either make Test
a separate public top-level class or use reflection to make it available.
You need to make the class public:
public class Test { ... }
Or, get a reference to the constructor and make it accessible:
Constructor<?> constructor = c.getConstructor();
constructor.setAccessible(true);
Object o = constructor.newInstance();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With