|
Objektorientiertes Programmieren mit C++ und JAVA
|
|  
|
28.4 Klassen über das Netz
|  
|
Klassen werden mit Hilfe eines Class-Loaders in die JAVA-Umgebung
geladen. ClassLoader ist eine abstrakte Klasse, die die folgende
Methode nicht implementiert:
- Class loadClass(String name, boolean resolve);
Diese wird im Interpreter oder
Browser dadurch konkretisiert, daß im lokalen Filesystem
(entsprechend CLASSPATH) nach den passenden .class-Dateien
gesucht wird.
Man kann nun von ClassLoader eigene Ableitungen bilden, die die
Methode loadClass() so konkretisieren, daß die
.class-Dateien über das Netz bezogen werden.
Die eigene Klasse braucht nur die rohen Daten in ein byte-Array
zu laden. Die schon konkreten Methoden von ClassLoader übernehmen
den Rest. Sie interpretieren die Daten und erstellen ein Class-Objekt,
über das man dann Objekte der Klasse erzeugen kann.
Damit Objekte erzeugt werden können, müssen alle offenen Referenzen
in der Klasse aufgelöst werden, d.h. es müssen alle die Klassen
geladen werden, die innerhalb der neuen Klasse verwendet werden. Dafür
gibt es in ClassLoader die Methode resolveClass().
loadClass() erhält als zweiten Parameter ein Flag, das diese
Auflösung erzwingen soll.
Beispiel: Wir schreiben einen eigenen Loader namens
MyClassLoader, der im Konstruktor einen URL erhält und danach von
der angegebenen Adresse Klassen lädt.
Folgendes Testprogramm versucht dann, die Klasse Chatterer zu laden und
gibt deren Methoden aus. Wir verwenden hier
getDeclaredMethods(),
um nicht auch alle von Oberklassen geerbten Methoden aufgelistet zu bekommen:
- public class loadtest
{
public static void main(String args[]) throws ClassNotFoundException
{
ClassLoader cl=new MyClassLoader(
"http://www.math.uni-wuppertal.de/~axel/java/Chat");
Class c=cl.loadClass("Chatterer");
Method[] m=c.getDeclaredMethods();
for (int i=0;i<m.length;++i)
System.out.println(m[i]);
}
}
Wenn unser Lader die Klasse nicht über den angegebenen URL erreichen
kann, landet er durch eine IOException
(z.B. eine MalformedURLException oder FileNotFoundException)
in einem catch-Block.
Dort versucht er, die Klasse normal über das System zu beziehen
(Class.forName(String)).
Das ist deswegen nötig, weil beim Auflösen der Referenzen
mit Sicherheit Systemklassen wie Object nachgeladen werden.
Dazu wird der gleiche ClassLoader wie für die ursprüngliche
Klasse verwendet, also unser eigener.
Wenn auch dieser Versuch fehlschlägt, wird die von forName()
ausgelöste ClassNotFoundException nach außen weitergegeben.
Außerdem vermeiden wir es, Klassen mehrfach zu laden. Von uns einmal
geladene Klassen werden in eine Hashtabelle einsortiert, in der bei jeder
Anfrage zunächst nachgeschaut wird.
- class MyClassLoader extends ClassLoader
{
String host;
Hashtable theClasses=new Hashtable();
public MyClassLoader(String host)
{
if (!host.endsWith("/")) host+="/";
this.host=host;
}
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class c=(Class)theClasses.get(name);
if (c==null)
{
String s=host+name;
if (!s.endsWith(".class")) s+=".class";
try
{
InputStream is=new URL(s).openStream();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int l;
byte[] data=new byte[1024];
while ((l=is.read(data))>0) baos.write(data,0,l);
data=baos.toByteArray();
c=defineClass(name,data,0,data.length);
theClasses.put(name,c);
}
catch (Exception e)
{
return Class.forName(name);
}
}
if (resolve) resolveClass(c);
return c;
}
}