Announce

PukiWiki contents have been moved into SONOTS Plugin (20070703)

java

Javaのメモ

Table of Contents

基礎

instanceof/getClass

Exception e = new ArrayIndexOutOfBoundsException();
boolean b = e instanceof ArrayIndexOutOfBoundsException;
boolean b = e.getClass() == ArrayIndexOutOfBoundsException.class;
// Class c = e.getClass();

char

java で char は unicode 2バイト。'A' でも

double = float

double = 3.4f;

3.400000485895

みたいないゴミがつく。Cなら0で埋める。仕様。

int a = 2;
double = (double)2;

これはゴミがつかない。

パッケージ

サブクラスは、スーパークラスと同じパッケージに含めなくてもよい。

デフォルトパッケージ内(無名パッケージ内)のクラスは import できない(1.4からコンパイルエラーがでるようになった)。 外部のパッケージから import したい場合は、そのクラスをなんらかのパッケージに入れるようにするしかない。

クラス修飾子

public class 異なるパッケージからでも使用できる
無指定 class 同じパッケージからのみ使用できる

public なクラスの場合、デフォルトコンストラクタは public に。 無指定なクラスの場合、デフォルトコンストラクタは 無指定に。

public クラスは1ファイル1つ。ファイル名とクラス名が同じ。

いくらメソッドが public でも、class 自体が無指定なら、異なるパッケージからは使用できない。

class Hoge {
    public Hoge() {}
}

にしてもやっぱり無理。

メソッド/フィールド修飾子

private    自分自身からのみ
無指定      同じパッケージ内アクセス可。
protected 自分自身とサブクラス、また同じパッケージ内のクラスからアクセス可。サブサブも可。サブクラスのみではない。無指定よりもアクセス許可度が広い。
public       どこからでも。

無指定は friendly とも呼ばれるが、friendly 修飾子があるわけではない。

c++ の protected は自分自身とサブクラスからのみアクセス可。 Java には C++の予約語protectedに相当するものは無い。(private protectedがあったが仕様から除外された)

private 変数についての考察

class ArrayListClass {
    private ArrayList list; // prohibits to substitute a new address
    public ArrayListClass() {
        list = new ArrayList();
        list.add("a");
        list.add("b");
    }
    public ArrayList get() {
        return list;
    }
}

public class Test {
    public static void main(String[] args) {
        ArrayListClass myclass = new ArrayListClass();
        ArrayList list =  myclass.get();
        System.out.println(list);
        list.remove(0); // getter さえあれば、こういう変更(set)はいくらでもできる。
        System.out.println(list);
    }
}

getter があれば、setter がなくても、private 変数の中身を、外から変更することができることになる。 ただし、setter がなければ、新しいアドレス値の代入は、できないことになる。 完全に変更を禁止にしたければ、getter で、return list.clone(); とでもしないといけなさそう。

  • 1 と書いたら int
  • 1.0 と書いたら double
  • float は 1.0f

真/偽

java では if() 内の判定は true/false のみ。C のように、0 かそれ以外ではない。

参照渡し

オブジェクトの代入はすべて参照渡し。配列も実質 Array オブジェクトなので配列渡し

int []a = new int[4];
int b[] = a;
b[0] = 1; // a[0] も変更
String a = "Hello";
String b = a;
b = "World"; // 新しいアドレスを代入する、の意であり、a が変更されるわけではない。

ちなみに String は C と違い、0 文字の文字列も可能。

配列

int []b も int []b も同じ。int [][]b と int b[][] も同じ

int []a = new int[]{1,2,3,4};
int []a = {1,2,3,4};
a[4] = 10;

はコンパイルエラーではなく、実行時エラー。Cでも実行時エラー(OSエラー)。

try/catch/finally の finally の意義

finally は常に実行される。throw されても。return されても。

try {
} catch(Exception e) {
    throw e;
} finally {
   // C
}
// 
try {
    return;
} catch(Exception e) {
} finally {
   // C
}
// D

