2007年11月9日金曜日

テスト申し込み手順

  1. バウチャーチケットの購入
    Sun のチケット購入ページからバウチャーチケットを購入します。
    バウチャーチケットの購入ルートは他にもあるようなので検索してみてください。
  2. PROMETRIC のホームページから申し込みます。
現時点で Sun の資格はバウチャーチケットでしか申し込めないのでバウチャーチケットを手に入れます。PROMETRIC で ID を取得して試験の日時、会場を指定します。

EDIT(2010年): 2010年にOracleがSunを買収しました。それに伴い、今後はSJC-PよりもOJC-Pという名前が浸透していくと考えられます。

2007年10月15日月曜日

変数

プリミティブ変数
型 ビット数
byte 8
short 16
int 32
long 64
float 32
double 64

プリミティブ変数のデフォルト値は 0 (0.0) です。


10進数
int cnt = 7;
int cnt = 7,2 (カンマを使っているのでコンパイルエラー)


8進数
先頭に 0 をつける
int seven = 07; (10進数の7)
int eight = 010; (10進数の8)
int nine = 011; (10進数の9)


16進数
先頭に 0x をつける
10進数の 1~15 までは0 1 2 3 4 5 6 7 8 9 a b c d e fで表す。
アルファベットは大文字小文字の区別なし。


サフィックス
double d = 1.2d;
float f = 1.2f;
long l = 100000l;

boolean 型と char 型

boolean 型

true か false を格納
デフォルト値は false

if 文の条件になる
(例)
if (1 < 10) {};


char 型

1つの文字、Unicode、16ビット符号なし整数(65535以下の正の整数)が使用できる。
(例)
char name = '円';
char name = '\u004F';

デフォルト値は \u0000

String 型

String 型はダブルクウォーテーションで囲む。
String 型は他のクラスと違いダブルクウォーテーションで囲む方法でインスタンスを生成できる。
その後同じ名前をダブルクウォーテーションで囲んで違う変数名で宣言した場合、すでにプールに納められている同じ文字を格納する String 型インスタンスが使われます。これは他のクラスにはない特殊な例です。

<サンプル>
String name1 = "Albert";
String name2 = new String("Albert");
String name3 = "Albert";

System.out.println(name1 == name2);
System.out.println(name1 == name3);
System.out.println(name1 == name3);

<出力結果>
false
true
false

キャスト

拡張変換
byte → short → int → long → float → double のような変換はキャスト不要

int intValue = 27;
double doubleValue = intValue

縮小変換
double doubleValue = 27.0;
int intValue = (int)doubleValue;

1次元配列

配列の宣言
要素数を必ず宣言して初期化する。
各要素は0で初期化される。
int[] myArray = new int[3];

宣言と代入
int[] myArray = {1, 2, 3};

配列はオブジェクトとして扱われる
Integer[] myArray = new Integer[3];
配列はオブジェクトとして扱われているが、Interger クラスのコンストラクタは呼び出されず、各要素は null で初期化される。

<サンプル>
Integer[] myArray = new Integer[3];
System.out.println(myArray[2]);

<出力結果>
null

2次元配列

2次元配列は1次元配列の各要素にさらに1次元配列が収められています。

2次元配列の初期化
int[][] myArray = new int[3][];
2次元配列の最初の要素数を指定して初期化します。
各要素は null で初期化されます。

値の代入
int[][] myArray = new int[2][2];
myArray[0][0] = 1;
...
myArray[1][1] = 4;

int[][] myArray = new int[2][];
myArray[0] = {1, 2};
myArray[1] = {3, 4};

int[][] myArray = {{1, 2}, {3, 4}};

オブジェクト参照変数

オブジェクト参照変数
<サンプル>
public class Sample {
   public static void main(String[] args) {
      Sub objSub = new Sub();
      Super objSup = objSub;
   }
}
class Super { }
class Sub extends Super { }

objSub, objSup はオブジェクト参照変数です。メモリー上にあるインスタンスを参照する変数(オブジェクト)です。

インスタンス変数

クラスのメソッドの外側で宣言した変数はインスタンス変数としてオブジェクト参照変数からアクセスできます。
インスタンス変数は自動で初期化されます。
<サンプル>
public class Sample {
   public static void main(String[] args) {
      Sub objSub = new Sub();
      Super objSup = objSub;
      System.out.println(objSub.x);
      System.out.println(objSup.x);
   }
}
class Super {int x;}
class Sub extends Super {int x;}

クラス変数

インスタンス変数と同じ位置(メソッドの外側)で static 修飾子で宣言されている静的変数はクラス変数といいます。
クラス変数へのアクセス
クラス変数は自動で初期化されます。

<サンプル>
public class Sample {
public static void main(String[] args) {
System.out.println(Suub.x);
System.out.println(Super.x);
}
}
class Super {static int x;}
class Sub extends Super {static int x;}

ローカル変数

自動で初期化されないので初期値を代入する必要があります。
初期化しないまま使用するとコンパイルエラーになります。

int x = 0;
System.out.println(x);

