Copy

MIDORIAck ソフトウェア材料の倉庫

Contents ♪

Javaの基本文法+コレクション
Javaの一通りの基本文法と、文字列の扱いに必須のStringBufferクラス、ファイルストリームの読み書きクラス、代表的なコレクションクラスの使い方を簡単なコーディング例と共に並べております。
以前に本家サイト(tenkaiken.com)で掲載していたコンテンツですが、ここに移設しました。
2025.04.26
Hello World
class Hello01 {
    public static void main(String[] args) {
        System.out.println("Hello Java");
    }
}
2025.04.26
コメント
class Comment01 {
    public static void main(String[] args) {
        System.out.println("Hello Java");
        // 行末までコメント 
        /*
            改行を含めたコメント区間
        */
        /**
            javadocでHTMLドキュメント化できるコメント区間
        */
    }
}
2025.04.26
文字型のリテラル
class Liter01 {
    public static void main(String[] args) {
        System.out.println('$');
        System.out.println('あ');
    }
}
文字型のリテラルはシングルクォーテーション「'」で1文字を囲む。
文字型は1文字のUnicodeの数値データである。
2025.04.26
数値のリテラル
class Liter02 {
    public static void main(String[] args) {
        System.out.println(123);            // 整数 
        System.out.println(012);            // 8進数
        System.out.println(0x2af);          // 16進数
        System.out.println(0766);           // 8進数(ファイルモード等)
        System.out.println(0b0110010);      // 2進数
        System.out.println(.234);           // 浮動小数点数(0.は省略可)
        System.out.println(1.2589e-2);      // 10のマイナス二乗(=0.012589)
        System.out.println(1.2589e3);       // 10の三乗(=1258.9)
        System.out.println(3.25f);          // float型を明示
        System.out.println(3.25D);          // double型を明示
        System.out.println(1000000L);       // long型を明示
        System.out.println(1_000_000);      // アンダーバーで区切り
    }
}
先頭が0以外の数字の並びは10進数の整数になる。
先頭が0の数値は8進数になる。12は10進数の12ではないことに注意。
 先頭が0xの数値は16進数である。16進数の10〜15はa〜f、またはA〜Fを使う。
0bから続く0と1の数値は2進数である。
小数点を含む数値は浮動小数点である。「0.」の場合の0は省略できる。浮動小数点は、仮数部と乗数を分けるe±nで表現できる。
数値リテラルにサフィックスをつけることで、型を指定できる。
サフィックス
float f(F)
double d(D)
long l(L)
桁の区切りを見やすくするために数字の間に任意に「_」を入れることができる。
2025.04.26
文字列のリテラル
class Liter03 {
    public static void main(String[] args) {
        System.out.println("ABCD123");
        System.out.println("東京都");
    }
}
文字列リテラルはダブルクォーテーション「"」で囲む。
文字列はUnicodeで格納される。
2025.04.26
エスケープシーケンス
class Liter04 {
    public static void main(String[] args) {
        System.out.print("ABC\tDEF");
        System.out.print("GHI\rZ\"\n");
        System.out.print("\100");
        System.out.print("\u30a2");
    }
}
改行やタブ文字のような制御文字を、「\(バックスラッシュ)」に続く1文字との組み合わせによるエスケープシーケンスで表すことができる。
Z"C DEFGHI
@ア
エスケープ文字  
\b バックスペース
\t タブ
\n 改行
\r 復帰(行頭へ移動)
\' '(シングルクォーテーション)
\" "(ダブルクォーテーション)
\\ \(エスケープ文字そのもの)
\035 3桁の0-7の数値からなる8進数が示すコードの文字
\u1340 u+4桁の16進数からなるUnicode文字
2025.04.26
ブール値(真理値)
class Liter05 {
    public static void main(String[] args) {
        boolean t = true;
        boolean f = false;
        Object a = null;
    }
}
ブール値(真理値)の真・偽はそれぞれ「true」「false」である。
2025.04.26
null
class Liter06 {
    public static void main(String[] args) {
        Object a = null;
        String s= null;
    }
}
「null」はオブジェクトの参照先がないことを示すリテラルである。
オブジェクトの変数にnullを代入することで、参照するインスタンスがないことを明示する。nullを代入した変数のオブジェクトはガベージコレクトされる。
2025.04.26
変数宣言・代入
class Var01 {
    public static void main(String[] args) {
        int a;                  // 変数宣言
        a = 100;                // 変数への代入
        System.out.println(a);  // 100

        int b = 200;            // 変数宣言と同時に初期化
        System.out.println(b);  // 200

        int x, y = 300;         // 同じ型をカンマで区切って複数指定
        x = b;                  // 変数から変数への代入
        System.out.println(x);  // 200
        System.out.println(y);  // 300

        //int z = zz;           // 変数宣言より前の照前は不可
        int zz = 10;
    }
}
使用する変数は、データ型と変数名を宣言する必要がある。
データ型 変数名 = 初期値;
変数宣言と同時に「=」の右辺により初期化できる。
メソッド内では、変数を参照する(使う)前に変数が宣言されている必要がある。
同じ型の変数をカンマで区切って複数宣言できる。
変数名(を含む全てのシンボル名)の長さに制限はない。
変数名には英字、数字、アンダーバー「_」、ドル「$」が使用できる。大文字小文字は区別される。「$」は主に中間処理に使用されるもので通常は使わない。
変数には「=」の右辺より、変数と同じデータ型のリテラル、変数、演算結果、メソッドの戻り値を代入する。
変数名 = リテラル;
変数名 = 変数名;
変数名 = 演算結果;
変数名 = メソッドの戻り値;
数値型同士では、値の精度が落ちない場合に限り、暗黙の型変換により異なる型へ代入できる。
データ型は次の種類がある。
基本データ型 char 文字型(Unicodeの1文字だが整数型の一種)
  byte 8ビット(1バイト)整数型
  short 16ビット整数型
  int 32ビット整数型(OSなどの環境に依存せず32ビット)
  long 64ビット整数型
  float 浮動小数点型(32ビット)
  double 浮動小数点型(64ビット)
  boolean   ブール型(真理値型)
オブジェクト型   String 文字列型(文字列クラス)
  クラス名 クラス定義したオブジェクト型
32ビット範囲内の整数リテラルはint型、浮動小数点のリテラルはdouble型となる。
class Var02 {
    public static void main(String[] args) {
        byte bn = 0x44; 
        short si = 32767;
        int i = 100;
        long li = 10000000;
        float f = 1.2F;
        double d = 0.3;
        boolean bl = true;
        char c = 'あ';
        String s = "埼玉県";
        System.out.println(bn);
        System.out.println(si);
        System.out.println(i);
        System.out.println(li);
        System.out.println(f);
        System.out.println(d);
        System.out.println(bl);
        System.out.println(c);
        System.out.println(s);
    }
}
文字列型Stringは、変数へ直接文字列リテラルを代入(初期化)できる。
String s = "ABCDEFG";
変数宣言時に初期化しない場合は、以下に示すデータ型のデフォルト値で暗黙に初期化される。
宣言データ型 デフォルト値
数値 0、0.0
文字型 \u0000
真理値型(boolean) false
文字列、配列、オブジェクトの参照   null
2025.04.26
定数 final
class Var03 {
    final static int CNST1 = 100;
    public static void main(String[] args) {
        final int CNST2 = 200;
        System.out.println(CNST1);      // 100
        // CNST2 = 300;                     Error
        System.out.println(CNST2);      // 200
    }
}
変数宣言をfinalで修飾することにより、変数を「定数」にする。
finalによる定数の変数宣言では、必ず初期化(初期値を代入)しなければならない。宣言以降はその変数に再代入できなくなる(変更操作はエラーになる)。
定数の変数名はすべて大文字にするという一般的な傾向がある(規則ではない)。
2025.04.26
変数の型推論 var
class Var04 {
    public static void main(String[] args) {
        var s = "ABCあいう";
        System.out.println(s);  // ABCあいう
        // s = 100;             バリアント型ではない
        s = "DEFG";             // これはOK
        System.out.println(s);  // DEFG
    }
}
変数宣言の初期化する右辺のデータ型が明らかな場合に限り、データ型をvarとして宣言し、varが示す具体的データ型のコンパイラの推論により決定させることができる。
var宣言は必ず初期化を伴う。
次のように続けて書くことはできない。
var a = 10, b = 20;     // bは不可
varは決してバリアント型ではないので、varで宣言した変数に初期化時以外のデータ型での再代入はできない。
2025.04.26
算術演算・論理演算
class Calc01 {
    public static void main(String[] args) {
        System.out.println(3/2);        // 1 --> 整数の割り算は整数の商
        System.out.println(3.0/2);      // 1.5 --> 浮動小数点として
        System.out.println(7%4);        // 3 モジュロ(剰余)
        System.out.println(5*2);        // 10
        System.out.println(-3+1);       // -2 マイナスの単行演算(+の単行演算も可)

        byte si = 0b0011;
        System.out.println(si & 0b1010);    // 2  = 00000010    AND
        System.out.println(si | 0b1010);    // 11 = 00001011    OR
        System.out.println(si ^ 0b1010);    // 9  = 00001001    XOR
        System.out.println(~si);            // -4 = 11111100    NOT
        System.out.println(true | false);   // true
    }
}
数値型データは演算子により算術演算、論理演算を行う。
演算子   演算
+ - 加算・減算(及び各単項演算)
* / 乗算・除算(整数の除算は整数の商を求める)
% 剰余(モジュロ)
& | 論理積(AND)・論理和(OR)
^ 排他的論理和(XOR)
~ 否定(NOT)
2025.04.26
インクリメント・デクリメント
class Calc02 {
    public static void main(String[] args) {
        int a = 5;
        a++;
        System.out.println(a);      // 6

        int b = --a;
        System.out.println(b);      // 5
        b = a--;
        System.out.println(b);      // 5    aをbに代入してからaを-1するのでbは5のまま
        System.out.println(a);      // 4    aは-1されている
    }
}
「x++」は整数変数xを+1加算し「x--」は-1減算する。
「++」と「--」は変数の前後どちらにも付けることができるが、次のように式の評価順番が異なる。
a=b++; a=b;
b=b+1;
a=++b; b=b+1;
a=b;
2025.04.26
シフト演算
class Calc03 {
    public static void main(String[] args) {
        int a = 0x00000001;
        System.out.println(a << 2);
            // 4  =0x4 = 0000 0100

        a = 0x00000020;
        System.out.println(a << 2);
            // 128  =0x80 = 1000 0000

        a = 0x80000000;
        System.out.println(a >> 1);
            // -1073741824 =0xc0000000 = 1100 0000 0000 0000 0000 0000 0000 0000

        a = 0x80000000;
        System.out.println(a >>> 1);
            // 1073741824 =0xc0000000 = 0100 0000 0000 0000 0000 0000 0000 0000
    }
}
シフト演算子は数値を左右へビットシフトさせる。「<<」は左シフトで「>>」は右シフトである。
byteやshort型をシフト演算するとき、暗黙に32ビットのint型に変換してから行う。
「>>」のときの最上位ビット(MSB)は符号ビットが保存される。
「>>>」では、最上位の符号ビットの有無を無視してMSBに0が入る。
2025.04.26
代入演算
class Calc04 {
    public static void main(String[] args) {
        int a = 10;
        a -= 3;
        a *=2;
        System.out.println(a);      // 14
    }
}
変数の演算とその変数自身への代入は代入演算子でまとめることができる。
例えば「a=a+n」は「a+=n」のようにできる。
  += -= *= /= %=
代入演算子の種類 &= ^= |=    
  <<= >>= >>>=    
2025.04.26
( )による演算の優先
class Calc05 {
    public static void main(String[] args) {
        int a = 10 + 20 * 2;
        System.out.println(a);      // 50
        a = (10 + 20) * 2;
        System.out.println(a);      // 60
    }
}
数学式と同様に、()で囲んだ式の評価が優先される。
式中の演算子の優先順位が同じ場合は、左から順に評価される。
2025.04.26
比較(関係)演算子
class Calc06 {
    public static void main(String[] args) {
        System.out.println(5 == 2); // false
        System.out.println(5 > 2);  // true
        System.out.println(5 >= 5); // true
        System.out.println(5 != 3); // true

        String s1 = new String("ABC");
        String s2 = new String("ABC");
        String s3 = s1;
        System.out.println(s1 == s2);   // false
        System.out.println(s1 == s3);   // true
        System.out.println(s1.equals(s2));  // true

        String sa = "DEF";
        String sb = "DEF";
        System.out.println(sa == sb);   // true
    }
}
比較演算(関係演算)は、数値を比較した結果を「ブール値」で返す。
オブジェクト型の比較は、両方のインスタンスが同一であれば結果をtrueとする。
比較演算子  
== 一致
!= 不一致
< > 大小比較
<= >= 大小比較か一致
文字列はStringクラスのオブジェクトなので、文字列の内容が同じでもインスタンスが異なれば一致にならない。そのため、文字列の内容が同じでも不一致になる場合がある。
文字列の内容の一致を調べるならば、「==」ではなくStringクラスのequals()メソッドで比較すべきである。
2025.04.26
文字列の連結演算
class Calc07 {
    public static void main(String[] args) {
        System.out.println("ABC" + "DEF");          // ABCDEF
        System.out.println("ABC" + 123 + "DEF");    // ABC123DEF
        String s = "GHI";
        s += "JKL";
        System.out.println(s);  // GHIJKL
    }
}
文字列は加算「+」演算で連結する。文字列のリテラルと文字列型の変数間で連結できる。
文字列と数値の加算では、数値が暗黙に文字列に変換され文字列として連結される。
2025.04.26
数値の型変換
class Cast01 {
    public static void main(String[] args) {
        int n;
        // n = 0.3;             // 精度が下がる方への暗黙の変換はエラーになる
        n = (int)2.7;           // 明示的な型キャスト
        System.out.println(n);  // 2  小数点以下が切り捨てられる

        double d;
        d = 100;                // 高い精度への暗黙の型変換
        System.out.println(d);  // 100.0
        System.out.println(d * 3);  // 300.0 式の項の中で最も高精度な型に暗黙に変換される

        d = 5 / 2;              // 整数の除算は浮動小数点への暗黙の型変換はされない
        System.out.println(d);  // 2.0
        d = (double)5/(double)2;
        System.out.println(d);  // 2.5
    }
}
変数の代入やメソッドの引数渡し時などで起こる数値型の型変換は、型の精度がより高い方への変換は暗黙に行われる。
精度が下がる側への変換は明示的に「(型)」によりキャストする必要がある。
(型名)数値
boolean型は、0や1のような数値型には変換できない。
2025.04.26
文字列を数値に変換
class Cast02 {
    public static void main(String[] args) {
        int i = Integer.parseInt("100");
        System.out.println(i);
        long l = Long.parseLong("10000000000");
        System.out.println(l);
        double d = Double.parseDouble("2.4");
        System.out.println(d);
        int ih = Integer.parseInt("ab", 16);
        System.out.println(ih);                 // 171
        int io = Integer.parseInt("064", 8);
        System.out.println(io);                 // 52
    }
}
数値型のラッパークラスのparse〜()メソッドにより、数値を表現した文字列を数値に変換できる。
parse〜()メソッドの第二引数にオプションで基数(進数)を指定できる。文字列は指定された基数に従って解釈される。
数値に変換できない文字列を指定した場合、NumberFormatException例外がスローされる。
class Cast03 {
    public static void main(String[] args) {
        int i = Integer.valueOf("200").intValue();
        System.out.println(i);
        int ih = Integer.valueOf("ab", 16).intValue();
        System.out.println(ih);
    }
}
parse〜()メソッドを使う以外に、数値型のラッパークラスのvalueOf()メソッドにより文字列を数値に変換し、intValue()、doubleValue()メソッド等でint型やdouble型に変換する方法がある。
2025.04.26
文字列に変換
class Cast04 {
    public static void main(String[] args) {
        String si = Integer.toString(100);
        System.out.println(si);
        si = Integer.toString(171, 16);
        System.out.println(si);         // ab
        String sf = Double.toString(1.23);
        System.out.println(sf);
    }
}
Objectクラスを継承するサブクラスがオーバライドしているtoString()メソッドにより、そのオブジェクトを適切な文字列に変換できる。
どのような文字列に変換されるかは、各クラスのtoString()メソッドの実装による。数値型の各ラッパークラスにはそれぞれにtoString()メソッドが存在する。
class Cast05 {
    public static void main(String[] args) {
        String si = String.valueOf(100);
        System.out.println(si);
        String sf = String.valueOf(1.23);
        System.out.println(sf);
    }
}
StringクラスのvalueOf()メソッドにより、int、doubleなどの数値型を文字列に変換できる。
2025.04.26
配列の宣言・配列要素の参照
class Array01 {
    public static void main(String[] args) {
        int[] ar;
        ar = new int[3];
        ar[0] = 10;
        ar[1] = 20;
        ar[2] = 30;
        System.out.println(ar[1]);  // 20
        int[] b = new int[3];
    }
}
変数宣言のデータ型に[]を加えることで、そのデータ型の配列を宣言できる。
配列宣言により配列を参照するための変数を用意する。このとき配列サイズは指定しない。
newにより、配列サイズの指定に従ったデータ領域を確保した配列データが生成される。生成した配列は、配列として宣言した変数に代入できるようになる。
データ型[]  配列変数;
配列変数 = new データ型[サイズ];

