Java問題 ~予備知識編~

予備知識問題

Java言語のイメージをつけれればよいかな?という程度にクイズゲーム的な感じでやってみてください。
「予備知識」と書きましたが、実装するための「予備知識」という意味です。もしかしたら「理解」と受け取る人もいるかもしれないので、一応迄に。。。
※ 問題は随時追加していきます。

問題

問題を解いて、理解の確認。もしくは暇つぶしにどうぞ。

問題1

Q1: Java言語の特徴として正しい説明はつぎのどれですか、2つ選択してください。

  1. Windows上で作成したプログラムを再コンパイルせずにLinux上で実行することができる
  2. Java SEの基本ライブラリは無償であるが、グラフィックやネットワーク関連は有償で提供される
  3. 分散型アプリケーションを作成することができる
  4. メモリ管理はプログラマが行う

答え:1, 3


問題2

Q2: 社内で使用するアプリケーションをJavaで開発することになりました。GUIでの操作が出k利用ような、独立型のアプリケーションを検討しています。使用すべきエディションはどれですか。1つ選んでください。

  1. Java EE
  2. Java SE
  3. Java ME
  4. Java DB
  5. Java FX

答え:2


問題3

Q3: 百貨店向けにブラウザベースのショッピングサイト・アプリケーションを開発する必要があります。使用すべきエディションは次のどれですか。1つ選んでください。

  1. Java EEのみ
  2. Java SEのみ
  3. Java MEのみ
  4. Java SEおよびJava EE
  5. Java SEおよびJava ME
  6. Java EEおよびJava ME

答え:4


問題4

Q4: JVMが行っていることとして、正しい説明は次のどれですか。3つ選んでください。

  1. クラスファイルのロード
  2. バイトコードの解釈
  3. クラスの実行
  4. アプリケーションのロギング
  5. クラスファイルのアセンブル
  6. ソースコードをコンパイルする

答え:1, 2, 3


問題5

Q5: クラスファイルに含まれるものは次のどれですか。1つ選んでください。

  1. スクリプトコード
  2. 実行コード
  3. ソースコード
  4. バイトコード

答え:4


問題6

Q6: ソースファイルの作成規則として正しいものは次のうちどれですか。2つ選んでください。

  1. ソースファイル名は、半角英数字のみ使用できる
  2. 「3.java」というソースファイル名も使用できる
  3. 拡張子は、「.java」もしくは、「.Java」を使用する
  4. ソースファイル内のインデントは、半角空白もしくはタブで行う

答え:2, 4


問題7

Q7: Test.javaとして次のコードがあります。

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

コンパイルするときのコマンドとして正しいのはどれですか?1つ選んでください。

  1. javac Test
  2. javac Test.java
  3. javac Test.class
  4. java Test
  5. java Test.java
  6. java Test.class

答え:2


問題8

Q8: コンソール(標準出力)に表示するコードはどれですか。2つ選んでください。

  1. System.out.print(Test);
  2. System.out.print('Test');
  3. System.out.print(Test);
  4. System.out.print(&100);
  5. System.out.print("100");

答え:4, 5


問題9

Q9: メインメソッドの定義として正しいものはどれですか。1つ選んでください。

  1. static void main(String args[])
  2. public void main(String args[])
  3. public static void main(String[] args)
  4. static main(String args[])
  5. public static void main(String args)

答え:3


問題10

Q10: 次のコードがしていされています。正しいものはどれですか。1つ選んでください。

public class Foo {
   public static void main() {
      System.out.println("Hello");
   }
}
  1. コンパイルエラーが発生する
  2. コンパイルは成功するが、実行時エラーが出る
  3. コンパイルは成功し、実行すると「Hello」と表示する
  4. コンパイルは成功するが、何も表示しない

答え:2


問題11

Q11: 次のプログラムにある「メソッド」をすべて答えなさい。正しいものを次の中から1つ選んでください。

public class Sample {
   public static void main(String[] args) {
      System.out.println("Hello");
   }
}
  1. mainメソッド
  2. printlnメソッド
  3. Systemメソッドとmainメソッド
  4. mainメソッドとprintlnメソッド

答え:「4」メソッドには「()」がつく、ちなみに「System」はクラスを示している。
public static void main(String[] args)はメインメソッドの定義


問題12

Q12: 次のプログラムの実行結果のうち、正しいものを次の中から1つ選んでください。ただし「」かっこの内部が表示された内容とします。改行の位置がポイントになります。

public class Sample {
   public static void main(String[] args) {
      System.out.println("Java プログラム");
      System.out.print("ただいま");
      System.out.print("作成中");
   }
}
  1. 「Java プログラム
    ただいま
    作成中」
  2. 「Java プログラム
    ただいま作成中」
  3. 「Java
    プログラム
    ただいま
    作成中」
  4. 「Java プログラムただいま作成中」

