Javaプログラミングの内部クラスを全面的に解読する

6625 ワード

Java内部クラスとそのインスタンス化Javaでは、1つのクラス(またはメソッド、文ブロック)の内部で別のクラスを定義できます.内部クラス(Inner Class)と呼ばれ、ネストクラス(Nested Class)と呼ばれることもあります.
内部クラスと外部カプセル化されたクラスの間には論理的な所属関係があり、一般的には定義されたクラスまたは文ブロック内にのみ使用され、汎用的な意味のない機能論理を実現し、外部で参照する際に完全な名前を指定する必要があります.
内部クラスを使用する主な理由は、内部クラスがプライベートなデータを含む外部クラスのデータにアクセスできることです.内部クラスは、同じパッケージ内の他のクラスを隠すことができます.コールバック関数を定義し、大量のコードを記述したくない場合は、匿名(anonymous)内部クラスを使用すると便利です.クラスのネーミング競合を軽減します.
次の例を見てください.

public class Outer {
  private int size;
  public class Inner {
    private int counter = 10;
    public void doStuff() {
      size++;
    }
  }
  public static void main(String args[]) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner();
    inner.doStuff();
    System.out.println(outer.size);
    System.out.println(inner.counter);
    //     ,             
    System.out.println(counter);
  }
}

このコードは、内部クラスInnerを含む外部クラスOuterを定義します.エラー文を注釈し、コンパイルすると、2つ生成されます.classファイル:Outer.ClassとOuter$Inner.class.すなわち,内部クラスは独立したバイトコードファイルにコンパイルされる.
内部クラスは、仮想マシンとは無関係にコンパイラ現象です.コンパイラは、外部クラス名と内部クラス名を$記号で区切る通常のクラスファイルに内部クラスを翻訳しますが、仮想マシンはこれについて何も知りません.
注意:内部クラスのオブジェクトを生成するには、外部クラスのオブジェクトが必要です.内部クラスは外部クラスのメンバー変数にアクセスする必要があるため、メンバー変数はインスタンス化する必要があります.
内部クラスはJava 1.1の新しい特性であり、一部のプログラマーはこれが称賛に値する進歩だと考えているが、内部クラスの文法は複雑で、良好なコード構造を深刻に破壊し、JavaがC++よりも簡単な設計理念に反している.
内部類が増えたように見えますが、優美で面白くて、必要のない特性で、Javaも多くの言語が苦しめられている壊滅的な道を歩み始めたのではないでしょうか.このチュートリアルでは、この問題について肯定的な答えを与えるつもりはありません.
Java静的内部クラス、匿名内部クラス、メンバー型内部クラス、およびローカル内部クラス内部クラスは静的(static)であってもよく、public、protected、privateアクセス制御子を使用してもよく、外部クラスはpublic、またはデフォルトしか使用できません.メンバー型内部クラス
外部クラス内で直接定義される(メソッド内部またはコードブロック内部ではない)クラスは、privateであっても外部クラスのすべての変数とメソッドを直接使用できるメンバー式内部クラスです.外部クラスが内部クラスのメンバー変数とメソッドにアクセスするには、内部クラスのオブジェクトを介して取得する必要があります.
次のコードを見てください.

public class Outer{
  private int size;
  public class Inner {
    public void dostuff() {
      size++;
    }
  }
  public void testTheInner() {
    Inner in = new Inner();
    in.dostuff();
  }
}

メンバー式内部クラスは、外部クラスの一般的なメンバーと同じです.
メンバー型内部クラスでは、public、protected、private、static、final、abstractなど、さまざまな修飾子を使用できます.
static修飾子がある場合はクラスレベル、そうでない場合はオブジェクトレベルです.クラスレベルは外部クラスから直接アクセスでき、オブジェクトレベルは外部のオブジェクトを生成してからアクセスする必要があります.
非静的内部クラスではstaticメンバーは宣言できません.
内部クラスは相互に呼び出すことができます.たとえば、次のようにします.

class A {
  // B、C        
  class B {}
  class C {}
}

