Java 内部类
Java面向对象的设计 - Java 内部类
什么是内部类?
作为包的成员的类被称为顶级类。
一个类可以在另一个类中声明。这种类型的类称为内部类。
如果在另一个类中声明的类被显式或隐式声明为static,它被称为嵌套类,而不是内部类。
包含内部类的类称为封闭类或外部类。
例子
下面的代码声明一个内部类。
class Outer {
  public class Inner {
    // Members of the Inner class go here
  }
  // Other members of the Outer class go here
}
        Outer类是一个顶级类。
Inner类是一个内部类。它是外类的成员。
外层类是Inner类的封闭(外部)类。
内部类可以是另一个内部类的封闭类。内部类的嵌套层次没有限制。
内部类的实例只能存在于其封闭类的实例中。
使用内部类的优点
以下是内部类的一些优点。
- 在将使用它们的其他类附近定义类。
 - 提供了一个额外的命名空间来管理类结构。
 - 一些设计模式使用内部类更容易实现。
 - 实现回调机制使用内部类是优雅和方便的。它有助于在Java中实现闭包。
 
访问局部变量的限制
下面的代码演示了访问局部内部类中的局部变量的规则。
main()方法声明两个局部变量x和y。这两个变量都是最终的。
变量x在被初始化之后从不改变,变量y不能被改变,因为它被声明为final。
public class Main {
  public static void main(String... args) {
    int x = 1;
    final int y = 2;
    class LocalInner {
      void print() {
        System.out.println("x = " + x);
        System.out.println("y = " + y);
      }
    }
    /*
     * Uncomment the following statement will make the variable x no longer
     * an effectively final variable and the LocalIneer class will not compile.
     */
    // x = 100;
    LocalInner li = new LocalInner();
    li.print();
  }
}
        上面的代码生成以下结果。

内部类和继承
内部类可以继承另一个内部类,顶级类或其封闭类。
class A {
  public class B {
  }
  public class C extends B {
  }
  public class D extends A {
  }
}
class E extends A {
  public class F extends B {
  }
}
        内部类中没有静态成员
Java中的关键字static使一个构造成为一个顶层结构。
因此,我们不能为内部类声明任何静态成员(字段,方法或初始化器)。
允许在内部类中有作为编译时常量的静态字段。
class A {
  public class B {
    public final static int DAYS_IN_A_WEEK = 7; // OK
    public final String str = new String("Hello");
  }
}
        生成的内部类的类文件
每个内部类都被编译成一个单独的类文件。
成员内部类和静态内部类的类文件名格式如下:
<outer-class-name>$<member-or-static-inner-class-name>
        局部内部类的类文件名的格式如下:
<outer-class-name>$<a-number><local-inner-class-name>
        匿名类的类文件名的格式如下:
<outer-class-name>$<a-number>
        类文件名中的<a-number>是从1开始顺序生成的数字,以避免任何名称冲突。
静态上下文中的内类
我们可以在静态上下文中定义一个内部类,例如静态方法或静态初始化器。
所有静态字段成员都可以访问这样的内部类。
class Outer {
  static int k = 1;
  int m = 2;
  public static void staticMethod() {
    // Class Inner is defined in a static context
    class Inner {
      int j = k; // OK. Referencing static field k
      // int n = m; // An error. Referencing non-static field m
    }
  }
}
        Java 内部类类型
Java面向对象设计 - Java内部类类型
您可以在类中的任何位置定义内部类,您可以在其中编写Java语句。
有三种类型的内部类。内部类的类型取决于位置和声明的方式。
- 成员内部类
 - 局部内部类
 - 匿名内部类
 
成员内部类
成员内部类在类中声明的方式与声明成员字段或成员方法相同。
它可以声明为public,private,protected或package-level。
成员内部类的实例可以仅存在于其封闭类的实例内。
以下代码创建了一个成员内部类。
class Car {
  private int year;
  // A member inner class named Tire public
  class Tire {
    private double radius;
    public Tire(double radius) {
      this.radius = radius;
    }
    public double getRadius() {
      return radius;
    }
  } // Member inner class declaration ends here
  // A constructor for the Car class
  public Car(int year) {
    this.year = year;
  }
  public int getYear() {
    return year;
  }
}
        局部内部类
