Java はじめて16 〜クラス設計から実装〜

クラス設計から実装

クラスの設計〜実装までの簡単な流れを記載したいと思います。いよいよ基本レベル2のプログラミングの始まりです。

余談

よく世間巷では「オブジェクト指向言語」などという言葉が使われていますが、これは「オブジェクト指向で作りやすい」という意味で使用します。オブジェクト指向という言葉がいろんな意味で解釈されているので(同様に、関数型も。。。)使用しません。逆に使用されていたら無視してください。

基本レベル2

筆者の考えですが、プログラミングの学習を行うのには、3つの段階があると思っています。その段階は次のようになると考えます。

  1. 基本文法の理解をする段階
    基本レベル1
  2. クラスとクラスの関係を作り、一つの仕組みを作るための知識を得る段階
    基本レベル2
  3. 新しい仕組みをくみ上げる、新しいテクノロジーを追求する段階
    応用編

そして、今回は基本レベル2の段階に学習を進めようというところです。
まずは、クラスの継承方法、抽象クラス、インターフェースの作り方を覚えましょう。
次に、ポリモーフィズムを理解しましょう。

まずは、クラスを作るときの基本的な考え方を理解しましょう。

よくないプログラムの例

public class Sample_Array {
    public static void main(String[] args) {
        // 1次元配列
        String[] lv1Array = new String[] {"a", "b", "c"};
        // 2次元配列
        String[][] lv2Array = new String[][] {{"a", "b", "c"}, {"d", "e", "f"}};
        // 3次元配列
        String[][][] lv3Array = new String[][][] {
            {{"a", "b", "c"}, {"d", "e", "f"}}
            ,{{"g", "h", "i"}, {"j", "k", "l"}}
            ,{{"m", "n", "o"}, {"p", "q", "r"}}
            };
        // 1次元配列を表示する
        System.out.println("*** 1次元配列を表示 ***");
        for (String nakami : lv1Array) {
            printf("", nakami);
        }
        // 2次元配列
        System.out.println("\n*** 2次元配列を表示 ***");
        for (String[] nakamiLv2 : lv2Array) {
            for (String nakami : nakamiLv2) {
                printf("", nakami);
            }
        }
        // 3次元配列
        System.out.println("\n*** 3次元配列を表示 ***");
        for (String[][] nakamiLv3 :lv3Array) {
            for (String[] nakamiLv2 : nakamiLv3) {
                for (String nakami : nakamiLv2) {
                    printf("", nakami);
                }
            }
        }
    }

    private static void printf(String label, String value) {
        System.out.print(label + "" + value);
    }
}

ポイントとしては、以下の部分が良くないプログラムです。

  1. プログラムのほとんどを1つのメソッドに書いているので、変更を加えようとすると必ずこのファイルを修正しないといけません。
  2. 処理の内容が少ないので問題ありませんが、処理が分割されておらず「〜の処理はXXXにまとめる」というように処理が分割されていない。

結局は小さなプログラムではあまり効果がないのですが、何かのアプリケーションを作成しようとした時に威力を発揮します。

簡単なアプリを作る

コンソールアプリになりますが、簡単に作成して行こう思います。まずは設計です。

設計を行うことで、「プログラムをどのように組むか?」というところを考えます。はっきり言って答えがないので考え続けることになると思います。
しかし、これができれば、プログラミングはもっと面白くなり、世間にも貢献できるようになるかもしれません。
まずは、考えるための材料をゲットしましょう。※知識を取得しましょうという意味です。

クラス図について知識をゲットしましょう。

UMLで設計関連のページ

詳細部分に関しては上記のリンク先を参照していただきたく思います。が簡単に「ATMのようなアプリケーション」を作成してみようと思います。

全体の流れ(ユースケース)

「全体の流れ」と記載しましたが、「人が使うときの流れ=ユースケース」になります。
実際のATMとはかけ離れてしまいますが。。。

<ユースケース>
【前提】

  1. コンソール画面のATMとします。
  2. ユーザー認証は行いません。
  3. 単純に金額を入金、引き出しができるものを作ります。

【仕様】

  1. アプリを起動してコンソールから「引き出す」「入金」を選択する
  2. それぞれの処理に対して「預金額」から足し算、引き算を行いその結果を表示する

仕様としては単純なものです。これをサンプルとして作成することを考えます。

必要な機能を考える

はっきり言ってこの部分には「正解」というものがありません。
ある意味「なんでも良い」のかもしれませんが、なるべく効率の良いものを作りたいと思うのが人情です。
そのために必要なことは以下のようになります。

無駄な処理を行わない

