学習6日目:カプセル化を使いこなしたい。

学習時間について。

  • Day:6
  • Today:8h
  • Total:50h

今日の学習内容。

実際に文字列を比較してみる。

昨日の続きから。

substring

文字列を切り出すためのメソッドとして利用する。引数で指定した位置を範囲として、文字列を切り出すことが可能。

public class sampleSabstring {

    public static void main(String[] args) {
        String str = "イロハニホ";
        String sub = str.substring(1, 3);
        System.out.println(sub);
    }
}

/* 結果

ロハ

*/
  • 引数で指定する数値は0番目からのスタート
    • 配列と同じ
  • 例:01……4(1つずつずれる)
  • 3なのににならないのはメソッド仕様のせい
    • 3つ目は含まずその前で切り取るというルールがある

replaceAll

public class sampleReplaceAll {

    public static void main(String[] args) {
        String str = "名無しさん";
        String sub = str.replaceAll("さん", "くん");
        System.out.println(sub);
    }

}

/* 実行結果

名無しくん

*/

一致した文字を置き換えるメソッド。

数学演算処理を行う「Mathクラス」。

public class sampleMath {

    public static void main(String[] args) {
        double flr = Math.floor(16.836153);// 切り捨て
        double rnd = Math.round(41.499);// 四捨五入
        double cl = Math.ceil(99.5);// 切り上げ
        double pow = Math.pow(15, 5);// 累乗、べき乗
        double sqrt = Math.sqrt(144);// 平方根
        double rad = 100 * 100 * Math.PI;// 円周率及び面積

        System.out.println(flr);
        System.out.println(rnd);
        System.out.println(cl);
        System.out.println(pow);
        System.out.println(sqrt);
        System.out.println(rad);
    }

}

/* 実行結果

16.0
41.0
100.0
759375.0
12.0
31415.926535897932

*/
floor切り捨て
ceil切り上げ
round四捨五入
powべき乗
sqrt平方根
randomランダムな値を出力
Math.PI円周率を扱う(定数)

プログラムを実行する時に、値を変更することがない値を定数として定義することがある(上記の円周率)。値を変更することがない場合以外にも、何らかの操作で値を変更されたくない時や数値の意味について説明を入れたい時にも効力を発揮する。

public class VoteSample {

    public static void main(String[] args) {
        final int VOTE_AGE = 18;

        int age = 16;
        if(age < VOTE_AGE) {
            System.out.println("18歳未満の方は投票できません");
        } else {
            // 投票する機能を書く(省略)
        }
    }
}
  • finalという修飾子をつけて変数を定数化
  • 18だけでは何の数値か分かりづらい(マジックナンバー)
    • そういった時にも定数は使える

配列とコレクション。

同じようなデータを扱う際に全てに変数を用意するのは面倒で非効率的なので配列という仕組みを利用する。

package test;

public class TestArray {

    public static void main(String[] args) {
        int[] coincase = { 1, 0, 2, 2, 8, 1 };
        int[] yen = { 1, 5, 10, 50, 100, 500 };

        int money = 0;
        for (int i = 0; i < 6; i++) {
            money += coincase[i] * yen[i];
        }
        System.out.println("小銭は" + money + "円あります");
    }

}

+=を思いつくのに時間がかかった。。

  • 配列自体は1つだが、中に6つの小分けされた箱があるイメージ
  • []内に書かれた数字のことを添字(Index)という
    • 添字は0から始まるので注意
  • 配列は参照型なので実際は値があるメモリの場所(アドレス)が記載されるだけ
  • 配列は手軽に扱えるものの、箱の数(要素数)を一度決めてしまうと、それ以上は増減できない
    • その不便さを解消するためにコレクションという機能がある(次項)

配列には多次元配列というのもあるそうだが今回はひとまず先に進めることにする。

import文。

コレクションを利用する際にimport文を使うのが一般的。

  • import パッケージ名.クラス名;
    • 例:java import java.util.List;
  • Eclipseを利用する場合、import文を自動で追記してもらうことが可能
  • 注意点
    • 同じクラス名で別パッケージのクラスをimportしない
    • コードが不要になって消す際は不要なパッケージも削除する
コレクションの種類
  • List:順番に値を並べたもので、配列に似ている
  • Set:ユニークな値の集合
  • Map:キーごとに対応するバリュー(値)を持つ集合。キーはユニーク
  • Queue):キュー。要素の追加と取り出し用の便利なメソッドがある
    • あまり使われない
  • 配列:固定長(要素を追加する時に手間がかかる)
  • コレクション:可変長(要素の数を自由に変更可能)

ArrayList