配列の初期化の場合は要素数を決める必要があります。値の代入がなくてもコンパイルエラーにはなりません。

Integer[] myArray = new Integer[3];
System.out.println(myArray[2]);

Java 予約語

変数名、メソッド名などに Java 予約語は使用できません。

Java 予約語一覧
abstract
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while
assert

アクセス修飾子

public : このメンバーにはどこからでもアクセスできる。
protected : このメンバーにはパッケージ外のサブクラスからでもアクセスできる。
修飾子なし(デフォルト) : このメンバーには同一パッケージ内からアクセスできる。
private : 同一クラス内からのみアクセスできる。

ローカル変数にアクセス修飾子は使用できない。

クラスの構造

<サンプル>
package sample;
import java.util.*;

public class MyClass {
    int x;
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.func1();
    }
    void func1() {
        System.out.println(func2());
    }
    int func2() {
        return 100;
    }
}

パッケージの宣言
クラスはパッケージで管理することが推奨されています。

インポート宣言
Java のクラスライブラリで java.lang パッケージ以外のライブラリはインポートする必要があります。
java.util.*; とすると java.util パッケージのすべてのインターフェース、クラスのインポートを宣言します。
java.util.Date; とすると java.util.Date クラスのインポートを宣言します。

main メソッド
Java プログラムは main メソッドからスレッドがスタートします。
main メソッドは
public static void main(String[] args)
というシグネチャですが args の部分は任意です。

main メソッドなどの静的メソッド(static メソッド)から自クラスの動的メソッドを呼び出す場合、自クラスのインスタンス(参照変数)から呼び出す必要があります。
(例)
MyClass obj = new MyClass();
obj.func1();

自クラスの動的メソッドから動的メソッドを呼び出す場合は、メソッド名を記述するだけです。
(例)
void func1() {
    func2();
}

final クラスと abstract クラス

final で修飾されたクラス
・継承(拡張)することができないのでサブクラスを作成できない。
・したがってどのメソッドもオーバーライドできない。

String クラスなどは final クラスです。

abstract クラス(抽象クラス)

abstract で修飾されたクラス
・インスタンス化できない。
・継承されることを目的とする
・abstract メソッド(抽象メソッド)を含むクラスは必ず abstract クラス(抽象クラス)である必要がある。
・抽象クラスに非抽象メソッドを含めることはできる。

<サンプル>
public class Sample {
    public static void main(String[] args) {
        Super obj = new Sub();
        obj.func();
    }
}
abstract class Super {
    abstract void func();
}
class Sub extends Super {
    @Override
    void func() {}
}

インターフェース

インターフェースの目的は抽象クラスと似ていますがすべてのメソッドが abstract です。
インターフェースのメソッドは中身を実装しません。したがってインターフェースの役割は、それを実装するクラスの方向性を定め柔軟な設計を可能にすると言うことができます。

インターフェースの変数
public static final でなければならい。省略は可能でコンパイラが補足する。
インターフェースの変数は「定数」として利用できる。

インターフェースのメソッド
public abstract でなければならない。省略は可能でコンパイラが補足する。

インターフェースの定義
interface で定義します。

インターフェースの実装
implements で定義します。
複数のインターフェースを実装できます。


インターフェースは他のインターフェースを継承できます。
インターフェースAを継承するインターフェースαを実装しているサブクラスはインターフェースαのメソッドだけでなくインターフェースAのメソッドも実装しなければならない。


<サンプル>

interface Human {
    void speak();
}
class Japanese implements Human{
    public void speak() {
        System.out.println("こんにちは");
    }
}
class English implements Human {
    public void speak() {
        System.out.println("Hello");
    }
}
public class Sample {
    public static void main(String[] args) {
        Human person1 = new Japanese();
        Human person2 = new English();
        person1.speak();
        person2.speak();
    }
}

<出力結果>
こんにちは
Hello


Human クラスを実装(implements)するサブクラスは必ず speak メソッドを実装しなければなりせん。
Human インターフェースを実装することで Japanese クラス、English クラスは同じ特性を持つことになります。


person1.speak();
person2.speak();
ではそれぞれ Japanese クラスと English クラスでオーバーライドされた speak メソッドが呼び出されています。
これは「多様性」といいます。

演算子

代入演算子

x = y     (x に y を代入)
x += y      (x = x + y)
x -= y     (x = x - y)
x *= y     (x = x * y)
x /= y     (x = x / y)
x %= y     (x = x % y)

インクリメント・デクリメント

x++     (x = x + 1)     x を利用してからインクリメント
x--     (x = x - 1)     x を利用してからデクリメント
++x     (x = x + 1)     x をインクリメントしてから利用
--x     (x = x - 1)     x をデクリメントしてから利用


論理演算子

x & y     x と y が true の場合 true を返す
x y     x か y のとちらかが true の場合 true を返す
x ^ y     排他的論理輪
!x     x でない場合 true を返す
x && y     x と y が true の場合 true を返す
            左辺が false の場合右辺を評価しない
x y     x と yの どちらかが true の場合 true を返す
            左辺が true の場合右辺を評価しない

