学習5日目:オブジェクト指向のイントロ。

学習時間について。

  • Day:5
  • Today:9h
  • Total:42h

今日の学習内容。

オブジェクト指向を使ってみる。

  • オブジェクト指向を使うことでクラスファイルを大量に作成する手間が省ける
    • パッケージを使用する

例:まずはtestパッケージを作成。次にStudentクラスを作成。

package test;

public class Student {
    // プロパティ
    public String studentName; // 生徒名
    public String clubName; // 部活動

    // 自己紹介メソッド
    public void introduce() {
        System.out.println("私の名前は" + studentName + "です。");
        System.out.println("部活動は" + clubName + "です。");

    }
}
  • packageの後にパッケージ名を書けば、ひとまとまりのパッケージとして扱うことが可能(1行目)
  • public:修飾子の一つ
    • 複数のファイルに分かれる場合は「どのオブジェクトが操作可能か」指定する必要がある
    • publicはどのオブジェクトも操作できることを指定する修飾子
  • 上記のコードは単体では実行できない(メインメソッドがないので)
    • 他から呼び出して利用するクラスである

インスタンスの作成。

次にStudentのインスタンスを生成する。

package test;

public class ObjectSample {

    public static void main(String[] args) {
        Student stu = new Student();
        stu.studentName = "きき";
        stu.clubName = "パソコン部";
        stu.introduce();
    
    }
}

/* 実行結果

私の名前はききです。
部活動はパソコン部です。

*/
  • Student stu = new Student();(6行目)で初期化
    • Studentクラスの変数stuに対し、右辺のnewによりインスタンスを作成して代入している
      • インスタンス=実物のオブジェクト
      • 上記を実行することでStudentクラスが1つのインスタンスとして実体化し、変数stu に格納される
        • stuが実在のものになる
  • stu.studentName = "きき";(7行目)とstu.clubName = "パソコン部";(8行目)
    • インスタンス化しただけではStudentクラスのプロパティの生徒名と部活動名には何も入っていない状態
    • なので<インスタンスが格納された変数名>.<プロパティ名> という記述で値をセット
      • . は、助詞の「の」みたいなもの
  • stu.introduce();(9行目)
    • Studentが持っている introduceメソッドを実行
    • <インスタンスが格納された変数名>.<メソッド名>(<引数(必要であれば)>)
      • 特定のクラスが持っているメソッドを実行可能

例:さらにStudentにプロパティとメソッドを追加していく。

package test;

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

    // 自己紹介メソッド
    public void introduce() {
        vitality = vitality - 10;
        System.out.println("私の名前は" + studentName + "です。");
        System.out.println("部活動は" + clubName + "です。");
    }

    // 挨拶をするメソッド
    public void greeting() {
        vitality = vitality - 10;
        System.out.println("おはようございます");
    }

    // 報告するメソッド
    public void report() {
        vitality = vitality - 10;
        System.out.println("今朝は2時間勉強しました");
    }

    // 残り体力を知らせるメソッド
    public void showVitality() {
        vitality = vitality - 10;
        System.out.println("残り体力は" + vitality);
    }

    // 登校するメソッド
    public void goToSchool() {

    }

    // 下校するメソッド
    public void leaveSchool() {

    }
  • プロパティに体力を追加
  • 登校と下校は未実装
  • 何か行動(この場合はメソッドを実行)すると、体力を10消費する
package test;

public class ObjectSample {

    public static void main(String[] args) {
        
        Student stu01 = new Student();
        stu01.studentName = "きき";
        stu01.clubName = "パソコン部";
        stu01.vitality = 150;
        stu01.introduce();
        stu01.showVitality();
    
    }
}

/* 実行結果

私の名前はききです。
部活動はパソコン部です。
残り体力は130

*/
  • データの項目はプロパティにもたせる
    • そのクラスで行いたい機能をメソッドとして実装

例:2人目のインスタンスを追加してみる。

package test;

public class ObjectSample02 {

    public static void main(String[] args) {

        Student stu02 = new Student();
        stu02.greeting();        
        stu02.studentName = "けいてん";
        stu02.clubName = "英語部";
        stu02.vitality = 250;
        stu02.introduce();
        stu02.report();
        stu02.showVitality();

    }
}

/* 実行結果

おはようございます
私の名前はけいてんです。
部活動は英語部です。
今朝は2時間勉強しました
残り体力は210

*/
  • 別々のインスタンス
    • それぞれが値を持っており、お互いに干渉しない
  • Studentクラスに基づいて大量生産ができる

プロパティの初期値。

例:設定する前に参照してみる。

package test;