例:リスト(データの塊)に対してデータを取得、追加、削除、要素の数を数える。

package test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class SampleArrayList {

    public static void main(String[] args) {
        // 行きたい温泉
        List<String> al = new ArrayList<String>();
        al.add("指宿温泉");
        al.add("乳頭温泉");
        al.add("有馬温泉");
        al.add("鉄輪温泉");
        al.add("草津温泉");
        al.add("玉川温泉");

        // 指定したデータを取得
        String place = al.get(2);
        System.out.println(place);

        // 要素数を数える
        int listCount = al.size();
        System.out.println(listCount);

        // 要素を削除する
        al.remove(2);
        place = al.get(2);
        System.out.println(place);

        System.out.println("ループここから");

        // 全部出力する
        Iterator<String> it = al.iterator();
        while (it.hasNext()) {
            String onsen = it.next();
            System.out.println(onsen);
        }
    }
}

/* 実行結果

有馬温泉
6
鉄輪温泉
ループここから
指宿温泉
乳頭温泉
鉄輪温泉
草津温泉
玉川温泉

*/
  1. ArrayListを作成して、そこに要素を1つずつ追加
    1. 追加にはaddというメソッドがある
    2. getが指定した添字Indexのデータを取得
  2. 要素数を数える
    1. sizeメソッドの戻り値はintなのでキャスト不要
  3. 削除にはremoveメソッドを用いる
  4. ループで全部出力する
  • List<String> al = new ArrayList<String>();:型引数の指定(ジェネリクス)
    • <>の部分に型やクラスを指定できる
    • しかし、参照型のみ指定可(プリミティブ型はNG)
  • ArrayList<String> al = new ArrayList<String>();と書くことも可能である
    • ただし、修正の面から考えると前述のジェネリクスの方が好ましい
  • <String>の部分は記述しなくてもプログラムは実行する
    • 警告が表示されるなど問題が起きやすい(下記例;文法上は正しい)
      • 指定の型のデータのみを扱うべき
package test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

class SampleArrayList {
    public static void main(String[] args) throws java.lang.Exception {
        List list = new ArrayList();

        list.add(new Integer(123));
        list.add(new String("abc"));

        Iterator it = list.iterator();
        while (it.hasNext()) {
            Integer i = (Integer) it.next();
            System.out.println(i);
        }
    }
}

/* 実行結果

123
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at test.SampleArrayList.main(SampleArrayList.java:16)


*/
  • ジェネリクスで型を指定しない場合はnext()で取り出したデータを指定の型にキャストする必要がある
    • 1回目のループ処理でArrayListから取り出したのは123で問題なし
    • 2回目のループ処理で取り出したのはString型のデータ(abc)である
      • String型をInteger型に変換しようとしたことでエラーが発生した

Set

  • 重複がない値の集合
    • 値が唯一無二であることを保証する
    • 順番の保証なし
  • Listとほぼ同じような使い方

HashMap

package test;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class SampleHashMap {

    public static void main(String[] args) {
        Map<String, String> hm = new HashMap<String, String>();

        // HashMapにキーと値を追加
        hm.put("P", "菅野");
        hm.put("C", "小林");
        hm.put("3B", "岡本");
        hm.put("SS", "坂本");

        // キーを指定して値を取得
        String member = hm.get("C");
        System.out.println(member);

        // サイズを確認する
        int size = hm.size();
        System.out.println(size);

        // 全部出力する
        Iterator<String> key_it = hm.keySet().iterator();
        while (key_it.hasNext()) {
            String key = key_it.next();
            System.out.println(key + ":" + hm.get(key));
        }
    }
}

/* 結果

小林
4
P:菅野
SS:坂本
C:小林
3B:岡本

*/
  • キーと値(Key、Value)が1組であり1つの要素
    • remove:削除
    • clear:全部まとめて消す
    • size:要素の数を確認
  • Keyの重複はNG
    • 同じKeyを指定して操作すると元のValueが変わってしまう
  • 順番は保障されない
    • HashMapはあくまでKey、Valueの集合である
  • コレクションのネスト
    • ArrayListの中にArrayListを入れたり、HashMapの中にArrayListを入れることが可能
      • 操作はややこしい

日付

  • 日付情報を操作するためのクラス
    • Date:特定の日付を保持するのみ(操作できない)
    • Calendar:日時に対する操作が可能
package test;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class SampleDate {

    public static void main(String[] args) {

        // 現在日時の取得と表示
        Date date = new Date();
        System.out.println(date);

        // 現在の日時から4週間後を表示する
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.WEEK_OF_MONTH, 4);
        Date fourWeeksLater = cal.getTime();
        System.out.println(fourWeeksLater);

        // 日付のフォーマット
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String str = sdf.format(date);
        System.out.println(str);
    }
}

