Java Basic 総復習編 〜基本と基本文法編〜

Java Basic 総復習

今まで、Javaを学習してきて、なんとなくはわかったけどクラスとかオブジェクトという言葉が出てきたら頭がこんがらがってきた。という事象があるかと思います。

<プログラミングのチュートリアル(説明)動画>

自分も昔そうでした。。。そうだったような気がします。

それならば、整理すれば良いのです。簡単な話です。

復習内容リスト

  1. プログラムの流れ
  2. 変数の扱い
  3. 式と演算子
  4. if文
  5. switch文
  6. while文
  7. for文

プログラムの流れ

まずは、プログラムの流れについて復習します。クラスやメソッドなどが出てきて処理があちこちに飛ぶように感じられると思いますが、そんなことはありません。順序立てて動いています。

まずは、メインメソッドが動く

どのクラスにも作成することができますが、1つのアプリケーションとして動かすときは、「必ずメインメソッドが動く」ということを忘れないでください。

例えば、下のようなコードがあったとします。

public class First {
 public static void main(String[] args) {
       System.out.println("Hello World");
       First main = new First();
       main.hello();
   }
   public void hello() {
       System.out.println("Second Hello World");
   }
}

この時に、メインメソッドの中では、クラスをnewしてやらないとメンバメソッド(インスタンスメソッド)は起動できません。
それは、「static」がついているメソッドは、特別なメソッドなので、インスタンスメソッドと区別されます。

ここでの、処理の順番は、以下のようになります。

  1. メインメソッドが動く
  2. 「Hello World」をコンソールに出力
  3. Firstクラスをインスタンス化
  4. インスタンスメソッドの「hello()」を呼び出す

このように動きます。ここでの注意ポイントは「First」クラス型の変数「main」です。

変数の扱い

変数はプログラムを動かすために必要になるものですが、あまり細かいところまで解説をすることが少ないです。
実際に、覚えることはint型は「整数」とか、「double」型は少数。。。
というように、データ型の用途に対する説明のみになってしまうからです。

変数には「プリミティブ型」と「参照型(クラス型)」がありますが、参照型に関しては自作クラスも参照型として分類されるので
プリミティブ型のように「これは整数型です」というような言い方ができません。

ただし、String型はクラス型です。他にも、int型を参照型にしたものがあります。それぞれ下のようになっています。
<プリミティブ型 -> 参照型>

  • int -> Integerクラス
  • double -> Doubleクラス
  • boolean -> Booleanクラス
  • long -> Longクラス
  • float -> Floatクラス

<リテラルに関する解説>

変数の定義方法は全て同じ

今までよく目にしている、下のような変数の宣言は理解できていると思います。

int num = 0;
String str = "はじめの一歩";

変数の宣言・初期化は必ず「データ型 変数名 = 代入する値」という形で行います。

これは変数のデータ型がどんなものになっても変わりません。

<変数の扱い方>

データ型とは

以下のものがあります。

  1. int, double, float, lognなどのプリミティブ型と呼ばれる(分類される)変数の型
  2. String, 配列(int[], double[], String[], クラス名[])などの参照型と呼ばれる(分類される)変数の型

例. 変数宣言(初期化)

int num = 0;
String str = "はじめの一歩";
First main = new  FIrst();
List<String> list = new ArrayList<String>();
Scanner scan = new Scanner(System.in);
Random rnd = new Random();

上から順に

  1. int型の変数numを0で初期化
  2. 文字列型(String型)の変数strを"はじめの一歩”で初期化
  3. First型の変数mainをFirstクラスのインスタンスを生成して初期化
  4. List\<String>型の変数listをArrayList\<String>クラスをインスタンス化して代入
  5. Scanner型の変数scanにScannerクラスをインスタンス化して代入
  6. Random型の変数rndにRandomクラスをインスタンス化して代入

式と演算子

演算子には以下のようなものがあります。
算術演算子:「+」 「-」 「*」 「/」 「%」 「^」 「++」 「--」
比較演算子:「==」 「!=」 「「<」 「>」 「<=」 「>=」
論理演算子:「&&」 「||」

そのほかの演算子
instanceof」:クラス型の比較を行います。

String st = "aa";
if (st instanceof String) {
    System.out.println("同じString型です。");
}

代入演算子:「=」値を代入します。

int num = 0; // int型の変数に0を代入
First first = new First(); // First型の変数firstにFirstクラスのインスタンスを代入
String st = "aaa"; // String型の変数stに文字列「aaa」を代入

ここで注意して欲しいのが、「1」と「"」がついていないもの、リテラル(値)は「数値」として扱われる
逆に「"」で囲われているもの、リテラル(値)は文字列としてある変われる

この「=」が、いまいち、ピンとこない人に向けて例を以下に書きます。

public static void main(String[] args) {
    String st = getString();
}
public static String getString() {
     return "String";
}

上のコードは、クラスを省略して書いていますが、メインメソッドから「getString()」というメソッドを呼び出します。
この「getString()」は返り値(戻り値)にString型(文字列型)を定義しています。

なので、「getString()」のメソッドを呼び出したらString型の値を受け取ることができます。

これに対して、クラスをnewして実行した時に関しても同じです。ちょっと長いですが。。。

public class Sample {
    /** フィールド変数 */
    private int field_int = 0;
    private String field_String = "もじれつ";
    //////// 注意(教科書の書き方はほぼ使わない) /////
    // String package_String = "使わない";

    public static void main(String[] args) {
        // Sampleクラス型の変数mainにSampleクラスをインスタンス化して代入
        Sample main = new Sample();
        // 返り値(戻り値)が「void」の場合は変数を受け取る必要がない
        main.hello();
        staticHello();

        // 返り値(戻り値)が定義されている場合(voidになっていない場合)
        // 返り値(戻り値)を受け取ることができる
        Sring result = getString();
        System.out.println("getString()の戻り値は" + result);
    }

    public static void staticHello() {
        System.out.println("Hello World");
    }
    /**
     * <コンストラクタの書き方>
     * アクセス修飾子 クラス名(引数) { ... }
     *
     * newした時の処理を書く
     */
    public Sample() {
        this.field_int = 5;
        this.field_String = "aaaa時の値";
    }

    /**
     * コンストラクタのオーバーロード
     * @param num
     * @param str
     */
    public Sample(int num, String str) {
        this.field_int = num;
        this.field_String = str;
    }

    /**
     * ハローメソッド
     */
    public void hello() {
        System.out.println(this.field_String);
        this.hello("こんにちは、フィールド変数:" + this.field_int);
        this.hello2();
    }
    /**
     * ハローメソッド
     */
    public void hello2() {
        System.out.println(this.field_String);
        this.hello("こんにちは、フィールド変数:" + this.field_int);
        String st = "aa";
        String gg = "ss";
        if (st instanceof String) {
            System.out.println("同じString型です。");
        }
    }

    public String getString() {
        return "String";
    }
}

上記のコードのうち戻り値が「void」のものは以下になります。

  • helllo()
  • hello2()
  • staticHello()

そして。返却値(戻り値)の指定があるもの(voidではないもの)は以下の通りです。

  • getString()

このgetString()メソッドはString型の値を返しますので、呼び出し元(メインメソッド)ではString型の変数resultで受け取っています。
当然受け取らなくても良いので、下のように書いてもエラーは出ません。

getString();

上記の場合は、String型の値を受け取っても、変数に代入していないのでメインメソッドでは使用することができません。
使用する必要がなければ、このようなメソッドの呼び出しもOKです。

if文

条件分岐処理の構文です。

if (論理式) {
   // trueの場合の処理
} else {
  // falseの場合の処理
}

実際に使用するときは下のように書く

String st = "aa";
if (st instanceof String) {
    System.out.println("同じString型です。");
}
if ("aa".equals(st)) {
    System.out.println("stはaaです。");
} else if ("bb".equals(st) ) {
    System.out.println("stはbbです。");
} else {
    System.out.println("stはその他の値です。");
}

それぞれif (論理式) { ... }の「論理式」の部分で値が「true / false」が帰ってきたところで処理の流れが変わります。

論理式

返り値としてbooleanが返される式のことです。以下の指揮がそれにあたります。

boolean isTrue = "aaa".equals("aaa");
boolean isFalse = "aAb".equals("aaa");
isTrue = 1 == 1;
isFalse = 2 == 1;
isTrue = 1 < 2;
isFalse = 1 != 2;

「返る」というのは「=」の反対側に値を渡すという意味です。

switch文

上記のif文と同じ処理がかけます。下のようになります。

switch(st) {
case "aa": 
    System.out.println("stはaaです。");
    break;
case "bb":
    System.out.println("stはbbです。");
    break;
default:
    System.out.println("stはその他の値です。");
}

while文

繰り返し処理の最もシンプルなものです。
下のように論理式の結果が「true」の間繰り返し処理を行います。

int num = 0;
while (num < 10) {
     System.out.println("num = " + num);
     num++;
}

上のコードは「num = 0」〜「num = 9」までを表示します。

for文

繰り返し処理の最もおポピュラーなものです。

for (int i = 0; i < 10; i++) {
     System.out.println("num = " + num);
}

上のコードは「num = 0」〜「num = 9」までを表示します。

大まかに基本文法と変数の扱い方などを記載しました。

Java じゃんけんゲームを作ってみる ~基本文法をマスターする~

じゃんけんゲーム

Javaプログラミングの基本文法を理解するためにも、実際にアプリケーションを作ってみるのが手っ取り早い方法です。

教科書などを眺めても、実践に勝る学習はありません。つまりは。。。

経験に勝る知識なし!

ということです。ちなみに、

サイトマップはこちらです。

Javaプログラム作成時の(推奨する)ルール

  1. クラスの名前ははじめを大文字にして、アルファベットを使用しましょう。

    Ex: SampleClass, FirstClass, MySample ...

  2. メソッドの名前ははじめを小文字にして、アルファベットを使用しましょう。メソッドの場合は、うしろに「()」を付けます。

    Ex: myMethod(), firstMethod(), firstTest(), handleRequest() ...

  3. 「{」を一つ入力したら、TAB(インデント)をいれましょう。「}」を入力したらTAB(インデント)を減らしましょう。

    public class FirstTest {
    public static void main(String[] args) {
        // 「{」が2個あるのでTABが2つあります。
        System.out.println("Hello World");
    }
    // 「}」が一つ出てきたので、TABが1つあります。
    }

じゃんけんゲームの処理内容

本来は、自分で考えて仕様を考えるべきですが、初めの一歩ということで仕様を提示します。

仕様とは、「〇〇はXXのように動く」ということを一つ一つ決めて(定義して)、どう動くのか決めることです。

作成するクラスは「JankenPon」クラスにします。

じゃんけんゲームの仕様

  1. 標準入力での、0:グー、1:チョキ、2:パーと定義する。つまり、0が入力されたら「グー」1ならば「チョキ」、2ならば「パー」と判断する
  2. ユーザーの手(入力)とCPUの手(乱数)を表示する
  3. 勝敗判定を行い、「YOU_WIN」、「YOU_LOSE」「DRAW」を表示する

この様に使用を決めたらそれぞれの項目(要件)を満たすようにプログラムを作ります。

