Thinking in java笔记:《第十章: 内部类》

可以将一个类的定义放到另一个类的定义内部,这就是内部类。

10.1 创建内部类

如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体指明这个对象的类型:OuterClassname.InnerClassName

10.2 链接到外部类

当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种关系,所以他能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。
当某个外围类的对象创建一个内部类对象时,次内部类对象必定会秘密地捕获一个指向那个外围类对象的引用(内部类是非static类时)。

10.3 使用.this 和 .new

如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this:OuterClassName.this
要想直接创建内部类的对象,你不能按照你想象的方式,去引用外部类的名字,而是必须使用外部类的对象来创建该内部类的对象。

在拥有外部类对象之前是不可能创建内部类对象的。
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。

10.4 内部类和向上转型

当内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类有了特殊的作用。内部类关于某个接口的实现能够完全不可见,并且不可用。我们只能得到指向基类或者接口的引用,所以能够很方便地隐藏实现细节。

10.5 在方法和作用域内的内部类

又名局部内部类
使用场景:

  1. 你实现了某类型的接口,于是可以创建并返回对其的引用
  2. 你要解决一个复杂的问题,想创建一个雷来辅助你的解决方案,但是又不希望这个雷士公共可用的。

10.6 匿名内部类

看起来似乎是你正要返回一个对象。但是然后(在到达预付结束的分号之前)你却说:“等一等,我想在这里插入一个类的定义”。即:定义并立即返回一个类的实例。

  • 可以使用实例初始化达到匿名内部类的构造器的效果。但是不能重载实力初始化方法,所以你仅有一个这样的构造器。
  • 匿名内部类既可以扩展类,又可以实现接口,但是不能两者兼备。而且实现接口,也只能实现一个接口。

如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器要求其参数引用是final的。

10.7 嵌套类 static

  1. 要创建嵌套类的对象,并不需要其外围类的对象
  2. 不能从嵌套类的对象中访问非静态的外围类的对象。

10.7.1 接口内部的类

如果你想要创建某些公用代码,使得他们可以被某个接口的所有不同实现所公用,那么使用接口内部的嵌套类会显得很方便。

10.8 为什么需要内部类

每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对内部类都没有影响。
如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。
如果不需要解决“多重继承”的问题,那么自然可以用别的方式编码,而不需要使用内部类。但如果使用内部类,还可以获得其他一些特性:

  1. 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立
  2. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
  3. 创建内部类对象的时刻并不依赖于外围类对象的创建。
  4. 内部类没有令人迷糊的"is-a"关系,他就是一个独立地实体。

10.8.1闭包与回调

内部类是面向对象的闭包。

interface Incrementable{
    void increment();
}

class MyIncrement{
    public void increment(){
        System.out.println("Other operation");
    }
    public static void f(MyIncrement mi){
        mi.increment();
    }
}

class Callee1 implements Incrementable{

    private  int i=0;
    @Override
    public void increment() {
        i++;
        System.out.println(i);
    }
}
class Callee2 extends MyIncrement{
    private int i=0;

    @Override
    public void increment() {
        super.increment();
        i++;
        System.out.println(i);
    }
    private class Closure implements Incrementable{
        @Override
        public void increment() {
            Callee2.this.increment();
            System.out.println("Closure increment");
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}

class Caller{
    private Incrementable callbackRefrence;
    Caller(Incrementable cbh){
        callbackRefrence=cbh;
    }
    void go(){
        callbackRefrence.increment();
    }
}

public class Callbacks {
    public static void main(String[] args){
        Callee1 c1=new Callee1();
        Callee2 c2=new Callee2();
        MyIncrement.f(c2);
        Caller caller1=new Caller(c1);
        Caller caller2=new Caller(c2.getCallbackReference());
        caller1.go();
        caller1.go();
        caller2.go();
        caller2.go();
    }

}

Callee1是简单地解决方式.Callee2继承自MyIncrement,已经有一个不同的increment()方法,与Incrementable冲突且完全不想关。所以不能为了Incrementable的用途而覆盖inrement()方法,于是只能使用内部类独立地实现Inrementable。

在Callee2中除了getCallbackReference()以外,其他成员都是private的。只能通过interface Incrementable接口。这是一个安全的钩子(hook),无论谁获得Incrementable的引用,都只能调用increment()方法。

10.8.2 内部类与控制框架

在控制框架中使用内部类 ,可以:

  1. 控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装起来。内部类用来表示解决问题所需的各种不同的action()。
  2. 内部类能够很容易地访问外围类的任意成员,避免实现变得笨拙。

10.9 内部类的继承

语法:
enclosingClassReference.super();

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

10.10 内部类可以被覆盖吗?

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

10.11 局部内部类

可以在代码块里创建内部类,典型的方式是在一个方法体的里面创建。局部内部类不能有访问说明符,因为他不是外围类的一部分;但是他可以访问当前代码块内的常量,以及此外围类的所有成员。

使用局部内部类而不是匿名内部类的理由:

  1. 我们需要一个已命名的构造器,或者需要重载构造器,而匿名内部类只能用于实力初始化。
  2. 需要不止一个该内部类的对象。

10.12 内部类标识符

内部类类文件的命名有严格的规则:外围类的名字,加上"$",再加上内部类的名字。
如果内部类是匿名的,编译器会简单地产生一个数字作为其标识符。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 1.本章上来第一句就说了什么是内部类,一个在类的定义放在另一个的类内部。 2.内部类拥有外部类其外围类的所有元素的...
    Gzw丶南山阅读 159评论 0 0
  • 内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性。 如果想从外部类...
    Lemon_Home阅读 219评论 0 0
  • 今天整理一下内部类,其中包含了内部类的特殊形式,对比普通类有什么区别和作用,内部类和外围类之间的联系,内部类的扩展...
    _小二_阅读 603评论 0 3
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • 月夜色正好,幽静又寂寥。 微风不喧闹,虫儿也悄悄。 唯有霓虹场,坏了夜幕殇。 夜深无处去?迷途心仿徨。
    序小木阅读 258评论 37 19