文字列連結演算子

String s1 = "Hello " + "world"; (Hello world)
String s2 = "Hello " + 100; (Hello 100)
どちらか片方が String の場合 + 演算子は文字列連結演算子となる。


ビットシフト演算子

x >> n     x を2の n 乗で割る(右に n ビットシフト)
x << n     x に2の n 乗を掛ける(左に n ビットシフト) 
x >>> n     右に n ビットシフトして最初の符号を 0 にする

<サンプル>
public class Sample {
    public static void main(String[] args) {
        int a = 0;
        int b = 0;
        for (int i = 0; i < 5; i++) {
            if ((++a < 2) && (++b < 2)) {
                a++;
            }
        }
        System.out.println("a = " + a + " b = " + b);
    }
}
<出力結果>
a = 6 b = 3

if 文

if else

if (x == y) {
System.out.println("Hello");
} else {
System.out.println("こんにちは");
}

if 文条件判断結果は boolean 型でなければならない。

if (x = y) { } なら条件判断は行わず代入が行われる。(代入演算子)

if - else if - else

else if を使うと条件分岐を増やすことができる。

if (x == y) {
...
} else if (x == z) {
...
} else {
...
}

if 文のボディを表す { } の中に実行文が1行しかない場合は { } は省略できます。
しかし、可読性が落ちることは確かです。

if (x == y) {
a = b;
}
System.out.println("Hello");

上記と同じ
if (x == y)
a = b;
System.out.println("Hello");

switch 文

int number = 7;
switch (number) {
case 2:
    System.out.println("小吉");
    break;
case 5:
    System.out.println("中吉");
    break;
case 7:
    System.out.println("大吉");
    break;
default:
    System.out.println("吉");
}

条件判断には int 型か int 型に拡張変換できる型(byte, short, char)のみ使用できます。

break キーワードがないとすべてのケースの実行文が実行されます。

default 文の位置は最後でなくてもいいです。

while ループ

int a = 0;
while (a < 10) {
.....
}

a が 10 未満の間繰り返し処理を行う。

int a = 0;
do {
.....
} while (a < 10);

最初に実行文を1回実行してからループする。
int a = 10 からスタートしたとしても実行文は1回実行される。

for ループ

for (int i = 0; i < 10; i++) {
    ...
}

i が 10 未満の間だけ繰り返し処理を行う。
for (宣言; 条件; 処理) {
    処理
}

for の()の中の処理はボディの処理が終わってから実行される

ネスト
ループの中のループ

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < i; j++) {
        .....
    }
}

ループで使うキーワード

break     そのループを即座に抜ける。
continue     現在の反復を中断して次の反復に即座に移る。
                    ループの中でのみ使用可能。
return     戻り値か無を返してメソッドを終了する。


下記の break キーワードにより "Break " の出力が一回になります。
for ( ) の中の i++ や j++ の処理は for 文のボディでの処理が終わってから実行されます。
したがって i++ と ++i では同じ結果になります。

<サンプル1>
public class Sample {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < i; j++) {
                System.out.print("Break ");
                break;
            }
            System.out.println(i);
        }
    }
}

<出力結果>
0
Break
1
Break
2
Break
3
Break
4

ラベルつきループ
先ほどのサンプルに outer ラベルをつけて break キーワードで即座に outer を抜けてみます。
<サンプル2>
public class Sample {
    public static void main(String[] args) {
        outer:
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < i; j++) {
                System.out.println("Break ");
                break outer;
            }
            System.out.println(i);
        }
    }
}

<出力結果>
0
Break

継承(拡張)

オブジェクト指向言語 Java の特徴のひとつに「継承」があます。
継承により、サブクラス(子クラス)はスーパークラス(親クラス)の機能を使用できます。
継承は extends キーワードで宣言します。

class Super {
    int super_x;
    void sayHello() {System.out.println("Hello");}
    void func() {System.out.println("Super");}
}

class Sub extends Super {
    int sub_x;
    void func() {System.out.println("Sub");}
}
__________________

Sub 型でインスタンスの生成
Sub objSub = new Sub();

スーパークラスのメンバーへアクセス
System.out.println(objSub.super_x);
obj.sayHello();

サブクラスの変数へアクセス
System.out.println(objSub.sub_x);
objSub.func();

Super 型でインスタンスの生成
Super objSuper = new Super();

Super 型からサブクラスのメンバーにはアクセスできない。
__________________

is-a 型でインスタンス生成
継承関係にあるスーパークラスとサブクラスの機能をフルに利用するには下記のようにインスタンスを生成します。

Super obj = new Sub();

Super 型で Sub のインスタンスを生成しています。

この場合、is-a 関係が成り立ちます。

is-a 関係とはその名のとおり Sub is a Super という関係です。
__________________

多様性
Super のメソッド func() は Sub でオーバーライドされています。
obj.func() を呼び出すと Super 型の obj から Sub の func() が呼び出されます。
これを多様性といいます。
多様性はインターフェースとそれを実装するサブクラス間でも利用できます。