データ型[] 配列変数 = new データ型[サイズ];
配列の要素は配列変数を[]の中に整数の添字(インデックス)を指定して参照する。
添字には、整数リテラルに限らず整数変数や式を指定できる。
先頭の添字は0である。長さ5で生成した配列ならば、添字の末尾は4になる。
配列変数[添字]
配列変数宣言の[]の位置は、C言語的「int ar[] 」のような書式が許容されている。
2025.04.26
配列の初期化
class Array02 {
    public static void main(String[] args) {
        int[] c = new int[]{11, 22, 33, 44};
        System.out.println(c[1]);       // 22
        int[] d = {100, 200, 300};
        System.out.println(d[2]);       // 300
    }
}
newによる配列の生成に続けて{〜}の中にカンマで区切って初期値を並べることで、配列要素の生成と同時に任意の値で初期化できる。
その場合は配列要素数は指定する必要はない。配列の長さは初期化データの数で決定するからである。
初期値を指定しない場合は、各要素はデータ型のデフォルト初期値で初期化される。
new データ型[]{初期値, 初期値, ...};
newを省略して次のように配列を初期化できる。
要素は配列変数のデータ型でなければならない。
配列変数 = {初期値, 初期値, ...};
初期値には、リテラルに限らず変数や式の結果も指定できる。
int a = 22;
int[] c = new int[]{11, a, 33, 44};
2025.04.26
配列変数の代入
class Array03 {
    public static void main(String[] args) {
        int[] ar1 = {10, 20, 30};
        int[] ar2;
        ar2 = ar1;
        ar2[0] = 200;
        System.out.println(ar1[0]);     // 200
    }
}
配列変数は参照型であり、配列のオブジェクトを参照する。
配列変数への代入は、配列の参照先の変更である。
上の例は、配列変数同士の代入が配列データのコピーではないことを示してる。
2025.04.26
配列の長さ
class Array04 {
    public static void main(String[] args) {
        int[] ar = {10, 20, 30};
        System.out.println(ar.length);      // 3  配列の長さ
    }
}
配列の長さ(格納可能な要素数)は、lengthフィールドで得ることができる。
配列変数.length
2025.04.26
多次元配列
class Array05 {
    public static void main(String[] args) {
        int[][] arr;
        arr = new int[2][3];
        arr[0][2] = 10;
        arr[1][2] = 100;
        System.out.println(arr[0][2]);      // 10
        System.out.println(arr[1][2]);      // 100
        String[][] sarr = {{"A", "abc"}, {"D", "def"}, {"G", "ghi"}}; 
        System.out.println(sarr[1][0]);     // D
        System.out.println(sarr[2][1]);     // ghi
    }
}
配列は1次元の数列以上の平方、立方のような多次元配列を生成できる。
多次元配列の変数は、[]を次元数だけ繰り返して宣言する。
データ型[][]  配列変数;
配列変数 = new データ型[サイズ][サイズ];
多次元配列の初期化は、{〜}の中に下の次元の{〜}を入れ子にする。
配列変数 = { {初期値, 初期値, ...}, {初期値, ...}, ...};
class Array06 {
    public static void main(String[] args) {
        int[][] arr = { {10, 20, 30}, {100, 200, 300}};
        System.out.println(arr.length);     // 2
        System.out.println(arr[0].length);  // 3
    }
}
配列要素の長さは、それぞれの次元の配列のlengthフィールドにより得らる。
2025.04.26
要素数が異なる多次元配列
class Array07 {
    public static void main(String[] args) {
        int[][] arr = new int[2][];
        arr[0] = new int[2];
        arr[1] = new int[3];
        arr[0][0] = 10;
        arr[0][1] = 20;
        arr[1][0] = 100;
        arr[1][1] = 200;
        arr[1][2] = 300;
        System.out.println(arr[1][1]);  // 200
        String[][] sarr = {{"A", "B", "C", "D"}, {"AB", "CD"}, {"EFG", "HIJ", "KLM"}};
        System.out.println(sarr[2][0]); // EGF
    }
}
多次元配列の各次元の要素数は、そろっていなくてもかまわない。各次元のそれぞれの配列の初期化の長さで確保される。
同一次元の他の配列要素より短い配列の、存在しない部分の添字で参照することはできない。
2025.04.26
条件判断 if
class CtIf01 {
    public static void main(String[] args) {
        int a = 10;
        if (a < 20)
            System.out.println(a);      // 10

        if (a < 20) {
            a -= 20;
            System.out.println(a);      // -10
        }

        a = 10;
        if (a > 10) {
            // 実行されない
        } else {
            System.out.println(a);      // 10
        }

        a = 10;
        if (a > 10) {
            // 実行されない
        } else if (a == 10) {
            System.out.println(a);      // 10
        } else {
            // 実行されない
        }
    }
}
if文は式の評価結果の真(true)と偽(false)の場合によって処理を分岐させる。
式の評価が真の場合ifブロックが実行され、偽の場合はelseのブロックが実行される。処理が1文の場合はブロック{〜}を省略できる。
elseは省略できる。elseの場合で別の判断をelse if()のように続けることができる。また、else ifは任意に連続できる。
if (式A) {
    Aがtrueのとき
} else if (式B) {
    Aがfalse、Bがtrueのとき
} else if (式C) {
    ABがfalse、Cがtrueのとき
} else {
    すべてfalseのとき
}
式の結果は真理値型(boolean)でなければならない。0やnullはfalseと等価として認められない。
2025.04.26
条件判断 switch-case
class CtSwitch01 {
    public static void main(String[] args) {
        int a;
        a = 20;

        switch (a) {
            case 10:
                System.out.println("case 10");
                break;
            case 20:
                System.out.println("case 20");  // case 20
                break;
            case 30:
                System.out.println("case 30");
                break;
            default:
                System.out.println("default " + a);
                break;
        }

        switch (a) {
            case 10:
            case 20:
                System.out.println("case 10 or 20");    // case 10 or 20
                break;
            case 30:
                System.out.println("case 30");
                break;
            default:
                System.out.println("default " + a);
                break;
        }
    }
}
switch文は、式の評価結果がcase文で指定する値に一致する、それぞれの場合に処理を分岐させる。
swtich文の式の結果は数値、文字、文字列(String)、列挙(enum)の何れかでなければならない。
caseにはリテラル(定数)、リテラルだけの演算式、finalによる定数の変数が指定できる。
変数を含む式、nullは評価できない。switchの結果がnullの場合はNullPointerException例外となる。
case 20 + n: NG
case 20 + 10: OK(定数のみの演算なら可能)
case n: NG(ただしfinalによる定数変数ならOK)
caseの処理の複数の式をブロックで囲む必要はない。
caseの処理はbreak文によりswitchブロックから脱出する。
breakを行わない場合は、次のcaseの評価を続けて実行する。
caseの処理を省略した場合は、なにも実行せずに次のcaseの評価を行う。
複数のcaseをまとめると、それらのOR条件の処理でまとめることができる。
defaultは、全てのcaseに一致しなかった場合に実行する。
defaultは必須ではなく省略できる。
defaultはcase文のどこでも配置できるが、一般には末尾に置く。
switch(式) {
    case A:
        式の結果がA
        ...
        break;
    case B:
    case C:
        式の結果がBかC
        ...
        break;
    case D:
        式の結果がD
        (breakしないで)
    case E:
        式の結果がDかE
        break;
    default:
        上の何れでもない
}
(各breakはここにジャンプ)
文字列はオブジェクト型でありながら文字列リテラルをcase文に適用できる。
この場合の文字列の一致判断には、Stringクラスのequals()メソッドが内部的に使われる。
class CtSwitch02 {
    public static void main(String[] args) {
        char c = 'あ';
        switch (c) {
            case 'あ':
                System.out.println("case あ");  // case あ
                break;
            case 'い':
                System.out.println("case い");
                break;
            default:
                System.out.println("default " + c);
                break;
        }
        String s = "あいう";
        switch (s) {
            case "あいう":
                System.out.println("case あいう");  // case あいう
                break;
            case "かきく":
                System.out.println("case かきく");
                break;
            default:
                System.out.println("default " + c);
                break;
        }
    }
}
2025.04.26
条件式の論理演算
class CtAndOr01 {
    public static void main(String[] args) {
        int a, b;
        a = 10;
        if (a >= 10 && a < 20) {
            System.out.println(a);      // 10
        }

        if ((a > 20) || (a == 10)) {
            System.out.println(a);      // 10
        }

        if (!(a > 10)) {
            System.out.println(a);      // 10
        }
    }
}
ifなど制御構文の評価式を、論理演算子により論理積(AND)論理和(OR)で論理演算ができる。
A && B AかつB
A || B AまたはB
制御構文の評価式は真理値(boolean)の演算なので通常の論理演算子で演算できる。
&, | 論理積(AND)・論理和(OR)
^ 排他的論理和(XOR)
~ 否定(NOT)
「&&」と「||」は、左辺から評価した時点で真偽が確定した場合は右辺の評価は行わない。それに対し「&」と「|」は左辺と右辺を両方評価する。
2025.04.26
条件演算(三項演算)
class CtCond01 {
    public static void main(String[] args) {
        int a = 10;
        System.out.println(a > 10? "TRUE":"FALSE"); // FALSE
    }
}
条件演算(三項演算)は、条件の結果により演算する式を選択する。
条件式の評価が真(true)と偽(false)の何れかの場合の式を評価する。
条件式?  真の場合の式 : 偽の場合の式
2025.04.26
繰り返し while
class CtWhile01 {
    public static void main(String[] args) {
        int i = 0;
        while (i < 4) {
            System.out.println(i);      // 0 .. 3
            i++;
        }
    }
}
while文は、式の評価結果が真(true)である間ブロックの処理を繰り返す。
繰り返す処理が1文のみの場合はブロック{〜}を省略できる。
while(式) {
    式がtrueの間の処理
    ...
}
式の結果は真理値型(boolean)でなければならない。
2025.04.26
繰り返し for
class CtFor01 {
    public static void main(String[] args) {
        int i;
        for (i = 0; i < 4; i++)
            System.out.println(i);      // 0 .. 3
        System.out.println(i);          // 4

        for (int j = 0; j < 4; j++) {
            System.out.println(j * 10);
            System.out.println(j);
        }
        // System.out.println(j);   // エラー(jはforブロック後には存在しない)

        int k = 0;
        for (; k < 4; ) {
            System.out.println(k);
            k++;
        }

        int m, n;
        for (m = 0, n = 5; m < 5; m++, n++) {
            System.out.println(m);
            System.out.println(n);
        }
    }
}
for文は、whileと同様に式の評価結果が真(true)である間ブロックの処理を繰り返す。
for文には繰り返しの判断式と共に、初期化など初回に実行する式と、次の繰り返しの直前に実行する式を記述できる。
for (初期化式; 条件式; 次の繰り返し前の式) {
    繰り返す処理
    ...
}
for文をwhile文で次のように置き換えることができる。
初期化式
while (条件式) {
    繰り返す処理
    ...
    次の繰り返し前の式
}
for文の中の初期化、条件、次の繰り返しの部分は任意に省略できる。
for(;;) {
    繰り返す処理(永久ループ)
    ...
}
for文の初期化式と繰り返し前の式は「,」で区切って複数の式を並べることができる。それらは左から順番に式を実行する。
for (初期化式, 初期化式; 条件式; 次の繰り返し前の式) {
初期化式のその場で変数宣言してもかまわない。その変数はforブロックのスコープとなる。
for (int i = 0; i < 10; i++) {
    j = i;
    ...
2025.04.26
繰り返し do-while
class CtDoWhile01 {
    public static void main(String[] args) {
        int i = 0;
        do {
            System.out.println(i);  // 0 ... 9
            i++;
        } while(i < 10);
    }
}
do-while文は、while文と同様に式の評価結果が真(true)である間ブロックの処理を繰り返す。
条件式は処理ブロック終わりのwhileで評価される。従ってdoに入る時点で条件が既に偽(false)であっても、最低1回はブロック内を実行することになる。
2025.04.26
ループの脱出・継続 break continue
class CtBreak01 {
    public static void main(String[] args) {
        int i = 0;
        while (true) {
            if (i == 4)
                break;
            System.out.println(i);  // 0 1 2 3
            i++;
        }

        i = 0;
        while (true) {
            System.out.println(i);  // 0 1 2 3
            i++;
            if (i < 4)
                continue;
            break;
        }
    }
}
break文は、for、while、do-while構文で繰り返すループ処理を途中で脱出する。
あるいは、case文の処理からswitchブロックを脱出する。脱出後は繰り返す処理ブロックの次から実行を継続する。
continue文は、繰り返し構文の処理内のループを中断し、繰り返しの最初に戻る。
class CtBreak02 {
    public static void main(String[] args) {
        int i = 0;
        jumplabel: for (i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == 3)
                    break jumplabel;
                System.out.println(i + "-" + j);
            }
        }

        jumplabel: for (i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                if (i % 3 == 0)
                    continue jumplabel;
                System.out.println(i + "-" + j);
            }
        }
    }
}
breakとcontinueが脱出または継続するループは、原則として現在繰り返している区間が対象であるが、breakとcontinueにラベルをつけることで、多重ループの内側から外側へ飛び越えた脱出または継続が可能である。
0-0
0-1
0-2
1-0
1-1
1-2
2-0
2-1
2-2

