《Java编程思想》 第七章 复用类 记录

  • Java可以通过创建新类复用代码。
  • 第一种方法非常直观:只需在新的类中产生现有类的对象。由于新的类是由现有类的对象所组成,所以这种方法称为组合。该方法只复用了现有程序代码的功能,而非它的形式。
  • 第二种方法则更细致一些,他按照现有类的类型来创建新类。无需改变现有类的形式,采用现有类的形式并在其中添加新代码,这种方式称为继承,而且编译器可以完成其中大部分工作。

2. 继承语法

  • 继承涉及基类和导出类这两个类,而不是只有一个类,所以要试着想象导出类所产生的结果对象。从外部来看,他就像是一个与基类具有相同接口的新类,或许还会有一些额外的方法和域。但继承并不只是复制基类的接口。当创建了一个导出类的对象时,该对象包含一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者的区别在于,后者来源于外部,而基类的子对象被包装在导出类对象内部。
  • Java会自动在导出类的构造器中插入对基类构造器的调用。
  • 想调用一个带参数的基类构造器,就必须用关键词super显式地编写调用基类构造器的语句,并且配以适当的参数列表。

4. 结合使用组合和继承

  • 关键词try表示,下面的块(用一组大括号括起来的范围)是所谓的保护区,这意味着他需要被特殊处理。其中一项特殊处理就是无论try块是怎样退出的,保护区后的finally子句中的代码总是要被执行的。
  • @Override注解可以防止你在不想重载时而意外地进行了重载。

5. 在组合和继承之间选择

  • 组合技术通常用于想在新类中使用现有类的功能而非他的接口这种形式。即,在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。为取得此效果,需要在新类中嵌入一个现有类的private对象。
  • 有时,允许类的用户直接访问新类中的组合成分是极其意义的;也就是说,将成员对象声明为private。如果成员对象都隐藏了具体实现,那么这种做法是安全的。
  • “is-a”(是一个)的关系使用继承来表达的,而“has-a”(有一个)的关系则是用组合来表达的。

6. protected关键字

  • 在实际项目中,经常会想要将某些事物尽可能对这个世界隐藏起来,但仍然允许导出类的成员访问它们。
  • 尽管可以创建protected域,但是最好的方式还是将域保持为private;你应当一直保留下“更改底层实现”的权利。然后通过protected方法来控制类的继承者的访问权限。

7. 向上转型

  • 由导出类转型成基类,在继承图上是向上移动的,因此一般成为向上转型。由于向上转型是从一个较专用类型向较通用类型转换,所以总是很安全的。也就是说,导出类是基类的一个超集。它可能比基类含有更多的方法,但它必须至少具备基类中所含有的方法。
  • 在向上转型的过程中,类接口中唯一可能发生的事情就是丢失方法,而不是获取它们。这就是为什么编译器在“未曾明确表示转型”或“未曾指定特殊标记”的情况下,仍然允许向上转型的原因。

8. final关键词

  • 对于编译期常量这种情况,编译期可以将该常量值代入任何可能用到它的计算式中,也就是说,可以在编译时执行计算式,这减轻了一些运行时负担。在Java中,这类常量必须是基本数据类型,并且以关键词final表示,在对这个常量进行定义的时候,必须对其进行赋值。
  • 一个既是static又是final的域只占据一段不能改变的存储空间。
  • 当对对象引用而不是基本类型运用final时,其含义会有一些迷惑。对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。
  • Java允许在参数列表中以声明的方式将参数指明为final。这意味你无法在方法中更改参数引用所指向的对象。
  • 使用final方法把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。
  • 类中所有的private方法都隐式地指定为是final的。由于无法取用private方法,所以也就无法覆盖它。
  • “覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为他的基本类型并调用相同的方法。如果某方法为private,他就不是基类的接口的一部分。它仅是一些隐藏于类中的程序代码,只不过是具有相同的名称而已。但如果在导出类中以相同的名称生成一个public,protected或包访问权限方法的话,该方法就不会产生在基类中出现的“仅具有相同名称”的情况。此时你并没有覆盖该方法,仅是生成了一个新的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的原因存在外,其他任何事物都不需要考虑到他。
  • 当将某个类的整体定义为final的,(通过将关键字final置于它的定义之前),就表明了你不打算继承该类,而且也不允许别人这样做。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者出于安全的考虑,你不希望它有子类。
  • 请注意,final类的域可以根据个人的意愿选择为是或不是final。不论类是否被定义为final,相同的规则都适用于定义为final的域。然后,由于final类禁止继承,所以final类中所有的方法都隐式指定为是final的,因为无法覆盖它们。在final类中可以给方法添加final修饰词,但这不会增添任何意义。

9. 初始化及类的加载

  • 因为Java中的所有事物都是对象,每个类的编译代码都存在于他自己的独立文件中,该文件只需要使用程序代码时才会被加载。一般来说,可以说:“类的代码在初次使用时才加载。”这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载。
  • 构造器也是static方法,尽管关键词static并没有显式地写出来。因此更准确的说,类是在其任何static成员被访问时加载。
  • 初次使用之处也是static初始化发生之处。所有的static对象和static代码段都会在加载时依程序中的顺序(即,定义类时的书写顺序)而依次初始化。当然,定义为static的东西只会被初始化一次。
  • 运行Java时,发生的第一件事就是试图访问main(),然后加载器开始启动并找出该类的编译代码(在该类的.class文件中),在对它进行加载的过程中,如果有关键词extends,编译器就会注意到他有一个基类,于是它继续进行加载,不管你是否打算产生一个该基类的对象,这都要发生。
  • 如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的static初始化即会被执行,然后是下一个导出类,以此类推。这种方法很重要,因为导出类的static初始化可能会依赖于基类成员能否被正确初始化。
  • 至此为止,必要的类都已加载完毕,对象就可以被创建了。首先,对象中所有的基本类型都会被设为默认值,对象引用被设为null,这就是通过将对象内存设为二进制零值而一举生成的。然后,基类的构造器会被调用,也可以用super来指定对基类构造器的调用。基类构造器和导出类构造器一样,以相同的顺序来经历相同的过程。在基类构造器完成之后,实例变量按其次序被初始化。最后,构造器的其余部分被执行。
  • 即:执行基类static块,派生类static块;基类成员,基类构造器;派生类成员,派生类构造器

10. 总结

  • 继承和组合都能从现有类型生成新类型。组合一般是将现有类型作为新类型底层实现的一部分加以复用,而继承复用的是接口。
  • 在使用继承时,由于导出类具有基类接口,因此它可以向上转型至基类,这对多态来讲至关重要。
  • 尽管面向对象编程对继承极力强调,但在开始一个设计时,一般应优先选择使用组合(或者可能时代理),只是确保必要时才使用继承。因为组合更具灵活性。此外,通过对成员类型使用继承技术的添加技巧,可以在运行时改变那些成员对象的类型和行为。因此,可以在运行时改变组合而成的对象的行为。
发布了57 篇原创文章 · 获赞 11 · 访问量 9890

猜你喜欢

转载自blog.csdn.net/qq_36160730/article/details/96022667