ということです。そのためにクラスを作成し役割分担を行います。
上のケースで行くと、以下の要件が出てきます。

  1. 「引き出し」と「入金」の2つをハンドルする処理が必要
  2. 「引き出し時の処理」と「入金時の処理」が必要
  3. 最終的にコンソールへ出力する処理が必要

大雑把に3つの要件が出てきます。

要件を満たす部品を考える

【前提】
コンソールアプリなのでメインメソッドを動かす中で全ての作業(入金、引き出しなど)を表現します。
<部品候補>

  1. メインメソッドを持ち、入力の受付も行うクラス
  2. 入金、引き出しを行う計算処理クラス

2つの部品を作る事で、役割を分担し、要件を満たすことができそうです。しかし、これでよいのかどうか?自分で考えてみましょう。

筆者が作成したのは、こんな感じです。ソースはGithubにあります。
結局、5個くらいのクラスを作成した形になりました(笑)。

<動画その1>

<動画その2>

でわでわ。。。

<<< 前回 次回 >>>



Java はじめて15 〜クラス型変数の使い方〜

イントロダクション

Javaはクラスをプログラムの単位としています。具体的にはJavaのプログラムを起動するのには下のようなコマンドを実行します。

java クラス名[プログラム引数]

つまり、プログラムを実行するのに「クラス」が必要なわけです。正確にはクラス内にある「メインメソッド」を検索して実行するのですが、「クラスが基本単位なんだな」という理解で十分です。

そして、クラスに役割を与えてそれぞれの役割でプログラムを実行するように作成すれば、デバック、プログラムの改修が楽になります。つまりより良いプログラムが作成しやすくなります。

クラスの扱い方イメージ

イメージとしては、演劇を考えるとわかりやすいかもしれません。
例えとして、役者一人一人が「クラス」であり、それぞれの演目の役(役割)を果たすことで「物語をリアルに再現できる」というのが「実行結果」になります。

今回の学習ポイント

今回は、クラスを使うための学習、練習を行います。なのである部隊の役者を一人引っ張って着て「~やって!」と指示するようなイメージです(笑)

プログラムでクラスを使うときには、以前学習したように基本的にはインスタンス化して実行することが多いです。
なので、変数にインスタンスをセットして実行します。

自作のクラスを使う場合はこんな感じでした。

public class Sample1() {
    public static void main(String[] args) {
        Sample1 main = new Sample1();
        main.hello();
    }
    public void hello() {
        System.out.println("Morning!");
    }
}

クラス変数の使い方

Javaで作成したクラスを使う方法を記載します。クラス・インスタンスの作成は、以下のようにコードを書くと作れます。今回の内容はクラス名 変数名 = new クラス名();のように使用するときの話です。

クラス定義の書き方
フィールド変数は「カプセル化」の観点から「private」にしましょう。もし「クラス変数.フィールド変数名」のように操作したいときは「public」にしてやればよいのですが、セキュリティ的によろしくないのでフィールド変数にアクセスるときは「getter, setter」を使いましょう。

/**
 * クラスのJavaDoc
 */
public クラス名 {
    // フィールド変数
    public String  firstName;
    // publicは外部から参照可能
    public int age;
    // privateは外部から参照できない
    private int zako;
    /**
    * メインメソッドのJavaDoc部分
    */
    public static void main(String[] args) {
        // メインメソッド(クラスのメンバメソッドではない)
    }
    /** メンバメソッドのJavaDoc */
    public void menberMethod() {
        // メンバーメソッド(newすると使える)
    }
}

クラス変数

今回は、作成したクラスの中にメインメソッドを作り、自分のクラスを変数に代入して使用します。
下にサンプルコードを示します。

  • データ型:プリミティブ型・参照型を含めて変数の型を意味します。例:「int型、String型、(作成した)Sample1型」

<通常の変数の書き方>

データ型 変数名の形で書く、SampleCls型の変数mainを宣言する
SampleCls main;

<クラスをインスタンス化して変数にセット>

同様に、初期化する場合
SampleCls main = new SampleCls();

サンプルコード1: メンバメソッドについて

クラスをインスタンス化して使用するときに呼び出すメソッドは基本的に「メンバメソッド(インスタンスメソッド)」です。
他にもstaticメソッドとかありますが、呼び名が複数あるので「static」のついたメソッドとそうでないメソッド(通常のメソッド)があると思っていただければよろしいと思います。

/**
 * クラスのJavaDoc
 */