1-0
1-1
1-2
2-0
2-1
2-2
4-0
4-1
4-2
ラベルの位置は、forやwhileの真横でなく直前の行でもかまわない。
JumpLabel:
for (int j = 0; j < 3; j++) {
    ...
2025.04.26
イテレーション(拡張for)
class CtForeach01 {
    public static void main(String[] args) {
        int[] ar = {10, 20, 30};
        for (int i : ar) {
            System.out.println(i);
        }

        String[] sarr = {"", "", "ABC"};
        for (String s : sarr) {
            System.out.println(s);
        }
    }
}
10
20
30


ABC
拡張for文は、配列やコレクションの要素を先頭から順番に取り出して繰り返す。全ての要素について繰り返したらループを終了する。
拡張for文で要素を走査できる対象は、iterableインタフェースを実装したイテレーション可能なクラスのオブジェクトである。
要素を参照する変数は拡張for文の中で宣言する。
配列等の要素を先頭から順番に変数に代入しながら処理ブロックを繰り返す。
for (変数宣言 : 配列等) {
    繰り返す処理
    ...
}
2025.04.26
クラスの定義・生成
class C1 {
    int a;
    double d;
}

class Cls01 {
    public static void main(String[] args) {
        C1 c;
        c = new C1();
        System.out.println(c.a);        // 0(既定値)
    }
}
classにより新たなクラスを定義する。
定義するクラスに任意の名前を付けることができる。クラスは、オブジェクトが保持するデータを「フィールド(メンバ変数)」として、振る舞いを「メソッド」として定義する。全てのデータやメソッドは必ずクラスに属して定義されなければならず、データのみやメソッドのみがグローバルスコープに存在するようなことはできない。
.javaソースファイルをコンパイルすると、ソース中に定義されているクラス名の「クラス名.class」ファイルが作成される。
ひとつのソースファイルに複数クラスが定義されていれば、それぞれのクラス名で.classファイルが作られる。mainメソッドが存在するクラスを実行すると、mainメソッドからプログラムの実行が始まる。
アクセス指定をpublicとするクラスは、ソースファイル内に1つのみとする。publicを指定したクラスを定義した場合、その.javaファイルは「クラス名.java」でなければならない。
クラス定義は、インスタンスが持つデータをフィールドとして変数宣言の書式で定義し、メソッドを、引数・戻り値と処理内容を定義する。
フィールドのみのクラスやメソッドのみのクラスを定義してかまわない。
class クラス名 {
    フィールド(変数宣言)
    フィールド(変数宣言)
    ...
    メソッド定義
    メソッド定義
    ...
}
定義したクラスはユーザ定義型として変数宣言できる。
クラス名  変数名;
クラスの変数宣言により、そのクラスの実体「インスタンス」を参照する変数が用意される。変数を宣言した時点では、クラスのインスタンスはまだ存在してない。
newによりクラスの実体(オブジェクト)であるインスタンスを生成する。
new クラス名()
クラスの変数宣言とインスタンスの生成と初期化をまとめることができる。
クラス名 変数名 = new クラス名()
2025.04.26
フィールド定義
class C1 {
    int a;
    double d;
}

class Cls02 {
    public static void main(String[] args) {
        C1 c;
        c = new C1();
        System.out.println(c.a);        // 0(既定値)
        c.a = 10;
        c.d = 2.34;
        System.out.println(c.a);        // 10
        System.out.println(c.d);        // 2.34
        C1 cc = new C1();
        cc.a = 20;
        System.out.println(cc.a);       // 20 
        System.out.println(cc.d);       // 0.0  デフォルトで0相当の値で初期化されている
    }
}
フィールドは変数宣言の書式で定義する。
class クラス名 {
    int a;
    String s;
}
変数が参照するクラスのインスタンスから、フィールドを「.」で区切って参照する。
変数.フィールド名
2025.04.26
メソッド定義
class C1 {
    int a;
    void set(int x) {
        a = x;
    }
    int get() {
        return a;
    }
    void show() {
        System.out.println(a);
        return;
    }
}

class Cls03 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(100);
        System.out.println(c.get());        // 100
    }
}
メソッドは、次のように引数・戻り値と処理を定義する。
戻り値型 メソッド名(引数宣言, 引数宣言, ...) {
    変数宣言・処理
    ...
    return 戻り値;
}
引数は変数宣言と同様である。引数が不要な場合は省略できる。
戻り値を返さないメソッドは戻り値の型に「void」型を宣言する。voidはメソッド定義のための特殊な型であり、void型のフィールドや変数を宣言することはできない。
メソッドのブロック内には、メソッド内で使用する変数(ローカル変数)の宣言と処理を実装する。メソッドのブロック内では、引数とフィールドを参照できる。
return文で返す戻り値は、値や式の結果が定義で示す戻り値型でなければならない。
戻り値を返さない(voidを返す)メソッドではreturn文を省略できる。あるいは戻り値を省略して「return」として任意の場所でメソッドから返ることができる。
return 戻り値;
return;
2025.04.26
フィールドのthis参照
class C1 {
    int a;
    void set(int a) {
        this.a = a;
    }
}

class Cls04 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(10);
        System.out.println(c.a);
    }
}
クラスのフィールドはメソッドで変数として参照できる。
自身のインスタンスのフィールドを参照する場合、インスタンスを「this」に置き換えて参照できる。
this.フィールド変数名
2025.04.26
メソッドから他のメソッド呼び出し
class C1 {
    int a;
    void set(int x) {
        a = x;
    }
    int get() {
        return a;
    }
    void show() {
        System.out.println(get());
        System.out.println(this.get());
    }
}

class Cls05 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(10);
        c.show();           // 10
    }
}
クラス定義の中のメソッドは、互いに他のメソッドを呼び出すことができる。
thisにより呼び出すメソッドが自身のインスタンスのメソッドであることを明示できる。
this.メソッド名(引数)
2025.04.26
スコープ
class C1 {
    int a;
    void set(int x) {
        if (x < 100) {
            int b = 100;        // ブロックスコープ
            x += b;
        }
        a = x;
    }
    void show() {
        int a = 200;            // ローカル変数
        System.out.println(this.a);     // 105
        System.out.println(a);          // 200
    }
}

class Cls06 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(5);
        System.out.println(c.a);        // 105
        c.show();
    }
}
メソッドの中で宣言したローカル変数は、メソッドの中のスコープとなる。
クラス内の同名の変数名は現在のブロックのスコープが優先される。ローカル変数とフィールド名が同じ変数名のとき、メソッド内ではローカル変数の方の参照となる。その場合にフィールドの方を参照するのであれば、thisによりインスタンスのフィールド参照であることを明示する必要がある。
メソッド定義を含めた{〜}で囲むブロックは、それぞれのローカルスコープを持つ。ブロック内の変数宣言は、そのブロックのスコープになる。
ブロックはメソッドや制御構文等に限らず、任意に設置できる。
メソッド() {
    {
        ブロック
    }
    {
        ブロック
    }
}
クラス定義の外側のグローバルスコープというものは存在しない。
2025.04.26
フィールド・メソッドの定義位置
class C1 {
    void show() {
        System.out.println(a);
        System.out.println(get());
    }
    int a;
    int get() {
        return a;
    }
}

class Cls07 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.a = 100;
        c.show();
    }
}
フィールドやメソッドを定義する順序はクラス定義の中で自由である。
メソッドが参照するフィールドがメソッド定義より前に書かれている必要はない。メソッドも同様に、メソッドの中から呼び出す別のメソッドがそれより前に定義されている必要はない。一方で、メソッドの処理中などブロック内の変数は、それが参照される前に宣言されていなければならない。
2025.04.26
フィールドの初期値
class C1 {
    int a = 100;
    final int b = 200;
}

class Cls08 {
    public static void main(String[] args) {
        C1 c = new C1();
        System.out.println(c.a);    // 100
        //c.b = 20;                 // エラー
        System.out.println(c.b);    // 200
    }
}
フィールドには、変数宣言と同様に初期値を与えることで、newによるインスタンス生成時の初期値を設定できる。
フィールドを初期化しない場合は、各データ型のデフォルトの初期値で自動的に初期化される。
フィールドの変数をfinalで宣言し初期値を与えると、その変数は定数になる。
finalで定数化したフィールドへの初期化以降の変更操作はエラーとなる。
2025.04.26
初期化ブロック
class C1 {
    int a;
    {
        a = 100;
    }
}

class Cls09 {
    public static void main(String[] args) {
        C1 c = new C1();
        System.out.println(c.a);    // 100
    }
}
クラス定義内のブロック{〜}は「初期化ブロック」としてインスタンス生成時に、コンストラクタより先に実行される。
クラスのインスタンスが生成されるときのフィールドを初期化する順番は次のとおり。
(1) クラスフィールドの初期値設定
(2) static初期化ブロックによるクラスフィールドの初期化
(3) インスタンスフィールドの初期値設定
(4) 初期化ブロックでインスタンスフィールドの初期化
(5) コンストラクタでフィールドに代入
2025.04.26
引数の参照渡し
class C1 {
    void chg(String s) {
        s = "ABCD";
    }
    void arrchg(int[] ax) {
        ax[1] = 123;
    }
    int[] arrrep(int[] ax) {
        ax = new int[] {11, 22, 33};
        return ax;
    }
}

class Cls10 {
    public static void main(String[] args) {
        C1 c = new C1();
        String s = "XYZ";
        c.chg(s);
        System.out.println(s);          // XYZ

        int[] a = new int[]{10, 20, 30};
        c.arrchg(a);
        System.out.println(a[1]);       // 123  20が123に書き換えられている

        int[] b = new int[]{100, 200, 300};
        int[] r = c.arrrep(b);
        System.out.println(b[0]);       // 100
        System.out.println(r[0]);       // 11
    }
}
メソッドの引数が参照型の場合、引数には呼び出し元のオブジェクトの参照が渡る。
メソッドの処理内で、呼び出し元オブジェクトを参照している引数に変更操作を行うと、呼び出し側が引数に与えたデータが変更される。
上の例のchg()メソッドとarrrep()メソッドは、何れも参照型である文字列と配列を引数で受け取るが、メソッドの中で引数へ新しい別のオブジェクトを代入しており、その時点で呼び出し元が与えたオブジェクトへの参照が切れることになるので、その結果呼び出し元は変化しない。
2025.04.26
インスタンス変数は参照の代入
class C1 {
    int a;
    double d;
}

class Cls11 {
    public static void main(String[] args) {
        C1 ca = new C1();
        C1 cb;
        cb = ca;
        ca.a = 100;
        System.out.println(cb.a);       // 100
        cb.d = 2.34;
        System.out.println(ca.d);       // 2.34
    }
}
クラス型の変数は、そのクラスのインスタンスの参照を代入したものであり、クラスの変数間の代入は同一インスタンスの参照の代入になる。クラスの変数間でフィールドはコピーされない。
2025.04.26
クラス型のフィールドを定義
class C1 {
    int a;
}

class C2 {
    C1 cc;
    void set(C1 x) {
        cc = x;
    }
}

class Cls12 {
    public static void main(String[] args) {
        C1 c1 = new C1();
        c1.a = 100;
        C2 c2 = new C2();
        c2.set(c1);
        System.out.println(c2.cc.a);    // 100
        c1.a = 200;
        System.out.println(c2.cc.a);    // 200
    }
}
フィールドは基本型に限らず他のクラス型のフィールドを定義できる。
クラスのフィールドは、そのクラスのインスタンスの参照である。上の例では、C2のccはC1のインスタンスのc1を参照しており、c1.aの変更はc2.cc.aを変更することと同じになる。
2025.04.26
オブジェクト配列
class C1 {
    int a;
    double d;
}

class Cls13 {
    public static void main(String[] args) {
        C1[] carr;
        carr = new C1[3];
        for (int i = 0; i < carr.length; i++) {
            carr[i] = new C1();
            carr[i].a = 10 + i;
        }
        for (int i = 0; i < carr.length; i++) {
            System.out.println(carr[i].a);      // 10 11 12
        }
    }
}
クラスは基本型と同様に配列変数を宣言できる。
宣言した配列変数に対しnewで配列を生成するが、その時点ではまだ配列の生成であり、要素は何のインスタンスも参照していない。
クラス名[] 配列変数
配列変数 = new クラス名[n]
配列変数[0] = インスタンス
配列変数[2] = new コンストラクタ()
,,,
2025.04.26
メソッドのオーバーロード
class C1 {
    int a;
    double d;
    void set(int x) {
        a = x;
    }
    int set(double x) {
        d = x;
        return 0;
    }
}

class ClsOvl01 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(10);
        c.set(2.34);
        System.out.println(c.a);        // 10
        System.out.println(c.d);        // 2.34
    }
}
メソッドのオーバーロードは、引数が異なる同名のメソッド定義する。
戻り値だけが異なるオーバーロードはできない。呼び出すメソッドは引数の差異によりコンパイル時に決定する。
2025.04.26
コンストラクタ
class C1 {
    int a;
    C1(int x) {
        a = x;
    }
}

class ClsCons01 {
    public static void main(String[] args) {
        C1 c = new C1(10);
        System.out.println(c.a);        // 10
    }
}
コンストラクタはクラス名と同名とする。戻り値は定義しない。
コンストラクタはメソッドと同様に引数を定義し、フィールドを参照できる。
コンストラクタはnewによるインスタンス生成時に呼び出す。
引数のないコンストラクタでも()は省略できない。
new クラス名()
new クラス名(引数)
コンストラクタを定義しないクラスは、引数と処理内容がないデフォルトコンストラクタが暗黙に定義される。
2025.04.26
コンストラクタのオーバーロード
class C1 {
    int a;
    double d;
    C1(int x) {
        a = x;
    }
    C1(double x) {
        d = x;
    }
}