try の中で return; しても、本当に return する前?に finally ブロックが実行される。絶対にやってくれる。exit しても(?)

Java用語

メソッドと関数は違う

オブジェクト指向において、メソッドとは

  • オブジェクトに対するメッセージ送信に対する応答

をさす。関数というものは、

  • 引数と呼ばれるデータを受け取り、定められた一連の手続きを実行して結果を返す一連の命令群

を指す。オブジェクトを利用しない一連の手続き。C++では、メソッドをメンバ関数と読んだりするけれど・・・。

AS3などでは、返り値のないものをメソッド(内部でメンバ変数を変更している)、返り値のあるものを関数(内部でメンバ変数を変更していない?)と呼ぶらしい。

ファイルIO

ファイルclose

ファイル.openの時点で確保したメモリを、ファイル.close で開放。

ファイル.close をしなくても、ガーベッジコレクションの時点で開放はしてくれるが、いつ実行されるのかわからないので、もちろん自分でやったほうが良い。

BufferredReader を close すれば、ラップしている元のファイルオブジェクトも、close してくれるはずだが(仕様では)、本当になっているのか気にしたくないので、

br.close();
if (in != null) in.close();

のように書いておこう。

FileReader in;
BufferedReader br;
try {
    in = new FileReader("file");
    br = new BufferedReader(in);
} catch (Exception e) {
} finally {
    try {
         if (br != null) br.close();
         if (in != null) in.close();
    } catch (IOException e) {
    }
}

スーパークラス/オーバーライド

Parent []p = new Parent[2];
p[0] = new Child1();
p[1] = new Child2();

Parent で定義した関数を Child1/2 でオーバーライドした場合、p[0].func(); p[1].func(); は呼べるが、Parent で定義されていない関数は、呼べない。

子クラスの関数を呼びたい場合

if ( p[0] instanceof Child1 ) {
   Child1 c = (Child1)p[0];
}

c++ でオーバーライド(ダイナミックバインディング)したい場合は、親クラスの関数に virtual 修飾子をつける。staticでないメソッドは常にダイナミックバインディングを使用しているのでJavaでは予約語virtualは存在しない。

オーバーロード

オーバーロード(多重定義)は異なる引数の型、個数でのみ可能。返り値の型は関係ない。C++ でも同様。

継承

C++ と異なり、継承 (extends) できるのは1つだけ。interface の implements は複数個できる。

interface/abstract

interface が interface を継承する場合は interface A extends B

interface のメンバ変数は public static final しているようなもの(クラス定数)

abstract は実体メソッドを定義してもいい。interface は抽象メソッドだけ。

interface も配列 A a[] = new A[10]; 可能

なぜ interface ならば多重継承(実装) を許可するのか。

C++ の時代

class CBase1
{
public:
	virtual void func();  // 何かする
};

class CBase2
{
public:
	virtual void func();  // 何かする
};

class CSub : public CBase1, public CBase2
{
};
int main()
{
	CSub* sub = new CSub();
	sub->CBase1::func();  // CBase1::func()を呼び出すように明示する
	
	delete sub;
	return 0;
}

このごちゃごちゃ加減を(利用者が気をつけなければならなくなるのを) Java では禁止したかった。 interface の場合は、中身の処理がないことが確定しているので、 どちらの親クラスを実質的に呼んでいようが関係ない(呼べない)

class test {
    public static void main(String[] args){
        new C().print();
    }
}
interface A {
    public void print();
}
interface B {
    public void print();
}
class C implements A, B {
    public void print() { System.out.println("C"); }
}

メンバ変数についても同様。せいぜいクラス定数を許可するのみ、と限定。

final

  • メソッド - オーバーライド不可
  • フィールド - 値の変更不可
  • クラス - 継承不可

コレクション

配列とArrayListの変換

配列→List

Arrays.asList(arr);

配列→ArrayList

new ArrayList(Arrays.asList(arr));

ArrayList→配列

String[] a = (String[])list.toArray(new String[0]);

