面向过程:側重分ステップ、機能を完成するプロセスを考慮する(自分で一歩ずつ進める)
側重分モジュール(人を探してタスクを分配し、タスクを解決する)
まとめ
オブジェクト指向の利点:拡張性、保守性が高い、柔軟性が高い、プログラムの結合度が低い
オブジェクト指向の欠点:性能が手続き型に比べて相対的に劣る
💪 メソッドの特徴#
- 静的メソッドはクラス名で呼び出し、コンストラクタでインスタンス化されたオブジェクトを呼び出し、メンバーメソッドはオブジェクトを呼び出す
- コンストラクタ
- 宣言:アクセス修飾子 メソッド名(パラメータリスト){メソッド本体}; メソッド名とクラス名は一致する必要がある
- クラスにコンストラクタがない場合、デフォルトで無引数のコンストラクタが存在する。自分で書いた場合はデフォルトのものはなくなる。一般的に自分でコンストラクタを書いた後に無引数のものも書くことが多い
- メンバーメソッドのみがメンバープロパティに直接アクセスでき、静的メソッドはメンバ変数にアクセスできない(メンバ変数が必要な場合はメンバーメソッドとして定義する)
- メンバーメソッドは静的変数にアクセスできる
🎗 クラスとオブジェクトの関係#
- クラス:事物の客観的な記述の基準
- オブジェクト:すべてはオブジェクトであり、クラスの基準に合致する具体的な個体
オブジェクトの使用#
インスタンス化されたオブジェクト:Student s1 = new Student();
get および set メソッドを通じてメンバープロパティを取得および設定する
🔌 JavaBean#
JavaBean
は Java
言語で書かれた再利用可能なコンポーネントであり、クラスである(クラスは具体的で公共であり、無引数のコンストラクタを持つ必要がある)
- プライベートなメンバ変数
- 外部に
get
およびset
メソッドを提供 - 無引数および全引数のコンストラクタを明示的に宣言
get
およびset
メソッドの役割- メンバ変数を保護
- インターセプトを行う
🎉 インスタンス化のメモリプロセス#
- クライアントクラスをロード
main()
メソッドをスタックにプッシュ- サーバークラスをロード
- コンストラクタを呼び出す
- ヒープメモリにスペースを確保し、インスタンス化されたオブジェクトを初期化
- コンストラクタがスタックから出て、インスタンス化されたオブジェクトが変数に代入される
💥 例外#
- コンパイル時例外
- 実行時例外
- NullPointerException(
null
でメンバ変数にアクセスする場合) - 配列のインデックスが範囲外
- 型変換例外
- NullPointerException(
⚠️ よくある間違い#
- メンバーメソッドとコンストラクタを区別する:戻り値があるかないか(コンストラクタは戻り値がない)
- オブジェクトは静的変数を呼び出すことができ、
javac
コンパイル時にクラス名で静的変数を呼び出すように変更される(したがって、NullPointerException も影響しない) - 静的メソッドはメンバ変数を呼び出すことができない
👈 this#
オブジェクト内の最初のメンバ変数で、現在のオブジェクトのメモリアドレスを保存している(静的メソッド内には出現できない)
役割#
- メンバーメソッド / コンストラクタ内で同名のメンバ変数とローカル変数を区別するために使用
- 現在のクラスの有引数コンストラクタをオーバーロードして呼び出す
必ず現在のコンストラクタのメソッド本体の最初の行に - チェーン呼び出し
⚙️ static#
修飾子 静的プロパティを示すために使用される。static
を使用しない場合はメンバプロパティとなる。
機能#
- 静的変数
- 静的メソッド
- 静的コードブロック
プログラムがロードされるときに呼び出される(main()
メソッドの前に実行される) 一度だけ実行される
コードブロック:無名メソッド、自動呼び出し
クラスの静的プロパティにアクセスする際に、そのクラスがメモリにロードされる
インスタンス文ブロックは無名のメンバーメソッドと見なすことができる
📦 カプセル化#
すべての構成要素を組み合わせ、アクセス制御修飾子を使用してデータを隠すことができ、ユーザーがクラスデータを変更する程度を制御できる
適切なカプセル化はコードをより理解しやすくし、保守が容易になり、コードの安全性も向上させる
パッケージのインポート#
package
の下に、class
の上に置く
静的インポート
特定のクラスの特定の静的変数をインポートする
アクセス制御#
範囲 | public | protected | default | private |
---|---|---|---|---|
同クラス | ✔️ | ✔️ | ✔️ | ✔️ |
同パッケージ | ✔️ | ✔️ | ✔️ | ❌ |
サブクラス | ✔️ | ✔️ | ❌ | ❌ |
全体 | ✔️ | ❌ | ❌ | ❌ |
🧬 継承#
既存のクラスから新しいクラスを派生させる。このクラスは通常、親クラスの特徴を含み、特有のプロパティを追加することができる。
📝 オーバーライド#
継承関係が必要
- メソッド名、パラメータリスト、戻り値が同じ
- 元のメソッドよりも低いアクセス権限を持ってはいけない
- 元のメソッドよりも広範な例外を持ってはいけない
オーバーライドとオーバーロードの違い#
アノテーション#
ソースコードアノテーション
コンパイルアノテーション
実行時アノテーション
🛑 final#
以下に配置可能
- クラス
- 変数
- メソッド
🌱 ポリモーフィズム#
親クラスの参照が子クラスのオブジェクトを指す
- 親クラス
- 参照が指す参照データ型
- 指すことができるもの
- 子クラスのオブジェクトを作成する
親クラスで作成された参照型変数は子クラスのオブジェクトを見つけることができる
親クラス 変数名 = new 子クラス();
アップキャスト(子クラスから親クラスへの変換)は自動的な型変換に似ている
非メンバーメソッド
利点#
- 結合度を下げる
- 拡張性、置換性、柔軟性が向上する
欠点#
子クラス特有のプロパティが失われる
メンバーメソッド 子クラスが親クラスのメンバーメソッドをオーバーライドした場合、子クラスのメンバープロパティが呼び出される
非メンバーメソッド 親クラスのメソッドを呼び出す
public class Poly {
public static void main(String[] args) {
Sup sub = new Sub();
// 2
System.out.println(sub.age);
sub.m1();
// Son m2
sub.m2();
// sub.m3();
}
}
class Sup{
int age = 2;
public void m1(){
System.out.println("Father m1");
}
public void m2(){
System.out.println("Father m2");
}
}
class Sub extends Sup{
int age = 1;
public void m2(){
System.out.println("Son m2");
}
public void m3(){
System.out.println("Son m3");
}
}
instanceof#
特定のオブジェクトが特定のクラスからインスタンス化されたかどうかを判断する
ダウンキャスト(親クラスから子クラスへの変換)
- 直接ポリモーフィズム
親クラス 変数名 = new 子クラス();
- 実引数と仮引数のポリモーフィズム
- 戻り値のポリモーフィズム
メソッドの戻り値は親クラスで宣言する
隠蔽ポリモーフィズム#
子クラスのオブジェクトを通じて、親クラスのメンバーメソッドを呼び出す際、この時の文脈環境はポリモーフィズム環境である
public class Poly_05 {
public static void main(String[] args) {
// Sup sup = new Sub();
// // 2 親クラス
// System.out.println(sup.age);
// // 親クラス 子クラスがないため
// sup.m1();
// // 子クラス 覆写されているため
// sup.m2();
// // エラー、親クラスにはないため、ポリモーフィズムは子クラス特有のプロパティを失う
// // sup.m3();
// // ダウンキャスト
// Sub sub = (Sub) sup;
// // 1 子クラス
// System.out.println(sub.age);
// // 親クラス 継承
// sub.m1();
// // 子クラス 子クラスがあるため
// sub.m2();
// // 子クラス、ポリモーフィズムを使用していないため
// sub.m3();
Sub sub = new Sub();
sub.m1();
}
}
class Sup {
int age = 2;
public void m1() {
/**
* this : オブジェクト内の最初のメンバ変数、現在のオブジェクトのメモリアドレスを保存
*
* thisが現在のオブジェクトのメモリアドレスを保存しているので、thisの型は現在のクラスの型、親クラスの型になる
*
* thisがどのクラスに書かれているかによって、そのクラスが現在のクラスとなる。したがって、現在のクラスはSup、親クラスはObject
*
* thisは現在のクラス内のすべてのプロパティを呼び出すことができ、失われていないため、thisはSupの現在のクラスの型である
*
* Sup this;
*
* this : どのオブジェクトがこのメンバーメソッドを呼び出したかによって、thisはそのオブジェクトを指す
*
* 最終的にはSubがm1メソッドを呼び出したため、thisはSubを指す
*
* Sup this = Sub;
*/
// System.out.println("親クラスm1");
System.out.println(this);
// 2 親クラス
System.out.println(this.age);
System.out.println(this.sakjdkashdhqwrjnfaksf);
System.out.println(age);
// 子クラス
m2();
// エラー
// m3();
}
public void m2() {
System.out.println("親クラスm2");
}
}
class Sub extends Sup {
int age = 1;
public void m2() {
System.out.println("子クラスm2");
}
public void m3() {
System.out.println("子クラスm3");
}
}
🐘 抽象#
abstract
修飾子は抽象クラスと抽象メソッドを修飾するために使用される
abstract
で修飾されたクラスは抽象クラスであり、抽象クラスはオブジェクトを作成できず、一般的に継承されるために使用される
abstract
で修飾されたメソッドは抽象メソッドであり、そのメソッドにはメソッド本体がなく、機能を実装せず、異なる子クラスにメソッドを実装させるために使用される
抽象メソッドは抽象クラス内に存在しなければならず、抽象クラス内には通常のメソッドが存在することができる
抽象クラスは特殊なクラスと見なすことができるが、オブジェクトを作成することはできない
abstract
とfinal
は同時に出現できない
- 子クラスが抽象クラスを継承する場合、すべての抽象メソッドを実装する必要がある。そうでない場合、その子クラスは
abstract
で修飾される必要がある - 抽象クラスが抽象クラスを継承する場合、0〜N 個の抽象メソッドを実装する必要がある
- 通常のクラスが抽象クラスを継承する場合、すべてのメソッドを実装する必要がある
👄 インターフェース#
interface
キーワード
1.8 以前はインターフェースは完全に抽象的であり、抽象メソッドと定数(psf
)のみが許可されていた
インターフェースには定数しかなく、変数はなく、psf
は省略可能であり、アクセス制御はデフォルトでpublic
である
抽象メソッドの abstract
は省略可能である
interface A{
int age = 1;
// デフォルトメソッド
default void dmethod(){
System.out.println("デフォルトメソッド");
}
}
class B{
void m(){
System.out.println(A.age);
}
}
🌌 Object#
Object
は Java が提供するルートクラスであり、すべてのクラスは直接または間接的にObject
を継承する
java.lang.Object
はjava.lang
パッケージにあり、コアパッケージ内のすべてのクラスはインポートする必要がない
toString#
equals#
==
基本型は値の大小を比較し、参照型はアドレスを比較する
==
は equal()
のデフォルトの底層実装である
デフォルトの equals()
はアドレスを比較する
finalize#
JVM はクロスプラットフォームであり、マルチスレッドであり、オブジェクト指向であり、自動的にガベージコレクションを行う
ガベージは、オブジェクトを指す参照がない場合、そのオブジェクトはガベージオブジェクトとなる
ガベージが回収されるとき、自動的にそのオブジェクトの finalize()
メソッドが呼び出され、オブジェクトのライフサイクルが終了する際に呼び出される
System.gc()
プログラマーは JVM にガベージ回収を提案することができる
🔗 クラス間の関係#
クラスとクラスの間の関係の詳細は こちら を参照
🧩 内部クラス#
メンバー内部クラス#
class A{
class B{
}
}
メンバ変数と等価であり、静的宣言を持つことはできない
内部クラスはアクセス制御修飾子を使用できる
外部クラスのプライベート(private
で修飾された)プロパティにアクセスできる
メンバー内部クラスは外部クラスのすべてのデータに直接アクセスできる
この時、B
クラスの完全なクラス名は A$B
となる
静的内部クラス#
class A{
private int a = 1;
private static int b = 1;
static class B{
int c = 1;
static int d = 1;
public void method(){
// System.out.print(a);
A aaa = new A();
System.out.print(aaa.a);
System.out.print(b);
}
}
}
- 静的内部クラスは静的変数と見なすことができる
- 静的内部クラスは外部クラスのメンバープロパティに直接アクセスできない
- 静的内部クラスは任意のデータ(静的 / メンバー)を宣言できる
- 外部クラスのオブジェクトを作成した後、外部クラスのメンバープロパティにアクセスできる
ローカル内部クラス#
- メソッド内のクラスはローカル内部クラスである
- ローカル内部クラスはローカル変数と見なすことができる
- ローカル内部クラスが外部メソッド内のローカル変数にアクセスする場合、その変数は
final
修飾子を付ける必要がある。1.8 以降はfinal
を省略できる - ローカル内部クラスはアクセス修飾子を使用できない
- ローカル内部クラスは静的宣言を持つことはできない
class A{
void method(){
class B{
}
}
}
この時、B
クラスの完全なクラス名は A$1B
となる
内部クラス名が重複する場合、外部クラス名 $2 内部クラス名となる
匿名内部クラス#
メソッドの引数としてクラスオブジェクトを渡す必要がある場合、匿名内部クラスを使用できる
構文#
new インターフェースを実装するクラス(){
// 匿名内部クラスの本体部分
}
インターフェースを実装する場合、インターフェースにはコンストラクタがないため、引数は空である
new 親クラスのコンストラクタ(実引数リスト){
// 匿名内部クラスの本体部分
}
親クラスのコンストラクタを呼び出す場合、引数は空でもよく、引数を渡すこともできる
例#
// インターフェース部分
interface Product{
public double getPrice();
public String getName();
}
public class Anonymous{
public void test (Product p){
System.out.println(p.getName()+"--------"+p.getPrice());
}
public static void main(String[] args ){
Anonymous as = new Anonymous();
// ここでインターフェースを実装し、抽象メソッドを実装する
as.test(new Product(){
// メソッドを実装
public double getPrice( ){
return 8888;
}
// メソッドを実装
public String getName(){
return "I can do it ";
}
});
}
}
上記のコードは非常にシンプルで、Anonymous
というクラスを定義し、その中にtest
メソッドを定義している。次に、Anonymous オブジェクトを作成し、そのインスタンスメソッド test () を呼び出す。
ただし、test()
メソッドを呼び出す際には、Product
オブジェクトを渡す必要がある。しかし、Product
はインターフェースであり、オブジェクトを作成できないため、インターフェースを実装する必要がある。したがって、ここでは匿名内部クラスの方法を使用し、インターフェース内のすべての抽象メソッドを実装している。
役割#
インターフェースを実装したり、抽象クラスを継承したりする方法を使用せずに実装することができる。しかし、匿名内部クラスを使用する利点は明らかであり、コードを少なくし、より簡潔にすることができる。