next up previous contents
Next: 演習課題 Up: Network Previous: Datagram

ClassLoader

Javaのネットワーク機能の最後に、ネットワークを通じて新しいクラスとその インスタンスを生成できるというとても強力な機能を紹介したいと思います。 Web上のAppletは、まさに、この機能を活用したものです。

デフォールトでは、Javaの runtime system は、CLASSPATH という環境変数 で指定されたディレクトリ上にファイルとして存在するクラス・ファイルを 読み込んで、クラスをつくります。この時には、ここで紹介するクラス loader は、必要ありません。

defineClass()

ところが、ある場合には、ファイル以外から、たとえばネットワークを通じて クラスがつくられることがあります。こうした時、ネットワークを通じて送られた バイト列をクラスに変えるのに、このClassLoader クラスが用いられます。 正確にいうと、ClassLoader クラスの中の defineClass メソッドが、バイト列 から、Classクラスのインスタンスを作ります。

/**
  * Converts an array of bytes to an instance of class Class. Before the
  * Class can be used it must be resolved.
  * @param   data    the bytes that make up the Class
  * @param   offset  the start offset of the Class data
  * @param   length  the length of the Class data
  * @return          the Class object which was created from the data.
  * @exception ClassFormatError If the data does not contain a valid
  * Class.
  * @see             ClassLoader#loadClass
  * @see             ClassLoader#resolveClass
  */
protected native final Class defineClass(byte data[], int offset, int length);

ただし、こうして作られたクラスのインスタンスは、他のクラスへの参照を 名前でおこなっているだけです。オブジェクト・ファイルは出来たけれども、 そこで呼ばれているライブラリがまだリンクされていない状態だと考えて ください。

resolveClass()

こうした作られたクラスの他のクラスの参照を解決するためには、resolveClass() というメソッドを使います。このメソッドは、このクラスを作成したClassLoader クラスの中の、loadClass()というメソッドを繰り返し使って、解決されていない 参照クラスを解決しようとします。

/**
  * Resolves classes referenced by this Class. This must be done before the
  * Class can be used. Class names referenced by the resulting Class are
  * resolved by calling loadClass().
  * @param   c       the Class to be resolved
  * @see             ClassLoader#defineClass
  */
 protected native final void resolveClass(Class c);

ここでは、resolveClassが呼び出すloadClass()が、resolveClass自身が属する ClassLoaderクラスから呼ばれることに注意して下さい。 また、resolveされていないままのクラスは、使うことが出来ないことにも 注意が必要です。

findSystemClass()

resolveClass()が、繰り返しloadClass()を呼ぶことになるのですが、 最終的には、システムのクラスを利用することが必要になります。 例えば、java.lang.System や java.io.PrintStream といった基本的なシステム・ クラスが利用できないと、System.out.println( a ); といった簡単なプログラム さえ実行できなくなります。

findSystemClass()は、こうしたシステム・クラスをロードする為の メソッドです。

loadClass()

これまで見てきたメソッドは、findSystemClassを含めて、全てnative methodです。 ところが、loadClass メソッドは、そうではありません。 実は、ClassLoader というクラスは、abstract Class です。このクラスを使う為には、 このクラスを継承したクラスが、自前で、loadClass()メソッドを定義しなければ ならないのです。

次にあげるのが、簡単なloadClassの定義です。ClassLoaderクラス中の abstract method である、loadClassのプロトタイプが、(String name, boolean resolve) となっていますので、自前のloadClassのプロトタイプもそれに合わせなければなり ません。

基本的な処理は、クラスの名前をみて、システムクラスに見つかれば、そのクラスを 返し、もし、見つからなければ、ネットからロードして、リゾルブするというもの です。

    protected Class loadClass(String name, boolean resolve)
                                       throws ClassNotFoundException {
        Class cl = null ;
        try {
            return findSystemClass(name);
        } catch (Throwable e) {}
        try {
            loadNetClass(name);
            if (resolve) {
                resolveClass(cl);
            }
        } catch( Exception e){
            System.out.println(e);
        }
        return cl;
    }

実行例

いま、上のメソッドloadClassを含む、simpleLoaderというクラスを、 ClassLoaderを継承して作成したとします。その時、このclass loader の働きを見るには次のようにします。

次のようなプログラム loadTest.javaを作ります。

import java.net.*;

class loadTest {
public static void main(String args[]){
  Class sc = null ;
  Object sample = null;
  try {

     //String base="http://ews1:8080";
     String base="http://www.wakhok.ac.jp/~maruyama";

     simpleLoader loader = new simpleLoader(base);
     sc = loader.loadClass(args[0]);
 
     sample = sc.newInstance();

  } catch ( Exception e){
     e.printStackTrace();
     System.out.println("Instance: " + e ) ;
  } 
 }
}

このプログラムは、ネット上のbaseで与えられた場所から、このプログラムの 引数で与えられた名前のクラスをロードして、実行します。 「実行する」にあたるのは、次の部分です。

     sample = sc.newInstance();

これで、クラスのコンストラクタが呼ばれたと同じように、クラスの新しい インスタンスが生成されます。

例えば、先の例だと、http://www.wakhok.ac.jp/ maruyama/x4.class という ファイルが存在して、x4.javaが次のような内容の時、 次のような実行結果が得られるはずです。

import java.io.*;

public class x4 {

   public x4(String str){
      System.out.println("One argument: " + str);
   }

   public x4(){
      DataInputStream stdin = new DataInputStream(System.in);
      try {
         System.out.println("No argument" );
         System.out.print("Input : ");
         System.out.flush();
         String str = stdin.readLine();
         System.out.println("argument : " + str);
      } catch (Exception e) {}
   }

   public static void main(String argv[]){
       //new x4(argv[0]);
       new x4();
   } 
}

この時、コンストラクタのx4が、public宣言されていることに注意して下さい。 コンストラクタが、public宣言されていないと、メソッドnewInstance()は、 IllegalAccess に陥ってしまいます。

     129 ews1 maru> java loadTest x4
     No argument
     Input : maruyama
     argument : maruyama



maruyama@wakhok.ac.jp