/* 実行結果

Thu Jul 16 13:03:18 JST 2020
Thu Aug 13 13:03:18 JST 2020
2020/07/16

*/

日付形式を変えたい場合はSimpleDateFormatクラスを使ってフォーマットする必要がある。

テキストファイルの読み書き

利用例:サイト利用者のアクセス記録をテキスト形式のファイルに残す(ログ)場合など。

  • 読むためのクラス
    • FileInputStream
    • BufferedReader
  • 書くためのクラス
    • FileOutputStream
    • OutputStreamWriter
  • 他にもある
  1. テキストファイルへ文字列を書き込むプログラムを作成・実行
    1. FileOutputStream
  2. テキストファイルから文字列を読み込むプログラムを作成・実行
    1. FileInputStream
package test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class FileOutput {

    public static void main(String[] args) {
        try {
            // ファイルを追記モードで開く
            OutputStream os = new FileOutputStream("test.txt", true);

            // 書き込み操作を行うオブジェクトを生成する
            OutputStreamWriter writer = new OutputStreamWriter(os);

            // ファイルへ追記する
            writer.write("ファイルへ書き込みます。\n");

            // ファイルを閉じる(writeとosの使用をやめる)
            writer.close();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. ファイルを開く
    1. OutputStream os = new FileOutputStream("test.txt", true);を使用
    2. 第2引数を trueにする
      1. プログラムが「追記モード」でファイルを開いてくれる(末尾に追記)
    3. [第2引数が false の場合は「書き込みモード」となる(上書き消去)]
  2. 書き込み操作を行うオブジェクトを生成
    1. OutputStreamWriter writer = new OutputStreamWriter(os);と記述
  3. 書き込み(追記)を実行する
    1. writer.write()と記述
  4. 引数に指定した文字列が保存される
    1. \n は改行記号
  5. 開いたファイルは必ず閉じる
    1. それぞれのオブジェクトにclose()

上記の操作を完了すればプロジェクト内にtest.txtが作成される。そして、FileOutputを1回実行するごとに「ファイルへ書き込みます。」と書き込みがされる。

また、ファイルがない(FileNotFoundException )場合やファイルへ正常に書き込めないトラブル(IOException )の場合でもプログラムが止まらないようにするために例外処理(trycatch)を行う。tryでトラブルが発生した場合は処理がストップし、代わりにcatch内に書かれた処理が実行される。

try およびFileNotFoundException とIOException に対するcatch を記述は必ず行う。

package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class FileInput {

    public static void main(String[] args) {
        try {
            // ファイルを読み込みモードで開く
            InputStream is = new FileInputStream("test.txt");

            // ファイルから読み込む操作をするオブジェクトを生成する
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));

            // 1行分だけ取得
            String str = reader.readLine();

            // strが空っぽでなければ中身を表示して
            // さらに次の行の内容を取得
            while (str != null) {
                System.out.println(str);
                str = reader.readLine();
            }

            // ファイルを閉じる(readerとisの使用をやめる)
            reader.close();
            is.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/* 実行結果

ファイルへ書き込みます。
ファイルへ書き込みます。

*/
  1. test.txtを読み込みモードで開く
    1. InputStream is = new FileInputStream("test.txt");を記述
  2. 読み込み操作を行うオブジェクトを取得する
    1. BufferedReader reader = new BufferedReader(new InputStreamReader(is));を記述
  3. test.txtから1行ずつ文字列の取得を試みる
    1. reader.readLine()を記述
    2. 中身が空(null)でなければ文字列を表示
  4. 開いたファイルは必ず閉じる
  5. FileInputを実行
    1. test.txtの内容がそのままコンソールに表示される

アクセス制御する機能「カプセル化」。

カプセル化とは誰もがデータに触れないように隠しておくこと。他のクラスからデータやメソッドを変更できると、想定外に発生する不具合などが発生することがあるのでそれを防ぐためなどに行う。

アクセス修飾子。

例:アクセス修飾子のおさらい。

public class test{ //クラスに対する修飾子

    private String field1; //プロパティに対する修飾子
    protected int field2; //プロパティに対する修飾子

    public void method(){ //メソッドに対する修飾子
        //何か処理を実装する
    }
}
publicすべてのクラスからアクセス可能
protected同じパッケージ内のクラスとサブクラスからアクセス可能
指定なし同じパッケージ内のクラスからアクセス可能
(パッケージプライベートと呼ばれる)
private同じクラス内からのみアクセス可能(外からアクセスできない)
上にあるものほど制限が緩く、下に行くほど厳しい。

クラスに指定できる修飾子はpublic指定なしだけだが、プロパティやメソッドには全ての修飾子が指定できる。

値の書き換え・読み取りに「セッターとゲッター」。

package test;

class StudentVo {
    // プロパティ
    private String studentName; // 生徒名
    private String clubName; //部活動
    private int vitality; // 体力

    // コンストラクタ
    StudentVo() {
    }

    // 引数ありコンストラクタ
    StudentVo(String studentName, String clubName, int vitality) {
        System.out.println("初期化時に引数ありコンストラクタが呼ばれました");
        this.studentName = studentName;
        this.clubName = clubName;
        this.vitality = vitality;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public String getClubName() {
        return clubName;
    }

    public void setClubName(String clubName) {
        this.clubName = clubName;
    }

    public int getVitality() {
        return vitality;
    }

    public void setVitality(int vitality) {
        this.vitality = vitality;
    }
}
  • Getterメソッドの名前:get+プロパティ名
    • boolean型の場合のみ:is+プロパティ名
  • Setterメソッドの名前:set+プロパティ名

例:インスタンスをそのまま実行してみる。

package test;

public class SetterGetterSample {

    public static void main(String[] args) {

        StudentVo ev = new StudentVo();
        ev.studentName = "きき";
    }
}

/* 実行結果

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	フィールド StudentVo.studentName は不可視です

	at test.SetterGetterSample.main(SetterGetterSample.java:8)

*/

StudentVo.javaPrivateは自分のクラスでしか操作できないので、別のクラスであるSetterGetterSampleからは操作できない。なので、下記の操作を行う。

package test;

public class SetterGetterSample {

    public static void main(String[] args) {

        StudentVo ev = new StudentVo();
        ev.setStudentName("きき"); // Setter
        String sname = ev.getStudentName(); // Getter
        System.out.println(sname); // getterで取り出したものをとりあえず、画面出力
    }
}

/* 実行結果

きき

*/

データだけ持たせるクラス(Vo)と、操作設定するクラス(Sample)は完全に別にする。

カプセル化の練習。

例:昨日のRPG風クラスをカプセル化してみる。

package test;

public class Character {
    private String name;
    private Integer hp;
    private Integer offense;
    private Integer defense;

    public Character() {
    }

    public Character(String name, Integer hp, Integer offense, Integer difense) {
        this.name = name;
        this.hp = hp;
        this.offense = offense;
        this.defense = difense;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getHp() {
        return hp;
    }

    public void setHp(Integer hp) {
        this.hp = hp;
    }

    public Integer getOffense() {
        return offense;
    }

    public void setOffense(Integer offense) {
        this.offense = offense;
    }

    public Integer getDefense() {
        return defense;
    }

    public void setDefense(Integer defense) {
        this.defense = defense;
    }

    // 攻撃メソッドの記述は省略

}

これでプロパティの値が隠蔽できた。RPGのメインメソッドを実行しても結果は同じなので書きません。

newが不要になる「static」。

  • メインメソッドにはpublic staticという2つの修飾子が付属している。
    • publicはアクセス修飾子の1つだと分かった。
  • staticは固定のもの(プロパティ、メソッド)を作成したい場合に活用する
    • new(初期化)しなくても呼べる
  • 書き方:クラス名.プロパティ名またはクラス名.メソッド名(引数)

今日の反省と明日の目標。

様々なメソッドが出てきて便利だなと思う反面、それらを適切に使えなければもったいないのでどういった場面で使うのかをイメージしながら学習することが必要だと感じた。

この日記自体は1週間後に自分が振り返って学習する時に使えれば良いというコンセプトで、忘れたら困ることだけメモ帳感覚で書き留めていた。しかし、Javaだけに限らないと思うが新しい概念を学習する時、それに関連した古い情報が必要になってくる。今日も昨日や一昨日に書いた記事を何度も見返して徐々に理解を深めてきた。

些細なことでもメモしていこう。多分それは明日の私は忘れている。

話は変わるが、抽象的な内容だとどうしても集中力が途切れて投げ出したくなってくる。そんなときに効果を発揮するのがトレーニング中に飲んでいたBCAA。

BCAAとは必須アミノ酸のうち、バリン、ロイシン、イソロイシンのことを指す。それらを摂取することで脳内へのトリプトファンの流入が減り、セロトニンの合成が抑制される。よって、眠気がなくなり集中力が増すというわけだ。まあ、それが例えプラシーボだとしても、たしかに気分転換にはなるので明日からも取り入れていこうと思う。

明日中にオブジェクト指向の基礎的な部分は全て終わらせたい。

閉じる