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;
}
}