引数の new String[0] は要素の型、を指定するために使われる

List

  • Vecotr - 同期化する(Threadで便利だが、ロックかける分、時間はかかる)
  • ArrayList - 同期化しない

ArrayList のほうが新しいので、めったなことがなければ ArrayList

Map

map.get("key") でキーが存在しないばあい、Exception はでず、null が返る。

  • Hashtable - null 値を保存不可。同期化しない(Thread)
  • HashMap - null 値を保存可。同期化する。

HashMap のほうが新しく、同期化サポートもなく早いので、通常 HashMap

SE5

annotation

事項環境やコンパイラに伝えるための設定情報を記述できる。(Javadoc のようなものではなく、コンパイラに伝える)

@アノテーション(属性名=属性値)
クラス/フィールド/メソッド宣言部
@SuppressWarnings
@Override

コンパイラが、本当にオーバーライドしたメソッドかどうかをチェックしてくれる

@Deprecated
@Resource(name=java:comp/env/jdbc/ssjdb)
private DataSource ds;
@Resource(name=java:comp/env/jdbc/ssjdb, type=javax.sql.DataSource)

複数アノテーションは並べればよい

@Override
@Deprecated

可変長パラメータ

void method(String[] args);
method(new String[]{"A","B","C"});

としなくていい。

void method(String... args);
method("A","B","C");

といける。

public class A {
    private static void method(String... args) {
        for (int i = 0; i < args.length; i++) {
            System.out.printf("args[%d]=%s\n", i, args[i]);
        }
    }     
    public static void main(String[] args) {
        method("a");
        method("b","c");
        method("d","e","f");
   } 
}

可変長引数にできるのは、最後の引数のみ。

拡張for文(foreach)

List<String> list = new ArrayList<String>();

list.add("すし");
list.add("カレー");
list.add("ハンバーグ");
       
for(String value : list){
    System.out.println(value);
}

列挙型(enum)

public enum SUIT { SPADE, CLUB, HEART, DIAMOND }
public enum SUIT { SPADE, CLUB, HEART, DIAMOND, }
enum SUIT { SPADE, CLUB, HEART, DIAMOND; }
SUIT.SPADE

とアクセス

SUIT suit = SUIT.SPADE;
System.out.println(suit);

は SPADE と出る。

class Hoge {
    public static final int A = 0;
    public static final int B = 1;
}

のようなもの。だから、最後の } に配列の初期化のような }; セミコロンがない。 正確には、

class Hoge {
    public static final Hoge A = new Hoge();
    public static final Hoge B = new Hoge();
}

のほうが近い(Hoge.A は Hoge 型の変数)

public enum Color {
  RED("赤"), BLUE("青"), YELLOW("黄"), BLACK("黒"), WHITE("白");
 
 // メンバ変数の定義
  private String name;


  // コンストラクタの実装
  private Color (String name) {
    this.name = name;
  }


  // メソッドのオーバーライド
  public String toString() {
    return name;
  }
}

Color.RED.toString() と呼ぶ。 enum 型は new できない。

switch では enum タイプ名. を省略できる

Color color;
...
switch (color) {
  case RED:     ...
  case BLUE:    ...
  case YELLOW:  ...
  case BLACK:   ...
  case WHITE:   ...

}

通常は外部から enum 型の定数を参照するときにはクラス名が必要ですが(たとえば、Color.RED というように)、case 文のラベルとして使用するときは特別に定数名だけで良い事になっています。

class Hoge {
    private enum A {a, b, c}
}

はあり。クラス内の private クラスになるだけ。

Generics

特定の型のみを要素とするコレクション

バージョン1.4まで

List list = new ArrayList();
list.add(new String("sample"));
System.out.println((String) list.get(0));

バージョン5.0

List<String> list = new ArrayList<String>();
list.add(new String("sample"));
System.out.println(list.get(0)); // キャストもいらない

