目次
- 継承とは?
- 継承にはどんなメリットがある?
- 継承の特徴
- 継承を使用する際の注意事項
- superとthisの違いは?
- メソッドのオーバーライド(上書き)
- finalキーワード
- ポリモーフィズム(多態性)
- 抽象(abstract)
- 内部クラス
- 異なる修飾子が修飾する内容(内部クラスとは無関係)
- 4種類のアクセス修飾子
継承とは?
複数のクラスに同じ属性や振る舞いが存在する場合、それらの内容を単独のクラスに抽出すれば、複数のクラスはこれらの属性や振る舞いを再定義する必要はなく、そのクラスを継承するだけでよい。
複数のクラスを子クラスと呼ぶ。また、この単独のクラスを親クラス、スーパークラスまたは基底クラスと呼ぶ。。
子クラスは親クラスの非privateの属性と振る舞いに直接アクセスできる。
キーワードを通じてextendsクラスとクラスの間に継承関係を生じさせる。
class SubDemo extends Demo{} //SubDemo是子类,Demo是父类
継承にはどんなメリットがある?
- 高めるコードの再利用性。
- クラス間に関係を生じさせ、ポリモーフィズムの前提。
継承の特徴
1.Javaは単一継承のみをサポートし、多重継承はサポートしない。
//一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
2.Javaは多層(多重)継承(継承体系)をサポートする。
class A{}
class B extends A{}
class C extends B{}
継承を使用する際の注意事項
- クラス間に「is a」の関係が存在するなら、継承の使用を検討できる。
- 一部の機能を継承するためだけに継承を使ってはならない。
superとthisの違いは?
supersuperはキーワードであり、親クラスのストレージ空間の識別子を表す。(親の参照と理解してよい)
superとthisの使い方は似ている。
thisはオブジェクトの参照を表す(誰が呼び出したかで、その誰かを表す);
superは現在の子クラスのに対して親クラスの参照を表す。
使用シーン
- 子クラスと親クラスに同名のメンバーがある場合、superを使って区別できる;
- 子クラスが親クラスのコンストラクタを呼び出す場合、super文を使用できる。
違い
1.メンバー変数
this.变量 -- 本类的 super.变量 -- 父类的
2.コンストラクタ
this(...) -- 本类的 super(...) -- 父类的
3.メンバーメソッド
this.方法名() -- 本类的 super.方法名() -- 父类的
super();とthis();はどちらもコンストラクタの最初の行に置く。両方を同時に記述することはできない。
メソッドのオーバーライド(上書き)
子クラスに親クラスとまったく同じメソッドが現れた場合(アクセス修飾子を除き、アクセス修飾子はprivateを含まず親クラス以上、戻り値の型、メソッド名、引数リストが同じ)、オーバーライド操作が発生し、再定義または上書きとも呼ばれる。
親クラスのprivateメソッドは子クラスから見えない。したがって、親クラスのprivateメソッドのオーバーライドはそもそも話にならない。
オーバーライドの注意事項:
- オーバーライドする場合、子クラスのメソッドのアクセス権限は必ず親クラスのメソッドのアクセス権限以上でなければならない;
- 静的メソッドは静的メソッドのみをオーバーライドできる。
オーバーライドの使用シーン:
子クラスが親クラスの機能を必要とし、その機能の本体に子クラス独自の内容がある場合、親クラスのメソッドをオーバーライドできる。これにより、親クラスの機能を受け継ぎつつ、子クラス特有の内容を定義できる。
メソッドのオーバーライドとオーバーロードの違いは何か??
メソッドのオーバーライドは、子クラスのメソッドが親クラスのメソッドと完全に同じ場合に使われる。アクセス修飾子を除き、戻り値の型、メソッド名、引数リストはすべて同じである。
オーバーロードは、同じクラス内で各メソッドのメソッド名が同じで、引数リストが異なる(戻り値の型とは無関係)場合に使われる。
子クラスと親クラスにおけるコンストラクタの使い方:
- 子クラスの初期化処理では、まず親クラスの初期化アクションが実行される。子クラスのコンストラクタにはデフォルトでsuper()が含まれているためである。子クラスが親クラスのメンバー変数を使用するには、この初期化が子クラスの初期化より先に完了していなければならない。したがって、子クラスの初期化処理では、親クラスの初期化が先に実行される。
- 親クラスに引数なしコンストラクタがない場合
- superを使って親クラスの引数付きコンストラクタを呼び出す。推奨される方法である。
- thisを使って自クラスの他のコンストラクタを呼び出す。
静的コードブロック、コンストラクタコードブロック、コンストラクタの実行順序:
親クラス静的コードブロック→子クラス静的コードブロック→親クラスコンストラクタコードブロック→親クラスコンストラクタ→子クラスコンストラクタコードブロック→子クラスコンストラクタ
finalキーワード
finalはキーワードであり、クラス、メンバー変数、メンバーメソッドを修飾するために使用できる。
特徴:
- finalが修飾するクラスは継承できない。
- finalが修飾するメンバー変数は定数である。
- finalが修飾するメンバーメソッドは子クラスでオーバーライドできない。
finalで修飾された定数の定義には一般的に命名規則があり、finalで修飾された定数名はすべての文字を大文字にする。
finalがメンバー変数を修飾する場合、初期化が必要である。初期化には2つの方法がある。
- 明示的初期化;
- コンストラクタによる初期化。
ただし、2つを同時に初期化することはできない。
finalとprivateの違い:
- finalが修飾するクラスはアクセスできる;
privateは外部クラスを修飾できないが、内部クラスは修飾できる(実際、外部クラスをprivateにすることは意味がない)。 - finalが修飾するメソッドは子クラスでオーバーライドできない;
privateが修飾するメソッドは、表面的には子クラスでオーバーライドできるように見えるが、実際にはできない。子クラスは親クラスのprivateメソッドを見ることはできないからである。 - finalが修飾する変数は、明示的初期化またはコンストラクタによる初期化のときに一度だけ値を代入でき、以後は変更できない;
privateが修飾する変数も、子クラスや同じパッケージ内の他のクラスから直接アクセスまたは変更することはできない。ただし、setメソッドとgetメソッドを使って値を変更したり取得したりすることはできる。
ポリモーフィズム(多態性)
概念:
オブジェクトが異なる時点で示す異なる状態。
ポリモーフィズムの前提条件:
- 継承または実装の関係が必要である。
- メソッドのオーバーライドが必要である。
- 親クラスの参照が子クラスのオブジェクトを指す必要がある。
プログラムでの現れ方:
親クラスまたはインターフェースの参照が、自分自身の子クラスのオブジェクトを指す、または受け取る。。
メリットと役割:
ポリモーフィズムの存在はプログラムの拡張性と後期の保守性を高める。。
欠点:
親クラスを使って呼び出す場合、親クラス内のメソッドしか呼び出せず、子クラス特有のメソッドは呼び出せない。将来どのような子クラスが自分を継承するか分からないからである。
ポリモーフィズムにおけるメンバーの特徴:
- メンバー変数:コンパイル時:参照型変数が属するクラスに、呼び出した変数があるかを確認する;
実行時:同じく参照型変数が属するクラスに、呼び出した変数があるかを確認する。
メンバー変数は、コンパイル時も実行時も参照型変数が属するクラスを見る。簡潔に覚えると、メンバー変数は、コンパイル時も実行時も等号の左側を見る。。 - メンバーメソッド:コンパイル時:参照変数が属するクラスに、呼び出したメンバーがあるかを確認する必要がある;
実行時:オブジェクトが属するクラスに、呼び出したメンバーがあるかを確認する必要がある。親子で同名のメソッドがある場合、子クラスのメソッドが実行される。メソッドにはオーバーライドの特性があるからである。
コンパイル時は左側を見て、実行時は右側を見る。。 - 静的メソッド:コンパイル時:参照型変数が属するクラスに、呼び出した変数があるかを確認する;
実行時:同じく参照型変数が属するクラスに、呼び出した変数があるかを確認する。
コンパイル時も実行時も等号の左側を見る。。
親クラスのオブジェクトを子クラスの型に変換することは絶対にできない!
親クラスの参照が子クラスのオブジェクトを指す場合、その参照はアップキャストすることも、強制型変換(キャスト)することもできる。。
ポリモーフィズムは、最初から最後まで子クラスのオブジェクトが変化しているのである!
//多态向下转型和向上转型的例子,多态转型解决了多态中父类引用不能使用子类特有成员的弊端。
class PolymorphicTest2 {
public static void main(String[] args) {
Phone p1 = new Nokia(); //向上转型,类型提升
Nokia no = (Nokia)p1; //向下转型,强制将父类的引用转换成子类类型,不能将Nokia类型转成Moto或Nexus类型
no.print(); //输出结果为Phone---null---0,因为继承了父类的方法
Phone p2 = new Moto();
Moto m = (Moto)p2;
m.print(); //输出结果为Moto---yellow---1599,方法重写,子类方法覆盖父类方法
Phone p3 = new Nexus();
Nexus ne = (Nexus)p3;
ne.print();
}
}
class Phone{
String color;
int price;
public void print(){
System.out.println("Phone---" + color + "---" + price );
}
}
class Nokia extends Phone{
String color = "red";
int price = 1009;
//public void print(){
// System.out.println("Nokia---" + color + "---" + price);
//}
}
class Moto extends Phone{
String color = "yellow";
int price = 1599;
public void print(){
System.out.println("Moto---" + color + "---" + price);
}
}
class Nexus extends Phone{
String color = "black";
int price = 1999;
public void print(){
System.out.println("Nexus---" + color + "---" + price);
}
}
}
抽象(abstract)
抽象とは、複数の事物から共通性のある本質的な内容を抽出することである。
抽象クラス:
Javaではメソッド本体のないメソッドを定義できる。そのメソッドの具体的な実装はサブクラス未完成、そのメソッドを抽象メソッドと呼び、抽象メソッドを含むクラスが抽象クラスである。
由来:
複数のオブジェクトがすべて同じ機能を持つが、機能の具体的な内容が異なる場合、抽出の過程で機能定義だけを抽出し、機能本体を抽出しない。すると、機能の宣言のみで機能本体を持たないメソッドを抽象メソッドと呼ぶ。
抽象クラスの特徴:
- 抽象メソッドは必ず抽象クラス内にある。
- 抽象メソッドと抽象クラスはどちらもabstractキーワードで修飾されなければならない。
- 抽象クラスはnewでオブジェクトを生成できない。抽象メソッドを呼び出しても意味がないからである。
- 抽象クラス内の抽象メソッドを使用するには、サブクラスがそのすべての抽象メソッドをオーバーライドした後、サブクラスのオブジェクトを生成して呼び出す必要がある。サブクラスが一部の抽象メソッドだけをオーバーライドした場合、そのサブクラスはまだ抽象クラスである。
- 抽象クラスには抽象メソッドも非抽象メソッドも持つことができる。抽象メソッドはサブクラスのインスタンス化に使用される。
- あるクラスが抽象クラスの場合、それを継承するサブクラスは、抽象クラスであるか、すべての抽象メソッドをオーバーライドするかのどちらかである。
特殊なケース:抽象クラスには抽象メソッドを定義しなくてもよい。これは単にそのクラスのオブジェクトを生成させないためである。
抽象クラスのメンバーの特徴:
- メンバー変数:変数でも定数でもよい。
- コンストラクタ:コンストラクタを持つ。
- メンバーメソッド:抽象メソッドでも非抽象メソッドでもよい。
abstract class 葵花宝典 {
public abstract void 自宫();
}
class 岳不群 extends 葵花宝典 {
public void 自宫(){
System.out.println("剪刀");
}
}
class 林平之 extends 葵花宝典{
public void 自宫(){
System.out.println("指甲刀");
}
}
class AbstractTest {
public static void main(String[] args) {
岳不群 岳 = new 岳不群();
岳.自宫();
林平之 林 = new 林平之();
林.自宫();
}
}
抽象クラスの注意事項:
抽象クラスはインスタンス化できないのに、なぜコンストラクタがあるのか?
classで定義されたクラスには必ずコンストラクタがある。抽象クラス内のコンストラクタはサブクラスのインスタンス化のためにある。
クラスに抽象メソッドがないのに、なぜ抽象クラスとして定義するのか?
継承されたくない、かつインスタンス化もされたくないから。
抽象キーワードabstractはどのキーワードと共存できないか?
- final:メソッドが抽象化されるとオーバーライドが必要になるが、finalはオーバーライドできないため、矛盾する。
- private:メソッドがprivateになると、サブクラスは直接アクセスできない。どうやってオーバーライドするのか?
- static:オブジェクトを必要とせず、クラス名だけで抽象メソッドを呼び出せる。しかし抽象メソッドを呼び出しても意味がない。
インターフェース(interface)
インターフェースである抽象メソッドと定数値の集合。本質的に、インターフェースは特殊な抽象クラスであり、この抽象クラスは定数とメソッドの定義のみを含み、変数やメソッドの実装を持たない。
形式:interface インターフェース名{}
インターフェースの登場により、「多重継承」が別の形態で表現されるようになった。すなわち「多重実装」である。
実装(implements)
形式:class クラス名 implements インターフェース名 {}
特徴:
- インターフェースはインスタンス化できない。
- クラスがインターフェースを実装する場合、抽象クラスであるか、インターフェース内のすべてのメソッドを実装するかのどちらかである。
インターフェースのメンバーの特徴:
インターフェース内のメンバーの修飾子は固定されている!
- メンバー定数:public static final。インターフェース内で定義される変数はグローバル定数であり、修飾子はこの3つのキーワードのみで、すべて省略できる。定数名は大文字にする。
- メンバーメソッド:public abstract。インターフェース内で定義されるメソッドはすべて抽象であり、2つの修飾子キーワードは省略できる。
- 推奨:常に手動で修飾子を指定すること。
継承と実装の違い:
- クラスとクラスの関係は継承関係と呼ばれる。そのクラスが抽象クラスであっても非抽象クラスであっても、内部に非抽象メソッドを定義でき、このメソッドはサブクラスが直接使用でき、サブクラスが継承すればよい。単一継承のみ可能で、多層継承は可能である。((class))
- クラスとインターフェースの関係は実装関係である。インターフェース内のメソッドはすべて抽象なので、サブクラスが実装して初めてインスタンス化できる。単一実装も可能、多重実装も可能。また、1つのクラスを継承すると同時に複数のインターフェースを実装することもできる。((class) extends (class) implements (interface1,interface2…))
- インターフェースとインターフェースの関係は継承関係である。あるインターフェースは別のインターフェースを継承し、新しい属性と抽象メソッドを追加できる。また、インターフェースは多重継承が可能である。((interface) extends (interface1,interface2…))
抽象クラスとインターフェースの違い:
メンバー変数
- 抽象クラスは変数も定数も持てる
- インターフェースは定数のみ持てる
メンバーメソッド
- 抽象クラスは非抽象メソッドも抽象メソッドも持てる
- インターフェースは抽象メソッドのみ持てる
コンストラクタ
-抽象クラスはコンストラクタを持つ
-インターフェースはコンストラクタを持たない
クラスと抽象クラスおよびインターフェースの関係
- クラスと抽象クラスの関係は継承(extends)
- クラスとインターフェースの関係は実装(implements)
インターフェースの思想的な特徴:
- インターフェースは外部に公開されるルールである;
- インターフェースはプログラムの機能拡張;
- インターフェースの登場は結合性を低減する;(モジュール化開発を実現し、ルールを定義しておけば、各自が自分のモジュールを実装でき、開発効率が大幅に向上する)
- インターフェースは多重実装に使用できる;
- 複数の無関係なクラスが同じインターフェースを実装できる;
- 1つのクラスは複数の互いに直接関係のないインターフェースを実装できる;
- 継承関係と同様に、インターフェースと実装クラスの間には多態性が存在する。
//运动员和教练的案例(下图是思路分析)
/*
篮球运动员和教练
乒乓球运动员和教练
现在篮球运动员和教练要出国访问,需要学习英语
请根据你所学的知识,分析出来哪些是类,哪些是抽象类,哪些是接口
*/
interface SpeakEnglish {
public abstract void speak();
}
interface GoAboard{
public abstract void aboard();
}
abstract class Person {
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
//吃饭
public abstract void eat();
//睡觉
public void sleep(){
System.out.println("Zzz...");
}
}
//运动员
abstract class Player extends Person {
public abstract void study();
}
//教练
abstract class Coach extends Person {
public abstract void teach();
}
//篮球运动员
class BasketballPlayer extends Player implements SpeakEnglish,GoAboard{
public void eat(){
System.out.println(getAge() + "岁的" + getName() + "吃鸡腿");
}
public void study(){
System.out.println(getAge() + "岁的" + getName() + "学扣篮");
}
public void speak(){
System.out.println(getAge() + "岁的" + getName() + " Say Hello World");
}
public void aboard(){
System.out.println(getAge() + "岁的" + getName() + " Go Aboard");
}
}
//乒乓运动员
class PingPangPlayer extends Player{
public void eat(){
System.out.println(getAge() + "岁的" + getName() + "吃鸡蛋");
}
public void study(){
System.out.println(getAge() + "岁的" + getName() + "学扣球");
}
}
//篮球教练
class BasketballCoach extends Coach implements SpeakEnglish {
public void eat(){
System.out.println(getAge() + "岁的" + getName() + "啃鸡爪");
}
public void teach(){
System.out.println(getAge() + "岁的" + getName() + "教扣篮");
}
public void speak(){
System.out.println(getAge() + "岁的" + getName() + " Say Hello Java");
}
public void aboard(){
System.out.println(getAge() + "岁的" + getName() + " Go Aboard");
}
}
//乒乓球教练
class PingPangCoach extends Coach{
public void eat(){
System.out.println(getAge() + "岁的" + getName() + "吃鸡蛋皮");
}
public void teach(){
System.out.println(getAge() + "岁的" + getName() + "教扣球");
}
}
class PlayerAndCoach {
public static void main(String[] args) {
//篮球运动员
BasketballPlayer bp = new BasketballPlayer();
bp.setName("郭艾伦");
bp.setAge(33);
bp.eat();
bp.sleep();
bp.study();
bp.speak();
bp.aboard();
System.out.println("***********************");
//篮球教练
BasketballCoach bc = new BasketballCoach();
bc.setName("波波维奇");
bc.setAge(65);
bc.eat();
bc.sleep();
bc.teach();
bc.speak();
bc.aboard();
System.out.println("***********************");
//多态
Person p = new BasketballPlayer();
p.setName("Kobe Bryant");
p.setAge(33);
p.eat();
p.sleep();
//p.study();
//p.speak();
BasketballPlayer bp2 = (BasketballPlayer)p;
bp2.study();
bp2.speak();
bp2.aboard();
System.out.println("***********************");
}
}
内部クラス
あるクラスを別のクラスの内部に定義する。内側のそのクラスを内部クラスと呼ぶ。内部クラスの登場により、Javaの単一継承の制限が再び打破された。
アクセスの特徴:
- 内部クラスは外部クラスのメンバーに直接アクセスできる。privateメンバーも含む。
- 外部クラスが内部クラスのメンバーにアクセスするには、内部クラスのオブジェクトを生成する必要がある。
内部クラスの分類と共通点:
共通点:
- 内部クラスは依然として独立したクラスであり、コンパイル後、内部クラスは独立した.classファイルにコンパイルされる。ただし、先頭に外部クラスのクラス名と$記号が付けられる。
- 内部クラスは通常の方法ではアクセスできない。内部クラスは外部クラスのメンバーであるため、privateかどうかに関わらず、外部クラスのメンバー変数に自由にアクセスできる。
メンバー内部クラス
外部クラスにはメンバー変数とメンバーメソッドがある。メンバー内部クラスは、クラス全体を外部クラスのメンバーとして扱うものである。
メンバー内部クラスはクラス内かつメソッド外に定義されるクラスである;
オブジェクト生成の形式は次の通り:外部クラス名.内部クラス名 オブジェクト名 = 外部クラスオブジェクト.内部クラスオブジェクト;
メンバー内部クラスが外部クラスのメンバーに直接アクセスできるのは、内部クラスが外部クラスのオブジェクトへの参照を保持しているからである:外部クラス名.this;
メンバー内部クラスで使用できる修飾子は、final、abstract、public、private、protected、static である。
静的内部クラス
静的内部クラスは、メンバー内部クラスにstatic修飾子を付けたもので、定義される場所はクラス内かつメソッド外。
外部クラス内で静的内部クラスにアクセスする場面は2つある:
- 外部クラス内で静的内部クラスの非静的メンバーにアクセスする場合:*外部クラス名.内部クラス名 オブジェクト名 = 外部クラス名.内部オブジェクト*、オブジェクトを生成してアクセスする必要がある;
- 外部クラス内で静的内部クラスの静的メンバーにアクセスする場合:上記の形式を使用してもアクセスできるし、直接外部クラス名.内部クラス名.メンバー。
ローカル内部クラス
ローカル内部クラスはメソッド内に定義されるクラスである。
- メソッド内部クラスは、その内部クラスが定義されているメソッド内でのみインスタンス化できる。このメソッドの外でインスタンス化することはできない。
- メソッド内部クラスのオブジェクトは、その内部クラスが存在するメソッドのfinalでないローカル変数を使用できない。
メソッド内部クラスに使用できる修飾子はfinal,abstract;
静的メソッド内のメソッド内部クラスは外部の静的メンバーのみアクセスできる。
匿名内部クラス
匿名内部クラスは内部クラスの簡略化した書き方であり、内容を持つ外部クラスまたはインターフェースのサブクラスの匿名オブジェクトを生成するものである。
前提:
内部クラスは外部クラスまたはインターフェースを継承または実装できる。
形式:
new 外部クラス名またはインターフェース名(){オーバーライドメソッド};
通常、メソッドの仮引数がインターフェースまたは抽象クラスであり、そのインターフェース内のメソッドが3つ以下の場合、匿名内部クラスを引数として渡すことができる。
異なる修飾子が修飾する内容(内部クラスとは無関係)
| 类 | メンバー変数 | メンバーメソッド | コンストラクタ | |
|---|---|---|---|---|
| private | Y | Y | Y | |
| デフォルト | Y | Y | Y | Y |
| protected | Y | Y | Y | |
| public | Y | Y | Y | Y |
| abstract | Y | Y | ||
| static | Y | Y | Y | |
| final | Y | Y | Y |
注意、一般的なルールは以下の通り:
- 今後、すべてのクラスはpublicで修飾する。また、1つのJavaファイルには1つのクラスだけを書く。
- 今後、すべてのメンバー変数はprivateで修飾する。
- 今後、すべてのメンバーメソッドはpublicで修飾する。
抽象クラスまたはインターフェースの場合:public abstract + … - 今後、すべてのコンストラクタはpublicで修飾します。
クラスがユーティリティクラスまたはシングルトンクラスの場合:コンストラクタはprivateで修飾します。
4種類のアクセス修飾子
| 同一クラス | 同パッケージ(無関係なクラスまたはサブクラス) | 異なるパッケージ(サブクラス) | 異なるパッケージ(無関係なクラス) | |
|---|---|---|---|---|
| private | Y | |||
| デフォルト | Y | Y | ||
| protected | Y | Y | Y | |
| public | Y | Y | Y | Y |
推奨:
- メンバー変数 private
- コンストラクタ public
- メンバーメソッド public
その他のチュートリアル
より詳細なチュートリアルは、次を参照してください:Javaチュートリアル
出典:http://www.codeceo.com/article/java-extends.html