【Java復習2日目】サンプルコードから学び直すJava入門②

# サンプルコード(printf)

```java:Printf.java
import java.util.Date;

class Printf {
    public static void main(String[] args) {
        // 九九の表を表示
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= 9; j++) {
                // 
                System.out.printf("%3d", i * j);
            }
            System.out.println();
        }

        System.out.println();

        Date date = new Date();

        // 最小桁幅が3桁の10進整数
        System.out.printf("your balance: $%3d\n", 1);
        // ,(カンマ)区切りの10進整数
        System.out.printf("your deposit: ¥%,d\n", 1234567);
        // 最小桁幅が7桁,小数点第2位までの小数
        System.out.printf("your BMI: %7.2f\n", 19.37593);
        // 足りない桁を"0"で埋めた, 最小桁幅が5桁の10進整数
        System.out.printf("%05d\n", 351);
        // 現在の日付
        System.out.printf("Today is %tF.\n", date);
    }
}
```

```java:実行結果
  1  2  3  4  5  6  7  8  9
  2  4  6  8 10 12 14 16 18
  3  6  9 12 15 18 21 24 27
  4  8 12 16 20 24 28 32 36
  5 10 15 20 25 30 35 40 45
  6 12 18 24 30 36 42 48 54
  7 14 21 28 35 42 49 56 63
  8 16 24 32 40 48 56 64 72
  9 18 27 36 45 54 63 72 81

your balance: $  1
your deposit: ¥1,234,567
your BMI:   19.38
00351
Today is 2021-06-25.
```

