naobe @ ウィキ

Java

最終更新:

Bot(ページ名リンク)

- view
管理者のみ編集可
言語に戻る

EJB

JavaEE5

MBean

JMX

JNDI

サーブレット

言語仕様

項目 説明
transient 修飾子。Seriariseオブジェクトの保管しないフィールドを修飾する。

クラスローダ

目的

  • 同じ名前でバージョンの異なるクラスを使用するために使う。クラスローダが異なればバージョンが異なっても良い。自分自身のクラスローダを得るにはgetClassLoader()メソッドを用いる。
  • クラスローダには親子関係があり、子クラスローダがクラスをロードするときには、親クラスローダにロードを依頼してロードできなければ、子クラスローダがロードする。
  • Class#getClassLoader()は、このクラスをロードしたクラスローダを返す
  • クラスローダによってロードされたClassはパーマネント領域に格納される。ClassLoaderがGCされるとGCから解放される。

VMのクラスローダ

ブートストラップクラスローダ

<JAVA_HOME>/lib下のJARファイルに含まれるクラスをロードする。JDK6は、<JAVA_HOME>/jre/lib下? 親はいない

拡張クラスローダ

<JAVA_HOME>/lib/ext下のJARファイルに含まれるクラスをロードする。JDK6は、<JAVA_HOME>/jre/lib/ext下?親はいない

システムクラスローダ

CLASSPATHで指定した位置からクラスをロードする。親は拡張クラスローダ。ブートストラップクラスローダは親子関係なしでも委譲されるということでは? 通常のnew Foo()ではこのクラスローダが使われる。

ブートストラップクラスローダ、拡張クラスローダ、システムクラスローダの順番でクラスをロードする。

コンテキストクラスローダ(JDK6 API Threadより)

コンテキスト ClassLoader は、クラスおよびリソースをロードするときに、このスレッドで実行中のコードが使用するためにスレッドの作成側によって提供されます。コンテキスト ClassLoader が設定されていない場合、デフォルトでは親 Thread の ClassLoader コンテキストになります。通常、親スレッドのコンテキスト ClassLoader は、アプリケーションのロードに使用されるクラスローダーに設定されます。
なので、設定しなければシステムクラスローダがコンテキストクラスローダになる。
コンテキスト・クラスローダを設定・取得するには、java.lang.Thread#setContextClassLoader,getContextClassLoaderメソッドを使う。

クラスローダ作成

JDK6 APIより
たとえば、アプリケーションはネットワーククラスローダーを作成して、サーバーからクラスファイルをダウンロードできます。コードは次のようになります。

      ClassLoader loader = new NetworkClassLoader(host, port);
      Object main = loader.loadClass("Main", true).newInstance();
             . . .
    

ネットワーククラスローダーのサブクラスは、ネットワークからクラスをロードするために findClass メソッドと loadClassData メソッドを定義しなければなりません。クラスを作成するバイトを一度ダウンロードしたら、defineClass メソッドを使ってクラスインスタンスを生成する必要があります。実装の例を次に示します。

        class NetworkClassLoader extends ClassLoader {
            String host;
            int port;

            public Class findClass(String name) {
                byte[] b = loadClassData(name);
                return defineClass(name, b, 0, b.length);
            }

            private byte[] loadClassData(String name) {
                // load the class data from the connection
                 . . .
            }
        }
    