このような Super 型の obj から Sub 固有のメソッドを呼び出すことは出来ません。
Sub 固有のメソッドを呼び出すには Sub 型にキャストして呼び出します。

((Sub)obj).subFunc();
__________________

<サンプル>
class Super {
    void func() {
        System.out.println("Super");
    }
}
class Sub extends Super {
    @Override
    void func() {
        System.out.println("Override");
    }
    void subFunc() {
        System.out.println("Sub");
}
public class Sample {
    public static void main(String[] args) {
        Super obj1 = new Super();
        Super obj2 = new Sub();
        obj1.func();
        obj2.func();
        ((Sub)obj2).subFunc();
    }
}

<出力結果>
Super
Override
Sub

オーバーライド

スーパークラスのメソッドと同じ名前のメソッドをサブクラスで定義することをオーバーライドといいます。

class Super {
    void func() throws IOException {
        System.out.println("Super");
    }
}
class Sub extends Super {
    @Override
    void func() {
        System.out.println("Sub");
    }
}

<ルール>
・ 引数リスト、戻り値型は変更不可。
・ アクセスレベルを緩く出来るが厳しくは出来ない。
・ 新たな例外や、上位クラスの例外を throw できない。
・ 例外の数を減らしたり、下位クラスの例外を throw することは出来る。
・ final メソッドはオーバーライドできない。

オーバーロード

メソッドは同じ名前でも引数の型、数、並び順のいずれかを変えることで違うメソッドとして記述できます。
これをオーバーロードといいます。
if 文などの条件分岐を減らすことも出来ます。

<ルール>
・引数の型、数、並び順のいずれかを変える
・戻り値型を変更できる
・修飾子を変更できる
・例外を追加できる

有効なオーバーロードの例

void func() {}
void func(int x){}
void func(int x, int y) {}
void func(double x, double y) {}
String func(double x, int y) {return null;}
private void func(File file) throws IOException {}

コンストラクタ

コンストラクタによってオブジェクトが初期化されます。
コンストラクタ内に記述した処理はインスタンス化時に実行されます。

class Sample {
    /* コンストラクタ */
    Sample() { }
}

デフォルトコンストラクタ
デフォルトコンストラクタは引数なしのコンストラクタです。
コンストラクタを記述しない場合、コンパイラはデフォルトコンストラクタを自動生成します。
引数ありののコンストラクタを記述した場合はコンパイラはデフォルトコンストラクタを自動生成しません。

super() と this()

基本的に、コンストラクタには super() が自動生成されます。
super() はスーパークラスのコンストラクタを呼び出します。
this() はそのクラスの引数なしのコンストラクタを呼び出します。
最終的にはすべてのクラスのスーパークラスである Object クラスのコンストラクタが呼び出されます。
super()this() に引数をつけることは可能です。
super()this() の併用は不可です。
super()this() はコンストラクタ内の処理の最初に記述する。

Sample sample = new Sample(1) 
下記の例では引数ありのコンストラクタ内で super() が自動生成されます。
class Sample {
    Sample() { }
    Sample(int x) { }     // super()が自動生成される
}

下記の例では引数なしコンストラクタ内に super() が自動生成されます。
class Sample {
    Sample() { }     // super() が自動生成される
    Sample(int x) {this();}
}

コンストラクタチェーン

コンストラクタは呼び出された順番でスタートし、その逆の順序で初期化処理を行いコンストラクタを完了します。

<サンプル>
class Sample {
    Sample() {
        // super() が自動生成され Object クラスのコンストラクタが呼び出される
        System.out.println("引数なしコンストラクタ");
    }
    Sample(int x) {
        this();     // 引数なしコンストラクタが呼び出される
        System.out.println("引数ありコンストラクタ");
    }
    public static void main(String[] args) {
        Sample sp = new Sample(1);
    }
}

<出力結果>
引数なしコンストラクタ
引数ありコンストラクタ

例外

例外 = フローの中のエラー


例外の種類

チェック例外     : Exception クラスを継承する例外(RuntimeException を除く)
Runtime 例外     : RuntimeException とそれを継承する例外
Error     : Error クラスとそれを継承する例外

Runtime 例外と Error は非チェック例外となります。
_______________

try - catch - finally ブロック

チェック例外は try - catch - finally ブロックで処理します。
finally ブロックは任意ですが、例外が発生してもしなくても必ず実行されます。

public class Sample {
    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.func();
    }

    /* dangerousMethod を try - catch - finally で処理 */
    void func() {
        try {
            dangerousMethod();
        } catch (Exception e) {
            System.out.println("例外が発生しました");
        } finally {
            System.out.println("必ず実行される処理");
        }
    }

    /* 例外が発生する可能性を throws キーワードで宣言 */
    void dangerousMethod() throws Exception {
        throw new Exception();
    }
}
_______________

例外には「処理か宣言」の原則があります。
例外の発生する可能性のあるメソッドで「処理か宣言」のどちらかを行います。

<処理>
    try - catch ブロックで囲む。
<宣言>
    throws キーワードで発生する可能性のある例外を宣言する。