答え:「2」println()は改行がつく、print()は改行がつかない。


問題13

Q13: 次の変数名のうち不適切なものを2つ選んでください。

  1. goto
  2. media
  3. back
  4. apps
  5. assert

答え:「1, 5」


問題14

Q14: 次のプログラムはエラーになります。理由を1つ選んでください。

public class Sample {
    public static void main(String[] goo) {
        System.out.println("Hello!");
        /*
          表示したい文字があればここに書く
          /* System.out.println("文字列");*/
               :}
        */
    }
}
  1. 「}」が不適切な位置にあるのでエラーになる
  2. メインメソッドの引数名「goo」が違うのでエラーになる
  3. 「*/」が不適切な位置に、二つあるのでエラーになる
  4. コメントの中にメソッドが書いてあるのでエラーになる

答え:「1, 5」


問題15

Q15: 二つの引数を比較して小さいほうの値を返すメソッド「min」の正しい記述をを1つ選んでください。

  1. Math.min(10, 3);
  2. Math.min(10. 3);
  3. Math.min(10: 3);
  4. Math.min(10; 3);
  5. Math.min(10@ 3);

答え:「1」


問題16

Q16: 次のコードを実行したとき、①mainメソッド ②printlnメソッドが呼び出される回数を答えてください。

public class Sample {
   public static void main(String[] args) {
       System.out.println("Hello Moning");
       hello();
       System.out.println("Hello Night");
   }
   public static void hello() {
       System.out.println("hello!");
   }
}

答え:「3回」


問題17

Q17: 次のコードを実行したとき、①コンストラクタ ②printlnメソッドが呼び出される回数を選んでください。

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

public class TestA {
   private String name;
   private int age:

   public TestA() {
       name = "taro";
       age = 1;
   }

   public void talk() {
      System.out.println("My name is " + name + ".");
      System.out.println("I am " + age + " years old.");
   }
}
  1. 1回
  2. 2回
  3. 3回

答え:「1回」


XMLファイルの書き方

XMファイルの書き方

Javaプログラミングでよく使用するXMLファイルですが、 かなり使える優れものというところをアピールしたく。この記事を書くことにしました。
※本当は、XMLの勉強をしたからです。

XMLとは

オラクルによると下記のようなものです。
しかし、WebLogicサーバー(ウェブサーバー)用のドキュメントのようです。

Extensible Markup Language (XML)とは、ドキュメント内のデータの内容および構造を示すのに使用するマークアップ言語です。

日本語にすると「拡張マークアップ言語」であり、とりあえずは、「拡張するのに使えるよ」というところです。

どうやって拡張するの?

同じようにオラクル社のドキュメントでは下記のようにあります。

ML ドキュメントの記述方法には、DTD および スキーマの 2 種類があります。

XML ドキュメントを定義する DTD