特定のディレクトリからクラスをロードするクラスローダを作成してみる。
  • MyClassLoaderのmainメソッドで、test.MyClassLoaderTargetをロードしClassを作成する
  • MyClassLoaderはClassLoaderを継承したクラスで、loadClassメソッドをオーバライドし、c:/mydoc/tmp/を優先的にロードするディレクトリとしている。c:/mydoc/tmp/にクラスがない場合は、ClassLoaderのloadClassメソッドを実行する
  • ClassLoaderのloadClassメソッドでは、キャッシュにあれば、そのクラスを返す。なければ親クラスローダに移譲する。なければシステムクラスローダを使ってクラスをロードする
  • Class#newInstance()を実行して、インスタンスを生成する
  • MyClassLoaderTarget#execute()を実行し、MyClassLoaderTarget2をnewしてexecute()を実行する
  • 実行結果を見ると、MyClassLoaderTarget2のクラスローダは、MyClassLoaderであることがわかる。つまりクラスローダを使ってロードしたクラスから別のクラスをnewするときはそのクラスローダを使うことがわかる。クラスローダを作成すると親クラスローダはシステムクラスローダになる。
  • コンテキストクラスローダは明示的に使用しない限り、使われることはなさそう。

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader {
  private static final int BUFSIZE = 1024;
  
  private byte[] result;
  private int last = 0;
  
  @Override
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    Class<?> c = null;
    try {
      c = findClass(name);
    } catch (ClassNotFoundException e) {
      c = super.loadClass(name);
    }
    return c;
  }

  private void dbg(String string) {
    System.out.println(string);
  }

  @Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
        byte[] b = loadClassData(name);
            return defineClass(name, b, 0, b.length);
        } catch (Exception e) {
          throw new ClassNotFoundException(e.getMessage(), e);
        }
  }

  private byte[] loadClassData(String name) throws IOException {
    result = new byte[0];
    last = 0;
    FileInputStream ins = null;
    try {
      byte[] buf = new byte[BUFSIZE];
      int readSize = 0;
      name = name.replace('.','/');
      File classFile = new File("c:/mydoc/tmp/" + name + ".class");
      ins = new FileInputStream(classFile);
      while((readSize = ins.read(buf)) != -1 ) {
        moveToResult(readSize, buf);
      }
      return result;
    } finally {
      if(ins != null) {
        ins.close();
      }
    }
  }

  private void moveToResult(int readSize, byte[] buf) {
    byte[] tmp = new byte[last + readSize];
    // 結果バイト配列をtmp配列の先頭にコピー
    System.arraycopy(result, 0, tmp, 0, result.length);
    // bufをtmp配列にコピー
    System.arraycopy(buf, 0, tmp, last, readSize);
    result = tmp;
    last += readSize; 
  }

  public static void main(String[] args) {
    try {
      MyClassLoader loader = new MyClassLoader();
      System.out.println("parent:" + loader.getParent().getClass().getName());
      Class<?> clazz = loader.loadClass("test.MyClassLoaderTarget");
      System.out.println("target class loader name:" + clazz.getClassLoader().getClass().getName());
      ((Executor)clazz.newInstance()).execute();
    } catch (Exception e) {
      e.printStackTrace();
    } 
    
  }

 }

package test;

public class MyClassLoaderTarget implements Executor {

  @Override
  public void execute() {
    System.out.println("AAA");
    MyClassLoaderTarget2 target2 = new MyClassLoaderTarget2();
    target2.execute();
    System.out.println("target2 class loader name:" + target2.getClass().getClassLoader().getClass().getName());
  }

  public static void main(String[] args) {
    MyClassLoaderTarget target = new MyClassLoaderTarget();
    target.execute();
  }
}

package test;

public class MyClassLoaderTarget2 implements Executor {

  @Override
  public void execute() {
    System.out.println("BBB");
  }

  public static void main(String[] args) {
    MyClassLoaderTarget2 target = new MyClassLoaderTarget2();
    target.execute();
  }
  
}

package test;

public interface Executor {
  public void execute();
}

実行結果
parent:sun.misc.Launcher$AppClassLoader
target class loader name:test.MyClassLoader
AAA
BBB
target2 class loader name:test.MyClassLoader


ThreadLocal

スレッドに固有のオブジェクトを保管するために使う(Thread Singleton)。Servletの中で、使用例としてDBに対するConnectionをスレッド毎に保管する場合があげられる。この場合、引数にConnectionを作成すると全てのメソッドにConnectionの引数が必要になり手間がかかる。スレッドにプライベートフィールドを作成して保管できないときに使える。

JDBC

タイプ 説明 備考
type2 ベンダのライブラリと共同して、DBと通信。ベンダのクライアントアプリが必要。
type4 直接DBと通信。JDBCドライバサイズが大きい

バッチ

insert, updateが連続して行われるときにネットワークトラフィックを削減するために使う。PreparedStatementオブジェクトに対して、addBatch(), executeBatch()メソッドを使うと複数のステートメントをまとめてDBサーバ送信する。

言語コード

コード名 説明
Windows-31J ShiftJISの拡張。NEC特殊コードもサポート。

スレッドダンプ

 稼働中のスレッドを標準出力にダンプする。
 kill -3 プロセス番号
出力例 daemon以外のスレッドを見る。以下の例ではSNMP APIで停止している。

 Full thread dump Java HotSpot(TM) Server VM (11.2-b01 mixed mode):
 
 "RMI Scheduler(0)" daemon prio=10 tid=0x0955e000 nid=0x15db waiting on condition [0x30cd1000..0x30cd1fa0]
  java.lang.Thread.State: TIMED_WAITING (parking)
       at sun.misc.Unsafe.park(Native Method)
       - parking to wait for  <0x37f243d8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
       at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
       at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1963)
       at java.util.concurrent.DelayQueue.take(DelayQueue.java:164)
       at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:583)
       at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:576)
       at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
       at java.lang.Thread.run(Thread.java:619)
 
 "Thread-7" prio=10 tid=0x0954bc00 nid=0x15bf in Object.wait() [0x30eb7000..0x30eb7ea0]
  java.lang.Thread.State: TIMED_WAITING (on object monitor)
       at java.lang.Object.wait(Native Method)
       - waiting on <0xa89ec300> (a com.adventnet.snmp.snmp2.SnmpCallback)
       at com.adventnet.snmp.snmp2.SnmpCallback.sleepUntilNotified(SnmpCallback.java:166)
       - locked <0xa89ec300> (a com.adventnet.snmp.snmp2.SnmpCallback)
       at com.adventnet.snmp.snmp2.SnmpCallback.run(SnmpCallback.java:152)



