はじめに
Javaの学習をはじめたばかりです。
まとめメモの様になってしまっています、ご了承ください🙇♀️
自分なりにまとめたり、学習してきたRailsと少しリンクさせて理解を深めようとしていますが、間違っている可能性があります。
static
オブジェクトを生成していなくても利用できるメンバ変数やメソッドを定義するときに使用
クラス名.変数名[メソッド名]
で使用可能
使い所
- 全てのインスタンスから共通して使う変数やメソッドを作りたいときに使う
- クラスの関連する便利なメソッドをまとめたいとき
例)いくつオブジェクトを生成したかcountしたいとき
class Student {
int counter = 0;
// コンストラクタにインクリメント
Student() {
︙
counter ++;
}
void display() {
System.out.println(counter + "人です");
}
}
これで行けるかと思いきやStudent stu1 = new Student();
とStudent stu2 = new Student();
を作るとそれぞれのcounterが1になるだけでカウントはしてくれない。(それぞれのメモリの中の話なのでstu1でcounterを作成してもstu2には関係ない)
解決するために
class Student {
static int counter = 0;
// コンストラクタにインクリメント
Student() {
︙
counter ++;
}
static void display() {
System.out.println(counter + "人です");
}
}
static
をつけることで、Studentクラスのメモリの中にcounter
とdisplay();
が生成されるのでcounterがどんどん増えていく + オブジェクトを生成していなくてもdisplay();を呼び出せる!
カプセル化
中身を隠蔽したままオブジェクトを利用すること
他のプログラムから直接変更できないようにする
Railsのコントローラーに置いてるprivateメソッドのようなものでしょうか?
「カプセル化しましょう」みたいな考え方なだけ?
"フィールド"のアクセス修飾子をprivate
にするとカプセル化
※後述
getter, setter
getterメソッド:フィールドの値を取得する
setterメソッド:カプセル化されたメソッドや値を変更する
public 戻り値の型(フィールドの型と同じ) getフィールド名() {
return this.フィールド名
}
public void setフィールド名(フィールドの型 引数) {
this.フィールド名 = フィールドの型の引数;
}
getName();
とかよく使っていましたがgetterメソッドだったのですね
アクセス修飾子
- public:外部からの参照が可能
- private:外部から隠蔽
中身の参照にはgetterメソッド、中身の変更にはsetterメソッドが必要
変数そのものをprivate
にすると見ることも変更することもできなくなるのですね
Railsではそういう感覚はなかったのでむずいです
結論
変数宣言のアクセス修飾子がprivate
だった場合はget〇〇();やset〇〇();を使わないとコンパイルエラーになる
継承
すでに作成してあるクラス(が持っているメンバ)を引き継いだクラスを作成すること
→自分が持っているかのように使用できてしまう!
継承元:スーパークラス
新しく作成したクラス:サブクラス
基本構文
修飾子 class クラス名 extends スーパークラス名 {
// サブクラスの処理
}
途中まではクラス作成時にすでに記述してあるのでextends スーパークラス名
を足すだけ
継承の関係
同じ特徴(情報や行動、振る舞い)を持っている場合に継承を使う
例1)
「動物クラス」
年齢 / 動く・声を出す
「犬クラス」 「人間クラス」
住所 / 話す
「犬の種類インスタンス」 「学生」 「会社員」
部署 / 働く
「会社員インスタンス」
下に行くほど、どんどん具体化していく際に使用
例2)packageはextend.human
Humanクラスを作成:大元となる『人類』を量産できるクラス
public class Human { }
Studentクラスを作成:『人類』から出来てる生徒を量産できるクラス
public class Student extends Human { }
Employeeクラスを作成:『人類』から出来てる会社員を量産できるクラス
public class Employee extends Human { }
Extend01クラス:上記のクラスに命令を出して望みのモノを作り出す台本(実行ファイル)
ここでは上記のファイルをimportする必要がある
package extend;
import extend.human.Human;
import extend.human.Student;
import extend.human.Employee;
↑自動変換で、ファイル名のところが*
で出てくるのでそれだと一行で済むと思いますが、とりあえず明示的に全て書いてます
継承できないこと
- 複数のクラス
- final修飾子が指定されたクラス
// 継承できないため他のクラスでextendsするとコンパイルエラー
final public class Human { }
-
コンストラクタ
コンストラクタはクラス名と合わせるというルールが有るためNG
呼び出して使用する事で解消!※superで後述
メリット
「あるクラスに処理を追加したいけど全てにその処理を施したくない」というときに便利
ソースコードの量を減らすことが出来る
!!!ここ変える!!!アップキャストの説明
アップキャスト、ダウンキャスト
アップキャスト:サブクラスのインスタンスをスーパークラスの変数に代入すること
ダウンキャスト:スーパークラスからサブクラスに代入すること
Student stu1 = new Student();
// アップキャスト 何も指定せずともok
Person psn = stu1;
// ダウンキャスト (Student)←キャストを明示してあげる必要がある
Student stu2 = (Student) psn;
Studentサブクラスの中にPersonスーパークラスが存在するイメージ
→つまりサブクラスはスーパークラスの情報を持っている、スーパークラスはサブクラスの情報を持っていない可能性がある
サブクラスのインスタンスをスーパークラスのインスタンスに代入はできる(情報:メモリ を予め持っているので問題ない)
スーパークラスのインスタンスをサブクラスのインスタンスに代入する時は、スーパークラスがサブクラスの情報:メモリ を持っているとは限らないので「本当に変換して良いのか?」とコンパイラーがエラーを出してくる
→明示的に「メモリはあるので大丈夫だよ」と言ってあげるためにキャストの記述が必要になる
super. ~ ;
親インスタンスを指す
super.フィールド名;
:親インスタンスのフィールドを参照
super.メソッド名();
:親インスタンスのメソッドを参照
super();
サブクラスからスーパークラスのコンストラクタを呼び出す際に使用
サブクラスのコンストラクタは最初にスーパークラスのコンストラクタを呼び出さなくてはいけない
→つまりほぼ必須?
使用時は先頭に書くルールがある
ただし引数なしのデフォルトコンストラクタの場合はsuper();がなくてもok
引数ありの場合は明示的にsuper(引数);が必要
class SubClass extends SuperClass {
// サブクラスのコンストラクタ
SubClass() {
// 最初にスーパークラスのコンストラクタを呼び出す
super(引数);
// この後にサブクラスの初期処理を書く
}
}
オーバーライド
サブクラスでスーパークラスのメソッドを再定義(上書き)すること
例えばサブクラスで新たに宣言した「学籍番号」も表示したいというときに使う
サブクラスでインスタンスを生成するとオーバーライドしたメソッドを優先して使ってくれる
よく使いそうな雰囲気
条件
- 戻り値の型
- メソッド名
- 引数の型と数
これらが全て同じであればオーバーライドが可能
// public以外がサブクラスでのオーバーライド時にも同じならオッケー
public void display() { }
オーバーライドするときに前述のsuper.メソッド名();
を使用することで繰り返し書かなくても良い処理ができる
例)
// Personスーパークラス フィールドにnameが宣言されていると仮定
public void display() {
System.out.println(name);
}
// Studentサブクラス フィールドにstuNoを宣言
public display() {
// わざわざnameを表示させる一文を書かなくて済む
super.display();
System.out.println(stuNo);
}
Student stu = new Student();
stu.display();
//=> オーバーライドされたメソッド -> 名前と出席番号両方が表示される
継承の種類
-
機能追加のための継承
スーパークラスからサブクラスを作っていく(上から下)
例)PersonクラスからTeacherクラス(教科の追加)とStudentクラス(学籍番号の追加) -
共通点をまとめるための継承
サブクラスを元にスーパークラスを作っていく(下から上)
サブクラスが持っている処理をまとめようという考え方(Railsのメソッド化みたいなもの?)
例)BasketballクラスとFootballクラスからClubクラスを作成
フィールドにname、メソッドに部活名の表示や練習内容を表示ができるメソッド持っているとする
→殆どが被っていて違うのはコンストラクタ(これはクラス名と同じ必要があるのでダメ)と練習内容くらい(定義:メソッド名 が同じで、中の処理は違う:表示する文字列が違う)
→→定義だけ抜き出して中の処理を書かないことが出来る
→→→これを抽象メソッドと呼ぶ
// 抽象メソッドを定義する時
abstract void メソッド名();
abstract
は抽象的なという意味、中身がないから
このabstract
が一つでも含まれている場合は抽象クラスと呼ぶ
→同じ感じでクラスの修飾子にabstract
をつける
abstract Class Club{ }
abstract
がついたクラスはインスタンス化は出来ない
(抽象メソッドを呼び出しても中身が無いので何も出来ないから)
どう使うか?
抽象クラスを継承してサブクラス内で抽象メソッドをオーバーライドして処理を追加していく
そしてサブクラスでインスタンス化してメソッドを使っていく
メリット
全てに共通している処理をまとめることでコンパクトになる
最大のメリットとしてStudentクラスでClub(スーパー)クラスのメソッドを使うことで、Basketballクラスのオブジェクトを渡せばBasketballクラスの処理・FootballクラスのオブジェクトならFootballクラスの処理を実行してくれる
→大枠が一緒だからサブクラスが増えてもStudentクラスを変更する必要が全くなくなる!
Studentクラス -参照-> Clubクラス -参照-> それぞれオブジェクトに合ったクラスを参照しに行く
ポリモフィズム
上の例を見るとClubクラスが変わらなければサブクラスがいくら増えようが関係なく同じ様に処理を実行できる
StudentクラスのメソッドはClubオブジェクトによって同じ記述内容でも異なる処理を取ることをポリモフィズムと呼ぶ
オブジェクトによって、いろんな動き・状態をすること
メリット
- メソッドが統一されて見やすくなる:オーバーライドが必要なので必然的にメソッドなどの処理が統一されていく
- 利用する側の書き方を統一できる:サブクラスが増えようが関係ない
インターフェース
クラスとともにインターフェースを作ることが出来る
クラス:メンバ変数 と メソッドを持つことが出来る
インターフェース:定数と抽象メソッドを持つことが出来る
基本構文
public interface クラス名 { }
例)
public interface Englishable {
// interfaceのおかげでコンパイル時に自動で public static final がつくイメージ 定数なので大文字
String LANG = "英語";
// public abstractが付くイメージ
void displayEng();
}
インターフェースを引き継ぐ時は継承ではなく実装という
抽象メソッドが入っているのでインスタンス化は出来ないが、インターフェースを実装した新しいクラスでは全てのメソッドを定義することで使うことが出来る
実装するために
基本構文
public class クラス名 implements インターフェース名 { }
implements:実装
例)
public class Student implements Englishable {
String name;
// オーバーライドしてあげる
void displayEng() {
System.out.println("名前:" + name);
}
}
抽象クラスとの違い
抽象クラスはクラスなのでサブクラスが継承できるのは1つだけ
インターフェースは複数実装できる
継承は枝を張るイメージ:継承されたクラスでしかメソッドなどは使用できない
実装は色を塗るイメージ:継承されていなくても実装ができる
例)
Personクラス -> Studentクラス・Teacherクラス
Clubクラス -> Footballクラス・Baseballクラス
留学生が来たのでstudentとbaseballの名前を英語表記にしたい!
継承関係のないクラスで同じメソッドを使用したい場合にインターフェースを使用する
Englishableの色を塗るイメージ
実際使うにはimplements
したクラスでdisplayEng() { 処理 }
を定義する必要がある
最後に
結局長々書いてしまいましたが、ほとんど理解できていないです。
一つ一つちゃんと分けてまた投稿しようかと思います。
閲覧いただきありがとうございました!