<!DOCTYPE address_book [
<!ELEMENT person (name, address?, phone?, email?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT address (street, city, state, zip)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT email (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)> 

<!ATTLIST person gender CDATA #REQUIRED>
<!ATTLIST phone area_code CDATA #REQUIRED>
]>

XML ドキュメントを定義する スキーマ(Schema)

オラクル社によると以下の通りです。

スキーマは、XML 仕様において最近開発され、DTD に取って代わるものとされています。
スキーマでは、DTD および DTD 以外の XML ドキュメント自体より柔軟かつ詳細に XML ドキュメントを記述します。スキーマ仕様は、現在 World Wide Web Consortium(W3C)によって開発中であり、DTD の制限の多くを克服するものとされています。XML スキーマの詳細については、http://www.w3.org/TR/xmlschema-0/ を参照してください。

XML Schema【XSD】

XML Schemaとは、XML文書の構造を定義するスキーマ言語の一つで、Web技術の標準化を進めるW3C(World Wide Web Consortium)が勧告したもの。 XML自体を用いてスキーマを記述する。

<xsd:schema xmlns:xsd="http://www.w3.org/1999/XMLSchema">

<xsd:element     name="address_book" type="bookType"/>

<xsd:complexType name="bookType">
  <xsd:element name=name="person"     type="personType"/>
</xsd:complexType>

<xsd:complexType name="personType">
  <xsd:element   name="name"     type="xsd:string"/>
  <xsd:element   name="address"  type="addressType"/>
  <xsd:element   name="phone"    type="phoneType"/>
  <xsd:element   name="email"    type="xsd:string"/>
  <xsd:attribute name="gender"   type="xsd:string"/> 
</xsd:complexType>

<xsd:complexType name="addressType">

  <xsd:element   name="street"  type="xsd:string"/>
  <xsd:element   name="city"    type="xsd:string"/>
  <xsd:element   name="state"   type="xsd:string"/>
  <xsd:element   name="zip"     type="xsd:string"/>
</xsd:complexType>

<xsd:simpleType name="phoneType">
  <xsd:restriction base="xsd:string"/>
  <xsd:attribute name="area_code" type="xsd:string"/>
</xsd:simpleType>

</xsd:schema>

規定の使い方

上記のファイルでタグの意味を定義付けていろいろな用途に使用するというのが、世の中にある「通常の使い方」です。
Javaプログラマがよく目にするのは、フレームワークで使用する設定ファイル「config.xml」、ウェブサーバーで使用する「web.xml」などがあると思いますが、列挙すると義理がありません。

しかし、ここではJavaをメインに記載していますので、Javaテクノロジーで使用しているXMLについてどのようなものがあるか以下に列挙します。

  • FXML: JavaFXで使用する画面表示のコンポーネントを記述するものです。これには、GUIエディタ「SceneBuilder」があります。
  • CID設定ファイルを使用してJavaEEでの依存性の注入を行っています。

色々ありますが、XMLファイルでクラスを指定して、そのクラスのインスタンスを取得する。DBのORマッピングを行うなどがあります、

使い方1:デコーダーとエンコーダー

XMLDecoderXMLEncoderというクラスがりますが、これもクラスをXMLファイルに出力したり、読み込んだりするクラスです。

使い方2:パーサー

使うのは、DocumentBuilderFactoryというクラス(API)で、下のような形で使用します。

    /**
     * XMLドキュメント(ファイル)を読み込む。
     * @param directory ファイルの配置しているディレクトリ
     * @param fileName ファイル名
     * @return Documentオブジェクト
     * @throws RpgException ファイルの読み込みエラー
     */
    private static Document loadDocumentBuilder(String directory, String fileName) throws Exception {
        //creating a constructor of file class and parsing an XML files
        Path path = Paths.get(directory, fileName);
        File file = path.toFile();// ("/Monster.xml");
        //an instance of factory that gives a document builder
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        //an instance of builder to parse the specified xml file
        DocumentBuilder db = null;
        Document doc = null;
        try {
            db = dbf.newDocumentBuilder();
            doc = db.parse(file);
            doc.getDocumentElement().normalize();
        } catch (ParserConfigurationException e) {
        }
        return doc;
    }

早い話が、DocumentBuilderFactoryクラスで、ファイルを読み込んで、タグを取得するというものです。

XMLファイルを使用するのに、どちらが便利か?

今回は、タグの名前を自由に決めたいので、「使い方2」を採用します。

以前、作成したXMLUtilでは、下のようにXMLを読み込んで、クラスを生成するという手順を踏みました。最終的に、XMLに対応したクラスを作成しておき、その値をXMLファイルから取得するという形での利用でした。

   /**
     * Job.xmlを読み込む。必ず、Command.xmlを読み込んでから実行する。
     * @return Jobリスト
     * @throws RpgException XMLの設定エラー
     */
    public static Map<String, RpgJob> loadJobs(String directory) throws RpgException {
        Map<String, RpgJob> jobMap = new HashMap<>();
        Map<String, RpgCommand> cmdMap = RpgConfig.getInstance().getCommandMap();
        if (cmdMap.size() <= 0) {
            throw new RpgException(MessageConst.ERR_BEFORE_LOAD_CMD.toString());
        }
        try {
            Document doc = loadDocumentBuilder(directory, "Job.xml");
            if (isDebug) System.out.println("Root element: " + doc.getDocumentElement().getNodeName());
            NodeList list = doc.getElementsByTagName("job");

            for (int i = 0; i < list.getLength(); i++) {
                Node node = list.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    RpgJob j = createJob(node, cmdMap);
                    jobMap.put(j.getJobId(), j);
                }
            }
        } catch (RpgException e) {
            e.printStackTrace();
            throw new RpgException(MessageConst.ERR_XML_PERSE.toString() + ": " + e.getMessage());
        }
        return jobMap;
    }

    /**
     * RpgJobクラスを生成する。依存するCommandListをセット。
     * @param node jobタグ
     * @return RpgJob 職業クラス
     * @throws RpgException XML設定エラー
     */
    private static RpgJob createJob(Node node, Map<String, RpgCommand> map) throws RpgException {
        Element e = (Element) node;
        String id = getTagNode(e, "id").item(0).getTextContent();
        String name = getTagNode(e, "name").item(0).getTextContent();
        String disc =  getTagNode(e, "discription").item(0).getTextContent();
        String commandList =  getTagNode(e, "commandList").item(0).getTextContent();
        String[] list = commandList.split(",");

        List<RpgCommand> cmdList = new ArrayList<>();
        for (String cmd : list) {
            RpgCommand cmdCls = map.get(cmd.trim());
            cmdList.add(cmdCls);
        }

        // RogConfigの値を変えないように気を付ける
        Map<String, RpgStatus> templateMap = RpgConfig.getInstance().getStatusMap();
        Map<String, RpgStatus> statusMap = new HashMap<>();
        statusMap.putAll(templateMap);
        Set<String> set = statusMap.keySet();
        // ステータス上昇値の設定
        for (String key : set) {
            if (isDebug) System.out.println("Status: " + key);
            String statsUp = getTagNode(e, key.toLowerCase()).item(0).getTextContent();
            RpgStatus st = new RpgStatus();
            st.setKigo(key);
            st.setValue(Integer.parseInt(statsUp));
            statusMap.put(key, st);
        }
        RpgJob job = null;
        job = new RpgJob(id, name, disc, cmdList);
        // ステータス上昇値のマップを設定する。
        job.setStatusUpMap(statusMap);
        return job;
    }