_______________

throw キーワード

throw キーワードで例外を投げることができます。
try - catch ブロックで例外をキャッチしてさらにそのメソッドの呼び出し元に例外を投げることもできます。

    void func() throws Exception {
        try {
            dangerousMethod();
        } catch (Exception e) {
            throw e;
        }
    }

こうすると呼び出し元でさらに try - catch する必要があります。
_______________

複数の例外を処理する際の注意

複数の例外を処理する際は、例外クラスの継承関係の下位にある例外から順番に処理します。
継承関係の上位にある例外を try - catch ブロックで先に処理するとコンパイルエラーになります。

    void func() {
        try {
            dangerousMethod();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    void dangerousMethod() throws IOException {
        throw new IOException();
    }

Exception を先に catch ブロックに書くと次の IOException のキャッチブロックは到達不能になり、コンパイル不可なります。
_______________

RuntimeException

RuntimeException は非チェック例外ですが、Exception クラスを継承しているので try - catch ブロックで処理することができます。
RuntimeException は throws キーワードで RuntimeException の発生の可能性を宣言する必要はありません。

    void func1() throws IOException {
        throw new IOException();
    }
    void func2() {
        throw new RuntimeException();
    }
_______________

finally ブロック

finally ブロックは例外が発生してもしなくても処理を実行するので
ストリームの切断など確実にしておきたい処理を記述しておくとよいでしょう。
しかし、下記のコードはコンパイルできません

public class Sample {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("C:/test.txt"));
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            br.close();
        }
    }
}

br.close(); の部分を try - catch ブロックで囲む必要があります。

アサーション

アサーションは仮説を検証するメカニズムです。(JDK 1.4 より導入)
アサーションは有効無効を切り替えられます。
不必要な try - catch ブロックや if 文などはプログラムの完成後に取り除くのは手間がかかりますが、
アサーションは有効無効の切り替えだけですみます。

JDK 1.4 ではデフォルトでアサーションが無効になっているのでアサーションのコードは実行時に無視されます。

アサーションを有効にするjava -ea
java -enableassertions

アサーションを無効にする
java -da
java -disableassertions

sample.Sample クラスのアサーションを有効にする
java ea:sample.Sample

sample パッケージとそのすべてのサブパッケージのアサーションを有効にする
java ea:sample

全体のアサーションを有効にし、システムクラスのアサーションのみを無効にする
java -ea -dsa

全体のアサーションを有効にし、sample パッケージのみを無効にする
java -ea -da:sample


アサーションの使い方
int x = 2 + 2;
assert (x == 4);     // true でないとき AssertionError を投げる

文字列の扱い

String クラス

オブジェクト参照変数の参照先に注意

String a = "Albert";
String b = "Bob";
System.out.println(a);
a = b;
System.out.println(b);

出力結果
Albert
Bob

String オブジェクトは不変
<サンプル>
String a = "Albert";
a.concat(" Einstein");
System.out.println(a);

<出力結果>
Albert
a が参照する String オブジェクトを変更することはできない。

<サンプル>
String a = "Albert";
a = a.concat(" Einstein");
System.out.println(a);

a に新しいオブジェクトを再代入している。
<出力結果>
Albert Einstein
_________

StringBuilder クラス

StringBuilder クラスは StringBuffer クラスと同じような機能を持ち StringBuffer クラスよりほとんどの面で高速に動作します。
文字列の連結などの操作は String クラスは使わず StringBuilder を使うほうが高速で有効。
<サンプル>
StringBuilder a = new StringBuilder("Albert");
a.append(" Einstein");
System.out.println(a);

<出力結果>
Albert Einstein
a が参照する StringBuilder オブジェクトを変更できる。

Math クラス

java.lang.Math クラスは final なので継承することはできません。
java.lang.Math クラスのコンストラクタは private なので Math クラスのインスタンスを作成することはできません。
<サンプル>
double number = Math.random() * 10;
System.out.println(number);
System.out.println("abs = " + Math.abs(number));
System.out.println("ceil = " + Math.ceil(number));
System.out.println("floor = " + Math.floor(number));
System.out.println("round = " + Math.round(number));

出力結果は毎回異なります。

1.8282950594054825
abs = 1.8282950594054825
ceil = 2.0
floor = 1.0
round = 2

ラッパークラス

プリミティブ(基本データ型変数)をオブジェクトとして扱うためにラッパークラスがあります。

プリミティブ         ラッパークラス
byte                     Byte
short                   Short
int                         Integer
long                     Long
float                     Float
double                Double
boolean              Boolean
char                     Character

ラッパークラスのオブジェクト生成の際のコンストラクタ引数はそれぞれのプリミティブ型が使用できます。
Character クラス以外は String 型引数も使用できます。

<オブジェクトの生成>
(例)
・ Double d = new Double(5.0);
・ Double d = Double.valueOf(5.0);
・ Double d = Double.valueOf("5.0");
・ Double d = Double.valueOf("5.0f");
・ Short s = d.shortValue(d);