class ClsCons02 {
    public static void main(String[] args) {
        C1 ca = new C1(10);
        C1 cb = new C1(2.34);
        System.out.println(ca.a);       // 10
        System.out.println(cb.d);       // 2.34
    }
}
コンストラクタをオーバーロード定義できる。newによる呼び出し時の引数により選択される。
2025.04.26
コンストラクタから他のコンストラクタ呼び出し
class C1 {
    int a;
    double d;
    C1(int x) {
        a = x;
    }
    C1(int x, double y) {
        this(x);
        d = y;
    }
}

class ClsCons03 {
    public static void main(String[] args) {
        C1 c = new C1(10, 2.34);
        System.out.println(c.a);        // 10
        System.out.println(c.d);        // 2.34
    }
}
コンストラクタをオーバーロード定義した場合、コンストラクタから別のコンストラクタをthis()として呼び出すことができる。
this(引数)
2025.04.26
アクセス制御
class C1 {
    private int a;
    public int b;
    void set(int a) {
        this.a = a;
    }
    private int get() {
        return a;
    }
}

class ClsAcc01 {
    public static void main(String[] args) {
        C1 c = new C1();
        //c.a = 100;            // privateなのでエラーになる
        //c.get();
        c.set(100);
        c.b = 200;
    }
}
各アクセス修飾子は、クラス(インスタンス)、フィールド、メソッドのそれぞれが参照(使用)できる範囲を指定する。
アクセス修飾子が指定する範囲外で生成、参照、呼び出し操作を行うとエラーになる。
                               
アクセス修飾子 クラス内 自パッケージ内 継承クラス 継承クラス
(他パッケージ)
他パッケージ
private 不可 不可 不可 不可
なし(デフォルト) 不可 不可
protected 不可
public
privateが指定されたものは、クラス定義内のコンストラクタやメソッドの中でのみ使用できる。
アクセス修飾子を指定しないデフォルトでは、パッケージ内ならば全てアクセス可能である。
各アクセス修飾子の適用対象は以下の通り。
           
アクセス修飾子 クラス フィールド メソッド
private
protected
public
2025.04.26
クラスフィールド(静的フィールド)
class C1 {
    static int s = 10;
    int a;
    C1(int x) {
        a = x;
        s += x;
    }
}

class ClsStatic01 {
    public static void main(String[] args) {
        C1.s += 5;
        System.out.println(C1.s);       // 15
        C1 ca = new C1(3);
        C1 cb = new C1(5);
        C1 cc = new C1(2);
        System.out.println(C1.s);       // 25
        System.out.println(ca.s);       // 25
        System.out.println(cb.s);       // 25
    }
}
static修飾子を指定するフィールドは「クラスフィールド」となり静的なフィールドになる。
クラスフィールドはインスタンスごとに生成されない。
クラスの定義により固定的に存在するので、インスタンスを生成しなくても参照できる。クラスフィールドは、どのインスタンスの変数から参照しても常に唯一のフィールドを参照することになる。
クラス名.クラスフィールド
インスタンス.クラスフィールド
クラスフィールドはメソッドの中から参照できる。
2025.04.26
クラスメソッド
class C1 {
    static int s = 0;
    int a;
    C1(int x) {
        a = x;
        s += x;
    }
    static int gets() {
        return s;
    }
}

class ClsStatic02 {
    public static void main(String[] args) {
        C1 ca = new C1(3);
        C1 cb = new C1(5);
        C1 cc = new C1(2);
        System.out.println(C1.gets());      // 10
        System.out.println(ca.gets());      // 10
        System.out.println(cb.gets());      // 10
    }
}
static修飾子を指定するメソッドは「クラスメソッド」となり静的なメソッドになる。
クラスの定義により固定的に存在するので、インスタンスを生成しなくても呼び出すことができる。
クラス名.クラスメソッド(引数)
インスタンス.クラスメソッド(引数)
インスタンスメソッドの中からクラスメソッドを呼び出すことができる。逆に、クラスメソッド内ではインスタンス変数やメソッドを使うことはできない。
クラスメソッド内ではクラスフィールドと他のクラスメソッドしか使うことができない。
2025.04.26
クラスメソッドでインスタンスを生成(ファクトリメソッド)
class C1 {
    int a;
    private C1() {
        a = 100;
    }
    static C1 getins() {
        return new C1();
    }
}

class ClsStatic03 {
    public static void main(String[] args) {
        C1 c = C1.getins();
        System.out.println(c.a);    // 100
    }
}
クラスメソッドの中で、そのクラスのインスタンスを生成できる。
自身のクラスのインスタンスを生成して返すクラスメソッドのことをファクトリメソッドと呼ぶ。
2025.04.26
クラスフィールドによるシングルトン
class C1 {
    private static C1 singleton = new C1();
    int x;
    private C1() {
        x = 100;
    }
    static C1 getins() {
        return singleton;
    }
}

class ClsStatic04 {
    public static void main(String[] args) {
        C1 ca = C1.getins();
        ca.x += 10;
        System.out.println(ca.x);   // 110
        C1 cb = C1.getins();
        cb.x += 10;
        System.out.println(ca.x);   // 120
        System.out.println(cb.x);   // 120
    }
}
クラスのインスタンスを、そのクラス自身のフィールドに生成することで、単一インスタンスのみが存在し続ける「シングルトン」にできる。
クラスフィールドを自身のクラスで定義し、その初期化としてnewで自身のインスタンスを生成する。
コンストラクタをprivateに指定することで、インスタンスの生成を禁止する。クラスフィールドに生成した唯一のインスタンスの参照を取得するためのメソッドを用意する。
2025.04.26
定数定義だけのクラス
class C1 {
    static final int MAX = 100;
    static final int MIN = 10;
    static final String INFO = "Const max min"; 
}

class ClsStatic05 {
    public static void main(String[] args) {
        System.out.println(C1.MAX);     // 100
        System.out.println(C1.MIN);     // 10
        System.out.println(C1.INFO);    // Const max min
    }
}
クラスフィールドにfinalを指定し変更禁止にすることで、クラス名を名前空間とした定数を定義できる。このようなフィールドはクラスメソッドであっても初期値から変更できない。
2025.04.26
クラスフィールドの初期化ブロック
class C1 {
    static int x;
    static {
        x = 100;
    }
}

class ClsStatic06 {
    public static void main(String[] args) {
        System.out.println(C1.x);   // 100
    }
}
staticの初期化ブロックは、インスタンスフィールドの初期化ブロックとは別に、クラスフィールドの初期化ができる。
static {
    クラスフィールドの初期化
}
クラスフィールドの初期化ブロックを含めたクラスの初期化順序は次のとおり。
(1) クラスフィールドの初期値設定
(2) static初期化ブロックによるクラスフィールドの初期化
(3) インスタンスフィールドの初期値設定
(4) 初期化ブロックでインスタンスフィールドの初期化
(5) コンストラクタでフィールドに代入
2025.04.26
インスタンス生成しないクラス
final class C1 {
    private C1(){ }
    static void show(int a) {
        System.out.println(a);
    }
    static int add(int x, int a) {
        return x + a;
    }
}

class ClsStatic07 {
    public static void main(String[] args) {
        System.out.println(C1.add(10, 5));
        C1.show(100);   // 15
                        // 100
    }
}
クラス定義にfinalを指定すると、そのクラスはインスタンスが生成できなくなる。
通常そのようなクラスは、フィールド、メソッドを全てstaticとして定義する。クラスを名前空間とした変数の参照とメソッドの呼び出しになる。
コンストラクタはnewで呼び出せないようにprivateとする必要がある。
2025.04.26
クラス内のクラス定義
class C1 {
    int a = 100;
    class I1 {
        int a = 20;
        void show() {
            System.out.println(a);          // 20
            System.out.println(this.a);     // 20
            System.out.println(C1.this.a);  // 100
        }
    }
    void show() {
        I1 i = new I1();
        i.show();
        System.out.println(a);              // 100
        System.out.println(i.a);            // 20
        System.out.println(this.a);         // 100
    }
}

class ClsCls01 {
    public static void main(String[] args) {
        C1 c = new C1();
        C1.I1 i = c.new I1();
        //C1.I1 i = (new C1()).new I1();    // 上の2行をまとめた場合
        i.show();
        c.show();
    }
}
クラス定義の中でクラスを定義できる。
内部のクラスから外側のクラスのフィールドやメソッドを使用できる。内部定義するクラスの参照とインスタンスの生成は、外側のクラスに関連付けられる。
外クラス.内クラス
外クラスインスタンス.new 内クラス()
2025.04.26
クラス内のstaticクラス
class C1 {
    int a = 100;
    static class I1 {
        static int a = 20;
        void show() {
            System.out.println(a);          // 20
            //System.out.println(C1.a);     // エラー 
        }
    }

    void show() {
        I1 i = new I1();
        i.show();                           // 20
        System.out.println(a);              // 100
        System.out.println(i.a);            // 20
        System.out.println(this.a);         // 100
    }
}

class ClsCls02 {
    public static void main(String[] args) {
        C1 c = new C1();
        C1.I1 i = new C1.I1();
        i.show();
        c.show();
    }
}
クラス定義の中でstaticクラスを定義できる。
staticで定義した内部クラスは、外側のクラスのインスタンスに関連しない、外側のクラスの名前空間で定義した独立したクラスになる。
外クラス.内クラス
new 外クラス.内クラス()
外側のクラスのメソッドから、内側のstaticクラスのインスタンスを生成できる。
2025.04.26
メソッド内のローカルクラス
class ClsCls03 {
    public static void main(String[] args) {
        String msg = "Local";
        class C1 {
            void show() {
                System.out.println(msg);    // Local
            }
        }
        C1 c = new C1();
        c.show();
    }
}
クラスの中のメソッドやコンストラクタの中でクラスを定義できる。
メソッド内で定義したクラスは、そのメソッドの処理中に定義からインスタンスの生成までを完結する。ローカルクラスの定義では、メソッドの内部に限らずメソッドを定義しているクラスのフィールドを参照できる。
2025.04.26
可変長引数
class C1 {
    void set(int... vx) {
        System.out.println("Length=" + vx.length);
        for (int x: vx)
            System.out.println(x);
    }
    void setadd(int n, int... vx) {
        for (int x: vx)
            System.out.println(n + x);
    }
    void setvobj(Object... vo) {
        for (Object o: vo)
            System.out.println(o);
    }
}

class ClsVarg01 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(10, 20, 30);
        c.set(1, 3, 5, 7, 9);
        c.set();
        c.setadd(100, 10, 20, 30, 40);
        c.setvobj(100, "ABC", 3.5);
    }
}
メソッドの引数は可変長で指定可能である。
可変長引数は、引数の変数型の後に「...」をつけて定義する。可変の引数が配列のように格納される。
格納される数は配列と同様にlengthで取得できる。
メソッド(型... 引数名)
引数名.length
Length=3
10
20
30
Length=5
1
3
5
7
9
Length=0
110
120
130
140
100
ABC
3.5
可変長引数の前に固定引数を設定できる。ただし可変長引数の後に固定引数を置くことはできない。可変長引数はメソッドの中に複数配置できない。
可変長引数は引数宣言したデータ型の引数に限定されるが、スーパークラスを指定すれば、その派生するサブクラスの可変長引数が取得できる。
class C1 {
    void setbyarr(int[] ax) {
        for (int x: ax)
            System.out.println(x);
    }
}

class ClsVarg02 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.setbyarr(new int[]{0, 5, 10});
    }
}
可変長引数と同等なことを配列で実装できる。その場合は呼び出し側で引数に与える配列を生成する必要がある。
2025.04.26
mainメソッド
class ClsMain01 {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}
mainメソッドは実行するプログラムにひとつだけ定義されている必要がある。Javaは、実行時に指定するクラスからmainメソッドを探し実行のエントリとする。
public static void main(String[]) {
引数の文字列配列は実行時のコマンドライン引数が格納される。
$ java ClsMain01 abc def 123
abc
def
123
2025.04.26
クラスの継承
class S1 {
    int s;
    int gets() {
        return s;
    }
}

class C1 extends S1 {
    int a;
    double d;
    void shows() {
        System.out.println(gets());
    }
}

class ClsExt01 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.a = 10;
        c.s = 20;
        c.d = 2.34;
        System.out.println(c.a);        // 10
        System.out.println(c.s);        // 20
        System.out.println(c.d);        // 2.34
        System.out.println(c.gets());   // 20 
        c.shows();                      // 20
    }
}
クラス定義をスーパークラスとして継承するサブクラスを定義できる。
class サブクラス extends スーパークラス
サブクラスはスーパークラスのフィールドとメソッドを継承する。
サブクラスが定義するメソッド定義の中でスーパークラスのフィールドの参照とメソッドを呼び出すことができる。
クラスの継承では、複数のスーパークラスからなる多重継承はできない。
2025.04.26
サブクラスのメソッドからsuperとthisの明示的参照
class S1 {
    int s;
    int a;
    int geta() {
        return a;
    }
}

class C1 extends S1 {
    int a;
    void seta(int x, int y) {
        this.a = x;
        super.a = y;
    }
    void sets(int x) {
        this.s = x;
    }
    void show() {
        System.out.println(super.geta());
    }
}

class ClsExt02 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.seta(10, 20);
        c.sets(30);
        System.out.println(c.a);        // 10
        System.out.println(c.s);        // 30
        c.show();                       // 20
    }
}
サブクラスのメソッド内で使うフィールドやメソッドがスーパークラスのものを指していることをsuper.により明示できる。
super.スーパークラスのフィールド
super.スーパークラスのメソッド()
thisは継承元のスーパークラスも含めた自身のインスタンスを示す。
2025.04.26
スーパークラスのコンストラクタ呼び出し
class S1 {
    int s;
    S1() {
        s = 20;
    }
    S1(int x) {
        s = x;
    }
}

class C1 extends S1 {
    int a;
    double d;
    C1(int x, int y) {
        super(x);
        a = y; 
    }
}

class ClsExt03 {
    public static void main(String[] args) {
        C1 c = new C1(10, 200);
        System.out.println(c.a);        // 200
        System.out.println(c.s);        // 10
    }
}
サブクラス内のsuper()は、スーパークラスのコンストラクタを呼び出す。
super()の呼び出しは、サブクラスのコンストラクタの先頭で行わなければならない。
サブクラスのコンストラクタから明示的なsuper()の呼び出しがない場合は、暗黙にスーパークラスのデフォルトコンストラクタが呼び出される。
2025.04.26
継承のアクセス制限
class S1 {
    int a;
    private int b;
    public int c;
    private int geta() {
        return a;
    }
}

class C1 extends S1 {
    void set(int x, int y, int z) {
        a = x;
        //b = y;    // privateなのでエラー
        c = z;
    }
    void show() {
        System.out.println(a);              // 100
        //System.out.println(super.geta()); // privateなのでエラー
        //System.out.println(b);            // privateなのでエラー
        System.out.println(c);              // 300
    }
}