一个局部内部类在块中声明。其范围仅限于声明它的块。
由于其范围限于其封闭块,因此其声明不能使用任何访问修饰符,例如public,private或protected。
通常,在方法内定义局部内部类。但是,它也可以在静态初始化器,非静态初始化器和构造器中定义。
下面的代码显示了一个局部内部类的例子。
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
  public static void main(String[] args) {
    StringList tl = new StringList();
    tl.addTitle("A");
    tl.addTitle("B");
    Iterator iterator = tl.titleIterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
class StringList {
  private ArrayList<String> titleList = new ArrayList<>();
  public void addTitle(String title) {
    titleList.add(title);
  }
  public void removeTitle(String title) {
    titleList.remove(title);
  }
  public Iterator<String> titleIterator() {
    // A local inner class - TitleIterator
    class TitleIterator implements Iterator<String> {
      int count = 0;
      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }
      @Override
      public String next() {
        return titleList.get(count++);
      }
    }
    TitleIterator titleIterator = new TitleIterator();
    return titleIterator;
  }
}
        上面的代码生成以下结果。

例子
下面的代码有一个局部内部类继承自另一个公共类。
import java.util.Random;
abstract class IntGenerator {
  public abstract int getValue() ;
}
class LocalGen {
  public IntGenerator getRandomInteger() {
    class RandomIntegerLocal extends IntGenerator {
      @Override
      public int getValue() {
        Random rand = new Random();
        long n1 = rand.nextInt();
        long n2 = rand.nextInt();
        int value = (int) ((n1 + n2) / 2);
        return value;
      }
    }
    return new RandomIntegerLocal();
  } // End of getRandomInteger() method
}
public class Main {
  public static void main(String[] args) {
    LocalGen local = new LocalGen();
    IntGenerator rLocal = local.getRandomInteger();
    System.out.println(rLocal.getValue());
    System.out.println(rLocal.getValue());
  }
}
        上面的代码生成以下结果。

匿名内部类
匿名内部类没有名称。因为它没有名称,它不能有一个构造函数。
匿名类是一次性类。您定义一个匿名类并同时创建它的对象。
创建匿名类及其对象的一般语法如下:
new Interface()  {
// Anonymous  class body  goes  here
}
        和
new Superclass(<argument-list-for-a-superclass-constructor>)  {
// Anonymous  class body  goes  here
}
        new运算符用于创建匿名类的实例。
它后面是现有的接口名称或现有的类名称。
接口名称或类名称不是新创建的匿名类的名称。
如果使用接口名称,则匿名类实现接口。
如果使用类名,则匿名类继承自类。
仅当新运算符后面跟有类名时,才使用<argument-list>。如果新运算符后跟接口名称,则它为空。
如果<argument-list>存在,它包含要调用的现有类的构造函数的实际参数列表。
匿名类主体像往常一样在大括号中。
匿名类主体应该简短,以便更好的可读性。
下面的代码包含一个简单的匿名类,它在标准输出上打印一条消息。
public class Main {
  public static void main(String[] args) {
    new Object() {
      // An instance initializer
      {
        System.out.println("Hello from  an  anonymous class.");
      }
    }; // A semi-colon is necessary to end the statement
  }
}
        上面的代码生成以下结果。

例2
以下代码使用匿名类来创建Iterator。
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
  private ArrayList<String> titleList = new ArrayList<>();
  public void addTitle(String title) {
    titleList.add(title);
  }
  public void removeTitle(String title) {
    titleList.remove(title);
  }
  public Iterator<String> titleIterator() {
    // An anonymous class
    Iterator<String> iterator = new Iterator<String>() {
      int count = 0;
      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }
      @Override
      public String next() {
        return titleList.get(count++);
      }
    }; // Anonymous inner class ends here
    return iterator;
  }
}
        Java 静态对象类
Java面向对象设计 - Java静态内部类
静态成员类不是内部类
在另一个类的主体中定义的成员类可以声明为静态。
例子
以下代码声明了顶级类A和静态成员类B:
class A {
  // Static member class
  public static class B {
    // Body for class B goes here
  }
}
        注意
静态成员类不是内部类。它被认为是一个顶级类。
静态成员类也称为嵌套顶级类。
A类的实例和B类的实例可以独立存在,因为它们都是顶级类。
静态成员类可以声明为public,protected,package-level或private,以限制其在其封闭类之外的可访问性。
使用静态成员类有两个好处:
- 静态成员类可以访问其包含类的静态成员,包括私有静态成员。
 - 一个包通过提供一个命名空间,就像一个顶级类的容器。具有静态成员类的顶级类提供了额外的命名空间层。
 
静态成员类是其封闭顶级类的直接成员,而不是包的成员。
静态成员类的对象的创建方式与使用new运算符创建顶级类的对象的方式相同。要创建一个B类的对象,你写
A.B bReference = new A.B();
        由于类B的简单名称在类A中的范围内,因此我们可以使用其简单名称在类A中创建其对象
B  bReference2 = new B(); // This  statement appears inside class  A  code
        我们还可以通过导入com.java2s.innerclasses.A.B类,使用A类之外的简单名称B.