0:グー、1:チョキ、2:パーと定義する

標準入力を受け取り、グー~パーを判定します。
どのように実装したらよいでしょうか?

今回は、試しに以下のような実装をしてみましょう。

if文をマスターする

マスターするといっても、普通に使うだけですが。。。
ポイント
次のプログラムは、標準入力を受け付けて、1行分のデータ(入力)を取得します。

Scanner scan = new Scanner(System.in);
scan.nextLine();

ちなみに、下のコードでは、数字以外の入力を行うとエラーが出ます。

public class FirstSample {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int input = Integer.parseInt(scan.nextLine());
        String jankenTe = null;
        if (input == 0) {
            jankenTe = "グー";
        } else if (input == 1) {
            jankenTe = "チョキ";
        } else if (input == 2) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
    }
}

じゃんけんの手をString型の変数JankenTeに設定(代入)します。
0, 1, 2以外の値が入力されたときは想定外の値です。

IF文を使用したときの動画

ユーザーの手(入力)とCPUの手(乱数)を表示する

CPUの手を乱数を使って表示します。これはもう説明する必要はないと思います。

public class SecondSample {
    public static void main(String[] args) {
        // 0-2の乱数を生成する
        cpuTe = String.valueOf(new Random().nextInt(2));

        if (cpuTe.equals("0")) {
            jankenCpu = "グー";
        } else if (cpuTe.equals("1")) {
            jankenCpu = "チョキ";
        } else if (cpuTe.equals("2")) {
            jankenCpu = "パー";
        }
    }
}

これも、if文で実装することができます。もちろん、より良いアイディアがあればそちらの実装を行うのが正しい判断です。検証するのを忘れないようにしてください。

勝敗判定を行い、「YOU_WIN」か「YOU_LOSE」「DRAW」を表示する

勝敗判定は、ちょっと良いアイディアが出ず。。。そのまま判定処理を作りました。
はっきり言ってダメダメなコードです。

public class ThirdSample {
    public static void main(String[] args) {
        String hantei = input + cpuTe;
        String result = null;
        if (hantei.equals("01")) {
            result = "YOU_WIN";
        } else if (hantei.equals("12")) {
            result = "YOU_WIN";
        } else if (hantei.equals("20")) {
            result = "YOU_WIN";
        } else if (hantei.equals("00")) {
            result = "DRAW";
        } else if (hantei.equals("11")) {
            result = "DRAW";
        } else if (hantei.equals("22")) {
            result = "DRAW";
        } else if (hantei.equals("02")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("10")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("21")) {
            result = "YOU_LOSE";
        } else {
            System.out.println("想定外の値です。" + hantei);
        }
    }
}

何とかじゃんけんゲームができました。しかし、このようなプログラムは美しくありません。

別に「アーティスチックでないからダメ」といっているわけではなく、見ずらいし、わかりずらい。

人にやさしくないプログラムが「ダメダメ」だということです。

人にやさしく

ズバリ、読みやすく、理解しやすいコードが良いブログラムです。もちろん、余計なメモリを使わないとかいろいろありますが。。。

ちなみに、上のようなコードで作成したじゃんけんゲームは以下のようなコードになりました。

public static void main(String[] args) {
    // 1.初めの処理
    System.out.println("じゃんけん ...");
    Scanner scan = new Scanner(System.in);

    // 2.標準入力受付
    String input = scan.nextLine();
    String jankenTe = null;

    // 3.ユーザーの手を判定する
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }

    String cpuTe = null;
    String jankenCpu = null;
    if (jankenTe == null) {
        // 強制終了
        System.exit(-1);
    } else {
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));

        if (cpuTe.equals("0")) {
            jankenCpu = "グー";
        } else if (cpuTe.equals("1")) {
            jankenCpu = "チョキ";
        } else if (cpuTe.equals("2")) {
            jankenCpu = "パー";
        }
    }
    System.out.println("ポン");
    System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

    // 5.勝敗判定を行う
    String hantei = input + cpuTe;
    String result = null;
    if (hantei.equals("01")) {
        result = "YOU_WIN";
    } else if (hantei.equals("12")) {
        result = "YOU_WIN";
    } else if (hantei.equals("20")) {
        result = "YOU_WIN";
    } else if (hantei.equals("00")) {
        result = "DRAW";
    } else if (hantei.equals("11")) {
        result = "DRAW";
    } else if (hantei.equals("22")) {
        result = "DRAW";
    } else if (hantei.equals("02")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("10")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("21")) {
        result = "YOU_LOSE";
    } else {
        System.out.println("想定外の値です。" + hantei);
    }
    // 6. 結果の表示
    System.out.println(hantei);
    System.out.println(result);
}

別の記事になりますが、「じゃんけんゲーム in コンソール」の記事になります。

しかし、やはりだめなコード(個人的にそのように思う)なので、これをまともなコードに直します。

この様な行為のことをリファクタリングといいます。

リファクタリング

まずは、読みずらいところを洗い出します。
以下の部分が読みずらいと思いました。

if文のネスト(階層化しているところ)が無駄なネストになっている

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
} else {
    // 4.CPUの手を判定する
    cpuTe = String.valueOf(new Random().nextInt(2));

    if (cpuTe.equals("0")) {
        jankenCpu = "グー";
    } else if (cpuTe.equals("1")) {
        jankenCpu = "チョキ";
    } else if (cpuTe.equals("2")) {
        jankenCpu = "パー";
    }
}

どこが、無駄かというとelse~の部分です。もしjannkenTeがnullになっているのならば、プログラムを強制終了するので、if-elseでわける意味がありません。無駄なコードになっているのです。

ではどのようになおすか?ズバリ下のように直します。elseが丸まる必要ないのです。

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
}

// 4.CPUの手を判定する
cpuTe = String.valueOf(new Random().nextInt(2));

if (cpuTe.equals("0")) {
    jankenCpu = "グー";
} else if (cpuTe.equals("1")) {
    jankenCpu = "チョキ";
} else if (cpuTe.equals("2")) {
    jankenCpu = "パー";
}

処理の内容を考えてみれば、不要ですよね?なぜなら

System.exit(-1);

でプログラムは強制終了(引数に「-1」を与えているので『異常終了』)するからです。

つまり、if文の中の処理を行うとき=jankenTeがnullの時は「CPUの手を判定」より後の処理が行われません。

メソッドを利用する

その他にも、イケていないところがあります。それは、似たような処理がたくさんあるというところです。
特にif文の処理が同じような処理を行っています。

なので、メソッドを作成して同じコードを書かないようにします。

今までに、ちょこっと書いたりしました。改めて理解しましょう。

具体的には、以下のif文です。2か所、同じコードがあります。

String jankenTe = null;
if (input.equals("0")) {
    jankenTe = "グー";
} else if (input.equals("1")) {
    jankenTe = "チョキ";
} else if (input.equals("2")) {
    jankenTe = "パー";
} else {
    System.out.println("想定外の値です。" + input);
}

これをメソッドとして切り出します。具体的には下のように切り出します。staticがついているのはメインメソッドから呼び出すためです。

public static String jankenHantei(String input) {
    String jankenTe = null;
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }
    return jankenTe;
}

そして、このコードと同じ部分を書き換えます。下のようなコードになりました。

public class JankenPon {
    public static void main(String[] args) {
        // 1.初めの処理
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = null;
        if (hantei.equals("01")) {
            result = "YOU_WIN";
        } else if (hantei.equals("12")) {
            result = "YOU_WIN";
        } else if (hantei.equals("20")) {
            result = "YOU_WIN";
        } else if (hantei.equals("00")) {
            result = "DRAW";
        } else if (hantei.equals("11")) {
            result = "DRAW";
        } else if (hantei.equals("22")) {
            result = "DRAW";
        } else if (hantei.equals("02")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("10")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("21")) {
            result = "YOU_LOSE";
        } else {
            System.out.println("想定外の値です。" + hantei);
        }
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }
}

これで、多少はよくなりました。しかし、まだです。

Mapインターフェースを使用する

じゃんけんの判定方法ですが、すごく頭の悪いやり方です。しかし、これから見せるコードも頭の良いやり方ではありません。なぜかというと、何の工夫もないからです。

頭の良いやり方ではない方法
まずは下のような、フィールド変数(定数)とメソッドを作成します。

<フィールド変数とは>
Javaクラスの持つことのできる要素の一つです。
具体的には、フィールド変数は、変数であり、クラスの中であればどこでも使用することができます。
UMLという表現方法では、下のように書きます。
Diagram1

同時に、クラスが持つ要素を表します。「属性」はフィールド変数、「操作」はメソッドを示します。
例えば、ボタンを押したらカウンターをカウントアップするアプリケーションを考えてみましょう。
「ボタンを押す」という操作(メソッド)を使用すると、「カウンター」という属性を1カウントアップ(プラス1)するという具合に、各要素を使用します。

つまりは、以下のような特徴があります。

  • フィールド変数はデータの管理、他のクラスを保持する変数。
  • メソッドは何かしらの処理(操作)を行うためのプログラムをまとめたもの。

<実際のコード>

public class SampleClass {
    /** フィールド変数(定数) */
    public static final String YOU_WIN = "YOU_WIN";
    public static final String YOU_LOSE = "YOU_LOSE";
    public static final String DRAW = "DRAW";</pre>

    // そして、次のメソッドを追加します。
    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }
}

作成したメソッドを使用して、メインメソッドを書き直します。

そうすると下のようになりました。

public class Chap0 {
    public static final String YOU_WIN = "YOU_WIN";
    public static final String YOU_LOSE = "YOU_LOSE";
    public static final String DRAW = "DRAW";

    public static void main(String[] args) {
        // 1.初めの処理
        Map<String, String> hanteiMap = createMap();
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU: " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = hanteiMap.get(hantei);
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }

    public static Map<String, String> createMap() {
        Map<String, String> hanteiMap = new HashMap<String, String>();
        hanteiMap.put("01", YOU_WIN);
        hanteiMap.put("12", YOU_WIN);
        hanteiMap.put("20", YOU_WIN);
        hanteiMap.put("00", DRAW);
        hanteiMap.put("11", DRAW);
        hanteiMap.put("22", DRAW);
        hanteiMap.put("02", YOU_LOSE);
        hanteiMap.put("10", YOU_LOSE);
        hanteiMap.put("21", YOU_LOSE);

        return hanteiMap;
    }
}

実行結果は下のような形で出力されました。

じゃんけん ...
1
ポン
あなた: チョキ CPU; チョキ
11
DRAW

如何でしょうか?だいぶすっきりしたように見えると思います。プログラムの処理内容が理解できていれば、無図解ことではありません。「このような方法もある」というのを知っておくと後々にカッコよいコードが書けるようになります。※自分も努力いたします。。。

<スマホアプリ版じゃんけんゲーム>※GLUONを使用

でわでわ。。。

Java Basic 実践学習編 〜変数の使用とデータ型、条件分岐、コードブロック〜

イントロダクション

前回は「Java Basic 実践学習編 〜ハローワールドから変数の宣言・初期化〜」を行いました。

今回は、表題の通りです。

変数の使用