class ClsExt04 {
    public static void main(String[] args) {
        S1 s = new S1();
        s.a = 10;
        //s.b = 20;     // Error privateなので
        s.c = 30;
        //System.out.println(s.geta()); // privateなのでエラー

        C1 c = new C1();
        c.set(10, 20, 30);
        c.a = 100;
        //c.b = 200;    // privateなのでエラー
        c.c = 300;
        c.show();
    }
}
スーパークラスのprivateでアクセス指定されたフィールドとメソッドには、サブクラスでは使用できない。
スーパークラスでアクセス指定のない(デフォルトの)フィールドやメソッドは、同一パッケージに属するサブクラスが継承した場合に限り使用できる。スーパークラスでpublicにアクセス指定したフィールドやメソッドは、パッケージの内外に関わらず継承したサブクラスで使用できる。
2025.04.26
メソッドのオーバライド
class S1 {
    int a;
    void show() {
        System.out.println("super " + a);
    }
    int get() {
        return a;
    }
}

class C1 extends S1 {
    void show() {
        System.out.println("override " + a);
        super.show();
    }
    //private int get() {   // エラー 弱いアクセス制限でオーバーライド
    public int get() {
        return a;
    }
}

class ClsExt05 {
    public static void main(String[] args) {
        C1 c = new C1(); 
        c.a = 100;
        c.show();       // override 100
                        // super 100
    }
}
スーパークラスと同じメソッドをサブクラスで定義した場合、サブクラスのメソッドのオーバーライドになる。
サブクラスのインスタンスは、オーバーライドしたサブクラス側のメソッドを呼び出す。
オーバライドメソッドのアクセス指定は、スーパークラスよりも制限する方には指定できない。
スーパークラスのprivateのメソッドはオーバーライドできない。
public > protected > 指定なし > private
2025.04.26
ポリモフィズム
class S1 {
    int a;
    void show() {
        System.out.println("super " + a);
    }
}

class C1 extends S1 {
    int b;
    int get() {
        return a;
    }
    void show() {
        System.out.println("override " + a);
    }
}

class ClsExt06 {
    public static void main(String[] args) {
        S1 c = new C1();
        c.a = 100;
        //c.b = 200;        // Error
        c.show();           // override 100
        //c.get();          // Error
        C1 e = (C1)c;
        System.out.println(e.get());    // 100
    }
}
サブクラスのインスタンスをスーパークラス型で宣言した変数に代入すると、その変数はスーパークラスとしての参照となるため、サブクラスしか定義していないフィールドやメソッドは参照できなくなる。その一方で、スーパークラスのメソッドをサブクラスでオーバーライドしている場合は、サブクラス側が呼び出される。
スーパークラスをサブクラスでキャストすると、サブクラスの定義で参照できる。
class S1 {
    int a;
    void show() {
        System.out.println("super " + a);
    }
}

class C1 extends S1 {
    void show() {
        System.out.println("override C1 " + a);
    }
}

class C2 extends S1 {
    void show() {
        System.out.println("override C2 " + a);
    }
}

class ClsExt07 {
    public static void main(String[] args) {
        S1 s[] = new S1[2];
        s[0] = new C1();
        s[1] = new C2();
        for (int i = 0; i < s.length; i++)
            s[i].a = 10 + i;
        for (int i = 0; i < s.length; i++)
            s[i].show();        // override C1 10
                                // override C1 11
    }
}
スーパークラスの変数で、それぞれのサブクラスのオーバライドしたメソッドを呼び出すことにより、同じメソッド呼び出し方で、個々のサブクラスの特有の振る舞いで実行させることができる(ポリモーフィズム)。
2025.04.26
スーパークラスとサブクラスの型によりthisを決定
class S1 {
    int a;
    void show() {
        System.out.println("super " + a);
    }
}

class C1 extends S1 {
    int a;
    void show() {
        super.show();                               // super 200
        System.out.println("override " + a);        // override 100
    }
}

class ClsExt08 {
    public static void main(String[] args) {
        C1 c = new C1();
        S1 s = c;
        c.a = 100;
        s.a = 200;
        c.show();   // super 200
                    // override 100
    }
}
サブクラスのインスタンスをスーパークラス型で参照する場合と、サブクラス型で参照する場合でインスタンスが参照する対象が切り替わる。
2025.04.26
finalによるオーバライドの禁止
class S1 {
    int a;
    final void show() {
        System.out.println("super " + a);
    }
}

class C1 extends S1 {
/*                                      finalによりオーバライドはエラーになる
    void show() {
        System.out.println("override " + a);
    }
*/
}

class ClsExt09 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.show();
    }
}
スーパークラスのメソッドにfinalを指定することで、そのメソッドをオーバーライドすることを禁止できる。
その場合に、サブクラスがオーバーライドを実装するとエラーになる。
2025.04.26
フィールドのオーバライド
class S1 {
    int a = 100;
}

class C1 extends S1 {
    double a;
    void show() {
        System.out.println("super " + super.a);
        System.out.println("this " + a);
    }
}

class ClsExt10 {
    public static void main(String[] args) {
        C1 c = new C1(); 
        c.a = 1.23;
        c.show();       //super 100
                        //this 1.23
    }
}
スーパークラスと同名のフィールドをサブクラスで定義した場合は、スーパークラス側が隠蔽される。
型が違っていても名前が同じならばサブクラスによるオーバーライドが成立する。
2025.04.26
静的フィールド・メソッドのオーバライド
class S1 {
    static int a = 100;
    static void show() {
        System.out.println("show static " + a);
    }
}

class C1 extends S1 {
    static int a = 200;
    static void show() {
        System.out.println("show sub " + a);
    }
}

class ClsExt11 {
    public static void main(String[] args) {
        S1.show();      //show static 100
        C1.show();      //show sub 200
        S1 c = new C1(); 
        c.show();       //show static 100
        ((C1)c).show(); //show sub 200
    }
}
スーパークラスのstaticフィールドとメソッドをサブクラスでオーバライドできる。
どちらが呼び出されるかは、呼び出し時のクラス名かインスタンスのクラス型によって決定する。
2025.04.26
継承を使わずに他のクラスを取り込む
class C0 {
    void show(String s) {
        System.out.println(s);
    }
}

class C1 {
    C0 c0 = new C0(); 
    void show(String s) {
        c0.show(s);
    }
}

class ClsExt12 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.show("ABC");
    }
}
クラスのインスタンスをフィールドに持つことにより、継承を使わずに他のクラスの機能を取り込むことができる。
2025.04.26
抽象クラスの継承
 
abstract class V1 {
    int a;
    abstract int get();
} 

class C1 extends V1 {
    int get() {
        return a;
    }
}

class ClsAbs01 {
    public static void main(String[] args) {
        V1 c = new C1();
        c.a = 100;
        System.out.println(c.get());    // 100
    }
}
メソッドのシグニチャー(戻り値・メソッド名・引数リスト)のみ定義し、実装を記述しない「抽象メソッド」を定義できる。
抽象メソッドにはabstractを指定する。また抽象メソッドを含むクラス定義は「抽象クラス」となり、abstractを指定する必要がある。abstractの指定と同時に暗黙にアクセスがpublicに設定される。
抽象クラスは直接newでインスタンスを生成できない。必ず継承される必要があり、サブクラスのインスタンスを生成する。
抽象クラスを継承するサブクラスは、全ての抽象メソッドをオーバライドして実装しなければならない。
抽象クラスには、抽象クラスと実装のある通常のメソッド(具象メソッド)を混在させてもかまわない。
abstract class V1 {
    int a;
    abstract int get();
    abstract void set(int x);
} 

abstract class C1 extends V1 {
    int get() {
        return a;
    }
    void show() {
        System.out.println(get());
    }
}

class C2 extends C1 {
    void set(int x) {
        a = x;
    }
}

class ClsAbs02 {
    public static void main(String[] args) {
        C1 c = new C2();
        c.set(100);
        c.show();       // 100
    }
}
抽象クラスを継承したサブクラスが抽象メソッドをオーバライドしない場合は、そのサブクラスも抽象クラスとなり、他のクラスに継承される必要がある。
2025.04.26
インスタンスのクラスの確認 instanceof
abstract class V1 {
    int a;
    abstract int get();
} 

class C1 extends V1 {
    int get() {
        return a;
    }
}

class C2 extends V1 {
    int get() {
        return a;
    }
}

class Insof01 {
    public static void main(String[] args) {
        V1 c1 = new C1();
        V1 c2 = new C2();
        System.out.println(c1 instanceof C1);   // true
        System.out.println(c1 instanceof C2);   // false
        System.out.println(c2 instanceof C1);   // false
        System.out.println(c2 instanceof C2);   // true
        System.out.println(c1 instanceof V1);   // true
        System.out.println(c2 instanceof V1);   // true
    }
}
インスタンスがそのクラスから生成されたものかどうかをinstanceofの式で判定できる。
インスタンス instanceof クラス名
インスタンスが指定するクラスから生成されている、式はtrueとなる。クラスが継承元のスーパークラスであってもtrueと判定される。
インスタンスが指定クラスの性質を継承しているかどうかを判断できる。
2025.04.26
インタフェース定義・実装クラス定義
interface I1 {
    int max = 10;
    int get();
    void set(int x);
}

class C1 implements I1 {
    int a;
    public int get() { return a; }
    public void set(int x) { a = (x > max)? max : x; }
}

class Iface01 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(8);
        System.out.println(c.get());    // 8
        c.set(12);
        System.out.println(c.get());    // 10
    }
}
インタフェースは、静的フィールドの定数定義と抽象メソッドの定義のみで定義する。
インタフェースは、実装のない定義のみを記述した特殊な抽象クラスであり、抽象クラスと同様にインタフェースから直接のインスタンスは生成できない。
interface {
    フィールド = 初期値;
    メソッド定義
}
メソッド定義は、
public abstract
が暗黙に指定される。
インタフェースをimplementsで指定し実装クラスを定義する。
実装クラスはインタフェースの抽象メソッド定義に従い実装(オーバライド)する。実装クラスは、インタフェース定義の全てのメソッドを実装しなければインスタンスを生成できない。
インタフェースのメソッドは暗黙にpublicとされる。そのため実装クラスでオーバライドするメソッドもpublicでなければならない。
class 実装クラス implements インタフェース名, インタフェース名,... {
    メソッドの実装
    ...
}
インタフェースに定義するフィールドは、
public static final
が暗黙に指定される。
インタフェースはインスタンスフィールドを持つことはできない。staticにより静的フィールドとなるので必ず初期化を伴う。
2025.04.26
複数インタフェースからの実装クラス
interface I1 {
    int max = 10;
    int get();
    void set(int x);
}

interface I2 {
    void show();
}

class C1 implements I1, I2 {
    int a;
    public int get() { return a; }
    public void set(int x) { a = (x > max)? max : x; }
    public void show() { System.out.println(a); }; 
}

class Iface02 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(8);
        c.show();   // 8
        c.set(12);
        c.show();   // 10
    }
}
複数のインタフェースから実装クラスを定義する多重継承ができる。
インタフェースの実装クラスに限り、多重継承が許可される。原則として、implementsで指定する複数のインタフェースのメソッドを全て実装する必要がある。
2025.04.26
インタフェースの継承
interface I1 {
    int max = 10;
    int get();
    void set(int x);
}

interface Iex extends I1 {
    int get();
    void show();
}

class C1 implements Iex {
    int a;
    public int get() { return a; }
    public void set(int x) { a = (x > max)? max : x; }
    public void show() { System.out.println(a); }; 
}

class Iface03 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(8);
        c.show();   // 8
        c.set(12);
        c.show();   // 10
    }
}
インタフェースを他のインタフェース定義へextendsにより継承できる。
継承先のインタフェースは継承元の定義フィールドと抽象メソッド定義を引き継ぐ。拡張先で同じメソッドを重複して定義する場合は、引数と戻り値は同じでなければならない。
2025.04.26
インタフェースのstaticメソッド
interface I1 {
    int max = 10;
    int get();
    void set(int x);
    static void myname() {
        System.out.println("Interface I1");
    }
}

class C1 implements I1 {
    int a;
    public int get() { return a; }
    public void set(int x) { a = (x > max)? max : x; }
}

class Iface04 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(8);
        System.out.println(c.get());    // 8
        I1.myname();                    // Interface I1
    }
}
インタフェースでstaticを指定するメソッドは静的メソッドとなる。
インタフェースの静的メソッドは、実装クラスで再定義しない。クラスの静的メソッドと同様に、インスタンスから呼び出すことはできない。
インタフェースの静的メソッドは暗黙にpublicとなるが、明示的にprivateを指定できる。
privateな静的メソッドは、そのインタフェースの定義内でのみ使用できる。
2025.04.26
インタフェースのdefaultメソッド 
interface I1 {
    int max = 10;
    int get();
    void set(int x);
    default void show() {
        System.out.println("max " + max);
    }
}

class C1 implements I1 {
    int a;
    public int get() { return a; }
    public void set(int x) { a = (x > max)? max : x; }
}

class Iface05 {
    public static void main(String[] args) {
        C1 c = new C1();
        c.set(8);
        System.out.println(c.get());    // 8
        c.show();                       // max 10
    }
}
インタフェース定義でdefaultを指定したメソッドには、例外として実装することができる。
インタフェースの実装クラスでは、原則として抽象メソッドを全て実装する必要があるが、例外としてインタフェースでdefaultで定義したメソッドは実装クラスでの実装を省略できる。また、defaultのメソッドを実装クラスでオーバーライドしてかまわない。オーバーライドされない場合は、インタフェースのdefaultの実装が適用される。
インタフェースのdefaultメソッドを、実装クラスから呼び出す場合は次のようにすればよい。
インタフェース名.super.デフォルトメソッド()
interface I1 {
    default void show() {
        System.out.println("a");
    }
}

class C1 implements I1 {
    public void show() {
        I1.super.show();
    }
}
多重継承したインタフェースのデフォルトのメソッドを使うときに、複数のインタフェース間で、同じデフォルトメソッドが定義され重複した場合は、コンパイラが関連付けを決定できないのでエラーとなる。同名でも引数か戻り値が異なっていれば衝突にはならない。
2025.04.26
インタフェース型によるポリモーフィズム
interface I1 {
    void show();
}

class C1 implements I1 {
    public void show() {
        System.out.println("implements C1");
    }
}

class C2 implements I1 {
    public void show() {
        System.out.println("implements C2");
    }
}