public class ObjectSample {

    public static void main(String[] args) {

        Student stu01 = new Student();
        //stu01.studentName = "きき";
        //stu01.clubName = "パソコン部";
        //stu01.vitality = 150;
        stu01.introduce();
        stu01.showVitality();
    }
}

/* 実行結果

私の名前はnullです。
部活動はnullです。
残り体力は-20

*/
  • null=参照なし
  • インスタンスとしてstu01は存在している
    • しかし、中身に値を代入しない限りはnullになる
    • 中身:String studentNameString clubName
  • intの場合は基本データ型なので0が入る

例:ということなので、プロパティに初期値を設定してあげる。

package test;

public class Student {
    // プロパティ
    public String studentName = "秘密"; // 生徒名
    public String clubName = "帰宅部"; //部活動
    public int vitality = 0; // 体力

}

/* ObjectSample未設定の実行結果

私の名前は秘密です。
部活動は帰宅部です。
残り体力は-20

*/

相互に依存しない独立した仕組みを作ることで、プログラムの維持管理や修正が容易になる。

コンストラクタ。

  • コンストラクタ=プロパティの初期値を設定するもうひとつの方法
    • クラスを初期化する時の処理をこちらで決めることが可能
    • インスタンスをnewした時に呼び出される
    • 呼び出さないというのはJavaの仕組み上不可能
    • メソッドではなく、特殊な扱いである

例:コンストラクタを利用してみる(前述の初期値は復元)。

package test;

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

    // コンストラクタ
    public Student() {
        System.out.println("初期化時にコンストラクタが呼ばれました");
        studentName = "秘密";
        clubName = "帰宅部";
        vitality = 0;
    }

    // 自己紹介メソッド
    public void introduce() {
        vitality = vitality - 10;
        System.out.println("私の名前は" + studentName + "です。");
        System.out.println("部活動は" + clubName + "です。");
    }

    // 挨拶をするメソッド
    public void greeting() {
        vitality = vitality - 10;
        System.out.println("おはようございます");
    }

    // 報告するメソッド
    public void report() {
        vitality = vitality - 10;
        System.out.println("今朝は2時間勉強しました");
    }

    // 残り体力を知らせるメソッド
    public void showVitality() {
        vitality = vitality - 10;
        System.out.println("残り体力は" + vitality);
    }

    // 登校するメソッド
    public void goToSchool() {

    }

    // 下校するメソッド
    public void leaveSchool() {

    }
}
package test;

public class ObjectSample {

    public static void main(String[] args) {

        Student stu01 = new Student();
        stu01.introduce();
        stu01.showVitality();

    }
}

/* 実行結果

初期化時にコンストラクタが呼ばれました
私の名前は秘密です。
所属部署は帰宅部です。
残り体力は-20

*/
  • クラス名()がコンストラクタの文法ルール
    • メソッドと似ている
    • クラス名と完全一致しないと単なるメソッドになるので注意
  • 書く順番は、プロパティ、コンストラクタ、メソッド
    • 暗黙の了解
  1. 通常、プロパティは型と変数名だけの定義でデータは持たせない
  2. インスタンス化した時に(実体化)データを設定/代入する
    • なぜなら、インスタンス化した時にクラスに書いてあるコンストラクタの処理が行われるから
      • 例:Student stu01 = new Student();の実行時に//コンストラクタ内の処理
  3. 初期化した時点で、Studentクラスのコンストラクタが呼ばれる
    • stu01には名前が秘密、部署名に帰宅部というデータが入った状態に
  4. introduceメソッドを実行した時に、stu01の初期値の2つがしっかりと表示される

例:引数つきのコンストラクタもあるので記述してみる。

package test;

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

    // コンストラクタ
    public Student() {
        System.out.println("初期化時にコンストラクタが呼ばれました");
        studentName = "秘密";
        clubName = "帰宅部";
        vitality = 0;
    }

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

    // 自己紹介メソッド
    public void introduce() {
        vitality = vitality - 10;
        System.out.println("私の名前は" + studentName + "です。");
        System.out.println("部活動は" + clubName + "です。");
    }

// (以下省略)

}
package test;

class ObjectSample {

    public static void main(String[] args) {
        Student stu01 = new Student();
        //stu01.studentName = "きき";
        //stu01.clubName = "パソコン部";
        //stu01.vitality = 150;
        stu01.introduce();
        stu01.showVitality();

        Student stu02 = new Student("けいてん", "英語部", 250);
        stu02.introduce();
        stu02.greeting();
        /*
        stu02.report();
        stu02.showVitality();
        */
    }
}