これを、使用してフレキシブルな形でクラスの生成をできるようにしたいと思っています。

結局

以下のような実装になりました。単純に読み込んだXMLファイルの文字列(タグ名、値)からクラスを生成する形をとろうと思います。

   /**
     * 各種設定ファイルを読み込むときに、ファイル名に対応した
     * クラスを生成して返却する。
     *
     * @param fileName
     * @return ConfigRpptを継承しているクラス
     */
    public static ConfigIF createConfig(String fileName) {
        ConfigIF conf = null;
        switch (fileName) {
            case StoryConfig.WORLD_XML:
                conf = new World();
                break;
            case StoryConfig.NATURE_XML:
                conf = new Nature();
                break;
            case StoryConfig.CREATURE_XML:
                conf = new Creature();
                break;
            case StoryConfig.REGIONS_XML:
                conf = new Region();
                break;
            case StoryConfig.EFFECTS_XML:
                conf = new Effects();
                break;
            case StoryConfig.STM_XML:
                conf = new STM();
                break;
            case StoryConfig.CPMMADS_XML:
                conf = new Command();
                break;
            case StoryConfig.JOB_XML:
                conf = new Job();
                break;
            case StoryConfig.MONSTER_TYPE_XML:
                conf = new MonsterType();
            case StoryConfig.MONSTERS_XML:
                /** TODO-[MonsterクラスはPlayerクラスを継承する] */
                //conf = new Monster();
                break;
            default:
                System.out.println("想定外の設定ファイル名です。 : " + fileName);
                System.exit(-1);
        }
        return conf;
    }

BlueJの使い方~インストール、クラス作成、サンプルプロジェクト~

BlueJの使い方

Oracle製のIDE「BlueJ」を使ってみようと思います。
そもそも、「なんで使ってみようと思ったのか?」に関して、以下の理由があります。
下の動画のように、作成したクラスがクラス図(簡易バージョン)で表現されるところです。
<HelloWorldの実行動画>

他にも、継承関係も表示できる用ですので、クラス間の関係性を説明するときに、自分で確認するときにも便利です。

ライブラリの追加

スタックオーバーフローより
「Tools -> Preferences -> Libraries -> Add file」ということで。

「ツール→カスタマイズ→ライブラリタブ」を開いて、Jarファイルを追加、BlueJを再起動することで使用可能になるようです。

BlueJインストール

こちらのページからダウンロードできます。MSIファイルなので、クリックするだけでインストールできました。

そして、ドキュメント、サンプルプロジェクトもそろっているようです。

そんなわけで、上記の赤丸部分をクリックしてダウンロードした。ファイルの中身を見てみます。

注意点

コンパイルするときには、修正したクラスを全て、コンパイルする必要があります。

サンプルプロジェクト

ダウンロードしたprojects.zipを展開すると、Chapter01~16までプロジェクトが入っていました。
これらの中にあるフォルダが、BlueJで開くべきプロジェクトになります。

Chapter1のプロジェクト(figure)

開いたプロジェクトは下のような形でした。

これに、メインクラスを作成し、メインメソッドを追加して実行しました。

<実行結果>

サンプルプロジェクトを見る

ダウンロードしたZIPファイルを展開したあと、「projects」-> 「chapter01」-> プロジェクトとフォルダを下がっていく形になります。
具体的には、「projects」-> 「chapter01」-> 「home」を開きます。

プロジェクトを開いた結果

ここから、Javaプログラムの解説になります。

Java プログラム

開いたプロジェクトは、下のようなクラスがあります。

  • Picture
  • Circle
  • Square
  • Triangle
  • Person
  • Canvas

クラスの関係について

上記のクラス図から、Circle, Square, Triangleの3クラスがPictureクラスに呼び出されています。

そして、Picture以外のクラスがCanvasクラスを呼び出しています。

なので、ここに「」クラスを作成し、Picture、Personクラスを起動するメインメソッドを実装、起動します。

Pictureクラス

このクラスは、Circle, Square, Triangleの3クラスを呼び出しています。
詳細は、フィールド変数に上記の3クラスを持っています。
そして、コンストラクタで各クラスのインスタンス化を行い。
draw()メソッドで描画を行っています。

