この記事は、Java を学んで以来のすべてのノートを記録し、常に更新を続けることを目的としています。頑張れるといいなと思います〜
この記事は、Java を学んで以来のすべてのノートを記録し、常に更新を続けることを目的としています。頑張れるといいなと思います〜
メソッドのオーバーロードとオーバーライド Overload と Override#
メソッドのオーバーロード Overload:クラス内で、複数のメソッドが同じ名前を持ち、しかし引数リストが異なる(個数が異なる、型が異なる、順序が異なることがある)。クラスが異なるデータ型を統一的に処理するための実装方法であり、メソッドを呼び出す際に異なる個数または型の引数を渡して、どのメソッドを呼び出すかを決定します。注意点として、メソッド名が同じであっても、引数リストが異なり、戻り値の型など他の部分は同じでも異なっても構いません。Java 仮想マシンは引数リストによって判断します。したがって、ユニークな引数リストが必要です。
メソッドのオーバーライド Override:子クラスで親クラスに存在するメソッドをオーバーライドし、親クラスのメソッドを拡張し、新しい機能を追加します。オーバーライドは継承関係に基づいています。オーバーライドされたメソッドのメソッドシグネチャは、親クラスの対応するメソッドシグネチャと完全に一致する必要があります(メソッドシグネチャはメソッド名と引数リストを指します)。Override は Java のポリモーフィズムを体現しています。
ポリモーフィズムは、同じ行動が複数の異なる表現形式や形態を持つ能力です。
ポリモーフィズムは、オブジェクトの多様な表現形式の具現化です。
現実の例として、F1 キーを押すという動作があります:
現在 Flash インターフェースにいる場合、表示されるのは AS3 のヘルプドキュメントです;
現在 Word にいる場合、表示されるのは Word のヘルプです;
Windows にいる場合、表示されるのは Windows のヘルプとサポートです。同じイベントが異なるオブジェクトで発生すると、異なる結果を生じます。ポリモーフィズムが存在するための 3 つの必要条件:1. 継承 2. オーバーライド 3. 親クラスの参照が子クラスのオブジェクトを指すこと。特定の型のメソッド呼び出しにおいて、実際に実行されるメソッドは、実行時の実際の型のメソッドに依存します。
オーバーライド時にアクセス修飾子を変更することができます。要件:子クラスのオーバーライドメソッドのアクセス修飾子は、親クラスメソッドのアクセス修飾子以上である必要があります。例えば、親クラスが protected の場合、オーバーライド後に public に変更できます。また、親クラスが default 権限の場合、public 権限または protected 権限に変更することもできます。アクセス権は大きくすることはできますが、小さくすることはできません。
オーバーライド時に異なる戻り値の型に変更することができます。要件:戻り値が参照データ型である場合のみ(戻り値の型が参照データ型であり、int などの基本データ型ではない)、このように戻り値の型を変更できます。また、親クラスメソッドの戻り値の型のサブクラスである必要があります。そうでなければ、変更せずに親クラスの戻り値の型と同じにします。例えば、親クラスの戻り値の型が Object の場合、子クラスの戻り値の型は Object でも任意のクラスでも構いません。小さくすることはできますが、大きくすることはできません。
アクセス修飾子#
アクセス修飾子 | 本クラス | 同パッケージ | 子クラス | その他 |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
引数バインディング#
呼び出し側が引数をインスタンスメソッドに渡すとき、呼び出し時に渡される値は引数の位置に従って一対一でバインディングされます。
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // nの値は15
p.setAge(n); // nの値を渡す
System.out.println(p.getAge()); // 15
n = 20; // nの値を20に変更
System.out.println(p.getAge()); // 15それとも20?
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
コードを実行すると、結果からわかるように、外部のローカル変数 n を変更しても、インスタンス p の age フィールドには影響しません。理由は、setAge () メソッドが受け取った引数は n の値をコピーしたため、p.age とローカル変数 n は互いに影響しません。
結論:基本型引数の渡し方は、呼び出し側の値のコピーです。双方の後続の変更は互いに影響しません。
次に、参照引数を渡す例を見てみましょう:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname); // 配列fullnameを渡す
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // fullname配列の最初の要素を"Bart"に変更
System.out.println(p.getName()); // "Homer Simpson"それとも"Bart Simpson"?
}
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
setName () の引数が現在配列であることに注意してください。最初に fullname 配列を渡し、その後 fullname 配列の内容を変更すると、インスタンス p のフィールド p.name も変更されていることがわかります!
結論:参照型引数の渡し方は、呼び出し側の変数と受け取り側の引数変数が同じオブジェクトを指していることです。どちらか一方がこのオブジェクトを変更すると、もう一方にも影響を与えます(同じオブジェクトを指しているため)。
次の例:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // bob変数を渡す
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bobをAliceに変更
System.out.println(p.getName()); // "Bob"それとも"Alice"?
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
結論に問題はありません:
-
整数、浮動小数点数、文字は基本型です。
-
文字列、配列は参照型です(メモリデータのインデックス)。
-
基本型引数の渡し方は、呼び出し側の値のコピーです。双方の後続の変更は互いに影響しません。
-
参照型引数の渡し方は、呼び出し側の変数と受け取り側の引数変数が同じオブジェクトを指していることです。どちらか一方がこのオブジェクトを変更すると、もう一方にも影響を与えます。
それでは、3 つの例について、
- 整数の引数渡しは理解しました。コピーされて分かれ、自分で管理し、クラスがデータを読み取ると変わりません。
- 文字列配列の引数渡しも理解しました。同じ場所を指していて、配列の 1 つの要素が変更されると、クラスがデータを読み取ると変わります(クラスは常にここを指しています)。
- 文字列も参照引数ですが、なぜクラスがデータを読み取ると変わらないのでしょうか?それは文字列全体をオーバーライドしたからです(新しいメモリを開いて指し直し、文字列変更章を参照)。クラスは依然として以前のメモリブロックを指しているため、クラスがデータを読み取ると変わりません。同じ結論 1 です。もし文字列メモリ内の特定の文字の値を変更するだけであれば、同じ結論 2 です。
簡単にまとめると:クラスは基本型に対してデータそのものをコピーし、新しいメモリを開きます。参照型に対してはアドレスをコピーし、メモリデータそのものが変わると、クラスがデータを読み取るとそれに従います。しかし、文字列の変更は新しいメモリと新しい指し直しを行い、クラスデータに影響を与えなくなります。
(上記の例とまとめは廖雪峰先生のブログおよびコメントからのものです)
コンストラクタ#
Person p = new Person("Xiao Ming", 15);
new の後の person はコンストラクタを指し、コンストラクタを呼び出すには new 演算子を使用する必要があります。
Java では、オブジェクトインスタンスを作成する際、次の順序で初期化が行われます:
-
まずフィールドを初期化します。例えば、int age =10; はフィールドを 10 に初期化することを示し、double salary; はフィールドがデフォルトで 0 に初期化され、String name; は参照型フィールドがデフォルトで null に初期化されます;
-
コンストラクタのコードを実行して初期化します。
コンストラクタのコードは後に実行されます。
コンストラクタにもポリモーフィズムがあり、引数リストに基づいて自動的にマッチングされます;
コンストラクタはコンストラクタを呼び出すことができ、**this.** キーワードを使用します。
native キーワード#
ちょうど問題を見ていたところです:2 つのオブジェクトの hashCode () が同じであれば、equals () も必ず true になりますか?hashCode () の定義を調べてみると、
public native int hashCode();
初めてnativeキーワードを見て、何なのか興味を持ちました。いろいろ調べてみました。
JNI#
まず理解すべきことは、JNI は Java Native Interface(Java ネイティブインターフェース)であり、これにより Java 言語と C または C++ が相互作用できるようになります。ウィキペディアでは次のように説明されています:「アプリケーションが完全に Java プログラミング言語で実装できない場合(例えば、標準 Java クラスライブラリがサポートしていない特定のプラットフォーム機能やプログラムライブラリの場合)、JNI はプログラマーがネイティブメソッドを記述してこの状況を処理できるようにします」。これは、Java アプリケーション内で必要な C++ ライブラリを使用し、Java コードと直接相互作用できることを意味し、呼び出すことができる C++ プログラム内で逆に Java メソッドを呼び出すこと(コールバック関数)もできます。(これは本当にすごいですね…
要約すると、native キーワードで修飾された関数(メソッド)は、このメソッドがネイティブ関数であり、C/C++ 言語で実装され、DLL にコンパイルされて Java から呼び出されることを示します。完了です〜
実装手順を補足します:
ネイティブメソッドは Java プログラムと C プログラムのインターフェースと見なすことができ、その実装手順は次のとおりです:
-
Java で native () メソッドを宣言し、コンパイルします;
-
javah を使用して.h ファイルを生成します;
-
.cpp ファイルを作成し、native エクスポートメソッドを実装します。このとき、2 番目のステップで生成された.h ファイルを含める必要があります(その中には JDK が提供する jni.h ファイルも含まれています);
-
3 番目のステップで生成された.cpp ファイルを動的リンクライブラリファイルにコンパイルします;
-
Java で System.loadLibrary () メソッドを使用して 4 番目のステップで生成された動的リンクライブラリファイルをロードします。この native () メソッドは Java 内でアクセス可能になります。
今後の学習サイト:IBM コミュニティでの Windows における Java ネイティブメソッドの実装なぜポリモーフィズムは型間の結合を解消できると言えるのか?#
言っても無駄かもしれませんが、あなたは大体理解していればいいです。これはコード量が必要です。。。簡単に言うと、ポリモーフィズムがなければ、等号の左側が何であれ、右側もそれでなければなりません。これが結合です。ポリモーフィズムがあれば、左側が親クラス(またはインターフェース)で、右側が子クラス(または実装クラス)であることを知っていれば、私はそのメソッドを呼び出すだけで、実装クラスがどのように実装するかはあなたの問題です。実装クラスを変更したければ、私は一行のコードも変更する必要がなく、これがデカップリングです。