java编程思想读书笔记 第十章 内部类(下)

1.内部类的继承
因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,必须使用特殊的语法来明确说清它们之间的关联:

public class WithInner {
    class Inner{
    }
}public class IntheritInner extends WithInner.Inner{
    IntheritInner(WithInner wi){
        wi.super();
    }
    public static void main(String[] args) {
        WithInner withInner = new WithInner();
        IntheritInner ii = new IntheritInner(withInner);
    }
}

可以看到,IntheritInner只继承自内部类,而不是外围类。但是当要生成一个构造器时,默认的构造器并不算好,而且不能只是传递一个指向外围类对象的引用。此外,必须在构造器内使用如下语法:enclosingClassReference.super();这样才提供了必要的引用,才不会报错。

2.内部类可以为覆盖吗
创建一个内部类,然后继承其外围类并重新定义此内部类,也就是“覆盖”内部类,但是内部类就好像是外围类的一个方法,其实并不起什么作用。例子如下:

public class Egg {
    private Yolk y;
    protected class Yolk{
        public Yolk() {
            System.out.println("Egg.Yolk()");
        }
    }
    public Egg() {
        System.out.println("new Egg()");
        y = new Yolk();
    }
}
public class BigEgg extends Egg{
    public class Yolk{
    public Yolk() {
        System.out.println("BigEgg.Yolk()");
        }   
    }
    public static void main(String[] args) {
        new BigEgg();
    }

}
输出:new Egg()
           Egg.Yolk()

这个例子说明,当继承了某个外围类的时候,内部类并没有发生什么特别神器的变化。这两个内部类时完全独立的两个实体,各自在自己的命名空间内。

3.局部内部类
可以在代码块里创建内部类,典型的方式是在一个方法体里面创建内部类。局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块内的常量,以及此外围类的所有成员。下面的例子对局部内部类与匿名内部类的创建进行了比较,例子如下:

public interface Counter {
    int next();
}
public class LocalInnerClass {
    private int count = 0;
    Counter getCounter(final String name){
        class LocalCounter implements Counter{
            public LocalCounter(){
                System.out.println("LocalCounter()");
            }
            @Override
            public int next() {
                System.out.println(name);
                return count++;
            }
                    }       
        return new LocalCounter();
    }
    Counter getCounter2(final String name){
        return new Counter() {
            {
                System.out.println("Counter()");
            }
            @Override
            public int next() {
                System.out.println(name);
                return count++;
            }
        };
    }
    public static void main(String[] args) {
        LocalInnerClass lic = new LocalInnerClass();
        Counter c1 = lic.getCounter("Local inner");
        Counter c2 = lic.getCounter2("Anonymous inner");
        for (int i = 0; i < 5; i++) {
            System.out.println(c1.next());
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(c2.next());
        }
            }
}

Counter 返回的是系列中的下一个值。我们分别使用局部内部类和匿名内部类实现了这个功能,它们具有相同的行为和能力。既然局部内部类的名字在方法外是不可见的,那为什么我们仍然使用局部内部类而不是匿名内部类呢?唯一的理由是,我们需要一个已命名的构造器,或者需要重载构造器,而匿名内部类只能用于实例初始化。所以使用局部内部类而不使用匿名内部类的另一个理由就是,需要不止一个该内部类的对象。

4.为什么加上final后的局部变量就可以在内部类中使用了?
因为加上final后,编译器是这样处理内部类的:如果这个外部局部变量是常量,则在内部类代码中直接用这个常量;如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用,所以调用的实际是自己的属性而不是外部类方法的参数或局部变量。这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值