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; // OKpublic 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 contextclass 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 publicclass 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 classpublic 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 - TitleIteratorclass TitleIterator implements Iterator<String> {int count = 0;@Overridepublic boolean hasNext() {return (count < titleList.size());}@Overridepublic 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 {@Overridepublic 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 classIterator<String> iterator = new Iterator<String>() {int count = 0;@Overridepublic boolean hasNext() {return (count < titleList.size());}@Overridepublic String next() {return titleList.get(count++);}}; // Anonymous inner class ends herereturn iterator;} }
Java 静态对象类
Java面向对象设计 - Java静态内部类
静态成员类不是内部类
在另一个类的主体中定义的成员类可以声明为静态。
例子
以下代码声明了顶级类A和静态成员类B:
class A {// Static member classpublic 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 - Monitorpublic 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 - Keyboardpublic 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 herepublic 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 herepublic 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 scopepublic class InnerLevelOne {private int v2 = 200;// Here, only v1 and v2 are in scopepublic class InnerLevelTwo {private int v3 = 300;// Here, only v1, v2, and v3 are in scopepublic 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 herepublic 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;}}