class Iface06 {
    public static void main(String[] args) {
        I1[] c = new I1[2];
        c[0] = new C1(); 
        c[1] = new C2(); 
        for (int i = 0; i < c.length; i++)
            c[i].show();        // implements C1
                                // implements C2
    }
}
スーパークラス型の変数からサブクラスのインスタンスを呼び出すポリモーフィズムと同様に、インタフェース型の変数に実装クラスのインスタンスを生成して、インタフェース型の変数から異なる実装クラスの同一メソッドを呼び出すことができる。
2025.04.26
無名クラスによるサブクラス実装
class S1 {
    String msg = "";
    S1(String m) {
        msg = m;
    }
    void show() {
        System.out.println(msg);
    }
}

class Anon01 {
    public static void main(String[] args) {
        S1 c = new S1("Anony") {
            void show() {
                System.out.println("new-" +  msg);
            }
        };
        c.show();   // new-Anony
    }
}
スーパークラスを継承するサブクラスを、newの時点で実装してすぐにインスタンスを生成できる。その場合、そのインスタンスのサブクラスは名前がない(付ける必要がない)無名クラスになる。
new スーパークラス (引数) {
    サブクラスの実装
}:
2025.04.26
インタフェースの無名実装クラス
interface I1 {
    void show();
}

class Anon02 {
    public static void main(String[] args) {
        I1 c = new I1() {
            public void show() {
                System.out.println("Anony Interface");
            }
        };
        c.show();   // Anony Interface
    }
}
インタフェースの実装クラスを、newの時点で実装してすぐにインスタンスを生成できる。その場合、そのインスタンスの実装クラスは名前がない(付ける必要がない)無名クラスになる。
2025.04.26
関数型インタフェース
@FunctionalInterface
interface FI {
    void show(String s);
}

class C1 {
    void msg(String s, FI f) {
        f.show(s);
    }
}

class FuncIf01 {
    static void print1(String msg) {
        System.out.printf("[%s]\n", msg);
    }
    static void print2(String msg) {
        System.out.printf("(%s)\n", msg);
    }
    public static void main(String[] args) {
        C1 c = new C1();
        c.msg("ABC", FuncIf01::print1);
        c.msg("ABC", FuncIf01::print2);
    }
}
[ABC]
(ABC)
抽象メソッドが一つだけ定義されているインタフェースは「関数型インタフェース」となる。
関数型インタフェースの実装クラスを定義しなくても、そこに唯一定義しているメソッドと同じ戻り値と引数型で定義した別のメソッドを、インタフェースから実装したクラスのインスタンスのように扱うことができる。
メソッドの戻り値と引数が同一ならば(メソッド名が異なっていても)、暗黙にインタフェースの抽象メソッドをオーバライドしたもの呼び出すことができる。
関数型インタフェースは、抽象メソッドが単一であればdefaultとstaticの定義が含まれていてもかまわない。関数型インタフェースを定義するときに、@FunctionalInterfaceアノテーションを付けることで、コンパイラが関数型インタフェースの要件を満たしているかどうかをチェックしてくれる。
2025.04.26
関数型インタフェースによるラムダ式
@FunctionalInterface
interface FI {
    void show(String s);
}

class FuncIf02 {
    public static void main(String[] args) {
        FI f1 = new FI() {
            @Override
            public void print(String s) {
                System.out.printf("[%s]\n", s);
            }
        };
        f1.show("ABC");
        FI f2 = (String s) -> { System.out.printf("[%s]\n", s); }; 
        f2.show("DEF");
    }
}
[ABC]
[DEF]
関数型インタフェースで定義する抽象メソッドのオーバライドを「ラムダ式」で記述できる。
(メソッド引数) -> { メソッドの実装 }
() -> { メソッドの実装 }
上の例のf2がラムダ式であり、f1はそのラムダ式と同等なことを無名クラスで実装している。
関数型インタフェースで抽象メソッドをオーバライドする方法よりも、ラムダ式ならば実装クラスもメソッド名も省略して、引数と処理だけで実装できる。
ラムダ式は次の省略ができる。
ラムダ式の引数型が関数型インタフェース定義で明らかな場合、型名を省略して引数名のみに省略できる。
引数が一つの場合は()が省略できる。
メソッドの実装が1行の場合はブロック{}が省略できる。
ラムダ式の実装がreturn文の1行のみの場合、ブロックとreturnが省略できる。
FI f2 = (s) -> { System.out.printf("[%s]\n", s); }; 
FI f2 = (s) -> System.out.printf("[%s]\n", s); 
FI f2 = s -> System.out.printf("[%s]\n", s); 

() -> { return x; };  ⇒  () -> x;
2025.04.26
ジェネリッククラスの定義
class G1<T> {
    T a;
    G1(T x) {
        a = x;
    }
    T get() {
        return a;
    }
}

