什么是内部类
定义在类内部的类就被称为内部类。外部类按常规的类访问方式使用内部类,唯一的差别是内部类可以访问外部类的所有方法与属性,包括私有方法与属性。
为什么需要内部类
- 内部类是为了更好的封装,把内部类封装在外部类里,不允许同包其他类访问
- 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private
- 实现多继承:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
- 匿名内部类用于实现回调
内部类的分类
内部类可分为成员内部类和静态内部类
静态内部类:
Inner是静态内部类。静态内部类可以访问外部类所有静态变量和方法。静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。 与外部类关系密切且不依赖外部类实例。 Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象,HashMap内部维护Entry数组用了存放元素,但是Entry对使用者是透明的。
成员内部类
成员内部类可以访问外部类所有的变量和方法,包括静态和实例,私有和非私有。和静态内部类不同的是,每一个成员内部类的实例都依赖一个外部类的实例(成员内部类是依附外部类而存在的)。其它类使用内部类必须要先创建一个外部类的实例。 注意点:
- 成员内部类不能定义静态方法和变量(final修饰的除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。
- 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
- 成员内部类与外部类可以拥有同名的成员变量或方法,默认情况下访问的是成员内部类的成员。如果要外部类的同名成员,需用下面的形式访问:
**OutterClass(外部类).this.成员**
扩展点1:为什么内部类不能定义静态变量? 如果成员内部类允许定义静态变量,那么这些静态变量将与外部类的实例化对象无关,这就违背了成员内部类依赖于外部类的设计原则。因此,Java 禁止在成员内部类中定义静态变量。 但是,成员内部类可以定义常量(final static)变量,因为常量变量在编译时就已经确定了它们的值,因此它们不依赖于外部类的实例化对象。 扩展点2:为什么Java中成员内部类可以访问外部类成员? 总结为2点:1、内部类对象的创建依赖于外部类对象;2、内部类对象持有指向外部类对象的引用。 1 编译器自动为内部类添加一个类型为Outer,名字为this$0的成员变量,这个成员变量就是指向外部类对象的引用; 2 编译器自动为内部类的构造方法添加一个类型为Outer的参数,在构造方法内部使用这个参数为内部类中添加的成员变量赋值; 3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。
匿名内部类
匿名内部类需要提前定义(必须存在) Out.java编译后匿名内部类会生成相应的class文件。 匿名内部类可以访问外部类所有的变量和方法。 匿名内部类常用于回调函数,比如我们常用的绑定监听的时候。
总结
内部类特点
- 非静态内部类对象不仅指向该内部类,还指向实例化该内部类的外部类对象的内存。
- 内部类和普通类一样可以重写Object类的方法,如toString方法;并且有构造函数,执行顺序依旧是先初始化属性,再执行构造函数
- 在编译完之后,会出现(外部类.class)和(外部类﹩内部类.class)两个类文件名。
- 内部类可以被修饰为private,只能被外部类所访问。事实上一般也都是如此书写。
- 内部类可以被写在外部类的任意位置,如成员位置,方法内。
内部类访问外部类
静态时,静态内部类只能访问外部类静态成员;非静态内部类都可以直接访问。(原因是:内部类有一个外部类名.this的指引)当访问外部类静态成员出现重名时,通过(外部类名.静态成员变量名)访问。如,Out.show(); 重名情况下,非静态时,内部类访问自己内部类通过this.变量名。访问外部类通过(外部类名.this.变量名)访问 。如Out.this.show(); 在没有重名的情况下,无论静态非静态,内部类直接通过变量名访问外部成员变量。
外部类访问内部类
内部类为非静态时,外部类访问内部类,必须建立内部类对象。建立对象方法,如前所述。 内部类为静态时,外部类访问非静态成员,通过(外部类对象名.内部类名.方法名)访问,如new Out().In.function(); 内部类为静态时,外部类访问静态成员时,直接通过(外部类名.内部类名.方法名),如 Out.In.funchtion(); 当内部类中定义了静态成员时,内部类必须是静态的;当外部静态方法访问内部类时,内部类也必须是静态的才能访问。
静态嵌套类(静态内部类)
Nested Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects) 这里出现的几个名词他们含义相同:外部类(封装类)、嵌套类、内部类(非静态嵌套类)、静态内部类(静态嵌套类)、顶级类 我们平时可能不太听到嵌套类这个词,而常说内部类,而常说的内部类其实就是指这里的嵌套类,而常说的成员内部类是指非静态嵌套类。 Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类 嵌套类分为两类:非静态类和静态类。非静态嵌套类称为内部类。声明的嵌套类称为静态嵌套类
class OuterClass {
...
class InnerClass {
...
}
static class StaticNestedClass {
...
}
}
INFO
A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.As a member of the OuterClass, a nested class can be declared private, public, protected, or package private. (Recall that outer classes can only be declared public or package private.)
嵌套类是外部类(封装类)的一个成员。 非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为 private。静态嵌套类无权访问封闭类的其他成员(非静态成员) 作为外部类的成员,嵌套类可以被声明为private, public, protected, or package private 为什么需要嵌套类?
- 一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类仅在一个地方被使用,那么可以将它作为一个嵌套类,这样这个类就作为外部类的一个服务类了,增加可代码可读性。
- 提高代码的可读性和可维护性:将小类嵌套在顶级类中会使代码更接近其使用位置。
- 提高代码的封装性:如果一个类A要调用另一个类B,但B又不想被其他类所引用就要申明为private,那把A嵌套在B中,就可以同时满足前面的两个要求了
内部类(非静态嵌套类) 与实例方法和变量一样,内部类与其封闭类的实例相关联,并**可以直接访问该对象的方法和字段。**此外,由于内部类与实例相关联,因此它本身无法定义任何静态成员。 内部类的实例只能存在于 外部类 的实例中,并可以直接访问其封闭实例的方法和字段。若要实例化内部类,必须先实例化外部类。
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
静态嵌套类 与类方法和变量一样,静态嵌套类与其外部类相关联.并且像静态类方法,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法:它只能通过对象引用使用它们。 静态嵌套类与其外部类(和其他类)的实例成员交互,就像任何其他顶级类一样。实际上,静态嵌套类在行为上是一个顶级类,为了便于打包而嵌套在另一个顶级类中。 实例化静态嵌套类的方式与实例化顶级类的方式相同
StaticNestedClass staticNestedObject = new StaticNestedClass();
内部类和静态嵌套类的例子
//外部类
public class OuterClass {
//外部类的属性
String outerField = "Outer field";
static String staticOuterField = "Static outer field";
//内部类(非静态嵌套类)
// 内部类可以直接访问外部类的属性和方法,但是不能定义静态成员
class InnerClass {
// The field staticInnerField cannot be declared static in a non-static inner type,
// unless initialized with a constant expression
// static String staticInnerField="Static inner field";
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);
}
}
成员内部类和静态内部类比较
因此正是因为成员内部类默认持有外部类的引用,这也是在使用内部类时造成内存泄漏的一大原因。