<プリミティブの抽出>
(例)
・ double value = Double.parseDouble("5.0");
・ double value = Double.parseDouble("5.0d");

<オブジェクトの比較>
オブジェクトの比較は == 演算子や equals メソッドで行います。
equals メソッドは Object クラスで定義されていて、String クラス、ラッパークラスでオーバーライドされています。
Object クラスの equals メソッドと String クラスやラッパークラスの equals メソッドは振る舞いが違います。
Object クラスの equals メソッドは比較する2つのオブジェクトがまったく同じオブジェクトの場合 true を返します。
String クラス、ラッパークラスの equals メソッドは2つのオブジェクトがまったく同じでなくても意味的に等しい場合 true を返します。

<サンプル>
Object o1 = new Object();
Object o2 = new Object();
System.out.println("o1 == o2 : " + (o1 == o2));
System.out.println("o1.equals(o2) : " + o1.equals(o2));

Integer i1 = new Integer(5);
Integer i2 = new Integer(5);
System.out.println("i1 == i2 : " + (i1 == i2));
System.out.println("i1.equals(i2) : " + i1.equals(i2));

<出力結果>
o1 == o2 : false
o1.equals(o2) : false
i1 == i2 : false
i1.equals(i2) : true

equals メソッドと hashCode メソッドのオーバーライド

自作クラスの2つのオブジェクトを equals メソッドを使って意味的に等しい場合 true を返すようにするには
自作クラスで equals メソッドをオーバーライドする必要があります。
Object クラスの equals メソッドはまったく同じオブジェクトの場合 true を返します。

equals メソッドのオーバーライドの例
public class Sample {
    public static void main(String[] args) {
        Tiger t1 = new Tiger(1);
        Tiger t2 = new Tiger(1);
        System.out.println(t1.equals(t2));
    }
}
class Tiger {
    private final int t;
    Tiger(int t) {
        this.t = t;
    }
    public int tigerValue() {
        return t;
    }
    public boolean equals(Object obj) {
        if (obj instanceof Tiger) {
            return ((Tiger)obj).tigerValue() == t;
        } else {
            return false;
        }
    }
}

<出力結果>
true
_______

hashCode メソッドのオーバーライド

自作クラスに equals メソッドをオーバーライドしたら hashCode メソッドもオーバーライドしましょう。
コレクションの HashMap などの Hash がつくクラスはオブジェクトを格納する際にハッシュコードを使用します。
これは検索速度の向上が意図されています。
Object クラスの hashCode メソッドは一意のハッシュコードを返します。
したがって、自作クラスでオーバーライドする際は
意味的に等しい場合は同じハッシュコードを返すようにするのが適切です。

適切かつ最も簡単な方法を Tiger クラスに実装すると

public int hashCode() {
    return t;
}

となります。

自作クラスに hashCode メソッドをオーバーライドしないとコレクションで安全に使用できません。

コレクションフレームワーク

コレクションフレームワークには List、Set、Map インターフェースを実装するものがあります。
詳細は Javadoc を参照ください。

コレクションの要素にプリミティブ型は使用できません。

<サンプル>

List list = new ArrayList();
for (int i = 0; i < 5; i++) {
    list.add(new Integer(i));
}
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

<出力結果>
0
1
2
3
4

ガーベージコレクション

Java のガーベージコレクションは
  • どこからも参照されないオブジェクト
  • 到達不能のオブジェクト

を自動で削除します。


<オブジェクトに null を代入>オブジェクト参照変数に null を代入するとそのオブジェクト参照変数はもはやどこも参照していないので削除対象になります。

<参照先の変更>
参照先を変更すると、元の参照先は削除対象になります。

Integer a = new Integer(1);
Integer b = new Integer(2);
a = b;

この時点で new Integer(1) というインスタンスはどこからも参照されなくなり削除対象となります。

<参照の孤立>
A⇔B
A = null
B = null

A と B の相互参照が孤立しガーベージコレクトの対象になる。

<ガーベージコレクションは即座に実行できるか?>
答えは No です。
カーベージコレクトを要求することはできます。
実行のタイミングは JVM が決定します。


<カーベージコレクションの要求>
・ System.gc();
・ Runtime.getRuntime().gc();

<finalize メソッド>
finalize メソッドはオブジェクトがガーベージコレクションによって削除される直前に呼び出されます。
finalize メソッドはオブジェクトに対し一度だけ呼び出される。
finailze メソッドに重要な処理を記述することは推奨されません。

インナークラス

内部クラスと呼ばれるものにはにはインナークラスのほかに静的ネストクラス、ローカルインナークラス、無名クラスがある。


インナークラス
インナークラスはアウタークラスのインスタンスから呼び出される。

<サンプル>
public class Sample {
    public static void main(String[] arvs) {
        Outer.I1 objInner = new Outer().new I1();
        objInner.func();
        Outer objOuter = new Outer();
        objOuter.func();
    }
}
class Outer {
    class Inner1 {
        void func(){
            System.out.println("Innerclass1");
        }
    }
    class Inner2 {
            void func(){
            System.out.println("Innerclass2");
        }
    }
    void func() {
        new Inner2().func();
    }
}

