在介绍成员类的继承和实现之前,这里先来介绍一下成员内部类的使用方式和基本规则:
成员内部类基础
成员内部类(member inner class),是最普通的内部类,其可以被priavate、public、protected等访问修饰符修饰但是不可以被static修饰的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类实例。
对于公有类型的内部类InnerClass,如果我们想获取其实例,我们必须通过其外部类来获取,获取的方法有多种,第一种就是前面介绍的那种,在外面new,如:
//首先,必须得到外部类的实例
OuterClass outerClass = new OuterClass();
//通过实例来获取内部类的实例
InnerClass innerClass = outerClass.new InnerClass();
//使用 内部类中的方法
innerClass.display();
第二种方法,就是在外部类中定义一个方法,如:
//获取成员内部类的实例
public InnerClass getInstance(){
return new InnerClass();
}
该方法,返回的是内部类的一个实例对象,通过该方法我们可以获取内部类(前提是要有访问权限)。
//首先,获取外部类的实例
OuterClass outerClass=new OuterClass();
//然后,通过调用外部类的方法来获取内部类的实例
InnerClass innerClass=outerClass.getInstance();
//调用内部类的方法
innerClass.display();
实现和继承
因为内部类以上的特性,有时候我们可以在类的内部定义接口、抽象类以及类来供外部使用。比如,Thead
类中定义了UncaughtExceptionHandler
内部接口,我们可以使用通过实现该接口并传入到Thread
中就可以完成对非捕获类异常的处理,默认的处理方式在线程内打印栈路径。使用方式如:
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("在测试线程中。。。");
// 抛出非捕获异常
throw new RuntimeException("测试UncaughtExceptionHandler");
});
// 对于公有的内部接口可以直接实现
UncaughtExceptionHandler handler = new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.format("在异常处理的类中,线程名称为:%s,异常消息为:%s", t.getName(), e.getMessage());
}
};
thread.setName("线程1");
thread.setUncaughtExceptionHandler(handler);
thread.start();
}
结果如下:
在测试线程中。。。
在异常处理的类中,线程名称为:线程1,异常消息为:测试UncaughtExceptionHandler
下面,我们针对成员内部接口、抽象类和类做一个外部实现的对比。
A.java内部类定义类:
public class A {
// 内部接口
public interface I {
void say();
}
// 内部抽象类B
public abstract class B {
public abstract void say();
}
// 内部类C
public class C {
public void say() {
System.out.println("C.say()");
}
}
// 在内部类中定义一个方法
public void hello() {
System.out.println("hello");
}
}
下面,是继承和实现内部类的方式TestInnerClass.java:
public class TestInnerClass {
// 实现A内部接口I,实现内部接口不需要实例化I的外部类A
class S implements I {
@Override
public void say() {
System.out.println("实现内部类的接口,可以直接实现,不需要实例化其外部类A");
}
}
class D extends B {
/**
* No enclosing instance of type A is accessible to invoke the super
* constructor. Must define a constructor and explicitly qualify its super
* constructor invocation with an instance of A (e.g. x.super() where x is an
* instance of A).
*/
public D() {
// 在构造方法的第一行需要实例化A且需要调用其super()方法,其代表将A的直接作为其父类了,但是不能使用除B中的其他方法和状态。
// 如果没有该语句编译器将会报上述注释的错误!
new A().super();
}
@Override
public void say() {
System.out.println("good");
}
}
class E extends C {
public E() {
// 使用方式和继承内部抽象类的一样,需要先实例化外部类A才能获得其应用
new A().super();
}
@Override
public void say() {
System.out.println("E");
}
}
}
值得注意的是,因为抽象内部类和内部类都会可以直接使用外部类中成员变量和方法而接口没有机会使用外部类的方法和接口,所以除了实现内部接口不需要在构造方法中实例化外部类并调用super()方法之外(new OutClass().spuer())之外,继承的方式都需要在构造方法中作特殊处理(如示例所示),否则会报编译器错误:No enclosing instance of type A is accessible to invoke the super constructor. Must define a constructor and explicitly qualify its super constructor invocation with an instance of A (e.g. x.super() where x is an instance of A)
。
注:JDK8中,虽然接口可以有定义default实现方法,但是为了兼容该特性,所以default方法中不能调用外部类的方法。
内部类的应用-ArrayList迭代器的实现
java API的内部类的典型应用如集合类型:ArrayList
,在该类中有一个私有的Itr内部类该内部类该内部类实现java.util.Iterator接口,同时在外部类中提供 public Iterator<E> iterator()
方法来返回Iterator
实例(注:思考一下为什么不直接返回Itr实例?),这样利用多态的特性通过访问Iterator
接口来实际操作Itr
实例。其关键代码如下:
public Iterator<E> iterator() {
return new Itr();
}
/**
*实现java.util.Iterator接口
*/
private class Itr implements Iterator<E> {}