class Gene01 {
    public static void main(String[] args) {
        G1<String> gs = new G1<String>("ABCDE");
        String s = gs.get();
        System.out.println(s);      // ABCDE
        G1<Integer> gi = new G1<Integer>(100);
        int i = gi.get();
        System.out.println(i);      // 100
    }
}
ジェネリクスにより、クラス定義中に使用する具体的なデータ型を決定せずにクラスを定義できる。
class クラス名<型パラメータ, ...> {
    ...
クラス内では型パラメータの名前を仮のデータ型として使用する。
ジェネリクスクラスを使用するときに、<〜>の中に具体的なデータ型名を与えることで実際のクラス定義が確定する。型パラメータにはクラス名や数値型名などを任意に設定できる。
クラス名<型名>
定義時の型パラメータの名前は任意だが、データの性質がわかるような大文字の1文字にすることが多く、よく見られるものに次がある。
T 一般的な型(Type)
E 配列やリストの要素の型(Element)
K ハッシュ・辞書などのキーとして(Key)
V ハッシュ・辞書などの値として(Value)
class G1<T, V> {
    T a;
    V b;
    G1(T x, V y) {
        a = x;
        b = y;
    }
    V get(T x) {
        if (x == a)
            return b;
        else
            return null;
    }
}

class Gene02 {
    public static void main(String[] args) {
        G1<String, Integer> g = new G1<String, Integer>("ABC", 100);
        try {
            int i = g.get("ABC");
            System.out.println(i);  // 100
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
型パラメータはカンマで複数設定できる。
2025.04.26
ジェネリック型のスーパークラスを指定
class G1<T extends Number> {
    T a;
    G1(T x) {
        a = x;
    }
    T get() {
        return a;
    }
}

class Gene03 {
    public static void main(String[] args) {
        G1<Integer> gi = new G1<Integer>(100);
        int i = gi.get();
        System.out.println(i);      // 100

        //G1<string> gs = new G1<String>("ABC");    // エラー 
    }
}
ジェネリクスの型パラメータが継承するスーパークラスをextendsで指定することにより、型パラメータに与えるものを、そのスーパークラスのサブクラスに限定することができる。
2025.04.26
ジェネリクスメソッド
class G1 {
    <T> void show(T x) {
        System.out.println(x.getClass());
    }
}

class Gene04 {
    public static void main(String[] args) {
        G1 g = new G1();
        g.<String>show("ABCDE");
        g.<Double>show(1.23);
    }
}
class java.lang.String
class java.lang.Double
メソッド定義に対しジェネリック型を適用できる。
型パラメータはメソッド内のみで使用できる。
<型パラメータ, ...> 戻り値 メソッド(
    ...
2025.04.26
Stringのメソッド
class Str01
{
    public static void main(String[] args)
    {
        String s1 = new String("XYZ東西");                  // コンストラクタ
        String s2 = "XYZ東西";                              // リテラルの代入

        System.out.println(String.valueOf(200));            // 数値の文字列変換

        System.out.println("ABCDE".length());               // 5 文字数
        System.out.println("あいう".length());              // 3
        System.out.println("あいう".codePointCount(0, "あいう".length()));  // 3

        System.out.println("ABC".equals("ABD"));            // false 文字列の比較
        System.out.println("ABC".compareTo("ABD"));         // -1

        System.out.println("".isEmpty());                   // true
        System.out.println(" \t\n".isBlank());              // true 空白文字のみの場合true

        System.out.println("ABCDEABCDE".indexOf("CD"));     // 2 部分文字列の位置検索
        System.out.println("ABCDEABCDE".lastIndexOf("CD")); // 7
        System.out.println("ABCDEABCDE".indexOf("CD", 3));  // 7
        System.out.println("ABCDEABCDE".indexOf("XY"));     // -1 結果なし

        System.out.println("ABCDE".contains("CD"));         // true  部分文字列が含まれているか
        System.out.println("ABCDE".startsWith("ABC"));      // true
        System.out.println("ABCDE".endsWith("CDE"));        // true

        System.out.println("ABCDE".substring(2, 4));        // CD 部分文字列 
        System.out.println("ABCDE".substring(2));           // CDE

        System.out.println("ABCDE".charAt(3));              // D 指定位置の文字

        System.out.println("[" + "  ABC ".strip() + "]");           // [ABC] 前後空白の削除
        System.out.println("[" + "  ABC ".stripLeading() + "]");    // [ABC ]
        System.out.println("[" + "  ABC ".stripTrailing() + "]");   // [  ABC]

        for (String s : "AB CD EF".split(" "))
            System.out.println(s);                          // AB CD DF

        for (String s : "AB CD,EF".split("[ ,]"))           // AB CD DF 区切り文字は正規表現
            System.out.println(s);

        String[] sa = "AB CD EF".split(" ");
        System.out.println(String.join("", sa));            // ABCDEF 結合
        System.out.println(String.join("-", sa));           // AB-CD-EF 区切り文字を入れて結合
    }
}
以下の文字列のStringクラスの主なメソッドがある。
String.valueOf(数値) 数値を文字列に変換する
文字列.length() 文字数を返す
文字列.codePointCount(開始位置, 終了位置) Unicodeのサロゲートペアを考慮した文字数を返す
文字列.equals(文字列)
文字列.compareTo(文字列)
文字列を比較する
文字列.isEmpty() 空文字列かどうか
文字列.isBlank() 空白文字のみの文字列かどうか
文字列.indexOf(部分文字列 [,開始位置]) 部分文字列を含む位置を返す
文字列.lastIndexOf(部分文字列) 部分文字列を含む位置を返す(後方一致)
文字列.contains(部分文字列) 部分文字列を含むかどうか
文字列.startsWith(部分文字列) 先頭が部分文字列と一致するか
文字列.endsWith(部分文字列) 末尾が部分文字列と一致するか
文字列.substring(開始位置[,停止位置]) 開始位置から終了位置の前の区間の部分文字列を返す
停止位置を省略した場合は末尾まで
文字列.charAt(位置) 指定位置の1文字を返す
文字列.strip() 文字列の前後の空白文字を削除する
文字列.stripLeading() 文字列の先頭部分の空白文字を削除する
文字列.stripTrailing() 文字列の後方部分の空白文字を削除する
文字列.split(区切り文字) 区切り文字で文字列を分割した文字列配列を返す
区切り文字は正規表現で指定できる
String.join(区切り文字, String[]) 文字列配列を区切り文字で連結して1つの文字列に結合する
文字列のリテラルは、Stringクラスのインスタンスとして直接メソッドを呼び出すことができる。
"文字列".メソッド()
文字列を比較するとき「==」でも比較できるが、その場合は文字列の内容が同じかどうかではなく、オブジェクトの一致の比較になる。文字列の内容の一致を調べるならば、equals()メソッドで比較すべきである。
文字数を取得する場合は通常はlength()を使うが、サロゲートペア文字を考慮する場合はcodePointCount()を使うほうが正確である。
2025.04.26
文字列フォーマット
class Str02 {
    public static void main(String[] args) {
        System.out.println(String.format("%d", 100));
        System.out.println(String.format("%4d", 100));
        System.out.println(String.format("%04d", 100));
        System.out.println(String.format("%04x", 200));
        System.out.println(String.format("%.2f", 123.456));
        System.out.println(String.format("%4.1f", 123.456));
        System.out.println(String.format("%s", "ABC"));
        System.out.println(String.format("%5s", "ABC"));
        System.out.println(String.format("%b", false));
        System.out.println(String.format("%c", 'あ'));
        System.out.println(String.format("%%"));        // %%で%という文字に置き換わる
        System.out.println(String.format("%s-%d", "ABC", 123));
    }
}
100
 100
0100
00c8
123.46
123.5
ABC
  ABC
false

%
ABC-[ 123]
format()メソッドは、文字列をC言語のprintf的な書式指定子により数値や文字列を整形する。
String.format(書式指定文字列, 値, 値,...) 
    書式指定子= %0n.pT
    0: ゼロで空白桁をうめる場合
    n: 総桁数
    p: 桁数うち小数点以下桁数
    T: 型指定
型指定
d 整数
x、X 16進数(Xは英字を大文字に)
f 浮動小数点数
s 文字列
b, B 真理値をtrue/false表記に(Bは大文字に)
c 文字
2025.04.26
基本型のラッパークラス
class Wrap01 {
    public static void main(String[] args) {
        Integer iobj1 = 100;        // Boxing
        int i1 = iobj1;             // Unboxing
        Integer iobj2 = Integer.valueOf(100);
        int i2 = iobj2.intValue(); 
    }
}
基本型には、それぞれのデータを扱うメソッド等の機能を実装したラッパークラスが用意されている。
ラッパークラス 基本型
Integer int
Short short
Long long
Float float
Double double
Character char
Byte byte
Boolean boolean
基本型と、それに対応するラッパークラスは、互いにインスタンス生成やメソッドを呼び出すことなく変換される。
基本型をラッパークラスの変数に代入するときを「ボクシング(Boxing)」、その逆を「アンボクシング(Unboxing)」と呼ぶ。
2025.04.26
ラッパークラスの変換メソッド
class Wrap02 {
    public static void main(String[] args) {
        Integer iw = Integer.valueOf(100);
        int i = iw.intValue();
        Integer is1 = Integer.parseInt("200");
        Integer is2 = Integer.parseInt("ab", 16);   // =171
        String s1 = Integer.toString(i);
        String s2 = Integer.toString(i, 8);     // ="144"
        String s3 = Integer.toHexString(i);     // ="64"
        String s4 = is.toString();
        System.out.println("min=" + Integer.MIN_VALUE); // min=-2147483648
        System.out.println("max=" + Integer.MAX_VALUE); // max=2147483647
    }
}
各基本型のラッパークラスには、共通して次の変換メソッドが備えられている。
Integer.valueOf(int)
Double.valueOf(double)
基本型からラッパークラスのインスタンスを返す
intValue()
doubleValue()
ラッパークラスのインスタンスの基本型を返す
Integer.parseInt(文字列 [,基数])
Double.parseDouble(文字列)
文字列から数値に変換してラッパークラスのインスタンスを返す
文字列が表現している基数を指定可
toString()
toString(数値 [,基数])
ラッパークラスのインスタンス、数値を文字列に変換
文字列表記の基数を指定可
toHexString(数値) 数値を16進数の文字列に変換
上記のIntegerやDoubleは、そのままLong、Short、Float、Booleanに置き換えられる。
2025.04.26
列挙クラス
enum Level {
    ERROR,
    WARNING,
    INFO,
    VERBOSE
}

class Enum01 {
    static void showlevel(Level lv) {
        System.out.println(lv.ordinal());
    }
    public static void main(String[] args) {
        showlevel(Level.INFO);      // 2
    }
}
enumは、昇順の数値の静的定数を定義する列挙クラスを定義する。
定数の先頭は0から始まる値が割り当てられる。列挙定数の番号はordinal()メソッドで取得できる。
2025.04.26
例外捕捉
class Excep01 {
    public static void main(String[] args) {
        try {
            int[] ar;
            ar = new int[3];
            ar[3] = 10;
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of range");
        }
    }
}
Array out of range
try-catch-finally構文により、実行中の例外の発生時に実行を分岐させて処理できる。
try-catch-finally構文は、実行中に発生する例外を補足する区間と補足後の処理ブロックを記述する。
try {
    例外を補足したい処理ブロック
} catch (例外クラス 変数) {
    例外補足時に実行する処理
} catch (...) {
    ...
} finally {
    例外発生に関わらない共通処理
}
tryブロック内で例外が発生時点で指定される例外に該当するcatchブロックへジャンプする。
catchブロックには補足したい例外クラスと、例外発生時にそのインスタンスを受け取る変数を宣言する。その場合varによる型推論は使うことはできない。
catchブロックは複数設けることができる。
finallyブロックは、例外発生に関わらず必ず最後に実行する処理を記述する。finallyブロックは省略できる。
2025.04.26
複数種類の例外捕捉
class Excep02 {
    public static void main(String[] args) {
        try {
            int[] ar;
            ar = new int[3];
            ar[2] = 10;
            ar[2] = 10 / 0;
        } catch(ArrayIndexOutOfBoundsException | ArithmeticException e) {
            System.out.println(e);
        }
    }
}
java.lang.ArithmeticException: / by zero
catchブロックで複数の中で何れかの例外を捕捉し、それらに同一の処理を実行させたい場合は、例外クラスを「|(OR)」で区切って複数指定すればよい。
class Excep03 {
    public static void main(String[] args) {
        try {
            int[] ar;
            ar = new int[3];
            ar[2] = 10;
            int a = 10 / 0;
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of range");
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
java.lang.ArithmeticException: / by zero
複数の種類の例外のそれぞれに対応する例外処理を実行させたい場合は、catchブロックをそれらの例外ごとに記述すればよい。
2025.04.26
finallyブロック
class Excep04 {
    public static void main(String[] args) {
        int[] ar;
        ar = new int[3];
        try {
            ar[3] = 10;
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of range");
        } finally {
            System.out.println("Finally1");
        }
        try {
            ar[2] = 10;
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of range");
        } finally {
            System.out.println("Finally2");
        }
    }
}
Array out of range
Finally1
Finally2
finallyブロックは、例外発生に関わらず必ず最後に実行される。
その必要がなければfinallyブロックは省略できる。
2025.04.26
非チェック例外の伝搬 throws
class Excep05 {
    static void func() {
        int[] ar;
        ar = new int[3];
        ar[3] = 10;
    }
    public static void main(String[] args) {
        try {
            func();
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of range");
        }
    }
}
Array out of range
tryブロックの中で呼び出したメソッドの中で発生した非チェック例外(unchecked例外)は、そのメソッドの中でtry-catchで捕捉処理しなければ自動的に呼び出し元に伝搬する。
import java.io.*;

class Excep06 {
    static void func() throws FileNotFoundException {
        BufferedReader br = new BufferedReader(new FileReader("abcdefg.xyz"));
    }
    public static void main(String[] args) {
        try {
            func();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
java.io.FileNotFoundException: abcdefg.xyz (そのようなファイルやディレクトリはありません)
メソッドの中で発生するチェック例外(checked例外)を、メソッド内で捕捉せずに呼び出し元に処理を委ねる場合は、その発生する可能性のある例外をthrowsで宣言する必要がある。
throwする例外の種類がメソッドに複数ある場合は、「,」で区切って宣言する。
void func() throws IOException, NullPointerException, ... {
非チェック例外とはRuntimeExceptionクラスを継承する例外クラスである。それ以外のExceptionクラスのサブクラスは、try-catchで処理するか、throwsにより呼び出し元に処理を委ねることを宣言する必要がある。
以下は代表的なチェック例外と非チェック例外である。
チェック例外 IOException
FileNotFoundException
ClassNotFoundException
非チェック例外 NullPointerException
ArithmeticException
ArrayIndexOutOfBoundsException
NumberFormatException
2025.04.26
例外のスーパークラスで全ての例外を捕捉
class Excep07 {
    public static void main(String[] args) {
        try {
            int[] ar;
            ar = new int[3];
            ar[3] = 10;
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
catchの例外クラスにExceptionクラスを指定すると、全ての例外を捕捉できる。
Exceptionは全ての例外クラスのスーパークラスである。例外の変数で具体的なサブクラスを参照できる。
2025.04.26
例外クラスの作成
class TheException extends Exception {
}

class Excep08 {
    public static void main(String[] args) {
        try {
            throw new TheException();
        } catch(Exception e) {
            System.out.println(e);  
        }
    }
}
TheException
Exceptionクラスをスーパークラスとするサブクラスを定義することで、ユーザ例外を定義できる。
作成した例外のインスタンスをnewで生成してthrowで送出する。
2025.04.26
例外の送出
class TheException extends Exception {
}

class Excep09 {
    static void func() throws Exception {
        throw new TheException();
    }
    public static void main(String[] args) {
        try {
            func();
        } catch(Exception e) {
            System.out.println(e);  
        }
    }
}
TheException
throwは、例外クラスのインスタンスを指定し例外を任意に発生させる。
throw 例外クラスのインスタンス
throw new 例外クラス()
2025.04.26
例外の再送出
class Excep10 {
    static private double divzero(int i) {
        try {
            return 10 / i; 
        } catch (ArithmeticException e) {
            throw e;
        }
    }
    public static void main(String[] args) {
        try {
            divzero(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
java.lang.ArithmeticException: / by zero
    at Excep10.divzero(Excep10.java:5)
    at Excep10.main(Excep10.java:12)

例外が発生し捕捉した例外オブジェクトを、catchブロック内からthrowにより呼び出し元へ伝搬するように再送出できる。
2025.04.26
例外のトレースログ表示
class Excep11 {
    public static void main(String[] args) {
        try {
            int[] ar;
            ar = new int[3];
            ar[3] = 10;
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    at Excep11.main(Excep11.java:7)
ExceptionクラスのprintStackTrace()メソッドは、発生した例外のトレースログを表示する。
トレースログには例外発生までのメソッドの呼び出し履歴が現れる。
2025.04.26
try-with-resource構文
import java.io.FileOutputStream;

class Excep12 {
    public static void main(String[] args) {
        try (var out = new FileOutputStream("outfile.txt")) {
            out.write("ABCDE".getBytes(), 0, 5);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
ファイル入出力等で、リソースを閉じる(close)処理をfinallyブロックで行うように実装することが一般的である。その場合、try-with-resource構文により、クローズ操作が必要なリソースオブジェクトを最初に記述することで、finallyで実装すべきクローズ操作が自動的に組み込まれる。
try (リソース[;リソース;]) {
    ...
} catch (...) {
    ...
}
try(...)の中に、処理終了時に閉じる処理を省略したいリソースのオブジェクトを記述する。
リソースの部分は一般に、対象とするインスタンスの変数をその場でnewで生成して代入する。「;」で区切って複数のリソース(文)を記述できる。
try-with-resource構文が使えるリソースのオブジェクトとは、AutoCloseableインターフェースまたはCloseableインタフェースの実装クラスである。代表的なものがファイルやソケットである。
2025.04.26
条件判定でアサーション例外
class Excep13 {
    static private void set(int i) {
        assert i < 10 : "invalid value=" + i;
        System.out.println(i);
    }
    public static void main(String[] args) {
        try {
            set(13);
        } catch (AssertionError e) {
            e.printStackTrace();
        }
    }
}
java.lang.AssertionError: invalid value=13
    at Excep13.set(Excep13.java:4)
    at Excep13.main(Excep13.java:9)
assert文は、条件式がFalseの場合にAssertionExceptionを送出する。
アサーション例外発生時に表示するメッセージ文字列を指定できる。
assert 条件式 : メッセージ文字列;
Javaの実行時のデフォルトではassert機能は無効で、条件の結果を無視して処理は続行する。
assertを有効にするには、実行時に「-enableassertions」または「-ea」オプションを付ける必要がある。
$ java -ea Excep13
2025.04.26
可変文字列クラス StringBuilder
class StrBld01
{
    public static void main(String[] args)
    {
        StringBuilder sb = new StringBuilder(); // コンストラクタ

        sb.append("ABC");                       // 文字列の追加 
        sb.append(3.14);                        // 数値を文字列として追加
        System.out.println(sb.toString());      // ABC3.14
        sb.insert(3, "南北");                   // 挿入(3文字目から)
        System.out.println(sb.toString());      // ABC南北3.14
        sb.delete(5, 7);                        // 削除(5〜6文字を削除)
        System.out.println(sb.toString());      // ABC南北14
        sb.replace(3, 5, "上下左右");           // 置換(3〜4文字を置き換える)
        System.out.println(sb.toString());      // ABC上下左右14

        System.out.println(sb.length());        // 9 
        sb.setLength(5);                        // 文字数の設定
        System.out.println(sb.toString());      // ABC上下

        sb = new StringBuilder("XYZ東西");      // コンストラクタに初期値を指定
        System.out.println(sb.toString());
    }
}
StringBuilderクラスは、文字列を格納し、更に文字列の追加や削除のような変更ができる。
Stringクラスでは生成時の文字列から変更ができない。「文字列 + 文字列」のような文字列の連結は、連結後の文字列のStringオブジェクトを新たに生成しなおしているので、見た目よりも実際は多くの処理がかかっている。頻繁な文字列加工はStringBuilderクラスを利用する方が効率的である。
StringBuilderクラスは、文字列の追加・挿入・削除・置換の各種編集メソッドが用意されている。
append(文字列) 末尾に追加
insert(位置文字列) 先頭を0とした位置から開始するように挿入
delete(開始終了) 開始から終了の手前までを削除
replace(開始終了文字列) 開始から終了の手前までの部分文字列を文字列に置き換える
削除と置換の開始と終了の範囲の中に、終了に指定した位置の文字は含まないことに注意が必要である。
文字列の長さはlength()メソッドで取得する。setLength()メソッドは文字数を変更する。
length() 文字数を返す
setLength(文字数) 文字数を変更する
setLength()メソッドで指定する文字数が格納されている文字列より短い場合は、指定文字数以降の文字列は削除される。逆に現在の文字列より長く指定した場合は、末尾の不足分はnull文字(\u0000)で埋められる。
setLength()メソッドは、決して固定バッファのように格納文字数の上限を設定するものではない。
2025.04.26
マルチスレッド対策可変文字列クラス StringBuffer
class StrBld02
{
    public static void main(String[] args)
    {
        StringBuffer sb = new StringBuffer();   // コンストラクタ

        sb.append("ABC");                       // 文字列の追加 
        sb.append(3.14);                        // 数値を文字列として追加
        System.out.println(sb.toString());      // ABC3.14
        sb.insert(3, "南北");                   // 挿入(3文字目から)
        System.out.println(sb.toString());      // ABC南北3.14
        sb.delete(5, 7);                        // 削除(5〜6文字を削除)
        System.out.println(sb.toString());      // ABC南北14
        sb.replace(3, 5, "上下左右");           // 置換(3〜4文字を置き換える)
        System.out.println(sb.toString());      // ABC上下左右14
    }
}
StringBufferクラスはStringBuilderクラスと機能が全く同じな互換クラスである。
StringBufferクラスがStringBuilderクラスと異なるのは、マルチスレッドに対応していることである(スレッドセーフ)。
StringBufferクラスの方がスレッドの考慮が不要で安全に使用できるが、内部処理が複雑になるため簡素なStringBuilderクラスよりも処理に時間がかかる。
ファイル入出力
2025.04.26
テキストファイルの書き込み・読み出し
import java.io.*;

class File01
{
    public static void main(String[] args)
    {
        try {
            FileWriter fw = new FileWriter("test.txt");
            fw.write("ABCDEFG\n");
            fw.close();

            FileReader fr = new FileReader("test.txt");
            int c;
            while ((c = fr.read()) != -1)
                System.out.print((char)c);  // ABCDEFG
            fr.close();
        } catch (IOException e){
        }
    }
}
$ cat test.txt
ABCDEFG
FileWriterクラスとFileReaderクラスは、ファイルへの文字列の書き込みと読み出しの機能を提供する。
コンストラクタに入出力を行うファイルのパス、もしくはFileクラスのオブジェクトを指定することで当該ファイルを開く。指定するファイルが存在しない場合は、FileWriterでは新規作成し、FileReaderでは例外(FileNotFoundException)となる。
生成されたインスタンスのwrite()・read()メソッドによりファイルへの文字列の書き込み・読み出しを行う。
入出力処理が終了してファイルを閉じるときはclose()メソッドを呼び出す。
int read() 1文字を読み出す。ファイル末尾に到達した場合は-1を返す。
write(文字列) 文字列を書き込む。
import java.io.*;

class File02
{
    public static void main(String[] args)
    {
        try {
            FileWriter fw = new FileWriter("test.txt", true);
            fw.write("HIJKLMN\n");
            fw.close();
        } catch (IOException e){
        }
    }
}
$ cat test.txt
ABCDEFG
HIJKLMN
FileWriterクラスのコンストラクタの第二引数(ブール値)は追加書き込みのオプションとなっており、trueを指定するとファイル末尾への追加書き込みで開かれる。
write()メソッドで書き込んだ文字列は、既存の内容の末尾に追記される。
falseの場合、または指定しない場合は、ファイルの既存の内容を空にしてからの書き込みになる。
2025.04.26
バイトデータのファイルストリーム入出力
import java.io.*;

class File03
{
    public static void main(String[] args)
    {
        try {
            FileOutputStream fo = new FileOutputStream("test.txt");
            byte[] b = {65, 66, 67, 68, 69, 70, 71};
            fo.write(b);
            fo.close();
            FileInputStream fi = new FileInputStream("test.txt");
            int c;
            while ((c = fi.read()) != -1) 
                System.out.print((char)c);
            fi.close();
        } catch (IOException e){
        }
    }
}
$ cat test.txt
ABCDEFG
FileOutputStreamクラス、およびFileInputStreamクラスは、バイトデータのファイルへの入出力機能を提供する。
コンストラクタに入出力を行うファイルのパス、もしくはFileクラスのオブジェクトを指定することで当該ファイルを開く。指定するファイルが存在しない場合は、FileOutputStreamでは新規作成し、FileInputStreamでは例外(FileNotFoundException)となる。
生成されたインスタンスのwrite()・read()メソッドによりファイルへのバイトデータの書き込み・読み出しを行う。入出力処理が終了してファイルを閉じるときはclose()メソッドを呼び出す。
int read() 1バイトを読み出す。ファイル末尾に到達した場合は-1を返す。
write(byte[]) バイトデータ配列を順番に書き出す。
2025.04.26
ファイルストリームの文字列変換入出力
import java.io.*;

class File04
{
    public static void main(String[] args)
    {
        try {
            FileOutputStream fo = new FileOutputStream("test.txt");
            OutputStreamWriter sw = new OutputStreamWriter(fo, "UTF-8"); 
            sw.write("あいうえお", 1, 3);
            sw.close();
            FileInputStream fi = new FileInputStream("test.txt");
            InputStreamReader sr = new InputStreamReader(fi, "UTF-8");
            int c;
            while ((c = sr.read()) != -1) 
                System.out.print((char)c);      // いうえ
            sr.close();
        } catch (IOException e){
        }
    }
}
$ cat test.txt
いうえ
OutputStreamWriterクラス、およびInputStreamReaderクラスは、バイトデータのストリームを文字列に変換しながらファイルへの入出力を行う。
コンストラクタにはOutputStream・InputStreamクラスを継承したストリームクラスを指定する。
ファイル入出力の場合は、FileOutputStream・FileInputStreamクラスにより開いたファイルを指定する。FileOutputStream・FileInputStreamクラスで開いたファイルをOutputStreamWriter・InputStreamReaderクラスを通じて文字列で扱う一連の手続きをまとめたものが、FileWriter・FileReaderクラスである。ただし、それらではCharsetは指定できない。
生成されたインスタンスのwrite()・read()メソッドによりファイルへの指定するCharsetでの文字列の書き込み・読み出しを行う。入出力処理が終了してファイルを閉じるときはclose()メソッドを呼び出す。
2025.04.26
ファイルストリームのバッファ入出力
import java.io.*;

class File05
{
    public static void main(String[] args)
    {
        try {
            BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
            bw.write("ABCDEFGあいうえお\n");
            bw.flush();
            bw.close();
            BufferedReader br = new BufferedReader(new FileReader("test.txt"));
            String s = br.readLine();
            System.out.println(s);  // ABCDEFGあいうえお
            br.close();
        } catch (IOException e){
        }
    }
}
$ cat test.txt
ABCDEFGあいうえお
BufferedWriterクラス、およびBufferedReaderクラスは、ストリームを内部的にバッファリングを行うことで効率的に入出力を行う。
コンストラクタにはWrite・Readerクラスの継承クラスを指定する。一般にはFileWriter・FileReaderクラスやOutputStreamWriter・InputStreamReaderクラスのオブジェクトである。
生成されたインスタンスのwrite()・read()メソッドによりファイルストリームへの書き込み・読み出しを行う。
write()メソッドでは、逐一ファイルへ出力せずに内部的にバッファリングを行う。flush()メソッドによりバッファの内容を強制的にファイルに書き出すことができる。readLine()メソッドはテキストファイルを行単位で読み出す。終端を読み出した場合はnullを返す。
入出力処理が終了してファイルを閉じるときはclose()メソッドを呼び出す。BufferedWriterではバッファを書き出してから閉じる。
2025.04.26
try-with-resourceによるcloseの省略
import java.io.*;

class File06
{
    public static void main(String[] args)
    {
        try (FileWriter fw = new FileWriter("test.txt")) {
            fw.write("ABCDEFG\n");
        } catch (IOException e){
        }
    }
}
close()メソッドをtryブロックの中に実装すると、処理中に例外が発生した場合はclose()メソッドが呼び出されない。
例外を考慮すればclose()メソッドはfinallyブロックで呼び出す必要があるが、そのときもtry〜catchにより例外捕捉する必要があるので、かなり面倒なものになる。
close()メソッドを持つ以下のクラス
FileWriter・FileReader
FileOutputStream・FileInputStream
OutputStreamWriter・InputStreamReader
BufferedWriter・BufferedReader
は、AutoCloseableインタフェースを実装しているので、try-with-resource構文を使うことでclose()メソッド呼び出しを省略できる。
import java.io.*;

class File07
{
    public static void main(String[] args)
    {
        try (FileWriter fw = new FileWriter("test.txt");
            BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write("ABCDEFGあいうえお\n");
            bw.flush();
        } catch (IOException e){
        }
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String s = br.readLine();
            System.out.println(s);
        } catch (IOException e){
        }
    }
}
try-with-resourceのtry()の中は複数行記述できる。それぞれのリソースのクローズは適切に行われる。
2025.04.26
Fileクラスでのファイルの指定
import java.io.*;

class File08
{
    public static void main(String[] args)
    {
        File f = new File("test.txt");
        try (FileWriter fw = new FileWriter(f);
            BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write("ABCDEFGあいうえお\n");
            bw.flush();
        } catch (IOException e){
        }
    }
}
各種クラスのコンストラクタに指定するファイルはパス名を文字列で指定できるが、Fileクラスのインスタンスでも指定できる。
コレクションクラス
2025.04.26
可変長配列 ArrayList
import java.util.List;
import java.util.ArrayList;

class ColArr01
{
    public static void main(String args[])
    {
        List<Integer> lst = new ArrayList<>();
        lst.add(100);
        Integer n = 200;
        lst.add(n);
        int a = 300;
        lst.add(a);
        lst.add(100);
        System.out.println(lst.size()); // 4
        for(int i: lst)
            System.out.println(i);      // 100 200 300 100
        lst.remove(1);                  // インデックスの要素を削除
        lst.set(1, 111);                // インデックスの要素を設定(変更)
        lst.add(2, 222);                // インデックスに挿入
        System.out.println(lst.get(1)); // 111 インデックスの要素を取得
        for(int i: lst)
            System.out.println(i);      // 100 111 222 100
        lst.clear();
    }
}
Listインタフェースは、任意のデータ型の要素からなる可変長な配列の機能を提供する。
ArrayListはListインタフェースの代表的な実装クラスである。可変長配列のデータ型は、ジェネリック型により任意に指定する。
インスタンスをListインタフェースではなく、次のように直接実装クラスのインスタンスとして生成してもかまわない。
ArrayList<Integer> lst = new ArrayList<>();
newの実装クラスのジェネリック型指定を省略せずに記述してもかまわない。
List<Integer> lst = new ArrayList<Integer>();
Listインタフェースの主なメソッドは以下の通り。
add([int index,] 要素) 要素を追加する。
set(int index, 要素) 指定インデックスに要素を設定する。
要素型 get(int index) 指定インデックスの要素を取得する。
remove(int index) 指定要素を削除する。削除した前後の要素は連結される。
int size() 要素数を返す。
clear() 要素を全て削除して空にする。
boolean isEmpty() リストが空か。
2025.04.26
セット HashSet
import java.util.Set;
import java.util.HashSet;

class ColSet01
{
    public static void main(String args[])
    {
        Set<String> hs = new HashSet<>();
        hs.add("ABC");
        hs.add("DEF");
        hs.add("GHI");
        hs.add("ABC");                  // 追加されない(セットは重複しない)
        System.out.println(hs.size());  // 3
        for (String s: hs)
            System.out.println(s);      // ABC DEF GHI
        hs.remove("DEF");               // 指定要素を削除
        for (String s: hs)
            System.out.println(s);      // ABC GHI
        hs.clear();
    }
}
Setインタフェースは、任意のデータ型の要素からなるオブジェクトのセット(集合)を提供する。
HashSetはSetインタフェースの代表的な実装クラスである。セットは同じ要素が重複しない順不同な集合である。
配列のようなインデックスによる順番管理はされてないが、イテレーションにより内部的な順番で取り出して参照することはできる。
セットのデータ型は、ジェネリック型により任意に指定する。
インスタンスをSetインタフェースではなく、次のように直接実装クラスのインスタンスとして生成してもかまわない。
HashSet<String> hs = new HashSet<>();
newの実装クラスのジェネリック型指定を省略せずに記述してもかまわない。
Set<String> hs = new HashSet<String>();
Setインタフェースの主なメソッドは以下の通り。
add(要素) 要素を追加する。
remove(要素) 指定要素を削除する。
int size() 要素数を返す。
clear() 要素を全て削除して空にする。
boolean isEmpty() セットが空か。
2025.04.26
マップ HashMap
import java.util.Map;
import java.util.HashMap;

class ColMap01
{
    public static void main(String args[])
    {
        Map<String, Integer> hm = new HashMap<>();
        hm.put("ABC", 100);
        hm.put("DEF", 200);
        hm.put("GHI", 300);
        for (String key: hm.keySet())               // キーをイテレーション
            System.out.println(key + hm.get(key));  // ABC100 DEF200 GHI300
        hm.remove("DEF");                           // キーの要素を削除
        hm.replace("GHI", 333);                     // キーの値を置き換え
        for (Integer val: hm.values())              // 値をイテレーション
            System.out.println(val);                // 100 333
        hm.clear();
    }
}
Mapインタフェースは、キーと値の組み合わせた要素からなるマップ(辞書)を提供する。
HashMapはMapインタフェースの代表的な実装クラスである。マップはキーが重複しない要素が順不同な辞書の集合である。
配列のようなインデックスによる順番管理はされてないが、イテレーションにより内部的な順番で取り出して参照することはできる。
キーの集合をKeySet()メソッドで、値の集合をvalues()メソッドで取得してそれぞれをイテレーションできる。
キーと値のそれぞれのデータ型は、ジェネリック型により任意に指定する。
インスタンスをMapインタフェースではなく、次のように直接実装クラスのインスタンスとして生成してもかまわない。
HashMap<String, Integer> hm = new HashMap<>();
newの実装クラスのジェネリック型指定を省略せずに記述してもかまわない。
Map<String, Integer> hm = new HashMap<String, Integer>();
Mapインタフェースの主なメソッドは以下の通り。
put(キー) キーおよび対応する値を追加する。
値型 get(キー) キーに対応する値を取得する。
replace(キー) キーに対応する値を置き換える(変更する)。
remove(キー) キーの要素をマップから削除する。
Set<キー型> keySet() キーだけからなるセットを取得する。
Collection<値型> values() 値だけからなるコレクションを取得する。
int size() 要素数を返す。
clear() 要素を全て削除して空にする。
boolean isEmpty() マップが空か。
2025.04.26
デック(ArrayDeque)
import java.util.Deque;
import java.util.ArrayDeque;

class ColQue01
{
    public static void main(String args[])
    {
        Deque<Integer> q = new ArrayDeque<>();
        // スタックとしての振る舞い 
        q.addLast(100);                             // 末尾に追加
        q.addLast(200);
        q.addLast(300);
        for (Integer i: q) 
            System.out.println(i);                  // 100 200 300
        while (q.size() > 0)
            System.out.println(q.removeLast());     // 300 200 100

        // キューとしての振る舞い 
        q.addFirst(100);                            // 先頭に追加
        q.addFirst(200);
        q.addFirst(300);
        while (q.size() > 0)
            System.out.println(q.removeFirst());    // 300 200 100

        q.addFirst(10);
        q.clear();
    }
}
Dequeインタフェースは、任意のデータ型の要素からなるデック(両端キュー)の機能を提供する。
ArrayDequeはDequeインタフェースの代表的な実装クラスである。
デックは両端から出し入れ可能なキュー構造になっている。配列のようなインデックスによる参照はできないが、オブジェクトがデックの両端からの追加順に整列しており、両端のどちらかから順番に参照・取り出しができる。
デックのデータ型は、ジェネリック型により任意に指定する。
インスタンスをDequeインタフェースではなく、次のように直接実装クラスのインスタンスとして生成してもかまわない。
ArrayDeque<Integer> q = new ArrayDeque<>();
newの実装クラスのジェネリック型指定を省略せずに記述してもかまわない。
Deque<Integer> q = new ArrayDeque<Integer>();
Dequeインタフェースの主なメソッドは以下の通り。
addFirst(要素) 先頭に要素を追加する。
addLast(要素) 末尾に要素を追加する。
要素型 removeFirst()
要素型 pollFirst()
先頭の要素を取得しデックから取り除く。
要素型 removeLast()
要素型 pollLast()
末尾の要素を取得しデックから取り除く。
要素型 getFirst()
要素型 peekFirst()
先頭の要素を取得する。
要素型 getLast()
要素型 peekLast()
末尾の要素を取得する。
int size() 要素数を返す。
clear() 要素を全て削除して空にする。
boolean isEmpty() デックが空か。
removeやgetでデックが空の場合はNoSuchElementException例外のスローとなる。pollやpeekではそのかわりにnullを返す。
2025.04.26
双方向リスト LinkedList
import java.util.LinkedList;

class ColList01
{
    public static void main(String args[])
    {
        LinkedList<String> ll = new LinkedList<>();

        ll.addLast("ABC");
        ll.addLast("DEF");
        ll.addFirst("XYZ");
        ll.addLast("PQR");
        ll.addFirst("GHI");
        for (String s: ll)
            System.out.println(s);      // GHI XYZ ABC DEF PQR
        ll.removeFirst();               // 先頭を削除
        ll.removeLast();                // 末尾を削除
        for (String s: ll)
            System.out.println(s);      // XYZ ABC DEF
        System.out.println(ll.size());  // 3
        ll.clear();
    }
}
LinkedListクラスは、デック(Deque)と可変長な配列であるリスト(List)の両方の機能を併せ持つ双方向リストを提供する。
import java.util.LinkedList;

class ColList02
{
    public static void main(String args[])
    {
        LinkedList<String> ll = new LinkedList<>();

        ll.add("ABC");
        ll.add("DEF");
        ll.add("GHI");
        ll.add("JKL");
        for (String s: ll)
            System.out.println(s);      // ABC DEF GHI JKL
        System.out.println(ll.get(2));  // GHI インデックスの要素を取得
        ll.remove(1);                   // インデックスの要素を削除
        ll.set(0, "XYZ");               // インデックスの要素を削除
        for (String s: ll)              // インデックスの要素を設定(変更)
            System.out.println(s);      // XYZ GHI JKL
        System.out.println(ll.size());  // 3
        ll.clear();
    }
}
LinkedListクラスは、DequeインタフェースとListインタフェースの両方を実装しているので、両方のメソッドを使用できる。
デックの追加順に整列したものとして扱うことができ、同時に可変長配列としてインデックス参照による編集操作もできる。
可変長配列のリストとして使用する場合と、デックとして使用する場合で使い分けるならば、ListやDequeインタフェースの変数にインスタンスを生成してもかまわない。
List<String> ll = new LinkedList<>();
newの実装クラスのジェネリック型指定を省略せずに記述してもかまわない。
Deque<String> ll = new LinkedList<String>();