前回も行いましたが、下のように、変数を宣言、初期化して使用します。

変数の宣言と初期化

<Code.09>

public class HelloWorld {
    public static void main(String[] args) {
        // 変数の宣言
        int seisu;
        // 変数の初期化
        double shosu = 0.0;
        // 変数の初期化
        String moji = "文字列";
    }
}
九九の二の段

<Code.10>

public static void main(String[] args) {
    int a = 2;
    int x = 1;
    System.out.println("2 x 1 = " + a * x);
    x++;
    System.out.println("2 x 2 = " + a * x);
    x++;
    System.out.println("2 x 3 = " + a * x);
    x++;
    System.out.println("2 x 4 = " + a * x);
    x++;
    System.out.println("2 x 5 = " + a * x);
    x++;
    System.out.println("2 x 6 = " + a * x);
    x++;
    System.out.println("2 x 7 = " + a * x);
    x++;
    System.out.println("2 x 8 = " + a * x);
    x++;
    System.out.println("2 x 9 = " + a * x);
}

上のコードは、九九の二の段を表示するプログラムです。ここでは変数xの値をインクリメントすることで、二の段の計算結果を表示しています。

変数の宣言と初期化を確認したところで次のコードに行きます。

コードブロック

一旦立ち止まって振り返ります。今まで動かしていた「メインメソッド」は下のように書きます。
この時に「{」から「}」までの間がメインメソッドの範囲、コードブロックになります。

<Code.11>

public static void main(String[] args) {
    // 何かしらの処理
}

これは決まっている部分ですので、そのまま覚えても問題ありません。と言うか自分は覚えました。

ポイント

「{}」は「中かっこ」、「波かっこ」など色々な言い方がありますが、「{}」の括弧で囲まれた部分が「ブロック」になります。
具体的には<Code.11>の「{」から「}」までの間が「(コード)ブロック」にあたります。

これは、スコープともいい「{」から「}」までの間だけ変数が機能します。
具体的には、下のように書くと変数「num」はエラーになると言うところです。

変数の有効範囲

<Code.12>

public static void main(String[] args) { // ここからメインメソッドのブロック
    int a = 0;
    if (a == 1) { // ここからIFのブロック
        int num = 0;
        num++;
    } else { // ここからelseのブロック
        num = 10; // ここでエラー
    }
}

「{」から「}」までの間が、変数のスコープ、機能する範囲なのでif() { ... }の中で宣言(初期化)された変数「num」はelse { ... }の範囲では機能しないのです。

詳細は以下のようになります。

  1. 1行目はメインメソッドの定義、メソッドの宣言
  2. 2行目でint型(整数型)の変数「a」の初期化
  3. if文で「aの値が1のときIFブロックに処理が進む」
  4. 同様に「if文の条件以外のばあいelseのブロックに進む」

この様に、メインメソッドのブロックの中に、ifのぶろっくとelseのブロックがあります。

そして、これを次のように書き換えるとエラーは出ません。

<Code.13>

public static void main(String[] args) {
    int a = 0;
    int num = 0;
    if (a == 1) {
        num++;
    } else {
        num = 10;
    }
}

変数「num」がメインメソッドのスコープ内で宣言されているので、その中にあるif() { ... }の中でもelse { ... }でも両方で機能することができます。

なので、次のようなコードでは、メソッドの中で宣言していてもエラーになります。別のメソッドだから、スコープの範囲が違うためです。

<Code.14>

public static void main(String[] args) {
    int a = 0;
    int num = 0;
    if (a == 1) {
        num++;
    } else {
        num = 10;
    }
}

/** メンバメソッド */
public void test1() {
    num; // ここでエラー
    System.out.println("test1");
    System.out.println("2 / 2 = " + 2 / 2);
}

ポイント

変数のスコープと言うのがありそれは「{」から「}」までの間がその範囲になります。
なので変数の宣言が、「{」から「}」までの間の外にあればそれはそれはエラーになります。

クラスとメソッドについて

今までプログラムを何個か作成してきましたが、Javaのプログラムを動かすための単位は「クラス」になります。
このクラスは下のように書き、フィールドとメソッドを持っています。

<Code.15>

public class HelloWorld {
    /** フィールド */
    private int field;

    /** メソッド */
    public void method(String hikisu) {
        int a = 0;
        int num = 0;
        if (a == 1) {
            num++;
        } else {
            num = 10;
        }
    }
}

このルールで、作成したクラスに「メインメソッド」を追加しているのです。今までに作成したものはメインメソッドのみでしたが。。。クラスとしては成り立つのです。ちょっと極端ですが、空クラスも、作成すればあります。

public class Kara {
}

Javaの実装ルールに違反していないのでOKなのです。まぁ作成する意味もないですが。。。

条件分岐

if文

次は、<Code.13>で出てきたif文に関して学習します。
俗にいう条件分岐です。これは、プログラムを実行するときに「~の場合の処理」と条件を付けて処理を分けたいときに使用します。具体的に以下のようなコードです。

<Code.16>

public static void main(String[] args) {
    int num = 10;
    if (num == 10) {
        System.out.println("No10");
    } else {
        System.out.println("Not No10");
    }
}

int型(整数型)の変数numが10の時「No10」とコンソールに表示します。それ以外の時は「Not No10」と表示します。
この場合は、プログラムを書き換えて変数numの値を変更してやる必要があります。

なので、プログラム引数を使用してプログラムを書き換えなくてもよいように修正します。

プログラム引数

プログラム引数は、Javaプログラムを実行するときに渡せる引数のことで、引数はString型の値になります。
ただし、文字列を複数渡すことができるのでString[](String型の配列)になっています。

具体的には下のように使用します。

コードとしては、動画のものと違いますが、下のように使います。

public static void main(String[] args) {
    // プログラム引数を取得する
    int num = Integer.parseInt(args[0]);
    if (num == 10) {
        System.out.println("No10");
    } else {
        System.out.println("Not No10");
    }
}

しかし、このコードでは、プログラム引数が渡されてないい場合はエラーになります。

なので次のように書き換えます。
<Code.17>

public static void main(String[] args) {
    // プログラム引数を取得する
    int num;
    if (args[0] != null && args[0].matches("[0-9]{1}")) {
        num = Integer.parseInt(args[0]);
    } else {
        num = 10;
    }

    if (num == 10) {
        System.out.println("No10");
    } else {
        System.out.println("Not No10");
    }
}

このように、想定外の入力があったときを考慮してプログラムを作成するとエラーが出ない、安全なプログラムができます。リアルでもプログラムでも安全第一です

処理の内容に関して
初めの

if (args[0] != null && args[0].matches("[0-9]{1}")) {

を分解して説明します。
if文に関しては後に説明しますが、

args[0].matches("[0-9]{1}") {

の部分に関して
この部分はString型のメソッドを使用しています。実はString型はJavaAPIで定義されているクラスなのです。
String[]はString型の配列ですので、配列の中身はString型の変数です、

String[0]

String[] hako = {"もじ", "123", "aaa"};

と初期化したときの「"もじ"」に当たります。
つまり

String[0] => "もじ", String[1] => "123", String[2] => "aaa"

となります。

なので、Stringクラスのメソッド「matches」を使用することができます。

このメソッドの処理は引数に「正規表現」を渡し返り値に正規表現にマッチするかどうか?を返す処理になります。

具体的には下のようになります。
<Code.18>

public static void main(String[] args) {
    String aa = "12345";
    boolean isNumber = aa.matches("[0-9]{1,}");
    System.out.println("入力値: " + aa + "は、正規表現「[0-9]{1,}」にマッチするか?: " + isNumber);

    boolean isAtoZ = aa.matches("[A-Z]{1,}");
    System.out.println("入力値: " + aa + "は、正規表現「[0-9]{1,}」にマッチするか?: " + isAtoZ);
}

実行結果は以下の通りです。

条件分岐本題

if文になれてきたところで、プログラムを理解していきましょう。

<Code.13>を見てください。初めのif文で変数aaの値が10の時...と処理が書いてあります。

public static void main(String[] args) {
    int num = 10;
    if (num == 10) {
        System.out.println("No10");
    } else {
        System.out.println("Not No10");
    }
}

ここでのnum == 10の部分が論理式になります。論理式とは返却値に真偽値(trueもしくはfalse)を返します。
コードで書くと下のようになります。

boolean is10 = num == 10;

何度も記載しますが、booleanは真偽値のデータ型です。true, falseどちらかの値しか持ちません。
なので、変数「num」が10の場合は、true, そうでない場合はflaseが変数「is10」に代入されます。

ちょっと座学臭い感じですが、「=」演算子は式の値を代入します。
初めの方に実装しましたが、変数の初期化を行った時には「=」で宣言と同時に値を代入します。

int num = 10;

同様に、初期化でなくても値は代入する事ができます。

num = 9;

下のように、プログラムを書いたときは変数「num」の値が変わって聞きます。そして、プログラムは必ず上から下に流れます。
<Code.19>

public static void main(String[] args) {
    int num = 10;
    if (num == 10) {
        num = 20;
    } else {
        num = 3;
    }
    if (num == 20) {
        System.out.println("Hello");
    } else {
        System.out.println("Bye");
    }
}

このコードは、「Hello」が表示されます。以下のように処理が進みます。

  1. int型の変数numを10で初期化
  2. もしnumが10であれば、numに20を代入
    3.それ以外ならnumに3を代入
  3. もしnumが20であれば、「Hello」を表示
  4. それ以外なら「Bye」を標示

条件分岐のバリエーション

if-elseを理解できたと思います。これに追加してelse-ifがあります。具体的には下のように書きます。
<Code.20>

public static void main(String[] args) {
    int num = 10;
    if (num == 10) {
        num = 20;
    } else if (num == 20){
        num = 3;
    } else {
        num = 2;
    }
    if (num == 20) {
        System.out.println("Hello");
    } else if (num == 3) {
        System.out.println("Else if");
    } else {
        System.out.println("Bye");
    }
}

今までの条件に「そのほかに、もしnumが20の時」という文言が加わりました。
作り方はif文の時と同じです。

文章をつなげると「もじnumが10ならば~その他にもし、numが20ならば~、それ以外なら~」というような文章、プログラムができました。

switch文

switch文はif文と同じです。ただ書き方が違います。
if文は以下のように書きます。

if (論理式A) {
    // 論理式Aがtrueときの処理
} else if (論理式B) {
    // 論理式Bがtrueときの処理
} else {
    // 何かしらの処理
}

switch文は以下の通りです。

switch (変数) {
case XX:
    // 変数がXの時のケース
    break;
case YY:
    // 変数がYの時のケース
    break;
default:
    // 変数が上記以外のとき
}

<Code.20>をswitch文に書き換えると下のようになります。

public static void main(String[] args) {
    int num = 10;
    switch (num) {
    case 10:
        System.out.println("No8");
        break;
    case 20:
        System.out.println("No9");
        break;
    default:
        System.out.println("それ以外");
    }
}
ポイント

このプログラムの

break;

に注目して下さい。この分がないとどうなるでしょうか?
<Code.21>

switch (num) {
case 8:
    System.out.println("No8");
case 9:
    System.out.println("No9");
default:
    System.out.println("それ以外");
}
if (num == 0) {
    break;
}

出力結果はいかのようになりました。入力値は「8」です。

8
No8
No9
それ以外

入力値が「9」の場合は

9
No9
それ以外

同様に上記以外の入力、「5」を入力しました。

5
それ以外

つまるところは、breakがないとそれ以降の処理がすべて動いてしまうということです。

ループ文

while文

やって来ました。ループ文です。ここまで来た方おめでとうおございます。
プログラミングで遊ぶ材料がそろいました。

まずは下のループ文を見てください。

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

    while(true) {
        int num = scan.nextInt();

        switch (num) {
        case 8:
            System.out.println("No8");
            break;
        case 9:
            System.out.println("No9");
            break;
        default:
            System.out.println("それ以外");
        }
    }
}
while(true) { ... }

これは無限ループのプログラムの型です。
「{}」の間(スコープ)を無限ループします。

このままだとプログラムが終了しません。

では、どのようにプログラムを終了するか?

特定の入力があった場合にプログラムを終了するようにプログラムを作ります。

今回利用しているJavaAPIは、java.util.Scannerです。
そして、使用しているメソッドはnextInt()です。

終了するための処理は下のコードです。

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

    while(true) {
        int num = scan.nextInt();

        switch (num) {
        case 8:
            System.out.println("No8");
            break;
        case 9:
            System.out.println("No9");
            break;
        default:
            System.out.println("それ以外");
        }
        if (num == 0) {
            break;
        }
    }
}

