被遗忘的技术----内部类

一、内部类的定义

        将一个类的定义放在另外一个类的内部,这就是内部类。内部类的特点,我们用一个例子来引出。

/**
 * 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()方法。即继承到了父类的方法,又自己实现了接口中相同的方法。可谓是“既继承,又实现”,有了“多重继承”的感觉。

五、要点

        外部类对象创建内部类对象的时候,内部类对象会秘密的捕获那个创建它的外部类对象的引用。

        当一个方法的参数是一个接口类型的时候,并且这个接口中的方法不超过三个,可以使用匿名内部类作为方法的实际参数进行传递。

        内部类对象向上转型为它的接口形式的时候,在别的类中只能用到此接口的引用,无法知道其实现细节。

        内部类间接的支持了“多重继承”。    

        内部类存在的意义:隐藏实现、多重继承。

posted @ 2022-07-17 12:16  小大宇  阅读(20)  评论(0编辑  收藏  举报