## printf()メソッド
[参考1: Format String Syntax](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/Formatter.html#syntax)
[参考2: 日付の表示](https://rainbow-engine.com/java-printf-dateformat/)
`PrintStream`クラスで定義される、`出力フォーマット`を指定して`出力ストリーム`に文字列を書き込む(=`コンソール`に出力する)メソッド。

`出力フォーマット`は以下のように記述する。なお、ここでは`オプション`は`[...]`で表すこととする。

> %[値に挿入される記号(フラグ)][最小桁幅]変換文字

また、出力する際に改行を行う場合は、`出力フォーマット`の末尾に改行文字`\n`を挿入する。

### 定義

```java
PrintStream PrintStream.printf(String format, Object args)
// パラメータ
// format: 「出力フォーマット」を表す文字列
// args: 参照する値
```

***

# サンプルコード(キャスト演算子)

```java:Cast.java
import java.util.Scanner;

class Cast {
    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        System.out.print("int x: "); int x = stdIn.nextInt();
        System.out.print("int y: "); int y = stdIn.nextInt();

        // 右辺はint型同士の演算 -> 結果はint型
        double ave = (x + y) / 2;
        System.out.println("average without casting: " + ave);
        // 右辺はint型とdouble型の演算 -> 暗黙的な二項数値昇格(=型変換)によって結果がdouble型に変換
        ave = (x + y) / 2.0;
        System.out.println("average with implicitl binary numerical promotion: " + ave);
        // 右辺はdouble型にキャストされたint型同士の演算 -> 結果はdouble型
        ave = (double) (x + y) / 2;
        System.out.println("average with cast operator: " + ave);
    }
}
```

```java:実行結果
int x: 7
int y: 8
average without casting: 7.0
average with implicitl binary numerical promotion: 7.5
average with cast operator: 7.5
```

## 二項数値昇格
表現範囲の異なる型同士の演算時に**暗黙的**に行われる、表現範囲の狭い型の`拡大変換`。

## キャスト演算子
`(<T>)`で表される、演算結果を指定した`型`に変換する演算子。

***

# サンプルコード(丸め誤差への対策)

```java:CalcError.java
class CalcError {
    public static void main(String[] args) {
        float sum = 0.0F;

        // float型による加算
        for (float x = 0.0F;x <= 1.0F; x += 0.001F) {
            sum += x;
        }
        System.out.println("addition with float: " + sum);

        sum = 0.0F;
        // int型による加算
        for (int y = 0; y <= 1000; y++) {
            float z = (float) y / 1000;
            sum += z;
        }
        System.out.println("addition with int: " + sum);
    }
}
```

```java:実行結果
addition with float: 500.49667
addition with int: 500.49997    // <- int型を利用した方が誤差が小さい
```

***

# 拡張表記(escape sequence)

|拡張表記|内容|Unicode拡張表記|
|:--|:--|:--|
|`\b`|後退(backspace)|`\u0008`|
|`\f`|書式送り(form feed)|`\u000c`|
|`\n`|改行(new line)|`\u000a`|
|`\r`|復帰(carriage return)|`\u000d`|
|`\t`|水平タブ(horizontal tab)|`\u0009`|
|`\<記号>`|エスケープ文字(escape)||
|`\o`|8進数(octal)||
|`\h`|16進数(hexadecimal)||

***

# サンプルコード(拡張for文, for-in文)

```java:AdvancedFor.java
class AdvancedFor {
    public static void main(String[] args) {
        int[] a = { 1, 2, 3, 4, 5};
        int sum = 0;

        // for-in文
        // -> iはint型配列aの各構成要素の値
        for (int i : a) {
            sum += i;
        }

        System.out.println("sum is " + sum + ".");
    }
}
```

***

# サンプルコード(スコープ)

```java:Scope.java
class Scope {
    // フィールド
    static int x = 700;

    // フィールドxを出力するメソッド
    static void printX() {
        System.out.println("field x: " + x);
    }
    public static void main(String[] args) {
        // フィールドxの出力
        System.out.println("field x: " + x);

        // 局所変数x
        // <- フィールドと有効範囲(=スコープ)が異なるため同名での宣言が可能
        int x = 800;

        // 局所変数xの出力
        System.out.println("local variable x: " + x);

        // Scopeクラスのフィールドxの明示的出力
        System.out.println("field in class Scope x: " + Scope.x);

        // フィールドxの出力
        printX();
    }
}
```

```java:実行結果
field x: 700
local variable x: 800
field in class Scope x: 700
field x: 700
```

***

# サンプルコード(ビット演算)

```java:Bits.java
import java.util.Scanner;

class Bits {
    // int型のビット構成を出力するメソッド
    static void printBits(int x) {
        for (int i = 31; i >= 0; i--) {
            // 大きい位のビットから、1とのビット論理積を求める
            System.out.print( ((x >>> i & 1) == 1) ? '1' : '0' );
        }
    }

    // int型のビット構成に含まれる、"1"であるビットの総数を返却するメソッド
    // <- 複数のアルゴリズムが存在
    static int count1InBits(int x) {
        // "1"であるビットの総数
        int count = 0;

        while (x != 0) {
            // 1とのビット論理積が1である場合の処理
            if ( (x & 1) == 1 ) count++;
            // 計算後は1ビット右方向に論理シフトしたものを再代入
            // ⇔ x = x >>> 1
            x >>>= 1;
        }

        // カウントした総数を返却
        return count;
    }

    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        System.out.print("int: "); int x = stdIn.nextInt();
        System.out.print("bit: ");
        printBits(x);
        System.out.println( "\n1 in bit: " + count1InBits(x) );
    }
}
```

```java:実行結果
int: 93
bit: 00000000000000000000000001011101
1 in bit: 5
```

## ビット計算で用いられる論理演算子

|記号|名称|内容|
|:--:|:--|:--:|
|`&`|ビット論理積演算子(bitwise and operator)|`AND`|
| &#124; |ビット論理和演算子(bitwise or operator)|`OR`|
|`^`|ビット排他的論理和演算子(bitwise exclusive or operator)|`EOR`(`XOR`)|
|`~`|ビット補数演算子(bitwise complement operator)|-|

## シフト演算子(shift operator)

|利用例|内容|
|:--|:--|
|`x << n`|左方向へ`n`ビットシフト|
|`x >> n`|右方向へ`n`ビット**算術シフト**|
|`x >>> n`|右方向へ`n`ビット**論理シフト**|

### 算術シフトと論理シフト
`算術シフト(arithmetic shift)`は最上位ビットを`符号ビット`とする。
`論理シフト(logical shift)`は全ビットを`値`とする。

***

# サンプルコード(アクセス修飾子と静的/動的メソッド)

```java:Accessibility.java
import java.util.Scanner;

// publicなクラスはファイル名と同名でなければならないため、publicキーワードは付与しない
class Day {
    // 外部クラスに公開しないprivateなフィールド
    // <- <Dayオブジェクト>.<フィールド>でのアクセス不可
    // => アクセサ(ゲッタ/セッタ)を用いてフィールドにアクセスしなければならない
    private int year;
    private int month;
    private int date;

    // クラスコンストラクタ
    // -> コンストラクタは通常外部クラスから利用するためpublicキーワードを付与
    public Day(int year, int month, int date) {
        // this参照によってフィールドと同名な仮引数を区別
        this.year = year;
        this.month = month;
        this.date = date;
    }

    // ゲッタ(=アクセサ)
    // <- privateなフィールドを外部クラスから間接的に参照
    public int getYear() { return year; }
    public int getMonth() { return month; }
    public int getDate() { return date; }

    // セッタ(=アクセサ)
    // <- privateなフィールドを外部クラスから間接的に設定
    public void setYear(int year) { this.year = year; }
    public void setMonth(int month) { this.month = month; }
    public void setDate(int date) { this.date = date; }

    // 外部クラスから利用可能(public)な静的メソッド(=クラスメソッド)
    // -> 「クラス」が所有するメソッド
    // => 引数によって結果は異なるが、インスタンスによって結果が異なることはない
    public static boolean isLeap(int y) {
        return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
    }

    // 外部クラスから利用可能(public)な動的メソッド(=インスタンスメソッド)
    // -> 「インスタンス(=オブジェクト)」が所有するメソッド
    // => インスタンスがもつフィールドの値を利用するため、インスタンスによって結果が異なる
    public boolean isLeap() {
        // 同名であるクラスメソッドisLeap(y:)を利用
        return isLeap(year);
    }

    // ツェラーの公式を用いた曜日を算出する動的メソッド(=インスタンスメソッド)
    public int dayOfWeek() {
        int y = year;
        int m = month;
        if (m == 1 || m == 2) {
            y--;
            m += 12;
        }
        return (y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + date) % 7;
    }
}

// publicなクラスはファイル名と同名でなければならないため、publicキーワードは付与しない
class DayComparator {
    // 外部クラスに公開しないprivateな静的メソッド(=クラスメソッド)
    // -> プログラムの中でDayComparatorインスタンス(=オブジェクト)は生成されないため、 静的メソッドで宣言
    private static boolean compRef(Day d1, Day d2) {
        // 「参照先」が同じであればtrueを返却
        return (d1 == d2);
    }

    // 外部クラスに公開しないprivateな静的メソッド(=クラスメソッド)
    // -> プログラムの中でDayComparatorインスタンス(=オブジェクト)は生成されないため、 静的メソッドで宣言
    private static boolean compVal(Day d1, Day d2) {
        // 「値」が同じであればtrueを返却
        return  d1.getYear() == d2.getYear() &&
                d1.getMonth() == d2.getMonth() &&
                d1.getDate() == d2.getDate();
    }

    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        // privateキーワードを付与すると"only final is permitted"とエラーが出力
        // <- main()メソッド内で宣言される局所変数(=ローカル変数)であり、そもそもprivate/publicの概念がないため
        //    局所変数に付与できるキーワードは"final"のみ
        int y, m, d;

        System.out.println("Day A: ");
        System.out.print("Year: "); y = stdIn.nextInt();
        System.out.print("Month: "); m = stdIn.nextInt();
        System.out.print("Date: "); d = stdIn.nextInt();
        Day dayA = new Day(y, m, d);

        System.out.println("Day B: ");
        System.out.print("Year: "); y = stdIn.nextInt();
        System.out.print("Month: "); m = stdIn.nextInt();
        System.out.print("Date: "); d = stdIn.nextInt();
        Day dayB = new Day(y, m, d);

        // 静的メソッド(=クラスメソッド)の呼び出し
        // -> "<クラス名>.~" で呼び出す
        System.out.println("Day B is " + (Day.isLeap(y) ? "" : "not ") + "leap year.");
        // 動的メソッド(=インスタンスメソッド)の呼び出し
        // -> "<クラス型変数(=インスタンス)名>.~" で呼び出す
        System.out.println("Day B is " + (dayB.isLeap() ? "" : "not ") + "leap year.");

        if ( compRef(dayA, dayB) ) {
            System.out.println("Reference is the same.");
        }
        else {
            System.out.println("Reference is different.");
        }

        if ( compVal(dayA, dayB) ) {
            System.out.println("Value is the same.");
        }
        else {
            System.out.println("Value is different.");
        }
    }
}
```

```java:実行結果
Day A: 
Year: 2021
Month: 6
Date: 25
Day B: 
Year: 2021
Month: 6
Date: 25
Day B is not leap year.
Day B is not leap year.
Reference is different.
Value is the same.
```

## アクセス修飾子
[参考: アクセス修飾子](https://www.sejuku.net/blog/22679)

|修飾子|同一クラス|同一パッケージ|サブクラス|すべて|
|:--|:--|:--|:--|:--|
|`public`(公開)|o|o|o|o|
|`protected`(限定公開)|o|o|o|x|
|`(なし)`(デフォルト)|o|o|x|x|
|`private`(非公開)|o|x|x|x|

## 動的メソッドと静的メソッド
`static`キーワードを**付与しない**`動的メソッド`は、個々の`インスタンス`によって結果が異なる処理。
`static`キーワードを**付与する**`静的メソッド`は、個々の`インスタンス`によらない結果をもたらす処理または**クラス全体に関係**する処理。

なお、`static`キーワードは`シグネチャ`に該当するため、**同名での宣言が可能**。

```java:動的メソッドと静的メソッド
// 外部クラスから利用可能(public)な静的メソッド(=クラスメソッド)
// -> 「クラス」が所有するメソッド
// => 引数によって結果は異なるが、インスタンスによって結果が異なることはない
public static boolean isLeap(int y) {
    return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
}

// 外部クラスから利用可能(public)な動的メソッド(=インスタンスメソッド)
// -> 「インスタンス(=オブジェクト)」が所有するメソッド
// => インスタンスがもつフィールドの値を利用するため、インスタンスによって結果が異なる
public boolean isLeap() {
    // 同名であるクラスメソッドisLeap(y:)を利用
    return isLeap(year);
}

// 静的メソッド(=クラスメソッド)の呼び出し
// -> "<クラス名>.~" で呼び出す
System.out.println("Day B is " + (Day.isLeap(y) ? "" : "not ") + "leap year.");
// 動的メソッド(=インスタンスメソッド)の呼び出し
// -> "<クラス型変数(=インスタンス)名>.~" で呼び出す
System.out.println("Day B is " + (dayB.isLeap() ? "" : "not ") + "leap year.");
```

***

# 用語集

|用語|内容|
|:--|:--|
|ビット(bit)|`binary digit`(2進数字)の略称。`0`と`1`で表現される。|
|2進整数リテラル(binary integer literal)|冒頭に`0b`または`0B`を付与して表現する2進整数。|
|8進整数リテラル(octal integer literal)|冒頭に`0`を付与して**2桁以上**で表現する8進整数。|
|16進整数リテラル(hexadecimal integer literal)|冒頭に`0x`付与して表現する16進整数。|
|縮小変換(narrowing primitive conversion)|**明示的**に行う必要のある、表現範囲の狭い型への変換。|
|拡大変換(widening primitive conversion)|**暗黙的**に行われる、表現範囲の広い型への変換。|
|FP厳密(FP-strict)|演算結果が実行環境に依存しないようにすること。<br>`クラス`や`メソッド`に`strictfp`キーワードを付与する。<br>※通常の計算では基本的に**不要**|
|書式送り|`改ページ`し、次のページの先頭に移動すること。|
|ガーベジコレクション(garbage collection)|参照先が`null`であるオブジェクトを自動的に解放し、<br>利用可能領域を拡げること。|
|フィールド(field)|**クラス内**で利用可能な変数。|
|局所変数(local variable)|`ローカル変数`。**メソッド内**で利用可能な変数。|
|シグネチャ(signature)|`識別子`、`仮引数`**の個数**、`仮引数`**の型**の3つの組み合わせ。|
|カプセル化(encapsulation)|非公開である`フィールド`と`フィールド`の値を利用する`メソッド`を連携させること。|
|ユーティリティクラス(utility class)|`クラスメソッド`と`クラス変数`のみを提供するクラス。|