上のコードを説明すると。。。

  1. Scanner scan = new Scanner(System.in);で標準入力の受付クラスをインスタンス化
    標準入力を受け付けられるようにします。
  2. while(true) {で無限ループを開始します。
  3. int num = scan.nextInt();で標準入力を待ち受けます。
  4. あとはswitch文で受け取った値の判定をしてそれぞれの処理を行います。
  5. if (num == 0) {入力値を代入した変数「num」が0の場合「{}」内の処理を行います。
  6. break;無限ループを抜けます。

for文

この無限ループは、ゲームを作成するときの基本的なプログラムになります。
そして、今回は回数制限のある「おみくじゲーム」を作成します。

占い回数を入力して、今日の運勢を%で算出します。

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

    System.out.println("今日の運勢を占います。占う回数を入力してください。");
    int num = 0;
    while(true) {
        num = scan.nextInt();
        if (num > 3) {
            System.out.println("3以下の値を入力してください");
            continue;
        }
        break;
    }
    System.out.println(num + "回占います。");

    int sisu = 0;
    for (int i = 0; i < num; i++) {
        int unsei = new Random().nextInt(3);
        if (unsei == 0) {
            System.out.println((i + 1) + "回目: 大吉");
            sisu += 4;
        } else if (unsei == 1) {
            System.out.println((i + 1) + "回目: 中吉");
            sisu += 3;
        } else if (unsei == 2) {
            System.out.println((i + 1) + "回目: 吉");
            sisu += 2;
        } else {
            System.out.println((i + 1) + "回目: 凶");
            sisu += 1;
        }
    }
    System.out.println("sisu: " + sisu);
    int un = new BigDecimal(sisu).divide(new BigDecimal(num * 4), 2, RoundingMode.DOWN).multiply(new BigDecimal(100)).intValue();
    System.out.println("un: " + un);
    System.out.println("今日の運勢は、" + un + "%好調です。");
}

これを改造するもよし、アイディアを出して別のものにするもよし、
遊んでみてください。

ポイント

今回使用したループ文はfor文といって下のように書きます。

for (カウンタの初期化; ループする論理式; ループした後の処理) {
    // 何かしらの処理
}

これは、どのような意味かというとfor int i = 0; i < 10; i++) { .. }<code>とコードを書いたときの場合は、int(整数)型の変数を0で初期化(</code>int i = 0<code>)して、この変数が10よりも小さい(i < 10)間、ループするたびにあとの処理(</code>i++)を行います。

ループ文の練習です、下のような問題があります。

問題1「4回ループするfor文を作ってください」

<for文の場合>

for (int i = 0; i < 5; i++) {
    System.out.pprintln("Hello" + i);
}

処理の順序は下のようになります。

  1. int型(整数型)の変数iを0で初期化
  2. i = 0なので論理式「i < 5」の評価後の値は「0 < 5」でtrue
  3. 論理式の値がtrueなので「{}」の中の処理を行う
  4. 「Hello0」を表示した後に
    i++

    の処理を行う

  5. iが1になったので、論理式
    i < 5

    の評価の値は「1 < 5」でtrue

  6. 「Hello1」を表示した後に
    i++

    の処理を行う

  7. iが2になったので、論理式
    i < 5

    の評価の値は「2 < 5」でtrue

  8. 「Hello2」を表示した後に
    i++

    の処理を行う

  9. iが3になったので、論理式
    i < 5

    の評価の値は「3 < 5」でtrue

  10. 「Hello3」を表示した後に
    i++

    の処理を行う

  11. iが4になったので、論理式
    i < 5

    の評価の値は「4 < 5」でtrue

  12. 「Hello4」を表示した後に
    i++

    の処理を行う

  13. iが5になったので、論理式
    i < 5

    の評価の値は「5 < 5」でfalse

  14. 論理式の値がfalseになったのでループを終了する

<while文の場合>

int i = 0;
while(i < 5) {
    System.out.println("Hello" + i);
    i++;
}
  1. int型(整数型)の変数iを0で初期化
  2. i = 0なので論理式「i < 5」の評価後の値は「0 < 5」でtrue
  3. 論理式の値がtrueなので「{}」の中の処理を行う
  4. 「Hello0」を表示した後に
    i++

    の処理を行う

  5. iが1になったので、論理式
    i < 5

    の評価の値は「1 < 5」でtrue

  6. 「Hello1」を表示した後に
    i++

    の処理を行う

  7. iが2になったので、論理式
    i < 5

    の評価の値は「2 < 5」でtrue

  8. 「Hello2」を表示した後に
    i++

    の処理を行う

  9. iが3になったので、論理式
    i < 5

    の評価の値は「3 < 5」でtrue

  10. 「Hello3」を表示した後に
    i++

    の処理を行う

  11. iが4になったので、論理式
    i < 5

    の評価の値は「4 < 5」でtrue

  12. 「Hello4」を表示した後に
    i++

    の処理を行う

  13. iが5になったので、論理式
    i < 5

    の評価の値は「5 < 5」でfalse

  14. 論理式の値がfalseになったのでループを終了する

こんな風に作成します。

今までに学習してきたことは大まかに下のようなことです。

  • リテラルの理解
  • 変数・データ型の理解
  • 条件分岐の理解
  • ループ文、繰り返し処理の理解

これらの処理、文法がプログラミングの基本になります。この基本は「初めの一歩」にして「奥義」たりえます。
基本を極めればどんなプログラムでも対応する事ができます。

そして、楽しく学習するのが一番学習効果が高いので「楽しく」プログラミングできるように想像力を働かせましょう。

具体的には「~出来たら面白そうだ」などと考えてみましょう。もしかしたらものすごい発見があるかもしれません。

でわでわ。。。

<<<前回

Java Basic 実践学習編 〜ハローワールドから変数の宣言・初期化〜

Javaの実践学習

今までに、色々と学習してきましたが、効率と理解度を考慮に入れるとズバリ下のような学習方法が一番だと思いました。

  1. コードを書く
  2. コードを動かす
  3. コードを書き換えて動かしてみる
    4.コードを理解する

この順序でいろんな書き方、プログラムの組み方を理解していくのが最もわかり易く、実践で使えると思いました。

この手順を使用してJavaの基本をやり直したいと思います。

初めてのJava

以前書いた記事はこちらです。

Lv1.ハローワールド

初めtのプログラムとして有名なものですが、これはJava言語に限ったことではありません。C言語、Python, C#, java script etc ...でも同じことです。
プログラムを起動して、「Hello World」と表示するだけのプログラムです。

このプログラムは、大きなアプリケーションに新たに取り組む、もしくはフレームワークを使用するなどの時に「プログラムが動くよね?」という確認のために使用することが多いです。

そして、自分はJava屋なので、Java言語で実装を行います。

下のコードは、「Hello クラス」を作成し、そこにメインメソッドを作成しました。

<Code.01>

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

Eclipse(開発ツール(IDE))での実行した時の動画は以下になります。

シンプルに、コンソール(標準出力)への文字列出力になります。

ポイント1

プログラムが動くことを確認するというところです。

Lv2.適当な文字の出力

上のコードを書き換えて、出力内容を変更します。

<Code.02>

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("I an Takunoji. It's verry hard.");
    }
}

出力した文言の言っていることは、意味がわかりませんが、とりあえずは、出力内容が変わります。

コードの実行確認は、読者の皆さんが行ってください。
※コピペしないほうが、理解も早いです。

ポイント2

"(ダブルクォーテーション)で囲った部分が文字列としてJVM(Javaを起動する機会、java.exeのこと)に、認識されます。
まとめると下のようなイメージになります。

System.out.println(「文字列」);

この「文字列」の部分を引数と呼びます。<Code.02>のコードを説明すると
「printlnメソッドの引数に文字列「I an Takunoji. It's verry hard.」を渡している」と説明できます。

よくあるミス

下のコードはエラーになります。

<Code.03>

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("I an Takunoji. It's verry hard.);
    }
}

すぐに気がつく人は気がつくのですが、気がつかない人はなかなか気がつきません。
自分がそうです。苦労しました、今でも、汚いコードとかにある、このような「小さなミス」を見つけるのに一苦労します。

コードは綺麗に書きましょう

一言で言うと自分があとで苦労します。。。

余談:綺麗なコードとは

単純に、以下の部分がポイントです。

  1. インデントが揃っている
  2. 余計な変数宣言がない
  3. 同じ名前の変数を別の用途に使いまわさない
  4. 処理が整理されていて、どの処理がどこにあるのかわかるようになっている

大まかに上のようなコードです。詳細に関しては、今後理解して行けば良いと思います。

Lv3.計算をしてみる

単純な足し算と引き算をコンソールに出力します。

<Code.04>

public class HelloWorld {
    public static void main(String[] args) {
        // 足し算
        System.out.println("1 + 1 = " + (1 + 1));
        // ひき算
        System.out.println("1 - 1 = " + (1 - 1));
    }
}

上のコードは、文字列「1 + 1 = 」に「1 + 1」の計算結果を文字列として連結して、出力しています。
次の行では、同様に、文字列「1 - 1 = 」に「1 - 1」の計算結果を文字列として連結して、出力しています。

ポイント3

ポイント2でも説明しましたが、System.out.println("1 + 1 = " + (1 + 1));<code>の</code>"1 + 1 = " + (1 + 1)の部分が引数になります。
この引数は"1 + 1 = "<code>が文字列を示し、</code>(1 + 1)が計算処理でその結果は2になります。
最後に、"1 + 1 = " + (1 + 1)にある真ん中の「+」が文字列の連結処理を行っているところです。

文字列の隣に来る「+」は文字連結を示します。