<出力結果>
Innerclass1
Innerclass2

静的ネストクラス

静的ネストクラスは静的メンバーと同じように扱える

<サンプル>
public class Sample {
    public static void main(String[] arvs) {
        Outer.Inner objInner = new Outer.Inner();
        objInner.func();
    }
}
class Outer {
    static class Inner {
        void func(){
            System.out.println("Innerclass");
        }
    }
}

<出力結果>
Innerclass

ローカルインナークラス

ローカルインナークラスはメソッドの中に定義する。
ローカールインナークラスのインスタンスをメソッド内に生成する。
アクセス修飾子は使用できない。final もしくは abstract を使用できる。
ローカルインナークラスから final 以外のローカル変数にアクセスできない。
ローカルインナークラスからアウタークラスのメンバーにアクセスできる。

<サンプル>
public class Sample {
    public static void main(String[] arvs) {
        final int x = 1;
        class Local {
            void func() {
                System.out.println(x);
            }
        }
        Local local = new Local();
        local.func();
    }
}

<出力結果>
1

無名インナークラス

名前のないインナークラス。
匿名クラスとも言う。
メソッド内にも記述できる

<サンプル>
public class Sample {
    public static void main(String[] arvs) {
        Super objSuper = new Super() {
            @Override
            void func() {
                System.out.println("無名インナークラス");
            }
        };
        objSuper.func();

    }
}
class Super {
    void func() {
        System.out.println("Superclass");
    }
}

<出力結果>
無名インナークラス

マルチスレッド

スレッドは Java プログラムの動作単位です。
マルチスレッドプログラムにより複数のスレッドを同時に(1CPU環境ではスレッドスケジューリングにより交互に)動かすことができます。

<マルチスレッドの実装方法>

java.lang.Thread クラスを拡張する

java.lang.Runnable インターフェースを実装する


<スレッドのライフサイクル>
生成 → 実行可能 → 実行 → 終了

<スレッドを実行不可状態にするメソッド>
sleep
wait

<スレッドを実行不可状態から実行可能状態にするメソッド>
notify
notifyAll

<実行中のスレッドを実行可能状態へ戻すメソッド>
yield

<スレッドの同期化>
マルチスレッドで動作するプログラムは必要に応じて各スレッドを同期化する必要があります。

Thread クラス

基本プログラム
Thread クラスを継承して run メソッドをオーバーライドする

class Sample1{
    public static void main(String[] args){
        Thread t1 = new Thread(){
            public void run(){
                for(int i=0;i<100;i++){
                    System.out.println("Hello");
                }
            }
        };
        Thread t2 = new Thread(){
            public void run(){
                for(int i=0;i<100;i++){
                    System.out.println("World");
                }
            }
        };
        t1.start();
        t2.start();
    }
}

出力結果は Hello と World が時々入れ替わって出力される。

Runnable インターフェース

基本プログラム
Runnable インターフェースを実装するクラスのオブジェクトを引数に Thread オブジェクトを生成する。
run メソッドをオーバーライドする。

class RA implements Runnable {
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println("スレッド "
                + Thread.currentThread().getName());
        }
    }
}
class RB implements Runnable {
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("スレッド "
                + Thread.currentThread().getName());
        }
    }
}
public class Sample {
    public static void main(String[] args) {
        RA ra = new RA();
        RB rb = new RB();
        Thread ta = new Thread(ra);
        Thread tb = new Thread(rb);
        ta.setName("A");// スレッド名をセット
        tb.setName("B");// スレッド名をセット
        ta.start();
        tb.start();
    }
}

出力結果は時々交互になる

join メソッド

マルチスレッドにおいて1つのスレッドが終了してからもうひとつのスレッドを動作させるには join メソッドを使います。
ta と tb というスレッドがある場合
ta.join() で ta が完了するまで他のスレッドは実行を待機する。