/* 実行結果

初期化時にコンストラクタが呼ばれました
私の名前は秘密です。
所属部署は帰宅部です。
残り体力は-20
初期化時に引数ありコンストラクタが呼ばれました
私の名前はけいてんです。
所属部署は英語部です。
おはようございます

*/
  • this:プロパティの方のstudentNameを明示して指定
    • プロパティと引数の見分けがつくように
    • 何もない方が引数の方のstudentName
  • プロパティと引数の名前は揃えなければならない
    • プロパティの変数名と同じ=どこに何の値が入ったか明確
  • コンストラクタはいくつでも作成可能
  • コンストラクタを書かなくても、デフォルトコンストラクタがJavaの実行時には自動的に作成、実行されている
    • 引数もなく中身は何もなく何も実行しないコンストラクタ
      • これによりコンストラクタを省略しても、Studentクラスの初期化が行えた

例:引数なしコンストラクタを削除して警告を出す。

package test;

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

    // コンストラクタ
    /*
    public Student() {
        System.out.println("初期化時にコンストラクタが呼ばれました");
        studentName = "秘密";
        clubName = "帰宅部";
        vitality = 0;
    }
    */

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

// (以下省略)

}
package test;

public class ObjectSample {

    public static void main(String[] args) {

        Student stu01 = new Student(); // コンストラクター Student()は未定義です
        stu01.studentName = "きき";
        stu01.clubName = "パソコン部";
        stu01.vitality = 150;
        stu01.introduce();
        stu01.showVitality();
    }
}

/* 実行結果

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	コンストラクター Student() は未定義です

	at test.ObjectSample.main(ObjectSample.java:7)

*/
  • Student stu01 = new Student();にエラーが発生
    • デフォルトコンストラクタ(空のコンストラクタ)が消えているから
      • 1つでも自分でコンストラクタを定義すると消えてしまう
  • この場合は引数なしのコンストラクタを定義するとコンパイルエラーは消える
    • 例:public Student() {}(デフォルトコンストラクタ)
  • 引数ありのコンストラクタを定義した場合は引数なしも記述しておくと安全

RPG風に作ってみる。

package test;

public class Character {

    public String name;
    public Integer hp;
    public Integer offense;
    public Integer defense;

    public Character() {
    }

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

    public void attack(Character opponent) {

        Integer damage = this.offense - opponent.defense;

        if (damage > 0) {
            opponent.hp = opponent.hp - damage;
            System.out.println(this.name + " は " + opponent.name + " に " + damage + " のダメージを与えた!");
        } else {
            System.out.println("ミス! " + this.name + " は " + opponent.name + " にダメージを与えられない!");
        }
    }
}
package test;

public class RPG {

    public static void main(String[] args) {

        Character hero = new Character("勇者", 10, 5, 3);
        Character slime = new Character("スライム", 5, 2, 2);

        hero.attack(slime);
        slime.attack(hero);
    }
}

/* 実行結果

勇者 は スライム に 3 のダメージを与えた!
ミス! スライム は 勇者 にダメージを与えられない!

*/

その他の便利なクラス。

  • String
    • 参照型でjava.langパッケージに所属
    • 利用頻度が高いのでnewせずに使える
  • 下記にStringメソッドで主に使われるものをメモ
public String(String original)
  • コンストラクタ。引数に文字列を渡す。
  • 例:String s = new String("abc");
public String(char value[])
  • コンストラクタ。引数に文字(char)の配列を渡す
  • 例:char[] cc = new char[]{'a','b','c'};
    • String s = new String(cc);
public String(byte bytes[],String charsetName)
  • コンストラクタ。引数にバイト(byte)の配列と文字コードを渡す。
  • 例:byte[] bb = ...
    • String s = new String(bb, "EUC-JP");
public char charAt(int index)
  • 指定した位置の文字(char)を返す
  • 例:String s = "abc";
public int length()
  • 文字列の長さを返す
  • 例:String s = "abc";
    • int len = s.length(); //len = 3
public boolean equals(object anobject)
  • 指定したオブジェクトと比較する。指定したオブジェクトがString型で同じ文字列の場合trueを返す
  • 例:String s1 = "abc";
    • String s2 = "abc";
    • boolean b = s1.equals(s2); //b = true
public boolean equalsIgnoreCase(String anotherString)
  • equalsと同様文字列を比較して一致する場合trueを返す。大文字小文字の違いは無視する
  • 例:String s1 = "abc";
    • String s2 = "ABC";
    • boolean b = s1.equalsIgnoreCase(s2); //b = true