では、次のコードを見て見ましょう、掛け算と割り算です。

<Code.05>

public class HelloWorld {
    public static void main(String[] args) {
        // かけ算
        System.out.println("2 * 2 = " + 2 * 2);
        // わり算
        System.out.println("2 / 2 = " + 2 / 2);
    }
}

ポイント4

このコードは、掛け算と割り算を行なっています。しかし、<Code.04>と比べてみるとかっこが足りません。
具体的に"1 + 1 = " + (1 + 1)<code>と</code>"2 2 = " + 2 2の部分です。

この部分は暗黙的なルールがあり、中学生くらいだと思いますが、「足し算と掛け算」があったら「掛け算」の方を先に計算すると言うルールがあったと思います。

プログラムでも同じなんです。

つまるところは以下のような理由で、上のような違いがあります。

<足し算と引き算の場合>

public static void main(String[] args) {
    // 足し算
    System.out.println("1 + 1 = " + (1 + 1));
    // ひき算
    System.out.println("1 - 1 = " + (1 - 1));
}

足し算と引き算の場合は、()かっこが必要です。それは「文字を連結する」と言う処理と、「計算をする」と言う処理にしようする演算子(「+」のこと)が同じなため

"1 + 1 = " + 1 + 1

のように書くと文字連結か、計算するかJavaコンパイラが判別できないためエラーになります。

<掛け算と割り算の場合>

public static void main(String[] args) {
    // かけ算
    System.out.println("2 * 2 = " + 2 * 2);
    // わり算
    System.out.println("2 / 2 = " + 2 / 2);
}

見た目にも、「+」と「*」で演算子が違うので「文字列の連結」と「計算」の区別がつきます。なのでかっこがなくてもビルドエラーになりません。

ついでにもう1つサンプルコード
<Code.06>

public class HelloWorld {
    public static void main(String[] args) {
        // かけ算と割り算
        System.out.println("2 * 2 = " + 2 * 2 / 2);
    }
}

この場合はどうのような処理結果が出るでしょうか?それは実行して見てからのお楽しみ。

変数を使う

プログラミングを行なっていると「変数」と言う言葉が出てきます。
プログラミングは、中学校くらいに習った(習う)数学と似たところがあります。

演算子 意味 サンプルコード
+ 足し算、文字列の連結 1 + 1, "文字" + "1"
- 引き算 1 - 1
* かけ算 1 * 1
/ わり算 1 / 1
% 剰余算 1 % 1
剰余算について

<Code.07>

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
        System.out.println("3 % 2 = " + 3 % 2);
    }
}

上のように割った後の余りを算出します。「3 / 2 = 1 あまり1」と言うふうになります。なので「3 % 2」の計算結果は「1」になります。

実際に使用するときは「変数Xが偶数か奇数か?」と調べるときに、下のような形で使用します。

<Code.08>

public class HelloWorld {
    public static void main(String[] args) {
        int X = 32;
        boolean isKisu =  X % 2 == 1;
    }
}

このときに変数Xを2で割った時の余りが1になる数は「奇数」です。逆に「0」になるものは偶数です。

このような「法則のようなもの?」を見つけてそれをプログラムに落とし込むと言うところもプログラミングの面白いところです。

変数を学ぶ

プリミティブ型の変数としては以下のようなものがあります。

[プリミティブ型]と呼ばれるデータ型の種類

データ型          読み      用途
byte            バイト        8ビットの整数(-127から127) を示すが実際はファイル入出力時にこのデータ型で扱うことが多い
int               イント        整数として使用する(四則計算など)、整数の計算で使用する事が多い
long              ロング      intよりも大きい数値を使用する時に使用する、日付計算などで使用する事が多い。
double          ダブル        小数点をつける数値として使用する
float            フロート    あまり使わない
char           キャラ          一文字を示す、またintでも扱うことができる 'a'(シングルクォーテーション使用)
boolean        ブーリアン 真(true)か偽(false)を示す、intではtrue=1, false=0

これは全てではないですが、大まかに変数の一覧になっています。

そして、よく使用するのが、以下の4つです。

  1. int型: 整数用の変数
  2. double型: 少数用の変数
  3. boolean型: 真偽値
  4. String型:文字列として使用する

今までにも使用しているコードですが、改めて眺めて見ましょう

<Code.09>

public class HelloWorld {
    public static void main(String[] args) {
        // 変数の宣言
        int seisu;
        // 変数の初期化
        double shosu = 0.0;
        // 変数の初期化
        String moji = "文字列";
    }
}

変数の宣言は値を代入しません。

int seisu;

、そして、初期化は値を代入します

int double shosu = 0.0;

上記ひと通りの内容を説明した動画が、以下になります。

四則計算の実装

次は、四則計算をプログラムで行います。上記の計算でも行いましたが、今回は変数を使用して実行します。

九九(2の段)を算出、表示する

サンプルコードは以下になります。

ポイント5

<Code.10>

public static void main(String[] args) {
    int a = 2;
    int x = 1;
    System.out.println("2 x 1 = " + a * x);
    x++;
    System.out.println("2 x 2 = " + a * x);
    x++;
    System.out.println("2 x 3 = " + a * x);
    x++;
    System.out.println("2 x 4 = " + a * x);
    x++;
    System.out.println("2 x 5 = " + a * x);
    x++;
    System.out.println("2 x 6 = " + a * x);
    x++;
    System.out.println("2 x 7 = " + a * x);
    x++;
    System.out.println("2 x 8 = " + a * x);
    x++;
    System.out.println("2 x 9 = " + a * x);
}

表示結果は以下になります。

ポイント

変数「a」と「x」を初期化して、掛け算した結果を表示しています。そして表示処理(

System.out.println("XXXX");

)の間にある

x++;

の処理は「インクリメント」と言って変数の値に「プラス1」します。
なので、これは2の段を表示できているのです。

今回は、ここまでです。次は、変数を使用して、簡単なプログラムを作成します。

Java テキスト 〜はじめてのJava コンソール表示〜

サイトマップはこちら

はじめてのJava

はじめてJava言語を使用してプログラミングを始めようとしている方向けです。Javaというプログラミング言語はC言語から生まれた言語で、現在(2020-11-11)は多くのアプリケーションで使用されています。具体的には以下のようなものにしようされています。

  1. 証券の取引や銀行のATMなど
  2. Androidアプリ
  3. 大手企業の基幹系システム

挙げればきりがないので、この辺にしておきますが、日本では多くの分野で使用されています。

有名なところだと、マインクラフトでもLWJGLというフレームワーク(Java製)を使用しています。下のような感じで動かせます。

Javaを動かしてみよう

Javaは以下のように、コードの作成~コンパイル~実行と行います。

  1. Javaコードを人力で書く(人間が読めるファイルができる)
  2. コンパイルする(人間が読めないファイルができる)
  3. 作成したプログラム実行

Hello World

まずは写経から入ります。以下のコードを書き写して動かしてみてください。

public class FirstCls {
  public static void main(String[] args) {
    System.out.println("Hello World");
  }
}

実行方法は以下の通り

  1. 上のコードを写してFirstCls.javaファイルを作成する

  2. 「ウィンドウズボタン+R」を押下してコマンドプロンプトを開く
    コマンドプロンプト

  3. javacコマンドでクラスファイルを作成する

    javac FirstCls.java
  4. javaコマンドで作成したクラスファイルを事項する

    java FirstCls

説明した動画です。Java Seapch APIを使用しています。ちょっと聞きづらいですが。。。

サンプルアプリ

  1. 以下のZIPファイルをダウンロード

  2. 展開後に/xylophone/src/xylophone/Xylophone.javaをEclipseプロジェクトにコピー、実行できるようにする

  3. Eclipseプロジェクト/resporces/の下に/xylophone/src/xylophone/audioをコピー

  4. 実行 ※エラーが出たら以下の部分を直してみる
    Xylophone.java:67行目にエラーが出たとき

    new AudioClip(Xylophone.class.getResource("audio/Note1.wav").toString());

    の「audio/Note1.wav」を「/audio/Note1.wav」に修正してみる

1章:ようこそJavaの世界へ

1.1 Javaによる開発の基礎知識

1.1.1 開発の流れ

①ソースコードの作成。

public class FirstCls {
  public static void main(String[] args) {
    System.out.println("Hello World");
  }
}

②コンパイル
javaファイルをclassファイルに変換することです。下のコマンドを使用します。

javac FirstCls.java

③実行
作成したクラスファイルを実行します。下のコマンドを使用します。

java FirstCls

1.1.2 開発環境の整備

※Eclipseはインストール済みの想定。Eclipseのインストールはこちらを参照ください。

外観を変更する

  1. ウィンドウ→設定→一般→外観→色とテーマの順にクリック
  2. ここで好きなテーマを選択する
    ※大半の人がデフォルトで使用している
    色・テーマの設定

文字コードの変更(確認)

  1. ウィンドウ→設定→一般→ワークスペースの順にクリック
  2. 「テキスト・ファイル・エンコード」がUTF-8になっていることを確認

JDKの確認

  1. ウィンドウ→設定→一般→Java→インストール済みのJREの順にクリック
  2. JDKを指定して、使用するJREを決定する
    JRE

1.2 Javaプログラムの基本構造

1.2.1 プログラムの骨格

ルール:クラス名とファイル名は同じ名前にする
「クラスの中にメソッドがある」

/** クラスの説明 */
public class クラス名 {
  /** メソッドの説明 */
  public static void main(String[] args) {
    // 何かしらの処理
  }
}
  • 中かっこ(「{」「}」)のことを「スコープ」と呼びます。スコープで囲っている部分を「ブロック」などと呼んだりします。

  • このスコープ(=「ブロック」)を開いたら(開始したら)、必ず閉じる(終了する)ことを意識する

  • 中かっこ(「{」「}」)で囲っている部分がそれぞれクラスの範囲、メソッドの範囲を決めている

上のサンプルコードでは「クラス名」クラスの中に「メインメソッド」がある

ちなみに、インデント(スペースやタブで位置をずらすこと)を入れないと、とても見にくい

何が見にくいというと、クラスの範囲と、メソッドの範囲を区別しずらいのです。

<インデントなし>

/** クラスの説明 */
public class クラス名 {
/** メソッドの説明 */
public static void main(String[] args) {
// 何かしらの処理
}
}

1.2.2 プログラムの書き方

Java言語(C言語も)の場合は、メインメソッドが起動しますので、クラスの中にメインメソッドを作成する

Javaはメインメソッドが動く

  1. どのようなプログラムを作りたいかを考えます。
  2. プログラムの名前を決めます。
  3. 「クラス名.java」という名前でファイルを作ります。
  4. 下のような、ソースコードの外側を記述します。
    /** クラスのJavaDocコメント */
    public class クラス名 {
    /** メインメソッドのJavaDocコメント */
    public static void main(String[] args) {
    // 処理の中身:通常のコメント
    /*
     * これも通常のコメント、あまり使われない。
     */
    }
    }
  5. ソースコードの中身を描きます。
    System.out.println("Hello World!");

     1.2.5 mainメソッドの中身

  • 上の骨格の「何かしらの処理」の部分に処理を描く。
  • 処理は上から下へ進み、1行の中では左から右へ進む
  • セミコロン「;」で1行が終わる
  • ドット「.」は参照呼出し