class RA implements Runnable {
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println("スレッド"
                + Thread.currentThread().getName());
        }
    }
}
class RB implements Runnable {
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("スレッド"
                + Thread.currentThread().getName());
        }
    }
}
public class ThreadRunnable1 {
    public static void main(String[] args) {
        RA ra = new RA();
        RB rb = new RB();
        Thread ta = new Thread(ra);
        Thread tb = new Thread(rb);
        ta.setName("A");    // スレッド名をセット
        tb.setName("B");    // スレッド名をセット
        ta.start();
        try {
            ta.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tb.start();
    }
}

出力結果は
スレッドA がすべての回数出力されてから スレッドB の出力が始まる。

スレッドの優先順位

スレッドには優先順位があります。
スレッドの優先順位を設定するには setPriority メソッドを使います。
優先順位は 1 ~ 10 の値を設定できます。
待機中のスレッドがすべて優先順位どおりに実行されるという保障はありません。
優先順位に依存したプログラムを書くことはよくありません。

スレッドの同期化

複数のスレッドからアクセスされるオブジェクトのメソッドは同期化処理をするべきです。
同期化は synchronized 修飾子を使用します。

下記のプログラムは 2人が同じ講座からお金を引き出すプログラムです。
残高が 10 未満なら引き出しをしません。
残高が 10 以上なら 10 ずつ引き出しをします。
したがって残高がマイナスになることはありません。

<サンプル>
public class Sample {
public static void main(String[] arvs) {
Account account = new Account(100);
Family taro = new Family(account, "太郎");
Family jiro = new Family(account, "次郎");
taro.start();
jiro.start();
}
}
class Family extends Thread {
private Account account;
private String name;
Family (Account account, String name) {
this.account = account;
this.name = name;
}
public void run() {
for (int i = 0; i < savings =" savings;">= amount) {
return true;
} else {
return false;
}
}
/* 引き出し処理の結果を返す */
public synchronized boolean withdraw(int amount) {
if (withdrawable(amount)) {
// 動きをわかりやすくするために1秒スリープ
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
savings -= amount;
return true;
} else {
return false;
}
}
public String toString() {
return String.valueOf(savings);
}
}

<出力結果>
太郎は引き出せました。残高 90
太郎は引き出せました。残高 80
太郎は引き出せました。残高 70
太郎は引き出せました。残高 60
太郎は引き出せました。残高 50
太郎は引き出せました。残高 40
次郎は引き出せました。残高 30
次郎は引き出せました。残高 20
次郎は引き出せました。残高 10
次郎は引き出せました。残高 0
次郎は引き出せませんでした。残高 0
次郎は引き出せませんでした。残高 0

同期化しない場合の出力結果
(例)
次郎は引き出せました。残高 80
太郎は引き出せました。残高 80
太郎は引き出せました。残高 60
次郎は引き出せました。残高 60
次郎は引き出せました。残高 50
太郎は引き出せました。残高 50
次郎は引き出せました。残高 30
太郎は引き出せました。残高 30
太郎は引き出せました。残高 10
次郎は引き出せました。残高 10
次郎は引き出せました。残高 -10
太郎は引き出せました。残高 -10

このように同期化をしないと残高がマイナスになるというおかしなことが起こります。


synchronized ブロックによる同期化
上記のコードは共通の Account オブジェクトをロックする形でも同期化できます。

public void run() {
synchronized (account) {
for (int i = 0; i < 6; i++) {
if (account.withdraw(10)) {
System.out.println(
name + "は引き出せました。残高 "
+ account.toString());
} else {
System.out.println(
name + "は引き出せませんでした。残高 "
+ account.toString());
}
}
}
}

この場合は実行中のスレッド以外は Account オブジェクトのすべてのメソッドへのアクセスができません。

同期化 = ロックの取得

ということができます。

wait、notify、notifyAll メソッド

wait、notify、notifyAll メソッドの使い方

これらのメソッドは同期化されたブロック内で使用できます。
これらのメソッドはロックを取得しているときに動作します。

wait
ロックを開放する

notify/notifyAll
同期コードを抜けた後ロックを開放する

メソッドの詳細は Javadoc を参照ください。

ジェネリクス

JDK 5.0 より導入された記述法

コレクションの記述

/* 従来型 */
List list1 = new ArrayList();
/* ジェネリクス */
List list2 = new ArrayList();
List list3 = new ArrayList();

list1.add("Conventional");
list2.add("Generics");
list3.add(new Integer(7));

String conventional = (String) list1.get(0);
String generics = list2.get(0);
/* java.lang.ClassCastException 発生 */
Integer i = (Integer)list1.get(0);
/* コンパイルエラー */
Integer i2 = (Integer)list2.get(0);


ClassCastException
継承関係のない型にキャストすると実行時エラーとなる。

ジェネリクスの使用によってこのようなキャストミスはコンパイル時に発見できる。

アノテーション

アノテーションとはプログラムに記述できる注釈のようなものです。
JDK 5.0 より導入された記述方法

@Override
適切にオーバーライドされていないとコンパイルエラー。

class SP {
    void func(){
        System.out.println("Super");
    }
}
class SU extends SP {
    @Override
    int func() {
        System.out.println("Sub");
    }
}

___________

@Deprecated
非推奨メソッドを定義し警告を出す。

public static void main(String[] args) {
    func();
    }
    @Deprecated
    static void func() { }
}
___________

@SuppressWarnings("deprecation")
非推奨警告の抑制

@SuppressWarnings("fallthrough")
case に fall-through する可能性を警告

@SuppressWarnings("finally")
finally 節が正常に完了してない警告を抑制

@SuppressWarnings("serial")
serialVersionUID 未定義警告の抑制

2つ以上の警告をまとめて抑制
@SuppressWarnings({"deprecation", "finally"})

2007年9月27日木曜日

SJCP とは

SJCP とは Sun Java Certified Programmer のことです。
詳しくは http://suned.sun.co.jp/JPN/certification/progdetails.html を参照ください。
Java プログラマーとしての基本的な知識を問われます。
管理人は数年前にこの資格を取りました。


EDIT(2010年): OracleがSunを買収したため、今後はSJCPよりもOJCPという名前のほうが浸透していくと考えられます。