Java 中的内部类
参考资料:
《Java SE 6.0 编程指南》 作者:吴亚峰 纪超 出版社:人民邮电出版社
《Thinking in Java Fourth Edition》 作者:Bruce Eckel
1、非静态内部类
1.1、语法规则
public class Outer {
/**
* 非静态内部类
*/
class Inner {
//内部类的成员
int i = 1;
}
//外部类的普通成员
int count = 2;
}
package innerclasses;
// Creating inner classes.
public class Parcel1 {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
}
// Using inner classes looks just like
// using any other class, within Parcel1:
public void ship(String dest) {
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel1 p = new Parcel1();
p.ship("Tasmania");
}
} /* Output:
Tasmania
*///:~
□ 内部类和外部类中的其他成员是一个级别的,其也是外部类的一个成员。
□ 在内部类也是一个单独的类,也有自己的成员变量和方法。
□ 内部类可以加上访问限制修饰符,包括 private、protected 和 public。
1.2、外部类之内创建内部类对象
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 内部类
*/
class Inner {
/**
* 打印输出
*/
public void show() {
System.out.println("调用内部类的show方法");
}
}
/**
* 外部类的方法,创建内部类对象
*/
public void createInner() {
//外部类中创建内部类对象
Inner inner = new Inner();
//调用内部类的方法
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
//调用创建内部类对象的方法
outer.createInner();
}
}
□ 外部类中创建内部类对象的语法与普通创建对象的方法相同,使用 new 操作符调用构造器。
□ 对内部类而言,在其类体中也可以拥有类所能拥有的一切成员。
1.3、外部类之外创建内部类对象
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 内部类
*/
class Inner {
/**
* 打印输出
*/
public void show() {
System.out.println("调用内部类的show方法");
}
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
//创建内部类对象
Outer.Inner inner = outer.new Inner();
Outer.Inner inner2 = new Outer().new Inner();
//调用内部类中的方法
inner.show();
inner2.show();
}
}
□ 在外部类之外声明内部类引用时,需用外部类名加以标识,不能直接使用内部类类名,而创建内部类对象时,需先创建外部类对象,再创建内部类对象。
1.4、内部类与外部类之间的成员互访
1.4.1、内部类中访问外部类成员
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/** 外部类的私有成员变量 */
private int o = 1;
/**
* 内部类
*/
class Inner {
/**
* 打印输出外部类的私有成员
*/
public void show() {
System.out.println("外部类的私有成员变量o = " + o);
}
}
public int getO() {
return o;
}
public void setO(int o) {
this.o = o;
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建内部类对象
Outer.Inner inner = new Outer().new Inner();
//调用内部类中的方法
inner.show();
}
}
1.4.2、外部类中访问内部类成员
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 内部类
*/
class Inner {
/**
* 打印输出
*/
private void show() {
System.out.println("成功访问内部类的私有方法");
}
}
/**
* 创建内部类对象并调用其方法
*/
public void getInner() {
//创建内部类对象
Inner inner = new Inner();
//访问内部类私有方法
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建外部类对象,并调用创建内部类方法
Outer outer = new Outer();
outer.getInner();
}
}
1.5、内部类与外部类的预定义对象引用 this
□ 内部类中需要使用外部类对象的 this,语法如下:<外部类名>.this.<外部类中需要被访问的成员名>。
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/** 外部类成员变量 */
int i = 1;
/**
* 内部类
*/
class Inner {
/** 内部类与外部类同名成员变量 */
int i = 10;
/**
* 输出外部类和内部类的成员变量
*/
public void show() {
//访问外部类的成员变量
System.out.println("外部类的成员变量 i = " + Outer.this.i);
//访问内部类的成员变量
System.out.println("内部类的成员变量 i = " + this.i);
}
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建内部类对象
Outer.Inner innerr = new Outer().new Inner();
//调用内部类方法
innerr.show();
}
}
package innerclasses;
// Qualifying access to the outer-class object.
public class DotThis {
void f() {
System.out.println("DotThis.f()");
}
public class Inner {
public DotThis outer() {
return DotThis.this; // A plain "this" would be Inner's "this"
}
}
public Inner inner() {
return new Inner();
}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
} /* Output:
DotThis.f()
*///:~
1.6、内部类的继承
package innerclasses;
// Inheriting an inner class.
class WithInner {
class Inner {}
}
public class InheritInner extends WithInner.Inner {
public InheritInner(WithInner inner) {
inner.super(); //若跳过外部类直接继承内部类,必须使用此语法才能编译通过
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
2、局部内部类
2.1、局部内部类的定义及创建
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 声明并创建内部类对象
*/
public void getInner() {
/**
* 内部类
*/
class Inner {
/**
* 输出
*/
public void show() {
System.out.println("内部类的show()方法");
}
}
//创建内部类对象并调用其方法
Inner inner = new Inner();
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
//调用外部类方法
outer.getInner();
}
}
2.2、局部变量与局部内部类
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 声明并创建内部类对象
*/
public void getInner() {
/** 定义局部变量 */
final int i = 2;
/**
* 内部类
*/
class Inner {
/**
* 输出
*/
public void show() {
System.out.println("方法中的局部变量 i = " + i);
}
}
//创建内部类对象并调用其方法
Inner inner = new Inner();
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
//调用外部类方法
outer.getInner();
}
}
□ 局部变量与局部内部类。普通局部变量随着语句块的结束而消亡,而创建的局部类对象则不会。如果在语句块结束后,调用了局部内部类对象中访问普通局部变量的方法,就会出现问题,因为要访问的局部变量已经不存在了。
□ final 的局部变量不会因为语句块的结束而消失,因此可以被局部内部类访问。
2.3、静态方法中的局部内部类
/**
*
*/
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/** 外部类的静态成员变量 */
static int i = 7;
/**
* 声明并创建内部类对象
*/
public static void getInner() {
/**
* 内部类
*/
class Inner {
/**
* 输出
*/
public void show() {
System.out.println("方法中的局部变量 i = " + i);
}
}
//创建内部类对象并调用其方法
Inner inner = new Inner();
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//调用外部类静态方法
Outer.getInner();
}
}
□ 若局部类位于静态方法中,则只能访问外部类的静态成员,这与静态方法访问成员的规则是一致的。
3、静态内部类
3.1、创建静态内部类的对象
/**
*
*/
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 静态内部类
*/
static class Inner {
/**
* 输出
*/
public void show() {
System.out.println("静态内部类的show()方法");
}
}
/**
* 声明并创建内部类对象
*/
public void getInner() {
//创建内部类对象并调用其方法
Inner inner = new Inner();
inner.show();
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//外部类之外创建静态内部类对象
Outer.Inner inner = new Outer.Inner();
inner.show();
//外部类中使用静态内部类对象
new Outer().getInner();
}
}
□ 外部类之外创建静态内部类对象的语法如下:<外部类类名>.<内部类类名> 引用变量 = new <外部类类名>.<内部类构造器>;
□ 静态内部类也可以称为静态嵌套类,或顶级嵌套类。由于创建静态内部类的对象已不需要外部类对象的存在,静态内部类其实只是一个在别的类中的一个普通类而已。static 关键字只是说明其创建对象时不依赖于外部类对象,并不表示这个类是静态的。
4、匿名内部类
□ 匿名内部类没有类名,因此匿名内部类在声明的同时也创建了对象。
□ 匿名内部类的声明要么是基于继承的,要么是基于接口实现的。
4.1、基于继承的匿名内部类
/**
*
*/
package com.aimartt.innerclass;
/**
* 父类
*/
class Father {
public void show() {
System.out.println("父类中的show()方法");
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//定义匿名内部类并创建其对象,并重写父类中的方法
Father father = new Father() {
public void show() {
System.out.println("重写父类的show()方法");
}
};
//访问匿名内部类中重写的方法
father.show();
}
}
□ 访问匿名内部类成员均是通过多态完成的,因为匿名内部类无法创建自身类型的引用。
4.2、基于接口实现的匿名内部类
/**
*
*/
package com.aimartt.innerclass;
/**
* 定义接口
*/
interface MyInterface {
void show();
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//定义匿名内部类并创建其对象,并实现接口中的方法
MyInterface mi = new MyInterface() {
@Override
public void show() {
System.out.println("实现接口的show()方法");
}
};
//访问匿名内部类中实现的方法
mi.show();
}
}
□ 匿名内部类要么实现一个接口,要么继承一个类,不能同时既实现接口又进行继承。
4.3、匿名内部类的初始化
/**
*
*/
package com.aimartt.innerclass;
/**
* 定义抽象类
*/
abstract class Father {
int i;
abstract void show();
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//定义匿名内部类并创建其对象
Father mi = new Father() {
//使用非静态块初始化成员
{
i = (int) (Math.random() * 100);
}
//重写抽象类中的方法
@Override
public void show() {
System.out.println("初始化的成员i = " + i);
}
};
//访问匿名内部类中实现的方法
mi.show();
}
}
□ 匿名内部类对象的初始化代码写在非静态语句块中。
package innerclasses;
// An anonymous inner class that performs initialization.
interface Destination {
String readLabel();
}
public class Parcel9 {
// Argument must be final to use inside anonymous inner class:
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}
□ 从外部传递给匿名内部类的参数,必须是 final 的。5、内部接口
5.1、定义在类中的内部接口
/**
*
*/
package com.aimartt.innerclass;
/**
* 外部类
*/
class Outer {
/**
* 非静态内部接口
*/
public interface InnerInterface {
void show();
}
/**
* 实现内部接口的内部类
*/
public class Inner implements InnerInterface {
@Override
public void show() {
System.out.println("内部类实现内部接口的show()方法");
}
}
/**
* 获取内部类对象的方法
* @return
*/
public InnerInterface getInner() {
return new Inner();
}
}
/**
* 实现内部接口的普通类
*/
class Normal implements Outer.InnerInterface {
@Override
public void show() {
System.out.println("普通类实现了内部接口的show()方法");
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
Outer outer = new Outer(); //创建外部类对象
Outer.InnerInterface oic = outer.getInner(); //获取内部类对象
oic.show(); //访问内部类的show()方法
oic = new Normal(); //获取普通类的对象
oic.show(); //访问普通类的show()方法
}
}
□ 内部接口无论是否使用 static 修饰,扮演的都是静态成员。
5.2、定义在接口中的内部接口
/**
*
*/
package com.aimartt.innerclass;
/**
* 外部接口
*/
interface OuterInterface {
/**
* 内部接口
*/
public interface InnerInterface {
void inShow();
}
/**
* 外部接口的方法
*/
void outShow();
}
/**
* 实现外部接口的类
*/
class OuterInterfaceImpl implements OuterInterface {
@Override
public void outShow() {
System.out.println("实现外部接口的outShow()方法");
}
}
/**
* 实现内部接口的类
*/
class InnerInterfaceImpl implements OuterInterface.InnerInterface {
@Override
public void inShow() {
System.out.println("实现内部接口的inShow()方法");
}
}
/**
* 主类
*/
public class Demo {
/**
* 主方法
* @param args
*/
public static void main(String[] args) {
//创建实现内部接口和外部接口的类的对象
OuterInterface.InnerInterface inner = new InnerInterfaceImpl();
OuterInterface outer = new OuterInterfaceImpl();
//调用两个对象的方法
inner.inShow();
outer.outShow();
}
}
□ 内部接口只是把普通接口放到了一个接口内部,在使用上没有很大的不一样,实现内部接口与普通接口的具体方法完全一样。