java编程思想总结-下

java编程思想总结(下)

第7章多边形

  • 若使用接口,我们可以同时获得抽象类以及接口的好处。所以假如想创建的基础类没有任何方法定义或者成员变量,那么无论如何都愿意使用接口,而不要选择抽象类。事实上,如果事先知道某种东西会成为基础类,那么第
    一个选择就是把它变成一个接口。只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。
  • 通常,我们只能对单独一个类应用 extends(扩展)关键字。但由于接口可能由多个其他接口构成,所以在构建一个新接口时,extends可能引用多个基础接口。正如大家看到的那样,接口的名字只是简单地使用逗号分隔。
  • 若我们在一个方法甚至一个任意的作用域内创建内部类。有两方面的原因促使我们这样做:
    • (1) 正如前面展示的那样,我们准备实现某种形式的接口,使自己能创建和返回一个句柄。
    • (2) 要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开。
  • 内部类可以对封装类的私有属性进行设置- 我们引用封装类的一个成员时,就利用那个(隐藏)的引用来选择那个成员。幸运的是,编译器会帮助我们照管所有这些细节。但我们现在也可以理解内部类的一个对象只能与封装类的一个对象联合创建。在这个创建过程中,要求对封装类对象的句柄进行初始化。若不能访问那个句柄,编译器就会报错。进行所有这些操作的时候,大多数时候都不要求程序员的任何介入。
  • ​这意味着对于一个复杂的对象,构建器的调用遵照下面的顺序:
    • (1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。
    • (2) 按声明顺序调用成员初始化模块。
    • (3) 调用衍生构建器的主体。
  • finalize()GC –首先执行衍生类的收尾,再是基础类的收尾。这是由于衍生类的收尾可能调用基础类中相同的方法,要求基础类组件仍然处于活动状态。因此,必须提前将它们清除(破坏)。
  • 设计构建器–因此,设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于private方法,它们自动具有final 属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。
  • 一个更好的思路是首先选择“合成”——如果不能十分确定自己应使用哪一个。合成不会强迫我们的程序设
    计进入继承的分级结构中。同时,合成显得更加灵活,因为可以动态选择一种类型(以及行为),而继承要
    求在编译期间准确地知道一种类型。常规的设计准则是:用继承表达行为间的差异,并用成员变量表达状态的变化。

第8章对象的容纳

  • Java 提供了四种类型的“集合类”:Vector(矢量)、BitSet(位集)、Stack(堆栈)以及
    Hashtable(散列表)
  • 缺点:这是一个很好的方案,只是不适用下述场合:
    • (1) 将一个对象句柄置入集合时,由于类型信息会被抛弃,所以任何类型的对象都可进入我们的集合——即
      便特别指示它只能容纳特定类型的对象。举个例子来说,虽然指示它只能容纳猫,但事实上任何人都可以把
      一条狗扔进来。(值得欣慰的是,Java 不允许人们滥用置入集合的对象)
    • (2) 由于类型信息不复存在,所以集合能肯定的唯一事情就是自己容纳的是指向一个对象的句柄。正式使用
      它之前,必须对其进行造型,使其具有正确的类型
  • Set拥有与 Collection完全相同的接口,所以和两种不同的 List 不同,它没有什么额外的功能。相反,Set
    完全就是一个Collection,只是具有不同的行为。
  • 会注意到由HashSet 维持的顺序与ArraySet 是不同的。这是由于它们采用了不同的方法来保存元素,以便它们以后的定位。ArraySet 保持着它们的顺序状态,而HashSet使用一个散列函数,这是特别为快速检索设计的)。创建自己的类型时,一定要注意 Set需要通过一种方式来维持一种存储顺序.
  • Map(接口) 维持“键-值”对应关系(对),以便通过一个键查找相应的值
    • HashMap:基于一个散列表实现(用它代替Hashtable)。针对“键-值”对的插入和检索,这种形式具有
      最稳定的性能。可通过构建器对这一性能进行调整,以便设置散列表的“能力”和“装载因子”
    • ArrayMap 由一个 ArrayList后推得到的Map。对反复的顺序提供了精确的控制。面向非常小的 Map设计,特
      别是那些需要经常创建和删除的。对于非常小的Map,创建和反复所付出的代价要比 HashMap低得多。但在Map变大以后,性能也会相应地大幅度降低
    • TreeMap 在一个“红-黑”树的基础上实现。查看键或者“键-值”对时,它们会按固定的顺序排列(取决
      于Comparable 或Comparator,稍后即会讲到)。TreeMap最大的好处就是我们得到的是已排好序的结果。TreeMap是含有 subMap()方法的唯一一种Map,利用它可以返回树的一部分。
  • ArrayList(以及 Vector)是由一个数组后推得到的;而LinkedList是根据常规的双重链接列表方式实现的,因为每个单独的对象都包含了数据以及指向列表内前后元素的句柄。正是由于这个原因,假如想在一个列表中部进行大量插入和删除操作,那么LinkedList 无疑是最恰当的选择(LinkedList还有一些额外的功能,建立于
    AbstractSequentialList 中)。若非如此,就情愿选择ArrayList,它的速度可能要快一些。作为另一个例子,
  • Set既可作为一个ArraySet 实现,亦可作为HashSet 实现。ArraySet 是由一个ArrayList后推得到的,设计成只支持少量元素,特别适合要求创建和删除大量Set 对象的场合使用。然而,一旦需要在自己的Set中容纳大量元素,ArraySet 的性能就会大打折扣。写一个需要 Set的程序时,应默认选择HashSet。而且只有在某些特殊情况下(对性能的提升有迫切的需求),才应切换到 ArraySet
  • 可以看出,在ArrayList 中进行随机访问(即get())以及循环反复是最划得来的;但对于LinkedList 却是
    一个不小的开销。但另一方面,在列表中部进行插入和删除操作对于LinkedList 来说却比ArrayList 划算得
    多。我们最好的做法也许是先选择一个ArrayList 作为自己的默认起点。以后若发现由于大量的插入和删除
    造成了性能的降低,再考虑换成LinkedList 不迟。(测试代码P250)
  • 进行add()以及contains()操作时,HashSet显然要比 ArraySet 出色得多,而且性能明显与元素的多寡关系
    不大。一般编写程序的时候,几乎永远用不着使用 ArraySet。(测试代码P250)
  • 即使大小为 10,ArrayMap 的性能也要比HashMap 差——除反复循环时以外。而在使用Map时,反复的作用通
    常并不重要(get()通常是我们时间花得最多的地方)。TreeMap 提供了出色的 put()以及反复时间,但 get()
    的性能并不佳。但是,我们为什么仍然需要使用TreeMap 呢?这样一来,我们可以不把它作为Map使用,而
    作为创建顺序列表的一种途径。树的本质在于它总是顺序排列的,不必特别进行排序(它的排序方式马上就
    要讲到)。一旦填充了一个TreeMap,就可以调用 keySet()来获得键的一个Set“景象”。然后用 toArray()
    产生包含了那些键的一个数组。随后,可用static 方法Array.binarySearch()快速查找排好序的数组中的
    内容。当然,也许只有在HashMap 的行为不可接受的时候,才需要采用这种做法。因为HashMap 的设计宗旨
    就是进行快速的检索操作。最后,当我们使用 Map时,首要的选择应该是 HashMap。只有在极少数情况下才
    需要考虑其他方法。(测试代码P252)
    • TreeMap的创建速度比其他两种类型明显快得多(但你应亲自尝试一下,因为据说新版本可能会改善ArrayMap的性能)。考虑到这方面的原因,同时由于前述 TreeMap出色的 put()性能,所以如果需要创建大量Map,而且只有在以后才需要涉及大量检索操作,那么最佳的策略就是:创建和填充TreeMap;以后检索量增大的时候,再将重要的TreeMap 转换成HashMap——使用HashMap(Map)构建器。同样地,只有在事实证明确实存在性能瓶颈后,才应关心这些方面的问题——先用起来,再根据需要加快速度。
  • 新集合也提供了能防止多个进程同时修改一个容器内容的机制。若在一个容器里反复,同时另一些进程介
    入,并在那个容器中插入、删除或修改一个对象,便会面临发生冲突的危险。我们可能已传递了那个对象,
    可能它位位于我们前面,可能容器的大小在我们调用size()后已发生了收缩——我们面临各种各样可能的危
    险。针对这个问题,新的集合库集成了一套解决的机制,能查出除我们的进程自己需要负责的之外的、对容
    器的其他任何修改。若探测到有其他方面也准备修改容器,便会立即产生一ConcurrentModificationException(并发修改违例)。我们将这一机制称为“立即失败”——它并不用更复杂的算法在“以后”侦测问题,而是“立即”产生违例。 (hashMap Or ArrayList底层都有其实现原理,就是计数)