バージョン1.4までは、コンパイル時に型のチェックが行えませんでした。これはコレクションの要素がすべてObject型として扱われるためです。 これに対しバージョン5.0では、コレクションに要素の型を指定することでその型に違反する要素を格納するようなコードをコンパイル時にチェックできるようになりました。

public List<String> func(List<String> list);

Generics で型制限しても、配列の時と同様、サブクラスの型は受け入れる。

AutoBoxing

オートボクシング

Integer itgr = 1; // new Integer(1); でなくてよい。

オートアンボクシング

int i = itgr; // itgr.intValue(); でなくてよい。

バージョン1.4まで

List list = new ArrayList();
list.add(new Integer(100));
int value = ((Integer)list.get(0)).intValue();

バージョン5.0. Generics と併用することで、new Integer() がいらなくなる。

List<Integer> list = new ArrayList<Integer>();
list.add(100);
int value = list.get(0);

コンパイラが、オートボクシング導入以前のコードに変換してから、コンパイルする。 明示的にやるよりのろい。

static import

static 変数や static メソッドのみ import する

import static java.lang.Math.PI;
PI;
// 従来
import java.lang.Math;
Math.PI;

これにより、クラス名を省略して利用可能になる。

チェック例外・非チェック例外

非チェック例外を作りたい場合は、extends RuntimeException

チェック例外を作りたい場合は、extends Exception

JDBC

import java.sql.*;

Class.forName("com.mysql.jdbc.Driver");

Connection conn = DriverManager.getConnection (
    "jdbc:mysql://localhost/[dbname]", "username", "password");

String sql = "insert into tablename values(?,?)";

PreparedStatement ps = conn.prepareStatement(sql);

int i = ps.executeUpdate(); // update,delete,insert

ResultSet rs = ps.executeQuery(); // select

ps.close();
conn.close();
conn.setAutoCommit(false); // ロックもかける?
conn.commit();
conn.rollback(); // どちらかでロック解除

トランザクション

conn.setAutoCommit(false);
conn.commit();

commit もしくは rollback の時点でロックが解除され conn.setAutoCommit(true); に戻る。 デフォルトが true か false かは、どこかで設定できるのかもしれない。

conn.setAutoCommit(false);
// いろいろ
conn.close();

commit せずに close すれば、データベースには何も反映されず、終了するだけ。

conn.setAutoCommit(true);
conn.commit();

とすると、true のときは commit できません、と SQLException

細かい話

JDBCドライバのロードの仕組み

Class.forName("com.......Driver");

Driver の実装クラスは、 static 初期化子に、自身を DriverManager に 登録する、という処理が記述されています。

static 初期化子は、そのクラスが最初にロード された時に実行されます。クラスがロードされるとは そのクラスの Class オブジェクトが生成されることで (厳密にはイコールではないですが)、 Class.forName は そのクラスの Class オブジェクトを、 クラスが未ロードならロードした上で返すメソッドです。

静的初期化メソッド

String は変更できない

767 デフォルトの名無しさん [sage] Date:2009/06/06(土) 18:24:55  ID: Be:
    Stringクラスのオブジェクトは内容の変更が出来なくて
    変更したいならStringBufferを使いなさいと書籍などで見ました。
    StringBufferだと参照はそのままで内容だけを変更できて、
    Stringは変数名は同一で別参照にして新たにオブジェクトを
    作れるのでしょうか、というのが疑問に思ったのです。
    疑問といいますか、よくわかってないですね。 

768 デフォルトの名無しさん [sage] Date:2009/06/06(土) 20:46:10  ID: Be:
    JVMはもともと文字列プールというものを持っていて、
    いわゆる文字型リテラルが指定された段階でプールにオブジェクトを作成し、そこへのポインタを返す。
    同一の文字列が既にプールに在る場合は、そこへのポインタを返す。 

jar作成

manifest.mf

Manifest-Version: 1.0
Main-Class: メインクラス
jar cfm Othello.jar manifest.mf *.class image

jar を exe にラップ

java をネイティブコードにコンパイル

  • JET (share)
  • gcj
  • EXE4J (share)?