对于内部类的整体概括:
内部类(nested class)被分为静态内部类(static nested class)与非静态内部类(inner class)。非静态内部类(Inner class)可以访问外部类的其他成员,即使相关的成员被声明为private或者protected等保护类型(这里是一个怎么在一个其他类中访问另一个类中私有类的问题的解答)静态内部类(static nested class)不能访问外部类中的一般成员
为什么使用内部类(nested class):
- 一个类1只对某一个类2起作用,对其他类3,4…没有作用,那么就可以将类1定义在类2中。
- 提升了类的封装特性。类1可以访问类2的私有成员,并且类1对其他外部类不可见。
1. 内部类Inner class
1.1 声明内部类(inner class)对象的方法:
有一句名言:内部类实例必须存在于外部类实例中。
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
1.2 内部类(inner class)不能干什么:
内部类不能定义任何静态成员变量
2. 特殊的静态内部类(static nested class)
此内部类被声明为static,也只有内部类才能被声明为static。如果只是希望将类隐藏再类的内部,而在内部类中不生成外部类的引用,或者并没有使用到外部类的实例字段。静态内部类从根本意义上来说,没有内部类的作用,而且本身也没有相关的内部类的限制
2.1声明静态内部类对象
StaticNestedClass staticNestedObject = new StaticNestedClass();
2.2 静态内部类能干什么
我们上面说了静态内部类功能很受限制,没有inner class一样可以直接访问外部类的一般成员,但是静态内部类是可以直接访问外部类的静态成员的。
以上所有的特性都可以在下面的例子中检验。
3. 一个小例子
StaticNestedClass staticNestedObject = new StaticNestedClass();
public class OuterClass {
String outerField = "Outer field";
static String staticOuterField = "Static outer field";
class InnerClass {
void accessMembers() {
System.out.println(outerField);
System.out.println(staticOuterField);
}
}
static class StaticNestedClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field outerField
// System.out.println(outerField);
System.out.println(outer.outerField);
System.out.println(staticOuterField);
}
}
public static void main(String[] args) {
System.out.println("Inner class:");
System.out.println("------------");
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
innerObject.accessMembers();
System.out.println("\nStatic nested class:");
System.out.println("--------------------");
StaticNestedClass staticNestedObject = new StaticNestedClass();
staticNestedObject.accessMembers(outerObject);
System.out.println("\nTop-level class:");
System.out.println("--------------------");
TopLevelClass topLevelObject = new TopLevelClass();
topLevelObject.accessMembers(outerObject);
}
}
public class TopLevelClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field OuterClass.outerField
// System.out.println(OuterClass.outerField);
System.out.println(outer.outerField);
System.out.println(OuterClass.staticOuterField);
}
}
4. 局部类(local classes)
局部类是定义在一个块结构中的类,我们一般遇到的局部类都是定义在方法体内的。
- 局部类可以访问外部类中的成员(成员对象、成员方法),如果局部类被声明在静态方法中,那么其不能访问外部类中的非静态成员与非静态方法(这个限制不是局部类的原因,而是外部静态方法的原因)
- 局部类也可以访问外部包围方法中的局部变量,但是只能访问被定义为final或者effectively final的局部变量,如下图的numberLength.
- 局部类也可以访问方法的形参,如果我们将局部类声明在一个方法中的话。
- 满足覆盖原则
public class LocalClassExample {
static String regularExpression = "[^0-9]";
public static void validatePhoneNumber(
String phoneNumber1, String phoneNumber2) {
final int numberLength = 10;
// Valid in JDK 8 and later:
// int numberLength = 10;
class PhoneNumber {
String formattedPhoneNumber = null;
PhoneNumber(String phoneNumber){
// numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber() {
return formattedPhoneNumber;
}
// Valid in JDK 8 and later:
// public void printOriginalNumbers() {
// System.out.println("Original numbers are " + phoneNumber1 +
// " and " + phoneNumber2);
// }
}
PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
// Valid in JDK 8 and later:
// myNumber1.printOriginalNumbers();
if (myNumber1.getNumber() == null)
System.out.println("First number is invalid");
else
System.out.println("First number is " + myNumber1.getNumber());
if (myNumber2.getNumber() == null)
System.out.println("Second number is invalid");
else
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
validatePhoneNumber("123-456-7890", "456-7890");
}
}
局部类类似与inner class, 其中不能声明静态成员与静态方法,但是可以声明静态常量成员。其中局部类中不能声明接口,因为接口本质上是静态的。
4. 匿名内部类(Anonymous classes)
匿名内部类可以使得声明和实例化一个类同时进行,匿名内部类可以理解为是没有名字的局部类。
声明方式的不同:局部类是标准的类声明的形式,但是匿名类是表达式的形式。
匿名类表达式的语法类似于构造函数的调用,但是由于是表达式的一部分,所以最后要加;
匿名类对于外部类以及外围方法中变量的访问权限与局部类基本类似。
其限制与与局部类基本类似。
匿名类中还可以再定义:
字段
额外的方法
实例化对象
局部类
但是匿名类中不能定义构造方法。
4.1 什么时候使用匿名内部类
当我们只需要使用一次的局部类的时候,我们可以考虑使用局部内部类
Example
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp =
new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}