アノテーション

注釈。プログラムに影響を与えない。プログラムから読める。コンパイラの動作に影響を与える。

@Override

Overrideアノテーションを付加したメソッドがオーバライドメソッドでなければコンパイルエラーになる。

@SuppressWarning

  • Xlintを指定したときに発生する警告メッセージを抑制する。

アプレット

  • jarファイルを指定するには、ARCHIVEタグを使う。CLASSPATHはきかない。

セキュリティポリシー

java.policyファイルの構文

 grant signedBy "signer_names", codeBase "URL",
 principal principal_class_name "principal_name",
 principal principal_class_name "principal_name",
       ... 
 {
   permission permission_class_name "target_name", "action",signedBy "signer_names";
   permission permission_class_name "target_name", "action",signedBy "signer_names";
     ...
 };

ここで、"signer_names"、"URL"、principal_class_name、"principal_name"、permission_class_name、"target_name"、"action"、"signer_names"は、変数。

signedBy 、codeBase、、principal は省略可能。

意味は、signer_namesで署名されたURLにあるプログラムが、permission_class_nameで規定されるtarget_nameに対して、actionすることを許可する。target_name, actionについては、permission_class_nameのAPIを参照。


 permission java.io.FilePermission "\\\\192.168.1.40\\-", "read,write";
192.168.1.40のリモートファイルに対する読み書きを許可する。

Linux firefoxへのJava Plugin インストール

 # ln -s  /usr/java/jdk1.6.0_20/jre/lib/i386/libnpjp2.so /opt/firefox/plugins 

インストールの確認

urlにabout:pluginsを設定して開く

Linuxでのplugin コントロールパネル

起動

$JAVA_HOME/bin/ControlPanel を実行

最大メモリ容量の修正

"-XX:MaxPermSize=256m"をJavaタブのアプレットパラメータに追加

その他

スタックトレースをStringに変換する

仕事の都合で、最後の行で改行を削除してタブをスペースに変換している。
 private String getStackTraceAsString(Exception e) {
   ByteArrayOutputStream ostream = new ByteArrayOutputStream(100);
   e.printStackTrace(new PrintStream(ostream));
   String sep = System.getProperty("line.separator");
   return ostream.toString().replaceAll(sep, "").replace("\t", " ");
 }


固定長レコードのファイルを出力


package test;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class SingleFille {

  private BufferedOutputStream bstream;

  
  /**
   * ファイルに書き込むデータ
   *  桁数
   *  内容
   */
  private String[][] data = 
    {
      {"aaa1", "bbb1", "ccc1"},
      {"漢字2", "bbb2", "ccc2"},
      {"aaa3", "漢字3", "ccc3"},
      {"aaa4", "bbb4", "漢字4"},
    };
  
  /** 列の桁数 */
  private int[] length = {10, 20, 30};
  
  /** ファイルオフセット */
  private int offset = 0;
  
  private static final String FILE_PATH = "singleFile.dat";
  
  
  public static void main(String[] args) {
    SingleFille sfile = new SingleFille();
    try {
      sfile.execute();
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public void execute() throws IOException  {
    File file = new File(FILE_PATH); 
    
    try {
      FileOutputStream stream = new FileOutputStream(file);

      bstream = new BufferedOutputStream(stream);
      
      for(int i = 0; i < data.length; i++) {
        String[] line = data[i];
        putFile(line);
      }
    } catch (IOException e) {
      throw e;
    } finally {
      if(bstream != null) {
        try {
          bstream.close();
        } catch (IOException e) {
          throw e;
        }
      }
    }
  }

  /**
   * @param line
   * @throws IOException
   */
  private void putFile(String[] line) throws IOException {
    for(int i = 0; i < line.length; i++) {
      //各列を桁数になるように整形
      byte[] term = createTerm(line[i], i);
      bstream.write(term);
      offset += term.length;
    }
  }

  /**
   * @param term
   * @param index
   * @return
   * @throws UnsupportedEncodingException
   */
  private byte[] createTerm(String term, int index) throws UnsupportedEncodingException {
    //項目を格納するバイト
    byte[] outterm = new byte[length[index]];
    //項目をShift_JISのバイトに変換
    byte[] interm = term.getBytes("SJIS");
    for(int i = 0; i < outterm.length; i++) {
      if( i < interm.length) {
        outterm[i] = interm[i];
      } else {
        //残りはスペース
        outterm[i] = 0x20;
      }
    }
    return outterm;
  }
  
}
ウィキ募集バナー