public SampleCls {
    // フィールド変数
    public String  firstName;
    public int age;
    // これは外部から参照できない
    private int zako;
    /**
    * メインメソッドのJavaDoc部分
    */
    public static void main(String[] args) {
        // メインメソッド(クラスのメンバメソッドではない)
        // クラス型の変数main: データ型 変数名の形で書く
        SampleCls main = new SampleCls();
        main.menberMethod();
    }
    /** メンバメソッドのJavaDoc */
    public void menberMethod() {
        // メンバーメソッド(newすると使える)
        System.out.println("Hello MemberMethod!);
    }
    /** staticメソッド */
    public void staticMethod() {
    // クラス名.メソッド名で呼び出せる ※クラスに一つだけ定義できる
        System.out.println("Hello StaticMethod!);
    }
}

<通常のメソッド>

呼び出し方
main.menberMethod();

<staticメソッド>

呼び出し方
SampleCls.staticMethod();

シンプルにこんな形でコードを作成しました。メインメソッドは、「static」がつくので、クラスの中にあっても「new」演算子でのインスタンス化が必要になります。
「書き方」と同じ構成なのでわかりやすいと思いますが、いかがでしょうか?細かい説明は省きますが、メインメソッドの場合は、クラスをnewして使用しないとクラス内のメソッドを使用することができません。これはメンバメソッドはインスタンス化しないと使用できないためです。
逆に、メソッドにstaticをつけてやれば、メインメソッドから使用することができます。

サンプルコード2: staticメソッド

/**
 * クラスのJavaDoc
 */
public SampleCls {
    // フィールド変数
    public String  firstName;
    public int age;
    // これは外部から参照できない
    private int zako;

    /**
    * メインメソッドのJavaDoc部分
    */
    public static void main(String[] args) {
        SampleCls main = new SampleCls();
        // メインメソッド(クラスのメンバメソッドではない)
        main.menberMethod();
        // スタティックメソッドの実行
        staticMethod();
    }

/** メンバメソッドのJavaDoc */
    public void menberMethod() {
        // メンバーメソッド(newすると使える)
        System.out.rintln("Hello MemberMethod!);
    }

    public static void staticMethod() {
        // スタティックメソッド
        System.out.rintln("Hello StaticMethod!);
    }
}

つまりメインメソッド(staticメソッド)以外のメソッドにはクラスをnewしないとアクセスできません。

理屈としては、メインメソッドはstaticメソッドなので、通常のメソッド(メンバ・メソッド)とは独立したメンバ(クラスの要素)になります。

staticとメンバについて

メンバ・メソッドと同様にメンバ・変数というのもあります。フィールド変数の事です。「メンバ」といいう言葉は、JavaだけでなくC言語、それに連なる言語でよく使用されます。

「メンバ」とはクラスの中に定義するフィールド変数とメソッドの事です。ただし、staticと修飾子が付くと話は変わります。

メンバはクラスのインスタンスを生成して使用するルールになっていますが、staticメソッドはクラスのインスタンスを必要としません

なので呼び出し方が下の様に別にになります。ここがインスタンスとオブジェクトの違いになります。※深く考える必要はありません。後々に学びます。

/** サンプルメソッド */
public void sample() {
    ClassA clsA = new ClassA();
    // メンバメソッド
    clsA.memberMethod();
    // staticメソッド
    ClassA.staticMethod();
}

クラスからクラスを呼ぶ

上のコードともう1つ作成して見ます。

public SecondSampleCls {
    // フィールド変数
    public String  firstName;
    public int age;
    // これは外部から参照できない
    private int zako;
    /** JavaDoc */
    public void secondMethod() {
        // メンバーメソッド(newすると使える)
        System.out.rintln("Hello MemberMethod!);
    }
}

はじめのクラスに「メインメソッド」がないだけの同じうようなクラスです。
そして、はじめのクラスをちょっと修正します。

public SampleCls {
    // フィールド変数
    public String  firstName;
    public int age;
    // これは外部から参照できない
    private int zako;
    /**
    * JavaDoc部分
    */
    public static void main(String[] args) {
        // メインメソッド(クラスのメンバメソッドではない)
        SampleCls main = new SampleCls();
        main.menberMethod();
        SecondSampleCls second = new SecondSampleCls();
        second.sendMethod();
    }
    /** JavaDoc */
    public void sendMethod() {
        // メンバーメソッド(newすると使える)
        System.out.rintln("This is SecondMethod!);
    }
}

このような形でメインメソッドから他の作成したクラスを呼び出します。当然java.lang.XXXのようなクラスもこれと同じようにメソッドを呼び出し使用することができます。

String(文字列)クラスを使ってみる

例えば、以下のコードはjava.lang.Stringクラスの「equals()」を使用しています。

public static void main(String[] args) {
    String str = "test";
    if ("test".equals(str) {
        System.out.println("testです");
    } else {
        System.out.println("testではありません");
    }
}

<復習用動画>

<注意点:リテラルを意識しましょう>

  • 「"(ダブルクォーテーション)」で囲った文字はStringクラスとみなされます。
  • 「'(シングルクォーテーション)」で囲った文字はchar型とみなされます。
  • 何もつけない「1」は数値としてみなされます。
String str = "文字列(String)";
char ch = 'も'; // 1文字のみ
int num = 1; // 数値

こんな感じです。

JavaAPIを使ってみる

「JavaAPI」というのは、JDKにい含まれているJavaのクラス群のことです。パッケージ名で言うならば以下のようなもののことです。

  • java.lang.*;
  • java.util.*;
  • java.nio.*;

詳細はこちらのオラクルのJavaaDovページを参照ください。
クラスパッケージはこちらです。

File読み込み処理

クラスの扱い方の一歩目として、File読み込み処理を学習します。
文字列や、配列(リスト)などは、すぐになれると思いますので、まずは飛ばします。

File IO(ファイル入出力)

このファイル入出力処理は、データ(文字列や配列)の扱いと少し違います。以下の操作を行うときには「ストリーム」を使用します。

  • ファイルを開く
  • データを読み込む
  • データをファイルに書き込む

ストリームという言葉

このストリームという言葉は、動画配信などで使用する「ストリーミング」とは別物です。
こちらのページによると下のような意味が書かれていました。

ストリームとは、小川、流れ、連続などの意味を持つ英単語で、ITの分野では連続したデータの流れや、データの送受信や処理を連続的に行うことなどを意味する。

ここでの「ストリーム」とは「プログラムとファイルの間を行き来するデータの流れる道」のことです。
なので、「ストリームを開く」とか「ストリームを閉じる」などというものの言い方をします。
ファイルの読み込みを行うときには、「ストリームを開きファイルにアクセス」してデータを読み取ります。また、読み込みや書き込みが終わったら「ストリームを閉じます。

ファイル読み込み処理

ファイル読み込み処理を実装してみます。ゲームっぽく入力 -> 表示を繰り返す形のプログラムを作りました。
このプログラムは「file」と入力すると「resources/FirstStage.txt」を開き、ファイルを読み込み、記述している内容を出力するというものです。

プログラムコードは下のようなものです。「readFile()」メソッドががいるの読み込み処理を実装したものです。
クラスの中で、ストリームを開くとか閉じるなどの処理を行っているので、プログラム上ではストリームを開くなどの処理を行っています。
なので、プログラム的には次のような形の説明ができます。

  • <ストリームを開く>:new File(fileName);の部分で引数に渡されている「resources/FirstStage.txt」を開く
  • <ファイルを読み込む>: buf.readLine()でファイルを1行ずつ読込む
  • <ストリームを閉じる>: buf.close();でファイルを閉じる(ストリームを閉じる)

使用している、APIはBufferedReaderを主軸にしています。これは、コンストラクタの引数に「Reader」クラスを渡してファイル操作を行う準備をします。詳細はこちらのJavaDocを見てください。

文字、配列、行をバッファリングすることによって、文字型入力ストリームからテキストを効率良く読み込みます。
バッファのサイズは、デフォルト値のままにすることも、特定の値を指定することもできます。デフォルト値は、通常の使い方では十分な大きさです。

一般的に、Readerに対して読込み要求が出されると、それに対応するベースとなる文字型ストリームまたはバイト・ストリームへの読込み要求が発行されます。このため、FileReaderやInputStreamReaderのようにread()オペレーションの効率の良くないReaderでは、その周りをBufferedReaderでラップすることをお薦めします。次に例を示します。

 BufferedReader in
   = new BufferedReader(new FileReader("foo.in"));

筆者が実装したコード

public class NinethProgram {
        public static void main(String[] args) {
            jp.zenryoku.sample.basic.NinethProgram main = new jp.zenryoku.sample.basic.NinethProgram();
            Scanner scan = new Scanner(System.in);

            while(true) {
                System.out.print("入力してください: ");
                String in = scan.next();

                if("bye".equals(in)) {
                    System.out.println("アプリ終了");
                    break;
                }
                String[] tmp = new String[] {"aaa"};
                try {
                    if (in.equals("test")) {
                        System.out.println("*** Testing now! ***");
                    } else if ("file".equals(in)) {
                        main.readFile("resources/FirstStage.txt");
                    } else if (tmp.length == 1) {
                        System.out.println("*** Not Error! ***");
                    }
                } catch (FileNotFoundException e) {
                    System.out.println("*** No File! ***");
                } catch (IOException e) {
                    System.out.println("*** What's up!? ***");
                }
            }
        }
        private void readFile(String fileName) throws FileNotFoundException, IOException {
            File f = new File(fileName);
            BufferedReader buf = new BufferedReader(new FileReader(f));
            String line = null;
            while((line = buf.readLine()) != null) {
                System.out.println(line);
            }
            buf.close();
        }
}

上記のBufrrerdReaderクラスの場合でもそうですがJavaDocにJavaAPIの使い方など全て書かれていますので、それを読んで理解できるようになれば、Javaを極めたといっても過言ではありません

筆者も学習中です。そのほか「デザインパターン」が実装済みのAPIもありますので、眺めてみるのもよいと思います。

でわでわ。。。

<Java関連の動画リスト>

<<< 前回 次回 >>>



Java はじめて14 〜クラスの作り方〜

イントロダクション

今回は、クラスの作り方をやります。以前似たようなことをやっていますが、違う視点で記載します。

<記載概要>

  • クラスの作り方(書き方)
  • インスタンス化
  • インスタンス化必要の場合

クラスの作り方(書き方)

とりあえずは、構文(書き方)を記載します。

public クラス名 {
   // フィールド変数
    public String  firstName;
   // このフィールドは外部から参照できる
    public int age;
    // これは外部から参照できない
    private int zako;
    /**
     * JavaDoc部分
     */
    public static void main(String[] args) {
        // メインメソッド(クラスのメンバメソッドではない)
        クラス名 clz = new クラス名();
        // アクセス就職しが「public」なので参照可能
        int sabayomi = clz.age - 10;
        // メンバーメソッドの呼び出し
        clz.menberMethod();
        // メソッドの呼び出し2:返り値がINT型になっている
        int res = clz.menberMethod("aaa");
    }
    /** JavaDoc */
    public void menberMethod() {
        // メンバーメソッド(newすると使える)
    }
    /** メソッドのオーバーロード */
    public int menberMethod(String str) {
        // メンバーメソッド(newすると使える)
        return 12;
    }
}

簡単に書くと、上のように書きます。

実際のところ、クラス作って、フィールドとかメソッドとか作ってどうするの?というところが、いまいち理解しづらいと思いました。

クラスを作る=カプセル化の意味

「カプセル化」とか「クラス」という言葉が出て来たときに、基本情報技術者試験やJava Bronze Silverなどの資格試験では「厳密な意味」を求められることがあるので、試験で使用する言葉とこのブログで記載する言葉で混乱しないように、「別物」として認識すうるようにしてください。要約して言葉を使っているためです。

上記の疑問「クラス作って、フィールドとかメソッドとか作ってどうするの?」に関して、クラスを作成しない場合と、作成する場合の違いを見て見るのが一番わかりやすいとお思います。

なので、簡単な「じゃんけんゲーム」を例に比較してみようと思います。

クラス分けしない、じゃんけんゲーム

メインメソッドのみを記載します。(FirstJankenMain)

/**
 * メインメソッド
 * @param args プログラム引数
 */
public static void main(String[] args) {
    FirstJankenMain main = new FirstJankenMain();

    main.createJudgeMap();
    Random random = new Random();

    boolean isJanken= true;
    // 無限ループ
    while(true) {
        main.printJankenAiko(isJanken);
        String input = main.acceptInput();
        if (main.inputCheck(input) == false) {
            System.out.println("0-2の値を入力してください。");
            continue;
        }
        // CPUの手を取得する(JavaSEのAPIを使用するのでテストしない)
        String cpuTe = String.valueOf(random.nextInt(2));
        // 4.「ポン!」or「しょ!」を表示
        main.printPonOrSho(isJanken);
        main.printTe(input, cpuTe);

        int judge = main.judgeWinLoose(input, cpuTe);
        if (main.printJudge(judge)) {
            isJanken = true;
            break;
        } else {
            isJanken = false;
        }
    }
    // 7.じゃんけんゲーム終了
}

この書き方だと、自分の認識では、いまいちなコードです。
理由は、単純に拡張することができないからです。例えば、JavaFXを使用して画面を作成した時にこのクラスを再利用することができません。
<JavaFX: 木琴>※オラクルのサイトからコードをダウンロードできます。

<JavaFXの動画リスト>

じゃんけんゲームの勝敗判定など使える要素はたくさんありますが、拡張する要素にかけるのです。

具体的には、このクラスは、全ての処理が1つのファイルに書いてあるので、クラスを呼び出すのに余計なメインメソッドがついてきます。

なので、メインメソッドはメインメソッド用のクラスを作成した方が無難なのです。そうすれば現状の処理を残したまま別のじゃんけんゲームをすぐに作成することができます。

例えば、現状はシングルスレッドで動きますが、これをマルチスレッド対応にすることができます。
<マルチスレッド対応方法>
メインクラスが独立していれば、「」クラスを継承したクラスを1つ追加して、以下の手順を行えばOKです。

  1. このクラスで、じゃんけんゲームのメイン処理を実装(コピペ)
  2. メインメソッドは、追加したクラスを呼び出すだけ
  3. 追加したクラスのテスト
  4. 起動確認

クラス分けをする。じゃんけんゲーム

メインメソッドのみを記載します。(SecondJankenMain)

/**
 * メインメソッドの実装をオブジェクト指向プログラミングっぽく
 * クラスの継承を使用して実装しなおしてみる。
 * @param args
 */
public static void main(String[] args) {
    SecondJankenMain main = new SecondJankenMain();
    Random random = new Random();

    boolean isJanken= true;
    // 無限ループ
    while(true) {
        main.printJankenAiko(isJanken);
        String input = main.acceptInput();
        if (main.inputCheck(input) == false) {
            System.out.println("0-2の値を入力してください。");
            continue;
        }
        // CPUの手を取得する(JavaSEのAPIを使用するのでテストしない)
        String cpuTe = String.valueOf(random.nextInt(2));
        // 4.「ポン!」or「しょ!」を表示
        main.printPonOrSho(isJanken);
        main.printTe(input, cpuTe);
        JankenConst judge = main.judgeWinLoose(input, cpuTe);
        main.printJudge(judge);
        if (main.printJudge(judge)) {
            isJanken = true;
            break;
        } else {
            isJanken = false;
        }
    }
    // 7.じゃんけんゲーム終了
}

この場合だと、上に記載した実装を行うことができます。
詳細に関してはGitリポジトリを見ていただきたいのですが、SecondJankenMainがメインメソッドを持っているので、このクラスの他にクラスを追加。。。(上記参照)

といった具体に実装が可能です。

他にも、
FirstJankenMainクラスには、printXXXの他にもメソッドが定義されていますが、SecondJankenMainクラスには、printXXXX()というメソッドのみが定義されている状態です。

SecondJankenMainに書いていないコードは、このクラスのスーパークラス(親クラス)に書いてあるので、このクラスには書かなくて良いのです。

しかし、この継承関係はあまりよくありません。というか意味がありません。

フィールド変数に、継承しているクラスを保持する方が読みやすいし、修正もしやすいです。

継承を実装することが目的にあったのでこのようになりました。継承関係をなくすのも簡単です。「extends XXXX」を削除するだけです。

インスタンス化

単純に「new」することです。この「new」を行うとクラスの中の「メンバ」が使用可能になります。
具体的には、以下の通りです。

public class MainClass {
    public static void main(String[] args) {
        // インスタンス化
        ClassA clsA = new ClassA();
    }
}

public class ClassA {
    private int age;
    private String name;
    /** コンストラクタ */
    public ClassA() {
        age = 39;
        name = "Takunoji";
    }
    /** メソッド */
    public void say() {
        System.out.println("I am " + name + ". " + age + "years old.");
    }
}

MainクラスとClassAを記載しています。MainクラスでClassAをnewしています。この状態では、ClassAのフィールド変数に決まった値しか設定することができません。

それでは、下のような実装ではどうでしょうか?

public class MainClass {
    public static void main(String[] args) {
        // インスタンス化
        ClassA clsA = new ClassA("10", "Takunoji_Jr");
    }
}

public class ClassA {
    private int age;
    private String name;
    /** コンストラクタ */
    public ClassA() {
        age = 39;
        name = "Takunoji";
    }
    /** コンストラクタのオーバーロード */
    public ClassA(int age, String name) {
        this.age = age;
        this.name = name;
    } 
    /** メソッド */
    public void say() {
        System.out.println("I am " + name + ". " + age + "years old.");
    }
}

このようにすると、コンストラクタに引数を渡した時は「デフォルト値」でのClassAのインスタンスを生成します。
そして、引数を与えた場合は引数に対応したインスタンスが作成されます。

メインメソッドのみを書くと下のようになります。
==インスタンスを2つ作る==

public static void main(String[] args) {
    ClassA defaultClassA = new ClassA();
    ClassA customClassA = new ClassA(10, "Takunoji_Jr");
    defaultClassA.say();
    customClassA.say();
}

表示内容にはそれぞれのフィールドに設定された値が表示されます。

この場合は、ClassAのインスタンスが2つアプリケーション(メインメソッドの処理内)にできていて、それぞれメモリ上にデフォルト値と引数に設定した値が保持されています。

インスタンス化不要の場合

メインメソッドがインスタンス化しなくても使用できます。
本当は「static」がついているので「静的メソッド」とか呼ばれるメソッドですが、後々にこの部分は記載します。

例えば、チェック処理を行おうとした時に、クラスのインスタンスの生成が必要ない場合。ちょっと極端ですが、下のようなメソッドがあったとします。

public class ClassA {
    public boolean isBoolean(boolean b) {
        return b;
    }
}

このメソッドは、フィールド変数を使わないので、インスタンス化する必要がありません。なので下のように修正し「静的メソッドにします。」

public class ClassA {
    public static boolean isBoolean(boolean b) {
        return b;
    }
}

このようにすると、下のように「new」しなくても呼び出すことができます。

public static void main(String[] args) {
    ClassA.isBoolean(true);
}

インスタンス化が必要になる場合

メンバXXXはインスタンス化が必要です。具体的には上のフィールド変数、メンバーメソッドはインスタンス化しないと使用できません。「なんでか」って?

インスタンス化(コンストラクタを起動)することで、クラスがただの「型」だったのがインスタンス(メモリ領域を確保)して、フィールド変数、メンバメソッドを使用するためのメモリ領域にデータを設定、インスタンスとしてクラス・オブジェクトを1つ生成するので、使用可能になります、。。

上の==インスタンスを2つ作る==を見ていただければわかる通り、同じクラスでも、フィールドの値が違います。
なので、sayメソッドで出力する結果も違います。

テキストRPGの場合

例えば、テキストRPGを作成しようと考えたとします。
そうした時に、プレーヤーの中で「勇者」とか「戦士」など作成したいと思います。

このような時に、プレーヤークラスを作成しておき「職業」というフィールド変数にそれぞれの職業を設定してやればクラスを2つ作らなくてもよくなります。 ※極端な話ですが。。。

UMLを見る

クラスとクラスの関係を描くのに UMLというもの(言語)があります。これを使うとイメージ的に理解しやすいと思います。

早い話が、クラスを絵にするので、関係が見やすいということです。

インスタンスを使う、具体的な例

あるウェブサーバーでユーザーがログインする時のケースで考えます。
この時に、ユーザークラスは「ログイン」する前はインスタンスがありませんが、ログインする時に、ユーザーの情報をDBから取得し、インスタン化してデータをクラスにセットします。
これは、ユーザーがログインした分だけ(つまりユーザーの数だけ)インスタンスが生成されます。

つまり、ウェブサーバーにログインした人はサーバー側では1つのインスタンスとして管理されるわけです。
当然、ログアウトしたときには、ログイン情報のインスタンスは削除されます。 ※再度ログインしたら、インスタンスが再度作成されます。
インスタンスが、作成される。というのは、例えば下のようなコードです。

// ユーザー名とセッションIDを保持する形でインスタンスを作成
LoginInfo login = new LogiinInfo(userName, sessionId)

インスタンスについて

はじめに「new」するとインスタンスができるということを話しましたが、3回LoginInfoクラスを「new」するとどうなるでしょうか?
LoginInfoのインスタンスが3つ存在することになりますが、クラスは同じものです。
しかし、ログインしている人が違うので、LoginInfoのユーザー名とかパスワードは別物になります。
コードで書くと次のような感じです。
<LoginInfo>

public LoginInfo() {
   private String userName;
   private String passwd;

   public void setUserName(String name) {
       this.userName = name;
   }
   public void setPasswd(String pass) {
       this.passwd = pass;
   }
}

それぞれログインしたときにフィールド変数に値(ユーザー名)などがセットされ、かつ、インスタンス化されるので各インスタンスが保持している情報(フィールド変数の値)は別物がセットされているというわけです。

問題

  1. メインメソッドを持つ、クラス「Yusha」を作成し、「Hello World」をコンソールに出力するクラスを作成してください。

  2. 上で作成したクラスに「kogeki()」メソッドを追加して、同様にコンソールに「勇者の攻撃!はぐれプログラマに8のダメージ。」と出力してください。

<<< 前回 次回 >>>

Java はじめて13 〜ループ処理C: do-while文〜

イントロダクション

while文の別バージョンです。while文はループする条件が最初に来ますが、do-whileは最後に来ます。
具体的には下のような形です。

<while文>

while (条件){
  // ループする処理
}

<do-while>

do {

} while(条件);

whileを使うか、do-whileを使うか。。。正直のところ、「実装者の好み」になると思っていますが、両方とも知っておくだけで、いざという時に使用することができるので、知っておいた方が、知らないより良いといったところです。

ポイント

do-whileの場合は、whileの場合と違い、1度は必ず処理が走るというところです。この部分はロジックにより使える文法だと思います。

実装動画

解説動画

do-while

今回は、do-while文をやります。正直のところこの文はあまり使わないのですが、知っておいて損はないのと、考えの幅が広がるので覚えておいて損はありません。

while文との違い

do-while文はwhileやfor文と違い、処理の後ろの方でループ判定を行います。ループ判定はFalseになると処理を抜けます。

do{
   // 何かしらの処理
} while(i < 3);

上のような書き方を行います。
ちなみに通常のwhile文は下のように書きます。

public static void main(String[] args) {
   int i = 0;
   while(i < 3) {
       System.out.println("カウント: " + i);
       i++;
   }
}

これと同じ処理をdo-whileで書くと

public static void main(String[] args) {
   int i = 0;
   do {
       System.out.println("カウント: " + i);
       i++;
   } while(i < 3);
}

上のようになります。
正直のところ、これ以上ではないし、これ以下でもないので他に書くようなことはありません、あとは実際にプログラムを書いて動かして、自分の頭の中でプログラムを組み立てる練習、実践を繰り返していくことが全てなので。。。

問題

do-while文を一回使用して、コンソール出力にて下のような絵を描画してください。「*(アスタリスク)」を使って描画します。
上から順にアスタリスクが1個, 2個, 3個, 4個, 5個, 6個と並んでいます。

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

<Java関連の動画リスト>

基本文法終了

ここまでの学習で、基本文法は終了です。これからはクラスを使った学習を始めていきます。
その前に、一度電卓アプリを作成することをお勧めします。

電卓アプリの仕様

電卓アプリといっても、標準入力と標準出力を使った単純なものです。
陽男県としては以下のようになります。

  1. 標準出力を受けて、数字と「+, -, *, /」のみの入力を許容する
  2. 計算式「XXX ○ □□□」のような形でエンターキーを押下した後計算結果を表示する

筆者が作成した、電卓アプリ(四則演算アプリ)配下のような感じです。※アプリ=アプリケーション

アプリのサンプル

四則演算の解説

でわでわ。。。

<<< 前回 次回 >>>

Java はじめて12 〜ループ処理B: while文〜

イントロダクション

while文は今までやった、for文との大きな違いは、「シンプル」というところです。
具体的には、「無限ループしたい」と思ったらなら下のようなコードでできます。

while (true) {
   // この部分が無限ループする
}

この無限ループを使用して、下のようなゲームを作成してみました。

while文の書き方

今回はwhile文をやります。今までのfor文と同じような感じですが、とてもシンプルです。whileの中の条件がFalseになると処理を抜けます。

while(条件) {
     // 処理
}


for文と比べて、初期化処理とか後処理がないので、まぁ楽です。
実際にコンソールゲームを作成するのにもwhile文を使用しています。初期化処理などが入らないので。。。

public static void main(String[] args) {
    Lv3_3_RefactorLv2 main = new Lv3_3_RefactorLv2();
    // 標準入力
    Scanner input = new Scanner(System.in);

    while(true) {
        System.out.println("コマンドを入力してください: ");
        String inStr = input.nextLine();
        CommandIF cmd = main.getCommandIF(inStr);
        if (cmd != null) {
            cmd.execute();
            continue;
        }
        if ("bye".equals(inStr)) {
            System.out.println("Good Bye");
            break;
        } else {
            System.out.println("対象のコマンドは登録されていません。: " + inStr);
        }
    }
}

これは無限ループを行なっているので、条件を指定しているのとは違うのですが。。。

public static void main(String[] args) {
   int i = 0;
   while(i < 3) {
       System.out.println("カウント: " + i);
       i++;
   }
}

これは、よくある形のwhile文です。for文と同じようなことをやっています。

問題

while文を一回使用して、コンソール出力にて下のような絵を描画してください。「*(アスタリスク)」を使って描画します。
上から順にアスタリスクが1個, 2個, 3個, 4個, 5個, 6個と並んでいます。

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

<Java関連の動画リスト>

ゲームループ

シンプルに無限ループの中に、ループを抜ける条件をつけてやるような形での実装です。
LWJGLの解説をしているときのゲームループは入力~画面の更新まで実装していましたが、今回は細かい部分を省略しています。

//標準入力
Scanner scan = new Scanner(System.in);
while (true) {
    String input = scan.nextLine();

    if ("bye".equals(input)) {
        // ループを抜ける
        break;
    }
}

<処理内容>

  1. 標準入力を受け取ります。
  2. "bye"と入力されたときプログラムを終了します。

逆にいうと、"bye"と入力しなければ、何を入力しても、ループして再度入力を求める処理が走ります。
String input = scan.nextLine();この処理で、入力を受け取ります。

なので、次のようなプログラムも組みやすいということです。

  1. 入力を受ける。
  2. 計算をする。
  3. 計算結果を表示する。

単純な電卓アプリを作成することも容易です。

他にも、このような形での実装は、いろんな部分で使用されます。

さらに。。。

他にも、ループ文があるので、使いやすいものを使用してアプリを作って遊んでみるのが良い学習になります。
でわでわ。。。

<<< 前回 次回 >>>