例2
下面的代码显示了如何使用静态内部类。
public class Main {
  public static void main(String[] args) {
    Car.Tire m = new Car.Tire(17);
    Car.Tire m2 = new Car.Tire(19);
    Car.Keyboard k = new Car.Keyboard(122);
    Car.Keyboard k1 = new Car.Keyboard(142);
    System.out.println(m);
    System.out.println(m2);
    System.out.println(k);
    System.out.println(k1);
  }
}
class Car {
  // Static member class - Monitor
  public static class Tire {
    private int size;
    public Tire(int size) {
      this.size = size;
    }
    public String toString() {
      return "Monitor   - Size:" + this.size + "  inch";
    }
  }
  // Static member class - Keyboard
  public static class Keyboard {
    private int keys;
    public Keyboard(int keys) {
      this.keys = keys;
    }
    public String toString() {
      return "Keyboard  - Keys:" + this.keys;
    }
  }
}
        上面的代码生成以下结果。

Java 内部类对象
Java面向对象设计 - Java内部类对象
局部内部类的对象是使用块中的新运算符创建的,它声明了类。
在声明类的同时创建一个匿名类的对象。
静态成员类是另一种类型的顶级类。
您可以按照创建顶级类的对象的方式创建静态成员类的对象。
成员内部类的实例始终存在于其封闭类的实例中。
语法
创建成员内部类的实例的一般语法如下:
OuterClassReference.new MemberInnerClassConstructor()
        OuterClassReference是包围类的引用,后跟一个后跟新运算符的点。
例子
成员内部类的构造函数调用遵循new运算符。
class Outer  {
    public class  Inner {
    }
}
        要创建内部成员内部类的实例,必须首先创建其封闭类Outer的实例。
Outer  out  = new Outer();
        现在,您需要在out参考变量上使用new运算符来创建Inner类的对象。
out.new Inner();
        为了将内部成员内部类的实例的引用存储在引用变量中,我们可以写下面的语句:
Outer.Inner in = out.new   Inner();
        以下代码显示了如何创建成员内部类的对象
public class Main {
  public static void main(String[] args) {
    Car c = new Car();
    Car.Tire t = c.new Tire(9);
    
  }
}
class Car {
  public class Tire {
    private int size;
    public Tire(int size) {
      this.size = size;
    }
    public String toString() {
      return "Monitor   - Size:" + this.size + "  inch";
    }
  }
}
        Java 内部类成员
Java面向对象设计 - Java内部类成员
内部类可以访问其所有实例成员,实例字段和其封闭类的实例方法。
class Outer {
  private int value = 2014;
  public class Inner {
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here
  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}
public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();
    out.printValue();
    in.printValue();
    out.setValue(2015);
    out.printValue();
    in.printValue();
  }
}
        上面的代码生成以下结果。

例子
以下代码显示如何访问内部类的内部变量。
public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();
    out.printValue();
    in.printValue();
    out.setValue(3);
    out.printValue();
    in.printValue();
  }
}
class Outer {
  private int value = 1;
  public class Inner {
    private int value = 2;
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here
  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}
        上面的代码生成以下结果。

在内部类中使用关键字this
以下代码显示如何在内部类中使用关键字this。
class Outer {
  private int value = 1;
  class QualifiedThis {
    private int value = 2;
    public void printValue() {
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);
      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }
    public void printHiddenValue() {
      int value = 2;
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);
      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }
  }
  public void printValue() {
    System.out.println("value=" + value);
    System.out.println("this.value=" + this.value);
  }  
}
public class Main {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.QualifiedThis qt = outer.new QualifiedThis();
    System.out.println("printValue():");
    qt.printValue();
    System.out.println("printHiddenValue():");
    qt.printHiddenValue();
    outer.printValue();
  }
}
        上面的代码生成以下结果。

隐藏变量
如果实例变量名称被隐藏,您必须使用关键字this或类名称以及关键字this限定其名称。
class TopLevelOuter {
  private int v1 = 100;
  // Here, only v1 is in scope
  public class InnerLevelOne {
    private int v2 = 200;
    // Here, only v1 and v2 are in scope
    public class InnerLevelTwo {
      private int v3 = 300;
      // Here, only v1, v2, and v3 are in scope
      public class InnerLevelThree {
        private int v4 = 400;
        // Here, all v1, v2, v3, and v4 are in scope
      }
    }
  }
}
        从外部类引用变量
以下代码显示如何从外部类引用变量。
public class Test{
  private int value = 1;
  public class Inner {
    private int value = 2;
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
      System.out.println("Outer: Value  = " + Test.this.value);
    }
  } // Inner class ends here
  public void printValue() {
    System.out.println("\nOuter - printValue()...");
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    System.out.println("\nSetting  Outer"s value to " + newValue);
    this.value = newValue;
  }
}