なので、メインメソッドでは、インスタンス化とdraw()を呼び出しを行っています。

/**
 * This class represents a simple picture. You can draw the picture using
 * the draw method. But wait, there's more: being an electronic picture, it
 * can be changed. You can set it to black-and-white display and back to
 * colors (only after it's been drawn, of course).
 *
 * This class was written as an early example for teaching Java with BlueJ.
 * 
 * @author  Michael Kölling and David J. Barnes
 * @version 2016.02.29
 */
public class Picture
{
    private Square wall;
    private Square window;
    private Triangle roof;
    private Circle sun;
    private boolean drawn;

    /**
     * Constructor for objects of class Picture
     */
    public Picture()
    {
        wall = new Square();
        window = new Square();
        roof = new Triangle();
        sun = new Circle();
        drawn = false;
    }

    /**
     * Draw this picture.
     */
    public void draw()
    {
        if(!drawn) {
            wall.moveHorizontal(-140);
            wall.moveVertical(20);
            wall.changeSize(120);
            wall.makeVisible();

            window.changeColor("black");
            window.moveHorizontal(-120);
            window.moveVertical(40);
            window.changeSize(40);
            window.makeVisible();

            roof.changeSize(60, 180);
            roof.moveHorizontal(20);
            roof.moveVertical(-60);
            roof.makeVisible();

            sun.changeColor("yellow");
            sun.moveHorizontal(100);
            sun.moveVertical(-40);
            sun.changeSize(80);
            sun.makeVisible();
            drawn = true;
        }
    }

    /**
     * Change this picture to black/white display
     */
    public void setBlackAndWhite()
    {
        wall.changeColor("black");
        window.changeColor("white");
        roof.changeColor("black");
        sun.changeColor("black");
    }

    /**
     * Change this picture to use color display
     */
    public void setColor()
    {
        wall.changeColor("red");
        window.changeColor("black");
        roof.changeColor("green");
        sun.changeColor("yellow");
    }
}

Mainクラス

このクラスは、筆者が作成したクラスです。単純にPictureクラスとPersonクラスをインスタンス化してそれぞれ描画するメソッドを起動しています。

public class Main
{
  public static void main(String[] args) {
      Picture pic = new Picture();
      Person per = new Person();

      pic.draw();
      per.makeVisible();
  }
}

Extension(拡張)

拡張機能に関して、こちらの記事にありました。

BlueJはSwingを使用して作成されているのか。。。拡張するのにSwingを使用して拡張するようです。

次の例では、ユーザーが開いたすべての BlueJ プロジェクトの名前をログに記録する拡張機能を実装しSystem.out 、他の拡張機能の使用方法を示します。インストールすると、次のようにも表示されます。

  • BlueJ ヘルプメニューの「Installed Extensions」パネルにあるエントリ
  • (あまり役に立たない) BlueJ の [ツール] メニューへのメニュー エントリ、および既存のクラスのソース コードにコメントを追加するものを含む、クラスおよびオブジェクト用に表示されるメニューへのエントリ。
  • Tools/Preferences/Extensions パネルのエントリで、お気に入りの色を入力 (および保存) します。
    拡張機能の完全なソース コードはこちらです。

単体テスト

こちらにあるドキュメントを見るとやり方が書いてあるのですが、ちょっと違うようで。。。

テストクラスを作成する

下の図のように、対象クラスを右クリックします。そして、「テストクラスの作成」を選択します。

すると、下のようなクラスが作成されます。
対象にしているクラスはXMLUtilクラスです。

作成したクラスは、下のように表示されます。

Java はじめて3 〜コメントの書き方〜

イントロダクション

コメントの書き方をやります。プログラムが小さくてすぐに終わるようなものであれば、コメントは不要かもしれませんが。超大作になった場合、コメントがないとわけがわからなくなります。「コードを覚えてればよくね?」と思った方は書かなくても良いでしょう(笑)。あとで苦労するのも良い経験になります。

コメントの書き方

コメントは大きく2種類あります。

  1. JavaDocコメント
  2. プログラム用コメント

このコメントはそれぞれに以下のように記述します。

// プログラム用1行コメント
/*
 * プログラム用複数行コメント
 * こんな感じです。
 */

複数行に渡るコメント、最近はあまり使われないように思える。。。。

/**
 * JavaDocコメント
 * @param メソッドの引数など、IDEを使用すれば自動出力してくれるので楽チンです。
 * @return メソッドの返却値です。
 * @see 参照するものURLやURI(ファイルパスとURLを含む)
 */

最近は知多のような書き方が多いように思えます。

// JavaDocコメント
// @param メソッドの引数など、IDEを使用すれば自動出力してくれるので楽チンです。
// @return メソッドの返却値です。
// @see 参照するものURLやURI(ファイルパスとURLを含む)