public static void main(String[] args) {
  System.out.println("Hello World");
}

上のコードは、Systemクラスから変数「out」を呼び出し、メソッド「println()」を呼び出している

練習:SecondProgram

写してください。以下のコードを書いて動かしてください。

public class SecondCls {
  public static void main(String[] args) {
    System.out.println("*** Second Program ***");
    System.out.println("日記ソフト");
    System.out.println("終了します。");
  }
}

実行方法は以下の通り

  1. SecondCls.javaファイルを作成する

  2. 「ウィンドウズボタン+R」を押下してコマンドプロンプトを開く
    コマンドプロンプト

  3. javacコマンドでクラスファイルを作成する

    javac SecondCls.java
  4. javaコマンドで作成したクラスファイルを事項する

    java SecondCls

1.3 変数宣言の文

1.3.1 変数宣言の文とは?

ズバリ下のように書きます。

  1. 変数の宣言
    // 「宣言」:データ型 変数名;
    int hensuMei;
  2. 変数の初期化
    // 「初期化」データ型 変数名 = 代入するデータ
    int dataGata = 0;

1と2の違いは、宣言と同時に値を代入するかしないかです。

==変数の宣言と初期化==

public static void main(String[] args) {
  // 変数の宣言
  int num;
  // 変数の代入
  num = 0;
  // 変数の初期化
  int age = 12;
  // 命令実行の文=メソッドの呼び出し
  System.out.println("対象年齢は" + num + "~" + age + "です。");
}
  • 「変数に値を代入するとき」は「=」を使用する。
  • 「命令の文」は「メソッド呼び出し」を行う

1.3.2 変数の名前(識別子)

==英語では==
変数=Valiable

名前つけのルール

  1. 予約語は使用しない
1 2 3 4 5
abstract assert boolean break byte
case catch char class const
continue default do double else
enum extends final finally float
for goto if implements import
instanceof int interface long native
new package private protected public
return short static strictfp super
switch synchrnized this throw throws
transient try void volatile while
  1. 宣言(初期化)済みの変数名は使用できない

  2. 大文字・小文字は区別される

  3. 変数名・メソッド名はキャメルケースを使用する

    // 変数名のキャメルケース(頭を小文字にする)
    int myAge;
    // メソッド名のキャメルケース(頭を小文字にする)
    public void methodName();
  4. クラス名はアッパーキャメルケース

    /** クラス名は頭を大文字にする */
    public class ClassName {
    }
  5. 演算子で使用している文字、予約語は変数名(クラス名)として使用できない

==計算処理==

public static void main(String[] args) {
  // 足し算
  int tashizan = 1 + 1;
  // 引き算
  int hikizan = 1-1;
  // 掛け算
  int kakezan = 1 * 1;
  // 割り算
  int warizan = 1 / 1;
}

1.3.3 データ型