第9章违例差错控制

  • 违例条件”表示在出现什么问题的时候应中止方法或作用域的继续。
  • 产生一个违例时,会发生几件事情。首先,按照与创建Java 对象一样的方法创建违例对象:在内存“堆”
    里,使用new来创建。随后,停止当前执行路径(记住不可沿这条路径继续下去),然后从当前的环境中释
    放出违例对象的句柄。此时,违例控制机制会接管一切,并开始查找一个恰当的地方,用于继续程序的执
    行。这个恰当的地方便是“违例控制器”,它的职责是从问题中恢复,使程序要么尝试另一条执行路径,要
    么简单地继续。
  • 若调用了break 和continue 语句,finally 语句也会得以执行。请注意,与作上标签的break 和continue
    一道,finally排除了 Java 对goto 跳转语句的需求。
  • 违例准则:用违例做下面这些事情:
    • (1) 解决问题并再次调用造成违例的方法。
    • (2) 平息事态的发展,并在不重新尝试方法的前提下继续。
    • (3) 计算另一些结果,而不是希望方法产生的结果。
    • (4) 在当前环境中尽可能解决问题,以及将相同的违例重新“掷”出一个更高级的环境。
    • (5) 在当前环境中尽可能解决问题,以及将不同的违例重新“掷”出一个更高级的环境。
    • (6) 中止程序执行。
    • (7) 简化编码。若违例方案使事情变得更加复杂,那就会令人非常烦恼,不如不用。
    • (8) 使自己的库和程序变得更加安全。这既是一种“短期投资”(便于调试),也是一种“长期投资”(改善应用程序的健壮性)
  • 通过先进的错误纠正与恢复机制,我们可以有效地增强代码的健壮程度。对我们编写的每个程序来说,错误恢复都属于一个基本的考虑目标。它在Java 中显得尤为重要,因为该语言的一个目标就是创建不同的程序组件,以便其他用户(客户程序员)使用。为构建一套健壮的系统,每个组件都必须非常健壮。
    在Java 里,违例控制的目的是使用尽可能精简的代码创建大型、可靠的应用程序,同时排除程序里那些不能控制的错误。

你可能感兴趣的:(java)