JavaDocとコメント

複数行コメントとの違いは、初めのアスタリスクが2個、もしくは1個という違いです。
具体的には、次のよう違いです。

「/**」で始まる→JavaDoc
「/*」で始まる→通常コメント

簡単にはこんな感じです。
これで、クラスファイルの数が1000とか2000になっても大丈夫です(笑)。大規模開発でないとこうはなりませんが。。。

じゃんけんゲームのサンプルコード(リンクはGithubへのものです。)>

ちなみに、クラスファイルを一つでじゃんけんゲームを作成してみました。クラス内に、フィールド変数として
「勝敗判定MAP」などがあります。「final」がついているものは定数です。

今後学びますので、今のうちに軽く眺めておくと理解も早まります。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

/**
 * じゃんけんゲームメインクラス。
 * オブジェクト指向プログラミングでないパターンの実装
 *
 * @author 実装者の名前
 *
 */
public class FirstJankenMain {
    /** 勝敗判定フラグ:勝ち(ユーザー) */
    private static final int YOU_WIN = 0;
    /** 勝敗判定フラグ:負け(ユーザー) */
    private static final int YOU_LOOSE = 1;
    /** 勝敗判定フラグ:あいこ(ユーザー) */
    private static final int AIKO = 2;
    /** グー */
    private static final String GU = "0";
    /** チョキ */
    private static final String CHOKI = "1";
    /** パー */
    private static final String PA = "2";

    /** 勝敗判定MAP **/
    private Map<String, Integer> judgeMap;
    /** 手のMAP(追加実装) */
    private Map<String, String> teMap;

    /**
     * メインメソッド
     * @param args プログラム引数
     */
    public static void main(String[] args) {
        // 0.じゃんけんゲーム起動
        FirstJankenMain main = new FirstJankenMain();
        // 1.勝敗判定MAP作成
        main.createJudgeMap();
        Random random = new Random();

        // 追加実装: 「じゃんけん」と「あいこ」の判定が行えてない
        boolean isJanken= true;
        // 無限ループ
        while(true) {
            // 2.「じゃんけん」or「あいこで」のメッセージ表示
            main.printJankenAiko(isJanken);
            // 3.ユーザーの入力(待ち)
            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);
            // 5.勝敗判定
            int judge = main.judgeWinLoose(input, cpuTe);
            // 6.勝敗判定の表示
            if (main.printJudge(judge)) {
                // 追加実装:「じゃんけん」と「あいこ」の判定が行えてない
                isJanken = true;
                break;
            } else {
                // 追加実装:「じゃんけん」と「あいこ」の判定が行えてない
                isJanken = false;
            }
        }
        // 7.じゃんけんゲーム終了
    }

    /**
     * 1.勝敗判定MAP作成
     */
    private void createJudgeMap() {
        // 勝敗判定MAPのインスタンスを生成する
        judgeMap = new HashMap<String, Integer>();
        // プレーヤーの勝ちケース
        judgeMap.put(GU + CHOKI, YOU_WIN);
        judgeMap.put(CHOKI + PA, YOU_WIN);
        judgeMap.put(PA + GU, YOU_WIN);
        // プレーヤーの負けケース
        judgeMap.put(GU + PA, YOU_LOOSE);
        judgeMap.put(CHOKI + GU, YOU_LOOSE);
        judgeMap.put(PA + CHOKI, YOU_LOOSE);
        // あいこのケース
        judgeMap.put(GU + GU, AIKO);
        judgeMap.put(CHOKI + CHOKI, AIKO);
        judgeMap.put(PA + PA, AIKO);

        // 手のマップ
        teMap = new HashMap<String, String>();
        teMap.put(GU, "グー");
        teMap.put(CHOKI, "チョキ");
        teMap.put(PA, "パー");
    }

    /**
     * 2.「じゃんけん」or「あいこで」のメッセージ表示
     *
     * @param isJanken true: 「じゃんけん」false: 「あいこ」
     */
    private void printJankenAiko(boolean isJanken) {
        // 追加実装、各手と入力値の票を表示する
        System.out.println("****************");
        System.out.println("*グー   = 0    *");
        System.out.println("*チョキ = 1    *");
        System.out.println("*パー   = 2    *");
        System.out.println("****************");
        // isJankenがtrueの時は「じゃんけん」を表示する
        if (isJanken) {
            System.out.println("じゃんけん ...");
        } else {
            System.out.println("あいこで ...");
        }
    }

    /**
     * 3.ユーザーの入力(待ち)
     *
     * @return 0: グー, 1: チョキ 2: パー
     */
    private String acceptInput() {
        // System.in = 標準入力
        Scanner scan = new Scanner(System.in);
        String input = scan.nextLine();
        return input;
    }

    /**
     * 4.「ポン!」or「しょ!」を表示
     */
    private void printPonOrSho(boolean isJanken) {
        if (isJanken) {
            // 「じゃんけん」の場合は「ポン!」
            System.out.println("ポン!");
        } else {
            // 「あいこで」の場合は「しょ!」
            System.out.println("しょ!");
        }
    }

    /**
     * 5.勝敗判定
     *
     * @param playerTe プレーヤーの手
     * @param cpuTe CPUの手
     * @return 勝敗判定 true: プレーヤーの勝ち false: プレーヤーの負け
     */
    private int judgeWinLoose(String playerTe, String cpuTe) {
        // 勝敗判定MAPのキーはプレーヤーの手とCPUの手を連結したもの
        // 例:「01」= プレーヤー「グー」、CPU「チョキ」
        // 勝敗判定マップから勝敗判定結果を取得する。
        String key = playerTe + cpuTe;
        int result = judgeMap.get(key);
        return result;
    }

    /**
     * 6.勝敗判定の表示
     *
     * @param resultJudge 判定結果
     * @return true: 終了 false: もう一度
     */
    private boolean printJudge(int resultJudge) {
        boolean isFinish = true;
        // 勝敗判定結果を表示する
        switch(resultJudge) {
        case YOU_WIN:
            System.out.println("YOU WIN!");
            break;
        case YOU_LOOSE:
            System.out.println("YOU LOOSE!");
            break;
        case AIKO:
            isFinish = false;
            System.out.println("DRAW!");
            break;
        }
        return isFinish;
    }

    /**
     * <追加実装>
     * プレーヤーの手とCPUの手を表示する。
     *
     * @param playerTe プレーヤーの手
     * @param cpuTe CPUの手
     */
    private void printTe(String playerTe, String cpuTe) {
        System.out.println("ユーザー:" + teMap.get(playerTe));
        System.out.println("CPU:" + teMap.get(cpuTe));
    }

    /**
     * <追加実装>
     * じゃんけんの手として適当な値であるか判定する。
     *
     * @param input ユーザー入力
     * @return true; じゃんけんの手として適当な値 / false: じゃんけんの手として不適当な値
     */
    private boolean inputCheck(String input) {
        // 判定フラグ
        boolean isJankenTe = false;
        // 正規表現で判定する
        if (input.matches("[0-2]")) {
            isJankenTe = true;
        }
        return isJankenTe;
    }
}