※あくまでも使用例として紹介しています。
==プリミティブ型データの変数型==

  • byte ファイル・ネットワーク通信時に使用
  • int 整数データを格納するのに使用
  • long 大きな整数値を格納するのに使用
  • float 画像処理、3Dモデル描画時に使用
  • double 少数データを格納するのに使用
  • char 1文字分のデータ、シングルクォーテーション「`」で囲む

==参照型データの変数型==

  • String 文字列
  • そのほかJavaAPIや自作のクラス名
    List<String> list; // List型の変数「list」

1.3.5 定数の利用

変数には値を何度でも代入できるので、定数を使用して値を変更できないようにする。

final データ型 変数名 = 代入するデータ

ルール:定数は大文字の変数名にする

final int OVER_TEN = 0; // 値が10以上を示すフラグ
final int UNDER_TEN = 1; // 値が10以下を示すフラグ

定数の使用例:フラグに使う

public static void main(String[] args) {

}

1.5 練習問題+α

コマンドプロンプト上に下のような図を表示するプログラムを作成してください。
※ 「=」の数が、上下ともに20あります。

<出力する図>

      *
    *   *
  *   *   *
      *
      *
  *   *   *
    *   *
      *

式と演算子

演算子

番号 演算子 意味
1 .(ドット) メソッド、フィールドの呼び出し
2 new クラスのインスタンスを作成するときに使用する
3 == 左右の数値が等しいか判定する(TRUE / FALSEを返す)
4 != 左右の数値が等しくないか判定する(TRUE / FALSEを返す)
5 & ビット演算 AND =「~かつ~」のときTRUE、それ以外はFALSE」
6 | ビット演算 OR =「~または~」のときTRUE、両方ともFALSEの場合はFALSE
7 ? XX : YY 三項演算子 =「論理演算 ? TRUEの時の値 : FALSEの時の値」:
例 String res = 1 == 1 ? "TRUEの場合": "FALSEの場合";

2.2 オペランド

2.2.1 リテラル

実際の値のこと

2.2.2 エスケープシーケンス

バックスラッシュ「\」もしくは、「¥」円記号(半角)をつけた文字のことでメタ文字を「文字」として使いたいときに使用する。

<例>

// 「"」は文字列を加工形で表示するためにエスケープシーケンスを使用する
// ※「"」を文字列として使用したいときは「エスケープ」する
String moji = "Javaのはじめの\"J\"は1文字目";

2.3 評価のしくみ

下のような式があったとします。
「1 + 5 -3」この式をプログラムで書くと次のコードになります。

public static void main(String[] args) {
  // 上の計算式
  int answer = 1 + 5 - 3;
}

このようなときに、処理の順序、「1 + 5」と「5 - 1」の評価はどのように行われるか?

左から順に評価していきます

つまり、以下の順序です。

  1. 1 + 5の計算結果を出す
  2. 上の結果に」「-1」する

==しかし掛け算は先に処理を行う
つまり以下のような優先順位で評価します。

  1. 「掛け算・割り算」→「足し算・引き算」の順序
  2. 左から順に評価

まとめると

  1. 「掛け算・割り算」→「足し算・引き算」の順序で評価
  2. 「=」以外の演算子は、左から順に評価
  3. 「=」は右から評価
問題+α

以下のコードの場合、どのような順序で評価されるでしょうか?

System.out.println("Hello World!");

実行順序として正しいものを、の中から選びなさい
<A>

  1. Systemクラスの公開フィールド変数out(PrintStreamクラス)を取得
  2. フィールド変数out(PrintStreamクラス)のメソッドprintln()を評価(呼び出している)

<B>

  1. メソッドprintln()を取得
  2. フィールド変数out(PrintStreamクラス)のSystemを評価(呼び出している)

2.4 演算子

2.4.1 算術演算子

演算子 機能 優先順位
+ 足し算
- 引き算
* 掛け算
/ 割り算

2.5 型の変換

2.5.3 強制的な型変換(キャスト)

「int型」を「double型」に変換するというような形のキャスト

<具体例>

int num = 10;
// 強制的な型変換
double dd = (doubble) num;

// 下のやり方はエラーになる
// int num1 = (int) "10";

// 型をメソッドを使用して変換している
String tmp = String.valueOf(num);

// Stringが票差化された(左に文字列が先にある)あとであれば
// intやdoubleも文字列として評価される(自動キャスト)
System.out.println("aaaa" + 10.1);

異なる型同士の算術演算

返却値(左側の変数型)は、大きいほうの型のデータ型が優先される

2.6 命令実行の文

2.6.5 乱数を生み出して代入する命令

// 3を上限値として乱数を生成する
int r = new java.util.Random().nexInt(3);

上にあるような「java.XXX.XXX」のようなパッケージはJDKの中にあるライブラリに定義されています。
そして、それらAPI群の使い方に関しては
JavaDocに書かれています。

2.7 まとめ

命令の実行はメソッド呼び出しのこと

public static void main(String[] args) {
    // 命令(メソッド)呼び出し
    hello();
}

public static void hello() {
    System.out.println("Hello World!");
}

Java Basic API ~Java Stringクラスを理解するwith JUnit~

イントロダクション

Javaでプログラミングを初めて慣れたころにJavaAPIに触れることが多くなると思います。

よくある「プログラミング練習問題」などで、文字列操作を行うことがあると思います。

この様な時に使用するのがStringクラス手に定義してあるメソッド群だと思います。しかし、説明書きしているJavaDocが難しく理解するのが難しいことが多いと思います。

この解決方法として、動かしてみるというのが手っ取り早い方法の一つだと思いますので、手っ取り早い方法を記載いたします。

JavaSEで提供しているクラスを理解

簡単にプログラムを動かす方法として、IDE(Eclipseを使用します)で動かしてみるのが手っ取り早い方法ですが、如何せんメインメソッドを使用していると毎回コードを書き直さなくてはいけないので面倒です。

なので、以下の方法を提案したく思います。

JUnitを使う

JUnitはテストツールとして使用することが多いもの(フレームワーク)です。これを使用すれば下のように簡単に、いくらでもコードを動かせます。

下のは、String#equalsメソッドをテストしたものですが、「@Test」をつけたメソッドはすべて実行されるので、どんどん作成して実行すればよいのです。
<例>

public class StringTest {

    /** String#equalsのテスト */
    @Test
    public void test01() {
        String st = "aaa";
        String s1 = "aaa";
        String s2 = "bbb";
        String s3 = "ccc";

        if (st.equals(s1)) {
            System.out.println("st == s1");
        } else {
            System.out.println("st != s1");
        }

        if (st.equals(s2)) {
            System.out.println("st != s2");
        } else {
            System.out.println("st == s2");
        }

        if (s3.equals(s1)) {
            System.out.println("s3 == s1");
        } else {
            System.out.println("s3 != s1");
        }
    }
}

実行結果

テストを増やす

StringクラスのJavaDocを見るとよく使うメソッドがあるので、それを紹介するついでに上のテストケースで実行します。
実際の作業を動画にしました。参考にどうぞ。

そして、実行するときにJUnitの設定をする必要があります。

Eclipseを使用しているならば、下のような手順ですぐに使用できます。

JUnitの設定

  1. Eclipeのプロジェクトを右クリック
  2. プロパティを選択
  3. ライブラリの追加をクリック
  4. JUnitを選択し次へ
  5. 次の画面では、完了をクリック

下のように、ライブラリが追加されているはずです。

String#substring

JavaDocでStringクラスを見るとStringクラスの中に定義されているメソッド群があります。説明がちょっと難しく理解に苦しむことがありますが、これを動かしてみれば、ドキュメントの内容がわからなくても問題ありません。逆に、ドキュメントの内容が理解できたりします。

具体的には、上の動画でも実行していますが、下のように実行します。

  1. JavaDocで試したいメソッドを見つける
  2. 対象のメソッドのテストケースを作成する
  3. テストを実行して挙動を確認する

作成したコード(テストケース)は下のような形です。

public class StringTest {

    /** String#equalsのテスト */
    @Test
    public void test01() {
        String st = "aaa";
        String s1 = "aaa";
        String s2 = "bbb";
        String s3 = "ccc";

        if (st.equals(s1)) {
            System.out.println("st == s1");
        } else {
            System.out.println("st != s1");
        }

        if (st.equals(s2)) {
            System.out.println("st != s2");
        } else {
            System.out.println("st == s2");
        }

        if (s3.equals(s1)) {
            System.out.println("s3 == s1");
        } else {
            System.out.println("s3 != s1");
        }
    }

    /**
     * JavaDocをみて、入力(引数)と出力(返却値)を確認後、実装して動かしてみる。
     * {@link String#substring(int)}のテストケース
     */
    @Test
    public void testSubstring() {
        String target = "abcdefg";
        // 2番目の文字を取得する
        String res = target.substring(1, 2);
        System.out.println("2番目の文字: ");
        // 取得結果が正しいか確認
        assertEquals("b", res);
        // 一番初めの文字を取得
        String res1 = target.substring(0, 1);
        assertEquals("a", res1);
        // 一番最後の文字
        String res2 = target.substring(target.length() -1, target.length());
        assertEquals("g", res2);
        // 初めから4文字分を切り出す
        String res3 = target.substring(0, 4);
        assertEquals("abcd", res3);
    }
}

equalsは調べなくてもわかると思いますが、substringはどうでしょうか?
ドキュメントには、下のような説明があります。

この文字列の部分文字列である文字列を返します。部分文字列は、指定されたbeginIndexから始まり、インデックスendIndex - 1にある文字までです。したがって、部分文字列の長さはendIndex-beginIndexになります。

とりあえずは、第一引数に開始、第二引数に終了のインデックス(数値)を設定します。

String res3 = target.substring(0, 4);

実行した結果がどのように取得できるか?これを確かめます。

assertEquals("abcd", res3);

実行後に、いまいちわからない場合は、値を変えてみます。上記の「0, 4」は初めから4文字を取得する場合ですが、2文字目から4文字目までを取得する場合はどうでしょうか?下のように「2, 4」と設定すれば想定通りに動くでしょうか?

String res3 = target.substring(0, 4);

それはテストしてみれば一発です。

assertEquals("取得するであろう値", 結果を格納した変数);

この様な形で、実装すればJavaAPIの理解を深めることができます。

色々なクラスを理解し、技術の幅を広げるのに役に立つと思います。

ちなみに、YoutubeにアップしたJUnit関連の動画はこちらのURLで一覧になっています。再生リストというやつです。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ(Mac版)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜

Java はじめて31 〜JUnitでのテスト駆動型開発8: 処理をつなげたテスト〜

イントロダクション

今まではユニットテスト(単体テスト)をやっていましたが、複数のクラスを実行するテストケースも作成することができます。
プログラム的には、実行するプログラムを増やすだですが。。。

イメージとしては、メインメソッドを動かすテストを実行するようなイメージです。
そうすれば、メインメソッドを動かした方が早いような気がしますが、大掛かりなアプリケーションの場合は、そうもいきません。

なので、「足し算をする処理のテスト」「ファイルを読み込むテスト」とテストを行った後に、左のテストをつなげてテストをするというような形です。

今回のような小さなプログラムでは、あまり意味がありませんが。。。

今回は、今まで作成した1処理を「機能」として動かせることを確認するテストケースを作成します。
通常のやり方では、1.実装、2.テストと分けた場合の「2.テスト」にあたります。

機能テスト

仕様は以下の通りです。

仕様
1. ファイルを操作するためのオブジェクトを作成する
2. ファイルにデータをCSV形式で書き出す。
3. ファイルが存在するのであれば、それを読み込みデータを保持する

今までの実装、テストで行くと「3」が実装したものに相当します。なので、これをテストケースで確認する様にします。
現状では、スタブのままなので未実装になっています。
なのでテストケースを先に作成します。
ちなみに、最終的なコードはGithubにあります。

  1. KozaManager.java
  2. KozaManagerTest.java
@Test
public void testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List<Data> dataList = target.readFile();
    // データは1件
    assertEquals(1, dataList.size());
    // ファイルにあるデータ
    Data data = dataList.get(0);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

とりあえずで、上の様に作成しました。初めにファイルの存在チェック処理を確認して、ファイルが存在しないならテスト失敗。
次にファイルの読み込みを行い、そのデータを取得しデータが想定通りのものが入っているか確認します。

余談

テストを複数回行っていたので「koza.csv」にデータが複数作成されていました。ヘッダー付きなのでこれはダメなテストになってしまうので、ちょっと修正します。
<修正1>
「ファイルが存在するときにはファイル作成をしない」が抜けていたのでこれを追加します。

// ファイルそ新規で作成するとき
if (file.exists() == false) {
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル書き込み処理
    write.write(build.toString());
    write.newLine();
}

そして、修正後のメソッドは以下の様になります。

public void dataOutput(Data data) throws IOException {
    if (file.canWrite() == false) {
        throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
    }
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ファイルそ新規で作成するとき
    if (file.exists() == false) {
        // ヘッダー部分の出力
        build.append(this.createCSVHeader());
        // ファイル書き込み処理
        write.write(build.toString());
        write.newLine();
    }
    // StringBuilderのクリア
    build.setLength(0);
    // データ部分の書き込み
    build.append(data.getName() + ",");
    build.append(data.getPassword());
    write.write(build.toString());
    write.newLine();
    write.close();
}

これで、ファイルが存在しているのに新たにファイルを作成することがなくなります。

しかし、テストはうまくいきませんでした。
これの原因は、テストケースにありました。

@Test
public void testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List<Data> dataList = target.readFile();
    // データは1件
    assertEquals(1, dataList.size());
    // ファイルにあるデータ
    Data data = dataList.get(0);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

取得したデータのサイズ(ファイルの行数)は1の想定ですが、テストを実行するたびにデータが追加されるので、テストが毎回同じ結果になりません。

これは良くない

なので、ここもちょっと修正します。

// データは0件ではない
assertNotEquals(0, dataList.size());

こんな感じで修正しました。そして、複数件あっても作成されるデータは同じなので初めの一件目が想定通りの値かどうか確認します。ここでちょっと注意「初めの1行目はヘッダー行なのでデータ部ではないですが、取得したときはリストの1番目に設定されています。なので以下の様に修正します。

public void testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List<Data> dataList = target.readFile();
    // データは0件ではない
    assertNotEquals(0, dataList.size());
    // ファイルにあるデータは2行目から
    Data data = dataList.get(1);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

こんな感じでファイル読み込みの一連の機能をテストしました。これと同様に、他ものもテストしました。わかりづらいですが、上のキャプチャにテストケースが、4つあります。
ファイル作成、ファイル存在チェックの処理の他は読み込みと書き込みをテストしています。

ちょっと締まりが悪いですが。今回はこれまでにします。

でわでわ。。。

<<< 前回

<Java関連の動画リスト>

<JUnit関連の動画リスト>



Java はじめて30 〜JUnitでのテスト駆動型開発7: テストクラスの実装方法〜

イントロダクション

前回までは、部分的にテストケースなどを作成してきましたが、テストケースの作成をするために必要なこと〜実装までを記載したいと思います。

今回は、まとめ的に、JUnitでの実装をやります。今までは、コードを書き終わったものを記載していましたが、今回はコードを書きながら記載していこうと思います。自分のやり方になりますが。。。

テストクラスの実装方法

仕様確認1

  1. koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す
  2. koza.csvがない場合は、システムエラーを出す

上の仕様を満たす様に、テストケースを考えます。シンプルなもので、単純に実装すれば良さそうです。
「〜そうです。」と記載したのは、実装などはやってみないとわからないこともあるのでこの様に記載しました。早速作ってみます。

テストケースを作る

まずは、単純にテストクラスを作成します。このクラス名は「テスト対象クラスの名前+Test」という形で作成します。
テストクラスと実装するクラスのセットが、そうするとわかりやすいからです。すでに実装していますが「初めから作る」というていで記載します。

作成するクラス
KozaManager(実装クラス)
KozaManagerTest(テストクラス)

<実装クラス>

public class KozaManager {

    public Listl<Data> fileRead() {
        // 実装前なのでスタブにします。
    }

} 

<テストクラス>

public class KozaManagerTest {
    @Test
    public void testFileRead() {
        // これもスタブ
    }
}

現状は、上記の様な状態です。
そして、考えます。何を?。。。仕様の1番目「koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す」にはどの様なテストが必要か?を考えます。単純に考えるならば以下の様になります。

  1. ファイルの存在を確認する処理が想定通りに動くか?
  2. ファイルが存在するならば、それを読み込みデータリストを返却するか?

そして、もう1つの仕様「koza.csvがない場合は、システムエラーを出す」に関しては

  1. ファイルが存在しないときはシステムエラーを出し、アプリケーションを終了するか?

それぞれ、上の様なチェック(テスト)を行います。ぶっちゃけて、まずはこの単純な確認を行ってから次に「穴はないか?」を考えれば良いと思います。

なのでシンプルにその様なテストケースを作成します。
まずは。「ファイルの存在を確認する処理が想定通りに動くか?」これを確認するために実装クラスとテストクラスの両方を修正します。着手するのは1つずつなので安心ください。
ますは、テストケース(テストクラス)
<テストクラス>

public class KozaManagerTest {
    @Test
    public void testIsFile() {
        // ファイルの存在チェック処理の確認
    }
    // 今はテストしないのでコメントアウト
    // @Test
    public void testFileRead() {
        // これもスタブ
    }
}

とりあえずは、ファイルの存在チェック処理を動かすためのテストケース(メソッド)を作成しました。
ここで、足りないものがあります。

  1. テスト対象クラス(実装するクラス)
  2. ファイル存在チェック処理メソッド

通常の開発だと、これらを実装してからテストしますが。先に箱(スタブ)だけ作ってしまいます。
<実装クラス>

public class KozaManager {
    public boolean isFile() {
        // とりあえずスタブです。
    }
    public Listl<Data> fileRead() {
        // 実装前なのでスタブにします。
    }

} 

そして、ちょっと考えてみると「ファイルの存在チェック処理を行うのに、ファイル名が必要だな。。。」と気がつくと思います。

ここが悩みどころ

ファイル名を(引数で)渡す処理方法と、ファイル名を固定して渡す方法のどちらもできるので「どちらの手段が汎用性、保守性共に高いだろうか?」と悩んでみます。
自分の結論は「引数でファイル名を渡す」方が良いと思ったのですが、すでに実装ている部分ではコンストラクタで生成しているので今回は、引数なしでのメソッドにします。なので以下の様なコードになります。
<実装クラス>

public class KozaManager {
    /** 作成するファイルが存在するかチェック */
    public boolean isFile() {
        // fileはフィールド変数
        return file.exists();
    }
    public Listl<Data> fileRead() {
        // 実装前なのでスタブにします。
    }
} 

<テストクラス>

public class KozaManagerTest {
    @Before
    public void initTest() {
        target = new KozaManager();
    }

    @Test
    public void testIsFile() {
        // コンストラクタでファイルが作成されることに注意
        assertTrue(target.isFile("koa.csv"));
    }

    // 今はテストしないのでコメントアウト
    // @Test
    public void testFileRead() {
        // これもスタブ
    }
}

実行結果は下にあります。

でわでわ。。。

<<< 前回 次回(ラスト) >>>

<Java関連の動画リスト>

<JUnit関連の動画リスト>



Java はじめて29 〜JUnitでのテスト駆動型開発6: ファイル読み込みのテスト〜

イントロダクション

前回は、ファイルの出力を実装しました。
もちろんテストケースありきの実装です(テスト駆動開発)。ここまでくると、だんだんと実装とテストと慣れてくると思います。
横っ飛びして、別なプログラムを作成し始めてみるのも1つです。

わからなくなったら、また調べれば良いのです。

今回は、今迄作成した「ファイル存在チェック」と「ファイル作成、更新」処理を繋げてテストします。

この段階で、ようやくテストらしいテストになります。ちなみにこのテストケースも実装前に作成出来ますが、小さなレベルで、テスト仕様を考える→実装→テスト実施…とやった方が早い気がします。

口座作成処理

ようやく、仕様レベルの話に戻って来ました。このテストケースはコーダー銀行に口座を開設する処理に当たりますので、実際に動かす時と同じような入力(テストなのでデータ)を渡して実行します。テストコードは下のようになります。

このテストケースが通ったらとりあえずは、製造完了です。お疲れ様でした。

ひと段落したので、一旦休むも良し、このまま突走るのも良し。

他の事に着手してみるも良し!

動く事が大事

何が言いたいかと言うと、プログラムは何かを作る、仕組みを作るなどに使用する。若しくは単純に触りたいだけ…など色々あると思いますが、ちゃんと動くモノを作った方が、実力もつくし、作ったもので遊べるし、良い事ずくめだと思うので、色々と作ってみるのが良いと思います。

今迄やって来ているのは、サンプルなので…主な目的は「Javaの学習」になっています、あまり作りたいものには届かないと思います。しかし、基礎を学べる様になっているのと、コンソールアプリなので応用はいくらでも出来ます。早い話が、コンソールアプリが作れれば、あとは環境が違うだけなので大まかな流れは理解できるずです。。。

続き

前置きは、このくらいにして、続きをやります。

早い話が、今迄作成したメソッドを続けて呼び出し、それらが想定通りに動くか?の確認を行います。

作成したメソッドは以下のものです。

そして、上のメソッドを結合してやる場合のテストケースから考えます。

データの読み込み

仕様

  • ファイル存在チェックを行いファイルが存在すれば読み込む
  • 存在しなければファイル作成を行う

仕様に関してはこの部分のみをみていましたが、ファイルの読み込みをした後に、データが入っているはずなので、これを取得する必要があります。

この部分は実装していないので、実装が必要です。とりあえずは、目の前のテストを片付けてしまいます。テストコードは下の様になりました。テストを実施しながら実装するので手直しをするからこうなりました。と言う表現を使いました。

テストが通ったので、次の事を考えます。つまり、ここまでは想定通りに行ったという事です。

データの取得

次は、読み込んだファイルのデータをクラスに設定してやる事を考えます。

ファイルに保存してあるデータを読み込んで、プログラムで使いやすい様にデータクラスを使ってデータを保持します。「保持」と言うのは、プログラムが止まると消えてしまう情報なので「保持」と言う言葉を使いました。

ちなみに、ファイルに保存すれば、プログラムが止まっても情報は残しておけます。

現状では、Dataクラスを使用してファイルにデータを書き込んでいます。
ちなみにデータクラスは下の様なコードになります。

public class Data {
    /** ユーザー名 */
    private String name;
    /** パスワード */
    private String password;

    /** 
     * コンストラクタ。口座情報を保持するクラスのインスタンスを生成。
     * 
     * @param name 口座のユーザー名
     * @param password 口座のパスワード
     */
    public Data(String name, String password) {
        this.name = name;
        this.password = password;
    }
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }
    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }
}

データクラスは、フィールドとGetter, Setterのみのクラスです。

読み込み時にも同じクラスが使えます。
まとめると以下の様な手順でファイルを読み込みます。

  1. ファイルを開く(コンストラクタで実装ぞ済み)
  2. ファイルを1行ずつ読み込みDataクラスへ設定
  3. データクラスをリストにして返却する

以上の様な手順でファイルからデータを取得します。
そして、これをテストケースにします。

テスト仕様

上の手順そのままですがこれを確認するためのテストケースを作成します。そしてこれもとりあえずは「スタブメソッド」を作成します。

/**
 * ファイルを読み込みデータをリストにして返却する
 * @return List<Data> CSVファイルのデータリスト
 */
public List<Data> readFile() {
    return null;
}

このメソッドはJavaDocコメントにある様に、リストにしてデータを返しますので、java.util.Listインターフェースを使用してデータを返却します。
Listインターフェースの配列型リスト=ArrayListクラスを生成してデータをセットする様に実装します。コードは以下の様になります。

public void testFileRead() {
    List<Data> dataList = target.readFile();
    // データは1件
    assertEquals(1, dataList.size());
    // ファイルにあるデータ
    Data data = dataList.get(0);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

このテストケースを実行しエラーが出ない様なコードを作成します。一応、CSVファイルはファイル作成時に出力したデータが設定されていることを確認しています。

<<< 前回 次回 >>>

<Java関連の動画リスト>

<JUnit関連の動画リスト>

Java はじめて28 〜JUnitでのテスト駆動型開発5: ファイル出力の実装〜

 ## イントロダクション
前回は実装するべきクラスの実装を行いました。しかし、ファイル入出力の部分は手付かずだったので、その部分を実装しようと思います。

今回は、ファイル出力処理を実装します。つまりテストケースの作成と実行を行います。記事の下に動画があるので、参考にどうぞ、

ファイル出力の実装

ファイル出力と言っても、ファイルの作成自体はすでに実装済みなのでデータをファイルに書き込む処理の実装ということになります。
<前回までにできていること>

  1. ファイル作成(koza.csv)
  2. ファイルの存在チェック(KozaManager#isFile)

ここまでが実装したものになります。ちなみにテストコードは下の様になります。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;
    /**
    *  テストの初期化
    *  各テスト実行前に起動する
    */
    @Before
    public void initClass() {
        target = new KozaManager();
    }
    /**
    * コンストラクタが起動したかどうかを確認する
    * テストケース
    */
    @Test
    public void testIsInstance() {
        assertNotNull(target);
    }
    /**
    * ファイルの存在チェック処理の確認
    */
    @Test
    public void testIsFile() {
        assertTrue(target.isFile());
    }
    /**
    * ファイルにデータを出力し保存するテストケースです。
    */
    public void testFileCeate() {
        Data data = new Data("名前", "パスワード");
        try {
            target.dataOutput(data);
        } catch(IOException ie) {
            ie.printStackTrace();
            fail();
        }
    }
}

ここでの注意点は、「@Test」のついていないテストケース(メソッド)がファイル出力のテストケースになります。
なので、今回はこのコードを実行しようと思います。
早速「@Test」を上のテストケースにつけます。

/**
* ファイルにデータを出力し保存するテストケースです。
*/
@Test
public void testFileCeate() {
    Data data = new Data("名前", "パスワード");
    try {
        target.dataOutput(data);
    } catch(IOException ie) {
        ie.printStackTrace();
        fail();
    }
}

想定通りにいかない原因

製造中につきものの「動かない。。。」にあたります。
本当であれば、テストコードの中でファイルの中身を確認するのですが、ファイル出力後のテストケースも考えていないので、とりあえず。。。と言ったところです。
さて、想定通りにいかない原因を考えます。というか探します。本体のコードを見直してみると。。。

public void dataOutput(Data data) throws IOException {
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル出力処理
    write.append(build.toString());
    // StringBuilderのクリア
    build.setLength(0);
    // データ部分の出力
    build.append(data.getName() + ",");
    build.append(data.getPassword());
}

「あー、これはファイルの中身がなくて当然だなぁ」と気がつきます(笑)
上のコードでは、出力用文字列作成用のオブエジェクトを作成しているだけです。

build.append(data.getName() + ",");
build.append(data.getPassword());

なので修正します。

public void dataOutput(Data data) throws IOException {
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル書き込み処理
    write.write(build.toString());
    // StringBuilderのクリア
    build.setLength(0);
    // データ部分の書き込み
    build.append(data.getName() + ",");
    build.append(data.getPassword());
    write.write(build.toString());
}

そして、ファイルに出力したらファイルの入出力で使用しているReaderとWriter、そのほかのフィールド変数の後始末をする必要があります。出ないとメモリ上にインスタンスが残ってしまいます。Javaの場合はガベージコレクションで最終的にメモリは解放されますが、ここでは明示的に「KozaManger」クラスのインスタンスが解放されるときに各フィールドの解放を行います。(デストラクタに実装)

/** デストラクタ */
@Override
protected void finalize() throws Throwable {
    // フィールド変数の後始末
    file = null;
    read.close();
    write.close();
    write = null;
    read = null;
}

それでは、改めてテストを実行してみます。しかし想定通りに行きませんでした。。。
ファイルは出力しているのですが、肝心の中身が出力されていない状態でした。
原因としては「ファイルを閉じていない」と「ファイルを開くときに追記モードで開いていない」ことでした。
そして、最終的には以下の様なコードになりました。

public void dataOutput(Data data) throws IOException {
    if (file.canWrite() == false) {
        throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
    }
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル書き込み処理
    write.write(build.toString());
    write.newLine();
    // StringBuilderのクリア
    build.setLength(0);
    // データ部分の書き込み
    build.append(data.getName() + ",");
    build.append(data.getPassword());
    write.write(build.toString());
    write.newLine();
    write.close();
}

というわけで、テストをしながら実装するのでやりやすい(色々と試しやすい)実装方法だと思います。
ちなみにテストケースは、色々と試したので少々変わっています。

@Test
public void testFileCeate() {
    Data data = new Data("test", "passwd");
    try {
        target.dataOutput(data);
    } catch(IOException ie) {
        ie.printStackTrace();
        fail("ファイル入出力に問題があります。");
    } catch(Exception e) {
        e.printStackTrace();
        fail("想定外のエラーが起きました。");
    }
}

そして、本体のクラス(メソッド)も同様に修正が入っております。最終的に以下の様な実装になりました。

public void dataOutput(Data data) throws IOException {
    if (file.canWrite() == false) {
        throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
    }
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル書き込み処理
    write.write(build.toString());
    write.newLine();
    // StringBuilderのクリア
    build.setLength(0);
    // データ部分の書き込み
    build.append(data.getName() + ",");
    build.append(data.getPassword());
    write.write(build.toString());
    write.newLine();
    write.close();
}

後、ファイルを開くときに追加書き込みモードで開く必要があるので。。。(今回の実装ではデータの保存を行うのでフィールドで保持する必要があるため)コンストラクタの修正も行いました。

// 修正前のコード
write = new BufferedWriter(new FileWriter(file));
// 修正後のコード
write = new BufferedWriter(new FileWriter(file, true));

実行結果は以下の通りです。

次回は、各処理を(ファイル存在チェック〜ファイル出力)テストします。最終的な確認のコードです。

でわでわ。。。

<<< 前回 次回 >>>

<Java関連の動画リスト>

<JUnit関連の動画リスト>