内部类指的是在一个类内部定义的类,它可以访问包含它的外部类的成员,包括私有成员。内部类可以分为成员内部类、局部内部类、匿名内部类和静态内部类。
以下是每种内部类的详细解释以及代码示例:
成员内部类
成员内部类是定义在一个类内部的类,并且在外部类的成员方法中可以通过创建实例来使用它。成员内部类有访问外部类中的私有成员的能力。
示例代码:
public class Outer {
private int outerField = 10;
public void outerMethod() {
Inner inner = new Inner();
inner.innerMethod();
}
class Inner {
void innerMethod() {
System.out.println(outerField);
}
}
}
局部内部类
局部内部类是在一个方法内部定义的类,它们有访问外部类的成员的能力,但只在该方法被调用时才能被实例化和使用。
示例代码:
public class Outer {
private int outerField = 10;
public void outerMethod() {
class Inner {
void innerMethod() {
System.out.println(outerField);
}
}
Inner inner = new Inner();
inner.innerMethod();
}
}
匿名内部类
匿名内部类是在创建对象时定义的内部类,没有类名,只有类体。它们通常用于在启动线程、动态事件监听器等方面。
示例代码:
> public class Outer {
> private int outerField = 10;
>
> public void outerMethod() {
> new MyInterface() {
> void myMethod() {
> System.out.println(outerField);
> }
> }.myMethod();
> } }
>
> interface MyInterface {
> void myMethod(); }
静态内部类
静态内部类是在类内部而不是实例内部定义的类。它只能访问外部类的静态成员,不能访问非静态成员。它可以通过类名来访问。
示例代码:
public class Outer {
private static int outerStaticField = 10;
private int outerField = 20;
public static class Inner {
public void innerMethod() {
System.out.println(outerStaticField);
}
}
public void outerMethod() {
new Inner().innerMethod();
}
}
定义
将一个类的定义放在另一个类的内部,这就是内部类。
为什么使用内部类
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类没有影响。
使用内部类可以继承对某个具体的或抽象的类的能力,使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
内部类允许继承多个非接口类型(类或抽象类)。如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
语义
在外围类的方法里使用内部类,与使用其他的类没什么不同。
外围类的方法返回一个指向内部类的引用。如果想要在外部类的静态方法之中的任意位置创建某个内部类的对象,必须指明对象的类型:OuterClassName.InnerClassName
内部类与向上转型
内部类可以转型为其基类和接口。内部类–某个接口的实现–对于其他人来说是完全不可见且不可用的。
内部类可以被声明为protect或private, 而普通类只可以赋予public和包访问权限。
局部内部类和匿名内部类
使用局部类而不使用匿名内部类的唯一理由是,需要不止一个该内部类的对象。
为什么使用?
实现某类型的接口,创建并返回对其的引用 创建一个类来辅助你的解决方案,但是不希望这个类是公共可见的,局部内部类在方法的作用域内创建一个完整的类,叫做局部内部类。
局部内部类是方法的一部分,而不是外围类的一部分。所以,在同一个目录下的任意类中对某个内部类使用与局部内部类相同的类标识符,不会有命名冲突。而在此方法之外是不能访问它的局部内部类的。
匿名内部类
1) 缺省参数
public class Parcel {
public Contents cont ( ){
renturn new Contents( ){ //调用基类缺省构造函数
private int i = 11;
public int value ( ){
return i;
} //创建一个继承自Contents的匿名类对象
};
public static void main ( ){
Parcel p = new Parcel( );
Contents c = p.cont( );
}
}
2)有参构造器
public class Parcel {
public Contents cont (int x ){
renturn new Contents( x ){ //调用特定基类构造函数
private int i = 11;
public int value ( ){
return i;
} //创建一个继承自Contents的匿名类对象
};
public static void main ( ){
Parcel p = new Parcel( );
Contents c = p.cont( );
}
}
3)如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么其参数引用必须是final的,否则编译器会报错。
4)在匿名内部类中通过实例初始化可以达到为匿名内部类创建一个构造器的效果,只不过不可以重载。
public class Parcel {
public Contents cont ( ){
renturn new Contents( ){
{ //实例初始化代码
}
private int i = 11;
public int value ( ){
return i;
} //创建一个继承自Contents的匿名类对象
};
public static void main ( ){
Parcel p = new Parcel( );
Contents c = p.cont( );
} }
5)链接到外部类
内部类具有外部类的所有元素的访问权。生成的内部类对象与制造它的外围对象之间有一种联系,所以它能访问外围对象的所有成员。
当某个外围类对象创建一个内部类对象时,此内部类对象必定会保存一个指向那个外围类对象的引用。然后在你访问此外围内对象的成员时,就是用那个“隐藏的”引用来选择外围类对象的成员。内部类对象只能在与其外围类对象相关联的情况下才能被创建。
6)static嵌套类
将内部类声明为static时,内部类对象将与外围类对象之间没有任何联系。
要创建嵌套类对象时,不需要其外围类的对象。
嵌套类的对象中只能访问静态的外围类对象。
普通内部类的字段与方法,只能放在类的外部层次上,所以普通内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。
正常情况下,不能再接口内部放置任何代码,但嵌套类作为接口的一部分,因为类是static的,只是将嵌套类置于接口的命名空间内,并不违反接口的规则。
7)生成内部类对象
如果要直接创建内部类的对象,必须使用此外围类的对象来创建其内部类对象。
OuterClassName.InnerClassName i = p.new InnerClassName( );
因此,除非有了一个外围类的一个对象,否则是不可能生成内部类的对象。然而如果用的是嵌套类,则不需要对其外围类的引用。
8)多层内部类嵌套
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。
9)继承特定的内部类
因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,必须传递一个指向外围类对象的引用,此外还必须在构造器内部使用如下语法:
class A {
calss B { }
}
public class C extends A.B {
C ( A a) {
a.super();
}
public static void main() {
A a = new A( );
C c = new C( a );
}
}
- 内部类可以被覆盖吗
如果创建一个内部类,然后继承其外部类并重新定义此内部类时,内部类并不会被覆盖。
这是因为两个内部类是完全独立的两个实体,各自在自己的命名空间内。
但是,明确地继承某个内部类也是可以的,然后可以覆盖其中的方法。