これを動かすと、このような形で動きます。

でわでわ。。。

<<< 前回  次回 >>>

サイトマップ
ゲームを作りながら覚えるJavaの基本

関連ページ

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Summary from Level1 to 5
  5. Java クラスの書き方〜ワンポイントレッスン的な〜


Java Basic 中級編 ~③メインメソッドを修正しないで拡張する~

イントロダクション

前回は、メインメソッドを修正しない形での実装を行いました。

この段階では、まだ役割分担を行っただけでこれがどんな効果があるのか?というところがわからないと思います。

なので、今回は、「〇×あてゲーム」を拡張することを考えていきたいと思います。
作成したプログラムコードはGithubにアップしてあります。

メインメソッドを修正しないで拡張する

今までに「役割分担」を行ったので、この役割分担を活用していきます。
Lv2Mainクラスはメインメソッドを動かすクラスなので、これ以外のクラスを実装していきます。

  • Lv2Main
  • MarubatsuConsole
  • MarubatsuUtils

Lv2Mainを見る

まずは、メインメソッドを見てみます。Lv2Main

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = MarubatsuUtils.getScanner();

        while (true) {
            // ゲーム開始文言
            MarubatsuConsole.printGameStart();
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                MarubatsuConsole.printTerminated();
                break;
            }
            // 0か1の値を返却する
            int res = MarubatsuUtils.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = MarubatsuUtils.judgeAtariOrNot(res, input);
            MarubatsuConsole.printAtatiorNot(isAtari, input);

            // 続けるのかどうか判定する
            if (MarubatsuConsole.printNextPlayOrNot(scan)) {
                break;
            }
        }
    }
}

コメントを並べただけですが、以下の順序で処理を行っています。

  1. 標準入力を受け取るクラスをインスタンス化
  2. ゲーム開始文言
  3. 標準入力を受け取る
  4. isFinishがtrueならば処理終了。
  5. 0か1の値を返却する
  6. 0: 〇 1: ×で当たったかどうかの判定
  7. 続けるのかどうか判定する

上の番号で行くと2~7がループ処理の中にあります。
なので、7の「続けるか判定する」部分でNo(続けない)を選択するまで無限ループします。

インスタンス化

Boolean

標準入力

フィールド変数の扱い

拡張ポイントを見つける

そして、役割分担を行ったところ、つまりは、下のクラスを使用している部分が拡張ポイントになります。
Githubにアップしてあります。

具体的には、以下のコメント部分です。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

