この記事では、記事タイトルで挙げた 3 つの問題を解決できる 2 つの知識ポイントを主に学びます。
目次
1. 抽象クラス
ポリモーフィズムの研究では、次のようなコードを書きました。
class Shape {
//属性....
public void draw() {
System.out.println("画图形!");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("♦");
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("●");
}
}
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("▲");
}
}
しかし、親クラスShapeのdraw()メソッドは一度も使用されていないことがわかります。draw ()の実装部分を記述することはできないでしょうか。答えは「はい」です。この問題を解決するために、抽象クラスが戻ってきます。
1.1 抽象クラスの概念
オブジェクト指向の概念では、すべてのオブジェクトはクラスによって記述されますが、逆に、すべてのクラスがオブジェクトの記述に使用されるわけではありません。クラスに特定のオブジェクトを記述するのに十分な情報が含まれていない場合、そのようなクラスは抽象クラスです。例えば:
例証します:
- 長方形、三角形、円はすべてグラフィックスであるため、Shapeクラスのプロパティと継承関係にある必要があります。
- draw メソッドはグラフィックス グラフShapeにも存在しますが、Shapeクラスは特定のグラフィックではないため、実際にはその内部にdrawメソッドを実装する方法はありません。
- Shapeクラスには特定のグラフィックを記述する方法がないため、そのdraw0メソッドを具体的に実装することはできません。そのため、 Shapeクラスは「抽象クラス」として設計できます。
グラフィックスを印刷する例では、親クラスShapeの描画メソッドには実際の作業がないようで、メインのグラフィックスは Shape のさまざまなサブクラスの描画メソッドによって描画されていることがわかりました。このように実際の作業メソッドなしでは、これを抽象メソッド (abstract メソッド) として設計することができ、その抽象メソッドを含むクラスを抽象クラス (abstract class)と呼びます。
1.2 抽象クラスの構文
Javaでは、クラスがabstractによって変更される場合、そのクラスは抽象クラスと呼ばれ、抽象クラス内で abstract によって変更されるメソッドは抽象メソッドと呼ばれます。抽象メソッドは特定の実装本体を与える必要はありません。
上記の形状クラスについては、次の変更を加えることができます。
abstract class Shape {
//属性....
public abstract void draw();
}
したがって、shapeは抽象クラスです。
// 抽象类:被abstract修饰的类
abstract class Shape {
// 抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea() {
return area;
}
protected double area; // 面积
}
注: 抽象クラスはクラスでもあり、通常のメソッドやプロパティ、さらには構築メソッドを含めることができます。
1.3 抽象クラスの機能
1. 抽象クラスはオブジェクトを直接インスタンス化できません
2. 抽象メソッドをプライベートにすることはできません
3. 抽象メソッドはサブクラスによって書き換える必要があるため、 finalおよびstaticによって抽象メソッドを変更することはできません。
4. 抽象クラスは継承する必要があり、サブクラスは継承後に親クラスの抽象メソッドを書き換える必要があります。そうでない場合は、サブクラスも抽象クラスであるため、abstractで変更する必要があります。
5. 抽象クラスには必ずしも抽象メソッドが含まれる必要はありませんが、抽象メソッドを持つクラスは抽象クラスである必要があります。
6. オブジェクトの作成時に親クラスのメンバー変数を初期化するサブクラスの抽象クラス内に構築メソッドを含めることができます。
1.4 抽象クラスの役割
抽象クラス自体はインスタンス化できないため、使用する場合は、抽象クラスのサブクラスを作成し、そのサブクラスで抽象クラスの抽象メソッドをオーバーライドする必要があります。
通常のクラスも継承でき、通常のメソッドもオーバーライドできると思われるかもしれませんが、なぜ抽象クラスや抽象メソッドを使用する必要があるのでしょうか。
確かに、しかし、抽象クラスの使用は、コンパイラ検証の追加レイヤーに相当します。
抽象クラスを使用するシナリオは上記のコードのようなもので、実際の作業は親クラスではなくサブクラスで行う必要があります。このとき、誤って親クラスとして使用しても、通常のクラス コンパイラを使用していればエラーは報告されません。ただし、親クラスが抽象クラスの場合はインスタンス化時にエラーが表示されるため、問題をすぐに発見できます。
多くの文法の意味は「間違いを防ぐ」ことです。たとえば、私たちが使用した Final も同様です。作成された変数はユーザーによって変更されませんが、定数と同等ですか? しかし、final を追加すると、誤って を変更したときに使用できます。コンパイラが時間内に通知してくれるようにします。コンパイラの検証を実際の開発に活用することは非常に有意義です。
2. 内部クラス
完全な構造を記述する必要があるものの別の部分があり、完全な内部構造が外部のものに対するサービスのみを提供する場合、完全な内部構造には内部クラスを使用するのが最善です。Java では、クラスは別のクラスまたはメソッド内に定義できます。前者は内部クラス、後者は外部クラスと呼ばれます。内部クラスもカプセル化の一種です。
【予防】
1.クラスクラス名{}の中括弧の外側に定義したものは、ファイル内であっても内部クラスと呼ぶことはできません。
2. 内部クラスと外部クラスは同じ Java ソース ファイルを共有しますが、コンパイル後、内部クラスは別のバイトコード ファイルを形成します。
2.1 内部クラスの分類
まず、クラス内のどこに内部クラスを定義できるかを見てみましょう。
class OutClass {
// 成员位置定义:未被static修饰 --->实例内部类
public class InnerClass1 {
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2 {
}
public void method() {
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5 {
}
}
}
内部クラス定義の場所に応じて、通常は次の形式に分類できます。
- メンバ内部クラス(通常の内部クラス:staticで変更されていないメンバ内部クラスと静的内部クラス:staticで変更されたメンバ内部クラス)
- ローカル内部クラス (修飾子は言うまでもありません)、匿名内部クラス
注: 実際、内部クラスは日常の開発ではあまり使用されません。一部のライブラリのコードを見ると、さらに多くのことに遭遇する可能性があります。日常の使用では匿名内部クラスが最もよく使用されます。
2.2 インスタンスの内部クラス
つまり、static によって変更されないメンバー内部クラスです。
インスタンスの内部クラス メンバー変数の定義:
インスタンスの内部クラスでは、最終的な変更が追加されない限り、静的変更のメンバー変数を定義できません。
インスタンス内部クラス オブジェクトを作成するときは、まず外部クラス オブジェクトを作成してから、インスタンス内部クラス オブジェクトを作成します。
インスタンスの内部クラスでは、外部クラスに直接アクセスできます。メンバーは任意のアクセス修飾子によって変更されます。
インスタンスの外部クラスと内部クラスに同じ名前のメンバーが存在する場合、内部クラス自身のメンバーが最初にアクセスされます。
外部クラスと同じ名前のメンバーにアクセスする場合は、外部クラス名.this.同じ名前のメンバー名を実行する必要があります。
class OuterClass {
public int data1 = 1;
public static int data2 = 2;
private int data3 = 3;
class InnerClass {
public int data1 = 111;
private int data4 = 4;
public static final int data5 = 5; //data5为常量
protected int data6 = 6;
public void test() {
System.out.println("InnerClass::test()");
System.out.println(data1);//优先访问的是内部类自己的data1
System.out.println(OuterClass.this.data1); //访问外部类的data1
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
}
}
public void test() {
System.out.println("OuterClass::test()");
System.out.println(data1); //访问外部类成员变量
System.out.println(data2);
System.out.println(data3);
//外部类方法访问内部类需要先创建内部类对象来访问
InnerClass innerClass = new InnerClass();
System.out.println(innerClass.data1);
System.out.println(innerClass.data4);
System.out.println(innerClass.data6);
}
}
public class Test3 {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.test();
outerClass.test();
}
}
出力結果:
【予防】
1. 外部クラスの任意のメンバーには、インスタンスの内部クラス メソッドで直接アクセスできます。
2. インスタンスの内部クラスの位置は、外部クラスのメンバーの位置と同じであるため、public や private などのアクセス修飾子によっても制限されます。
3.インスタンスの内部クラスのメソッドで同名のメンバにアクセスする場合は、自分のメンバにアクセスしてから、外部クラスの同名のメンバにアクセスする場合は、外部クラス名.this にアクセスする必要があります。同じ名前の.member。
4. インスタンスの内部クラスオブジェクトは、まず外部クラスオブジェクトを前提として作成する必要があります。
5. インスタンスの内部クラスの非静的メソッドには、外部クラスのオブジェクトへの参照が含まれています。
6. 外部クラスでは、インスタンスの内部クラスのメンバーに直接アクセスすることはできませんので、アクセスしたい場合は内部クラスのオブジェクトを作成する必要があります。
2.3 静的内部クラス
staticで変更された内部メンバークラスをstatic 内部クラスと呼びます。
静的内部クラス オブジェクトを作成するには、最初に外部クラス オブジェクトを作成する必要はありません。
静的内部クラスは、外部クラスの非静的メンバー変数に直接アクセスできません。
class OuterClass {
public int data1 = 1;
public static int data2 = 2;
private int data3 = 3;
public static class InnerClass {
public int data4 = 4;
public static int data5 = 5;
private int data6 = 6;
void test() {
System.out.println("InnerClass::test()");
OuterClass outerClass = new OuterClass();
//System.out.println(data1); //编译报错
System.out.println(outerClass.data1);
System.out.println(data2);
//System.out.println(data3); //编译报错
System.out.println(outerClass.data3);
System.out.println(data5);
System.out.println(data6);
}
}
}
【予防】
1. 静的内部クラスでは、外部クラスの静的メンバーのみにアクセスできます。
2. 静的な内部クラス オブジェクトを作成する場合、最初に外部クラス オブジェクトを作成する必要はありません。
2.4 匿名の内部クラス
インターフェイスを勉強したことのある人なら誰でも、インターフェイスではオブジェクトをインスタンス化できないことを知っています。
ただし、インターフェイスを通じて匿名の内部クラス オブジェクトを作成できます。
出力結果:
2.5 ローカル内部クラス
外部クラスのメソッド本体または {} で定義されるこのタイプの内部クラスは、定義された位置でのみ使用でき、一般に使用されることは非常にまれです。構文形式について簡単に説明します。
public class Test3 {
public static void func() {
class AA {
public int a = 1;
}
AA aa = new AA();
System.out.println(aa.a);
}
public static void main(String[] args) {
func();
}
}
【予防】
1. ローカル内部クラスは、定義されたメソッド本体内でのみ使用できます。
2. public、static、その他の修飾子では変更できません
3. コンパイラには、独自の独立したバイトコード ファイルもあり、命名形式は次のとおりです。 外部クラス名 $number 内部クラス名.class
4. ほとんど使用されない