public int indexOf(String str)
  • 引数で指定した文字列の文字列中の位置を返す。含まれない場合は-1を返す
  • 例:String s = "abc";
    • int pos = s.indexOf("bc"); //pos = 1
public int lastIndexOf(String str)
  • indexOfと同様に位置を返すが、指定した部分文字列に複数回一位する場合、indexOfは最初に現れた位置を返すが、lastIndexOfは最後に現れた位置を返す
  • 例:String s="abcabc";
    • int pos = s.lastindexOf("bc"); //pos = 4
public boolean startsWith(String prefix)
  • 指定した文字で始まる場合はtrueを返す
  • 例:String s = "abc";
    • boolean b = s.startsWith("bc") //b = true
public boolean endsWith(String suffix)
  • 指定した文字で終わる場合はtrueを返す
  • 例:String s = "abc";
    • boolean b = s.endsWith("bc"); //b = true
public String substring(int beginIndex)
  • 引数で指定した位置以降の部分文字列を返す
  • 例:String s = "abcde";
    • String s2 = s.substring(1); //s2 = "bcde"
public String substring(int beginIndex, int endIndex)
  • 引数で指定した開始位置(beginIndex)から終了位置(endIndex)の前までの部分文字列を返す
  • 例:String s = "abcde"
    • String s2 = s.substring(1,3); //s2 = "bc"
public String trim()
  • 左右の空白を切り取った文字列を返す
  • 例:String s = " abc " //前後に空白
    • String s2 = s.trim(); //s2="abc"
public String replace(char oldChar, char newChar)
  • 文字列中の指定した文字を置換文字で置き換える
  • 例:String s = "abc";
    • String s2 = s.replace('a','z'); //s2 = "zbc"
public String replaceAll(String regex, String replacement)
  • 文字列中の指定した文字列(regex)を置換文字列(replacement)で置き換える。引数のregexには正規表現パターンが指定できる
  • 例:String s = "abcabc";
    • String s2 = s.replacementAll("ab","z"); //s2 = "zczc"
public String replacementFirst(String regex, String replacement)
  • 文字列中の指定した文字列(regex)を置換文字列(replacement)で置き換える。replaceAllと異なり、置き換えるのは一致した部分のみ
  • 例:String s = "abcabc";
    • String s2 = s.replaceFirst("ab","z"); //s2 = "zcabc"
public String[] split(String regex)
  • 文字列を指定した区切り文字で分割して文字列の配列にして返す。引数のregexには正規表現パターンが指定できる
  • 例:String s = "ab,cd,ef ";
    • String[] ss = s.split("."); //ss[0] = ab,ss[1] = cd,ss[2] = ef
public Static String format(String format, Object ... args)
  • 文字列を書式でフォーマットして返す。書式指定はprintfメソッドと同じものが利用できる
  • 例:String s = String.format("[%05d]",12); //s = [00012]

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

例:基本データと参照型データの違いを検証。

public class sampleEquals {

    public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = new String("abc");
        if (str1 == str2) {
            System.out.println("一致しました");
        } else {
            System.out.println("しませんでした!");
        
        }
    }
}

/* 実行結果

しませんでした!

*/

文字列は同じなのにも関わらず結果はfalse。そうなった理由として考えられるのは==。箱の中にあるインスタンスへのポインタが同じである場合はtrueを返すが、それにも関わらずfalseとなったのはインスタンスが異なるから。

  • 宣言した時に変数の箱の中にはアドレスしか入っていない
    • その先のアドレスに値が入っている
    • 別々に new でインスタンス化しているので異なったインスタンスである
      • メモリ上の場所が違う
  • このような場合に文字列同士を比較するのがequalsメソッド(下記例)
public class sampleEquals {

    public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = new String("abc");
        if (str1.equals(str2)) {
            System.out.println("一致しました");
        } else {
            System.out.println("しませんでした!");

        }
    }
}

/* 実行結果

一致しました

*/
  • newを使わない代入(String str1 = "abc"; および String str2 = "abc";)であればtrueになる
    • javaが自動でインスタンスを作成してくれるから
    • 同じ文字列の場合に限り同じメモリ領域に
      • メモリの節約が目的

気になった点。

覚えるのが精一杯で疑問とか湧いてこなかった。これは危ない兆候かもしれない。

今日の反省と明日に向けて。

カリキュラムを何度も見返して、サンプルコードを実際に書くことで何となくだけど理屈は分かってきた気がする。しかし、自分で一からコードを書けと言われれば無理だろうから、本当に理解したとは言えないのが実情。落ち着いてきたらJavaの本を見比べてみるのも理解を深めるのにはいいかもしれない。

閉じる