JAVAクラスの継承と多型
含まれている内容は包括的ではありません。前面のいくつかの単純な継承文法はスキップされ、いくつかの理解できない場所が主に記録されます。リファレンスブック:「JavaCore TechnologyVolumeI」
1.多形性を正しく理解する
オブジェクト変数は、ポリモーフィズム(ポリモーフィズム)と呼ばれる実際のさまざまなタイプの現象を示すことができ、動的バインディング(ダイナミックバインディング)と呼ばれる実行時に適切なメソッドを自動的に選択できます。
多形性には3つの要素があります。
- 継承関係が必要です
- 親クラスのメンバーメソッドをオーバーライドするサブクラスが必要です
- サブクラスを参照するには、親データタイプが必要です
多態性の後、親データタイプはサブクラスの一意のプロパティとメソッドを使用できません。
栗の場合、Manager
クラスはクラスemployee
のサブクラスであり、getSalary
メソッドをオーバーライドします。そのためemployee
、staff[0]
呼び出しのタイプはgetSalary
多態性を示しstaff[0]
ますがManager
、クラス固有のメソッドを呼び出すことはできません。
public class helloworld {
public static void main(String[] args) {
employee[] staff = new employee[3];
staff[0] = new Manager("Jack", 5000, 2015, 10, 25, 2000);
staff[1] = new employee("Bob", 3000, 2017, 8, 7);
staff[2] = new employee("Alice", 2000, 2018, 11, 25);
for(employee e:staff){
System.out.println(e.getName() + "'s salary:"+e.getSalary());
}
}
}
**オーバーライド(オーバーライド)**に関しては、サブクラスメソッドの可視性をスーパークラスメソッドの可視性より低くすることはできないことに注意してください。
たとえば、スーパークラスメソッドはパブリックであり、サブクラスメソッドもパブリックとして宣言する必要があります。
2.強制型変換
すべてのオブジェクト変数には型があります。値が変数に格納されると、コンパイラは約束が多すぎるかどうかをチェックします。
サブクラスの参照がスーパークラス変数に割り当てられている場合、コンパイラーはそれを許可します(上記の多形栗など)が、スーパークラスの参照がサブクラス変数に割り当てられている場合、約束が多すぎます。強制的な型変換が必要です。
注意すべき点が2つあります。
- 強制は、継承階層内でのみ実行できます
- キャストは失敗する可能性があるため、キャストを行う前に
instanceof
オペレーターが検出する必要があります
public class helloworld {
public static void main(String[] args) {
employee staff = new employee("Bob", 3000, 2017, 8, 7);;
if(staff instanceof Manager){
// 判断是否能够转换
Manager boss = (Manager) staff; // 如果可以就进行转换
boss.show();
}
}
}
3.最終的なクラスとメソッド
特定のクラスでサブクラスを定義したくない場合があります。拡張を許可しないこの種のクラスは、最終クラスと呼ばれます。クラスを定義するときに使用する必要があるのは、最終修飾子のみです。
同様に、クラス内のメソッドをオーバーライドしたくない場合は、メソッドを定義するときにfinal修飾子を使用することもできますが、これにより多態性が破壊されます(もちろん、多くのプログラマーは多態性は必要ないと感じています)。
4.抽象クラス
一般的に言って、上位クラスはより一般的で抽象的かもしれません。ある観点からは、祖先クラスはより一般的です。人々は一般に、他のクラスを派生させるための基本クラスとしてのみ使用し、目的のクラスを構築するためには使用しません。抽象クラスである特定の例!
たとえば、人のための抽象クラスを作成し、Person
名前などの高レベルの抽象クラスのいくつかの一般的な属性を設定しname
ます。次にgetDescription
、人の説明を取得するためのメソッドを追加しますが、具体的なサブクラスを採用する必要性を考慮します。さまざまな形式の説明では、このメソッドを定義できないため、サブクラスがこのメソッドをオーバーライドするため、abstract
キーワードを使用する必要があります。
public abstract class Person {
private final String name;
public Person(String name){
this.name = name;
}
public abstract void getDescription();
public String getName(){
return this.name;
}
}
また、1つ以上の抽象メソッドを含むクラス自体は、次のように宣言する必要があります。 abstract
次に、抽象クラスを継承してサブクラスを定義できるようになります(すべてのabstract
メソッドをオーバーライドする必要があることに注意してください。そうしないと、サブカテゴリは抽象のままです!)
public class Employee extends Person {
// 员工类
private double salary;
public Employee(String name, double salary){
super(name);
this.salary = salary;
}
public void getDescription(){
// 重写抽象方法
System.out.println(super.getName() + " is an employee with a salary of " + this.salary + "$");
}
}
public class Student extends Person{
// 学生类
private String major;
public Student(String name, String major){
super(name);
this.major = major;
}
public void getDescription(){
// 重写抽象方法
System.out.println(super.getName() + " is a student majoring in " + this.major);
}
}
このようにして、抽象クラスに基づいて2つの具体的なサブクラスを定義しました。
抽象クラスはオブジェクトインスタンスを作成できませんが、抽象クラス変数を定義して、非抽象サブクラスのオブジェクトを参照することはできます。次に例を示します。
public class helloworld {
public static void main(String[] args) {
// 抽象类变量引用非抽象子类对象
Person a = new Student("Alice", "compute science");
a.getDescription();
}
}
5.すべてのクラスのスーパークラス:オブジェクト
オブジェクトクラスは、Javaのすべてのクラスのスーパークラスです。
5.1等しい方法
Objectクラスのequalsメソッドは、オブジェクトが別のオブジェクトと等しいかどうかを検出するために使用され、その実装メソッドは、 2つのオブジェクトの参照が等しいかどうかを判別することです。ただし、多くの場合、状態に基づいてオブジェクトの同等性を検出する必要があります。2つのオブジェクトが同じ状態にある場合、それらは等しいと見なされます。
したがって、Objectから継承したequalsメソッドを書き直す必要があります。しかしその前に、私==
はeuquals
違いを区別する必要があります。
- 基本タイプ(char、boolean、byte、short 、int、long、float、double)の場合、オブジェクトではありません。
equals
定数プールに格納されているメソッドを使用することはできません==
。値から時々決定されます。 - ヒープに格納されている一般オブジェクトの場合、
==
メモリ内のヒープアドレスが等しいかどうかを判断するときに使用し、判断するときにメソッドでequals
定義されたクラスを呼び出して、equals
重みがクラスでないかどうかを判断します。 writeequals
メソッド、それはスーパークラスのequals
すべてのタイプに対してそのスーパークラスメソッドを呼び出します:オブジェクト、そのequals
アプローチは==
、ここにそのソースがあります。(Stringなどの一部の事前定義されたクラスは、優れた状態ベースのequals
メソッドを実現しています)
- 基本的なタイプのパッケージの場合、スタックに格納され、定数プールを格納され、そのサイズは
Integer
例に基づいて決定されます。これは、定数プールの範囲内で[-128,127]、それを超えるストレージです。山の中には、下の栗がはっきりと見えます。
public class helloworld {
public static void main(String[] args) {
Integer a = 127; // 存储在常量池中,使用==比较的是其值
Integer b = 127;
System.out.println(a == b); // 输出true
Integer c = 128; // 存储在堆中,使用==比较的是其堆地址
Integer d = 128;
System.out.println(c == d); // 输出false
// 使用equals方法,已预定义好,是比较其状态值(即其int值)
System.out.println(c.equals(d)); // 输出true
}
}
違いを理解==
したequals
後equals
、主に以下のようなアイデア(たとえば、Employeeクラスがある)のために、クラスにメソッドをオーバーライドさせることができます。
...
public boolean equals(Object otherObject){
// 1.在开始比较之前,首先比较地址值,如果地址值相等就无需后续比较了
if(this == otherObject)
return true;
// 2.判断是否为空,非常重要!否则容易引发异常
if(otherObject == null)
return false;
// 3.如果由子类决定相等性概念,我们就使用getClass来进行判断
if(this.getClass() != otherObject.getClass())
return false;
//3*.如果由超类决定相等性概念,我们就使用instanceof来进行判断
//if(!(otherObject instanceof Employee))
// return false;
// 类型转换
Employee other = (Employee) otherObject;
// 4.进行状态比较
return Objects.equals(other.getName(), this.getName()) && other.salary == this.salary;
}
...
簡単にテストしてみましょう。
public class helloworld {
public static void main(String[] args) {
Employee a = new Employee("Bob", 2000);
Employee b = new Employee("Bob", 2000);
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false
}
}
以上です〜
5.2hashCodeメソッド
ハッシュコードの取得方法
hashCodeメソッドはObjectで定義され、その値はそのストレージアドレスから派生し、通常必要なハッシュ値はそのコンテンツに基づいて派生するため、クラスを作成するときに書き直す必要があります。
hashCodeメソッドは、Stringクラスなどのいくつかの事前定義されたクラスで書き直されました。
int hash = 0;
for(int hash = 0; i < length(); i++)
hash = 31 * hash + charAt(i);
私たちが自分で定義するクラスはどうですか?
カスタムクラスは通常、基本的なデータタイプといくつかの事前定義されたクラスで構成されているため、hashCodeを組み合わせるだけで済みます。
次のコードを直接見るか、例としてEmployeeクラスを取り上げます。
...
public int hashCode(){
// 使用预定义类Double和String的hashCode函数生成新的hashCode
return 7 * Double.hashCode(this.salary) + 9 * Objects.hashCode(this.getName());
}
...
次に、実装したhashCodeを試してください
public class helloworld {
public static void main(String[] args) {
Employee a = new Employee("Bob", 2000);
Employee b = new Employee("Bob", 2000);
System.out.println(a.hashCode()); // 查看a的hashCode
System.out.println(a.hashCode() == b.hashCode()); // true 成功实现基于内容的hashCode
}
}
すごい!
5.3toStringメソッド
Object:にtoString
は非常に重要なメソッドもあります。デフォルトのメソッドは次のとおりです。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
LocalDateクラスを出力する場合、次のコードを簡単に記述できます。
LocalDate now = LocalDate.now();
System.out.println("现在时间是" + now);
文字列Stringのみが単に「+」操作を実行できることはわかっていますが、LocalDateも機能するのはなぜですか?
また、文字列をまとめると、Javaコンパイラは、演算子「+」toString
メソッドによってオブジェクトが自動的に呼び出されるため、非常に便利に使用できます。
これらの事前定義されたクラスについてtoString
は、独自のクラスを書き直す必要があるカスタムクラスの良い方法を書き直しました。ほとんどのtoString
メソッドは、クラスの名前、次に角括弧のペアの場合の形式に従いました。フィールド値。
上記のコードでは、例としてEmployeeクラスを取り上げます。
...
public String toString(){
return this.getClass()+"[name="+this.getName()+",salary="+this.salary+"]";
}
...
やってみて〜
public class helloworld {
public static void main(String[] args) {
Employee a = new Employee("Bob", 2000);
System.out.println(a.toString());
}
}
出力結果:
class com.panfeng.Employee[name=Bob,salary=2000.0]
悪くない〜