もしも、他の部分を拡張したいと思たのならば、メインメソッドを少し修正する必要があります。
何かの処理をほかのクラスに任せてある状態(MarubatsuConsole, MarubatsuUtilsを使用している状態)ならば
メインメソッドを修正する必要がありません。
しかし、直接メインメソッドを修正する必要がある場合はやはり、修正する必要があります。

このような実装方法を理解すると、下のようなプログラムが作れます。

※ビルドするのに3分くらい時間がかかっています。
このプログラムは、メインの処理部分も、別クラスにしているので、まったく別のプログラムを起動するように修正することもできます。
しかし、現状では、テキストRPGを作成するところに注力しているので役割分担を行いそれぞれの役割を拡張して実装しています。
具体的には、テキストファイルを読み、それをもとにデータ(ステータスやアイテム)を生成してそれをゲームの中で使用する形での実装を行っております。
ちょっと残骸が残っていますが。。。

拡張するとき

先に示したように、別のクラスを呼び出している(使用している)部分を拡張、つまり処理を追加することができるので、次の部分を拡張することができます。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

具体的には、ゲーム開始文言を変更することができる。とか、当たったかどうかの履歴を付ける。とか
そこらへんは、実装者のアイディア次第でどこまでも広げることができます。

ゲームの開始文言を変更する

先ほど、拡張する例としてゲームの開始文言を変更するということを上げました。これを実際にやってみます。

やっている手順としては、以下の通りです。

  1. JUnitテストケースの作成
  2. 現状のプログラム実行確認
  3. プログラムの修正、実行確認(目視で確認)

最後の実行確認(目視で確認)に関しては、すべてプログラムで確認することができます。
そのためには、ちょっと面倒なことをする必要があるので、割愛しました。
具体的には標準出力の出力先を変更して(別のPrintStreamを使用)そのストリーム内の出力内容と期待値を比較するという形になります。

現状としては、単純に〇×あてゲームの初期表示文言を変更するだけなので、目視で確認しました。

0: 〇 1: ×で当たったかどうかの判定

この部分は上記の動画では、挙動がおかしくなっていました。なので、これを修正し想定通りの実行結果が得られるようにプログラムを修正します。

実行した結果は下のようになります。

******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
0
******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
1

その前に、問題点を明確にします。

今回の問題点は「あたりのときの値、つまりは、『〇』が当たりなのか?『×』が当たりなのか?」が明確でないというところです。
なので、これも修正します。

先ほどと同じようにまずは、修正ポイント(先ほどは拡張ポイントと記載しました。)を見つけます。
実行結果を見ると、「はずれ:×」のように当たりはどちらなのか?がわからない状態ですので、これを明確にします。

具体的には、「「〇」があたりです。「×」が当たりですなどの文言を表示するように修正」する形で対応しようと考えております。

当然、他に良いアイディアがあれば、それを実装するとよいと思います。

当たり判定処理の修正

今回作成した「当たり判定処理」は、MarubatsuUtils#judgeAtariOrNotで実装しているのでこれを修正します。

現状のプログラムは下のようになっています。

    /**
     * 生成した乱数と、入力値が等しいか判定する。
     *
     * @param res 生成した乱数
     * @param input 入力値
     * @return true: 等しい false: 違う
     */
    public static boolean judgeAtariOrNot(int res, int input) {
        // 0: 〇 1: ×で当たったかどうかの判定
        return res == input;
    }

このプログラムの問題点は、〇と×のどちらが当たりなのか表示されない点です。
なので、これを表示するようにプログラムを修正する必要があります。

テストケースを作成する

先ほど初期表示の文言を表示するテストケースを作成しました。
これに追加して、「〇と×のどちらが当たりなのか表示する」テストケースを実装します。

仕様から考える

今回の要件(どのように動いたらよいか?)は「〇と×のどちらが当たりなのか表示する」ということです。
これを確かめるプログラムを考えます。

確認項目をリストアップ

  1. あたりは「〇」「×」どちらか表示する
  2. 予想を入力したユーザーの入力は「〇」「×」どちらか表示する

簡単ですが、2項目になります。

これもテストケースを作成する

ちなみに、このクラスのテストケースは以前、他のテストケースを作成していたので、これに今回のテストケースを追加します。

JUnitのセットアップ方法

テストケースの作り方①

テストケースの作り方②

テスト実行

サンプル動画

やったことは、同じです。

  1. 現状のプログラム実行確認
  2. プログラムの修正
  3. プログラムの実行確認

ちょっと長めの動画になりましたが、行ったことをそのまま動画にしてあります。
ポイントとしては、テストケースのプログラムの実装、作成方法、考え方(この記事に記載)を実際に行った
動画にしてあります。
別な言い方をすると、自分がこの作業を行ったものを動画にしました。

でわでわ。。。

<<< 前回