メンバー型内部クラスへのアクセス
内部クラスのオブジェクトは、メンバー変数として依存する外部クラスオブジェクトの参照を記録するため、外部クラスオブジェクトを見つけてメンバーにアクセスできます.このメンバー変数は、static以外の内部クラスに自動的に追加されます.名前は「outClassName.this」に設定されています.
1)内部クラスで定義されている非静的変数とメソッドを使用する場合は、外部クラスのオブジェクトを作成してから、[outObjectName.new]オペレータで内部クラスのオブジェクトを作成し、次に示すように内部クラスのメソッドを呼び出します.

public class Demo{
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
    inner.dostuff();
  }
}
class Outer{
  private int size;
  class Inner{
    public void dostuff() {
      size++;
    }
  }
}

2)static内部クラスは、その外部クラスのstaticメンバーに相当し、そのオブジェクトと外部クラスオブジェクトとの間に依存関係がないため、直接作成できます.例は次のとおりです.

public class Demo{
  public static void main(String[] args) {
    Outer.Inner inner = new Outer.Inner();
    inner.dostuff();
  }
}
class Outer{
  private static int size;
  static class Inner {
    public void dostuff() {
      size++;
      System.out.println("size=" + size);
    }
  }
}

実行結果:

size=1

3)内部クラスは外部クラスのコンポーネントに直接アクセスできるため、内部クラスとその外部クラスに同名の属性またはメソッドが存在する場合、ネーミングの競合も発生します.したがって、多層呼び出しでは、次のように指定します.

public class Outer{
  private int size;
  public class Inner{
    private int size;
    public void dostuff(int size){
      size++; //      size;
      this.size; //      size
      Outer.this.size++; //      size
    }
  }
}

ローカル内部クラス
ローカル内部クラス(Local class)は、コードブロックに定義されたクラスです.これらは、定義されたコードブロックでのみ表示されます.
ローカルクラスにはいくつかの重要な特性があります.
  • は、それらを定義したコードブロックにのみ表示される.
  • は、それらを定義するコードブロック内の任意の局所final変数を使用することができる.
  • 局部類はstaticであってはならず、中にはstaticメンバーを定義することもできない.
  • ローカルクラスはpublic、private、protectedで修飾することはできません.デフォルトしか使用できません.

  • ローカルクラスはabstractであってもよい.
    次のコードを見てください.
    
    public class Outer {
      public static final int TOTAL_NUMBER = 5;
      public int id = 123;
      public void func() {
        final int age = 15;
        String str = "http://www.weixueyuan.net";
        class Inner {
          public void innerTest() {
            System.out.println(TOTAL_NUMBER);
            System.out.println(id);
            // System.out.println(str);   ,         final  
            System.out.println(age);
          }
        }
        new Inner().innerTest();
      }
      public static void main(String[] args) {
        Outer outer = new Outer();
        outer.func();
      }
    }
    

    実行結果:
    
    5
    123
    15

    匿名の内部クラス
    匿名内部クラスは、ローカル内部クラスの特殊な形式であり、変数名がこのクラスのインスタンスを指しておらず、具体的なクラス実装がこの内部クラスに書かれている.
    注:匿名クラスは、親クラスを継承するか、インタフェースを実装する必要があります.
    匿名の内部クラスを使用しないで抽象的な方法を実装します.
    
    abstract class Person {
      public abstract void eat();
    }
    class Child extends Person {
      public void eat() {
        System.out.println("eat something");
      }
    }
    public class Demo {
      public static void main(String[] args) {
        Person p = new Child();
        p.eat();
      }
    }
    

    実行結果:
    
    eat something

    PersonクラスをChildで継承し,Childの一例を実現し,Personクラスの参照に上方転換したことが分かる.しかし,ここでChildクラスが一度しか使用されない場合,それを独立したクラスとして記述するのは面倒ではないか.
    このとき匿名の内部クラスが導入された.匿名の内部クラスを使用して実装:
    
    abstract class Person {
      public abstract void eat();
    }
    public class Demo {
      public static void main(String[] args){
        
        //    Person  
        new Person() {
          public void eat() {
            System.out.println("eat something");
          }
        }.eat();
      }
    }

    匿名クラスはPersonクラスを継承し,括弧で抽象クラスを実現する方法が見られる.
    内部クラスの文法は複雑で、実際の開発でもあまり使われていません.このチュートリアルでは深く説明するつもりはありません.読者の皆さんも内部クラスをJavaの勉強の重点にすべきではありません.