被遗忘的技术----内部类
一、内部类的定义
将一个类的定义放在另外一个类的内部,这就是内部类。内部类的特点,我们用一个例子来引出。
/**
* Created by jay.zhou on 2018/2/23.
*/
public class Outer {
private int year = 2018;
class Inner{
//static int size = 5; 报错
public void show(){
System.out.println(year);//内部类直接访问到外部类的成员变量
}
}
public static void main(String[] args) {
new Outer().new Inner().show();//运行结果:2018
}
}
运行结果表明,内部类对象访问到了创建它的外部类对象的属性,并且普通的内部类不允许有静态成员。因此,内部类的特点如下:内部类可以直接访问外部类成员,仿佛自己拥有一样。产生此现象的原因是:当外部类对象创建内部类对象的时候,内部类对象会秘密的捕获创建它的外部类对象的引用。
二、内部类捕获外部类对象的引用
普通的成员内部类对象的创建方法是,使用 " 外部类对象. new 内部类名()" ,比如 new Outter(). new Inner()。
既然当外部类对象创建内部类对象的时候,内部类对象会秘密的捕获那个创建它的外部类对象的引用。那在内部类中如何才能获取到这个引用呢?使用 “外部类类名.this”即可。
/**
* Created by jay.zhou on 2018/2/23.
*/
public class Outer {
private int year = 2018;
class Inner{
public Outer getOutter(){
return Outer.this;
}
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer outer2 = outer.new Inner().getOutter();
if(outer.equals(outer2)){
System.out.println("内部类对象使用 外部类类名.this 返回 创建它的外部类对象的引用");
}
}
}
运行结果是:内部类使用 外部类类名.this 返回 创建它的外部类对象的引用。
这说明内部类能够使用"外部类类名.this"获取到创建它的外部类对象。
三、几种特殊的内部类
第一种是嵌套类。嵌套类是使用static 修饰的内部类。嵌套类的特点是:嵌套类的创建不需要外部类对象,并且嵌套类对象不能访问外部类对象的非静态成员。原因很简单,因为嵌套类对象不需要从外部类对象创建,因此也无法捕获到创建它的外部类对象的引用,自然就不能访问到属于外部类对象的普通成员变量和成员方法。嵌套的类名是 "外部类名.内部类名",因此创建嵌套类对象的方法是: new Outter.Inner();
第二种是匿名内部类。匿名内部类就是没有名字的类。它通过new表达式返回的引用被自动向上转型为其父类。new 父{重写}; 这创建的是父的子类对象。 new 接口 {实现};这创建的是接口的实现类对象。并且匿名内部类对象它是没有构造函数的,因为它没有名字,通常可以使用“构造代码块”当作其构造函数。根据类的初始化过程,创建子类对象之前,它的父类对象必须先要被初始化完毕,因此,父类的构造函数将会被调用。
/**
* Created by jay.zhou on 2018/2/23.
*/
public abstract class Annimal {
public Annimal() {
System.out.println("初始化一个动物");
}
public abstract void eat();
}
class Apply{
public static void apply(Annimal annimal){
annimal.eat();
}
public static void main(String[] args) {
apply(new Annimal() {
@Override
public void eat() {
System.out.println("某动物在吃东西");
}
});
}
/**
* 运行结果:
* 初始化一个动物
* 某动物在吃东西
*/
}
很显然,在创建匿名内部类对象的时候,调用了其父类的构造函数。匿名内部类大量应用在“多线程”的相关Java代码中。一般的使用匿名内部类的场景是:当一个方法的参数是一个接口类型的时候,并且这个接口中的方法不超过三个,可以使用匿名内部类作为方法的实际参数进行传递。另外就是当某个类只需要使用一次的时候,也可以考虑使用匿名内部类。
四、内部类存在的意义
意义一:封装实现细节。下面用个简单的例子来演示一下。
/**
* Created by jay.zhou on 2018/2/23.
*/
//电源接口有个方法是 发电
public interface PowerSource {
void power();
}
//发电机,英语太差,只能用拼音了
class FaDianJi {
//太阳能
private class TaiYangNeng implements PowerSource {
@Override
public void power() {
System.out.println("太阳能发电成功");
}
}
//电池
private class DianChi implements PowerSource {
@Override
public void power() {
System.out.println("电池发电成功");
}
}
/**
* 发电机供电
*
* @param b 是否停电
* @return 能够发电的工具
*/
public PowerSource power(boolean b) {
TaiYangNeng taiYangNeng = new TaiYangNeng();
DianChi dianChi = new DianChi();
if(b){
return dianChi;
}else{
return taiYangNeng;
}
}
public static void main(String[] args) {
//有一个发电机
FaDianJi faDianJi = new FaDianJi();
//停电了
PowerSource powerSource = faDianJi.power(true);
powerSource.power();
//没停电
powerSource = faDianJi.power(false);
powerSource.power();
}
/**
* 运行结果:
* 电池发电成功
* 太阳能发电成功
*/
}
这个例子我想表达出,当某个方法将内部类对象“向上转型”为它的父类的时候,尤其转型成接口类型的时候,它的实现类将完全不可见。在"发电机"类的power(boolean b)方法中,有两个电源PowerSource的实现类,分别是“太阳能”对象和“电池”对象。此方法的返回值是一个接口类型,因此这两个对象在返回的时候将会自动发生“向上转型”。这样做的效果是,在我们只知道返回的是这个接口的实现类对象,无法知道这个类的具体实现。在应用的代码中,比如在主函数中,我们只能通过发电机拿到一个电源接口的实现类对象。但是这个电源是太阳能的,还是电池的,我们在客户端的代码上是看不到的,只知道发电机拿到的是一个电源的实现类对象。
内部类对象向上转型为它的接口形式的时候,在别的类中只能用到此接口的引用,无法知道其实现细节。
意义二:内部类间接支持“多重继承”
/**
* Created by jay.zhou on 2018/2/23.
*/
public class A {
public void show(){
System.out.println("父类继承到的show方法");
}
}
interface B{
void show();
}
class C extends A {
private class D implements B{
@Override
public void show() {
System.out.println("内部类实现的show方法");
}
}
public B getD(){
return new D();
}
}
class Apply2{
public static void apply1(A a){
a.show();
}
public static void apply2(B b){
b.show();
}
public static void main(String[] args) {
C c = new C();
apply1(c);
apply2(c.getD());
}
/**
* 父类继承到的show方法
* 内部类实现的show方法
*/
}
这个例子想说明:
类A有一个方法show(),接口B中也有一个抽象方法show()。类C继承了类A并且要求它实现B。
现在的问题在于,类C如果继承了A的话,那么它将会继承到A的show()方法,这将会自动实现B接口的show()。这并不是我们想要的,我们想要的是类C即要继承A的show()方法,又想自己实现B接口中的show()方法。那么类C使用内部类来实现接口,并且有一个方法将这个内部类对象,也就是这个实现类对象,向上转型为它的接口形式,作为方法的返回值,这个例子就是类C中getD()方法。现在的效果就是:类C的对象,既能作为它的父类A应用到客户端代码中,执行继承到的show()方法,也能作为接口B调用自己实现的show()方法。即继承到了父类的方法,又自己实现了接口中相同的方法。可谓是“既继承,又实现”,有了“多重继承”的感觉。
五、要点
外部类对象创建内部类对象的时候,内部类对象会秘密的捕获那个创建它的外部类对象的引用。
当一个方法的参数是一个接口类型的时候,并且这个接口中的方法不超过三个,可以使用匿名内部类作为方法的实际参数进行传递。
内部类对象向上转型为它的接口形式的时候,在别的类中只能用到此接口的引用,无法知道其实现细节。
内部类间接的支持了“多重继承”。
内部类存在的意义:隐藏实现、多重继承。