转载

【Java编程思想】【笔记】

  • 将程序员开发人员按照角色分为类创建者(那些创建行数据类型的程序员)和客户端程序 员(那些在其应用中使用数据类型的类消费者)是大有裨益的。客户端程序员的目标是收 集各种用来实现快速应用开发的类。类创建者的目标是构建类,这种类只向客户端程序 员暴露必须的部分,而隐藏其他部分。
  • 访问控制存在的原因
    • 第一个存在的原因就是让客户端程序员无法触及他们不应该触及的部分。
    • 第二个存在的原因就是允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员。
  • JAVA边界值
    • public: 表示紧随其后的元素对任何人都是可用的。
    • private: 关键字表示除类型创建者和类型的内部方法以外的任何人都不能访问的元素。 private就像是你与客户端程序员之间的一堵砖墙,如果有人试图访问private成 就会在编译时得到错误信息。
    • protected: protected关键字与private作用相当,差别仅在于继承的类可以访问 protected成员,但是不能访问private成员。
    • 默认访问权限: 当没有使用前面提到的任何访问指定词时,它将发挥作用。这种权限通常被称为包访问权限,因为在这种权限下,类可以访问在同一个包(库构件)中的其他类的成员,但是在包之外,这些成员如同指定了private一样。

1.5 复用具体实现

  • 代码复用是面向对象程序设计语言锁提供的最了不起的优点之一。

  • 新的类型可以由任意数量,任意类型的其他对象以任意可以实现新的类中想要的功能的方式组成。

  • 因为是在使用现有的类合成新的其他对象的类,所以这种概念被称为组合(composition),如果组合是动态发生的,那么它通常被称为聚合(aggregation)。

  • 在建立新类时,应该首先考虑组合,因为它更加简单灵活。如果采用这种方式,设计会变得更加清晰,一旦有了一些经验之后,便能够看出必须使用继承的场合了。

1.6 继承

  • 当源类(被称为基类,超类或父类)发生变动时,被修改的“副本”(被称为导出类,继承类或者子类)也会反应出变动。

  • 可以创建一个基类来表示系统中某些对象的核心概念,从基类型中导出其他类型,来表示此核心可以被实现的各种不同方式。

  • 当继承现有类型时,也就创建了新的类型。这个新的类型不仅包括现在类型的所有成员(尽管private成员被隐藏起来,并且不可访问),而且更重要的是它复制了基类的接口。也就是说,所有可以发送给基类对象的消息同时也可以发送到导出类对象。

  • 通过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛!

  • 有两种方法可以使基类与导出类产生差异

      1. 直接在导出类中添加新方法。这些新方法并不是基类接口的一部分。这意味着基类并不能满足你的所有需求,因此必需添加更多的 方法。
      1. 使导出类和基类之间产生差异的方法是改变现有基类的方法的行为,这被称之为覆盖(overriding)那个方法。
  • 继承应该只覆盖基类的方法(而不添加在基类中没有的新方法),在某种意义上,这是一种处理继承的理想方式。我们经常将这种情况下的基类与导出类之间的关系称为is-a(是一个)关系。

  • 有时必须在导出类型添加新的接口元素,这样也就扩展了接口。这种情况我们可以描述为is-like-a(像是一个)关系。

1.7 伴随多态的可互换对象

  • 通过导出新的子类型而轻松扩展设计的能力是对改动进行封装的基本方式之一。

    这种能力可以极大地改善我们的设计,同时也降低软件维护的代价。

  • 面向对象程序设计的最重要妙诀:编译器不可能产生传统意义上的函数调用。一个非面向对象编程的编译器产生的函数调用会引起所谓的 前期绑定 ,这个术语你可能以前从未听说过,可能从未想过函数调用的其他方式。这么做意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。然而在OOP(面向对象编程)中,程序直到运行时才能够确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的机制。

  • 当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为是弱类型的),但是并不知道将被执行的确切代码。

  • 在java中,动态绑定是默认行为,不需要添加额外的关键字来实现多态。

  • 把将被导出类看做是它的基类的过程称为向上转型。

    转型这个名称的灵感来自于模型铸造的塑模动作;而向上(up)这个词来源于继承图的典型布局方式;通常基类在顶部,而导出类在其下部散开。因此,转型为一个基类就是在继承图中向上移动,即“向上转型”。

  • 正是因为多态才使得事情总是能够被正确处理。编译器和运行系统会处理相关的细节,你需要马上知道的只是事情会发生,更重要的是怎样通过它来设计。当向一个对象发送消息时,即使涉及向上转型,该对象也知道要执行什么样的正确行为。

1.8 单根继承结构

  • 在OOP中,是否所有的类最终都继承自单一的基类?答案是yes,这个终极基类的名字就是Object。 事实证明,单根继承结构带来了很多好处。
  • 在单根继承结构中的所有对象都具有一个共用的接口,所以它们归根到底都是相同的基本类型。
  • 单根继承结构保证所有对象都具备某些功能。
  • 单根继承结构使垃圾回收器的实现变得容易很多,而垃圾回收器正是Java相对C++的重要改进之一。 由于所有对象都保证具有其类型信息,因此不会因无法确定对象的类型而陷入僵局。这对于系统级操作(如异常处理)显得尤其重要,并且给编程带来了更大的灵活性。

1.9 容器(集合)

  • 容器(也称为集合,不过java类库以不同的含义使用“集合”这个术语,所以本书使用“容器”这个词),在任何需要时都可扩充自己以容纳你置于其中的所有东西。因此不需要知道将来会把多少个对象置于容器中,只需要创建一个容器对象,然后让它处理所有细节。

  • Java容器

    • List(用于存储序列)
    • Map(也被称为关联数组,是用来建立对象之间的关联)
    • Set(每种对象类型只持有一个)
    • 诸多队列,树,堆栈等更多的构件。
  • 使用时还是要对容器有所选择,原因如下:

    • 不同容器提供了不同类型的接口和外部行为。堆栈相比于队列就具备不同的接口和行为,也不同于集合列表的接口和行为。它们之中的某些容器提供的解决方案可能要比其他容器灵活得多。
    • 不同的容器对于某些操作具有不同的效率。最好的例子就是两种List的比较:ArrayList和LinkedList。它们都是具有相同接口和外部行为的简单的序列,但是它们对于某些操作所花费的代价却有天壤之别。在ArrayList中,随机访问元素是一个花费固定时间的操作;但是,对于LinkedList来说,随机选取元素需要在列表中移动,这种代价是高昂的,访问越靠近表尾的元素,花费的时间越长。而另一方面,如果想在序列中间插入一个元素,LinkedList的开销却比ArrayList要小。
  • 我们可以在一开始使用LinkedList构建程序,而在优化系统性能时改用ArrayList。接口List所带来的抽象,把在容器之间进行转换时对代码产生的影响降低到最小限度。

  • 参数化类型(范型) 在JavaSE5之前,容器存储的对象都只具有Java中的通用类型:Object。

    单根继承结构意味着所有东西都是Object类型 ,所以可以存储Object的容器可以存储任何东西。这使得容器很容易被复用。

  • 向上转型是安全的,例如Circle是一种Shape类型;但是不知道某个Object是Circle还是Shape,所以除非确切知道所要处理的对象的类型,否则向下转型几乎是不安全的。

  • 如果向下转型错误的话,就会得到被称为异常的运行时错误。

  • 参数化类型(范型)就是一个编译器可以自动定制用于特定类型上的类。例如,通过使用参数化类型,编译器可以定制一个只接纳和取出Shape对象的容器。

  • 为了利用泛型的优点,很多标准类库构建都已经进行了修改。就像我们将要看到的那样,范型对本书中的许多代码都产生了重要的影响。

1.10 对象的创建和生命期

  • 在使用对象时,最关键的问题之一便是它们的生成和销毁方式。

    每个对象为了生存都需要资源,尤其是内存。当我们不再需要一个对象时,它必须被清理掉。使其占有的资源可以被释放和重用。

  • 为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定。

    • 这可以通过将对象置于堆栈(它们有时被称为自动变量)或限域变量或静态存储区域内来实现。

      这种方式将存储空间分配和释放置于优先考虑的位置,某些情况下这样的控制非常有价值。但是,也牺牲了灵活性,因为必须在编写程序时知道对象确切的数量,生命周期和类型。

    • 第二种方式是在被称为堆(heap)的内存池中动态地创建对象。

      在这种方式中,直到运行时才知道需要多少对象,它们的生命周期如何,以及它们的具体类型是什么。

  • 如果需要一个新对象,可以在需要的时刻直接在堆中创建。因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间。

  • 创建堆栈存储空间的时间依赖于存储机制的设计

  • 动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂。所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。

  • java完全采用动态内存分配方式。每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。

  • 还有一个议题,就是生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。

  • java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。

    垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码。更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄漏问题。

  • java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其他方面)。垃圾回收器“知道”对象何时不再被使用,并自动释放对象占用的内存。

1.11 异常处理:处理错误

  • 异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。异常处理就像是与程序正常执行路径并行的、在错误发生时执行的另一条路径。因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。
  • 被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略,异常不能被忽略,所以它保证一定会在某处得到处理。
  • 异常提供了一种从错误状况进行可靠恢复的途径。现在不再是只能退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出更健壮的程序。

1.12 并发编程

  • 在计算机编程中有一个基本的概念,就是在同一时刻处理多个任务的思想。
  • 在程序中,彼此独立运行的部分称之为线程。
  • 对于大量的问题,把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。称之为“并发”。
  • 线程只是一种为单一处理器分配执行时间的手段。
  • 如果有多个并行任务都要访问同一项资源,那么就会出问题。例如,两个进程不能同时向一台打印机发送信息。为了解决这个问题,可以共享的资源,例如打印机,必须在使用期间被锁定。因此,整个过程是:某个任务锁定某项资源,完成其任务,然后释放资源锁,使其他任务可以使用这项资源。

第2章 一切都是对象

"如果我们说另一种不同的语言,那么我们就会发觉一个有些不同的世界。——Luduing Wittgerstein( 1889~1951 )"

2.1 用引用操纵对象

  • java中,一切都被视为对象,因此可采用单一的语法。尽管一切都看作是对象,但操作的标识符实际上是对象的一个“引用”。

    例:遥控器(引用)操作电视机(对象)。

    • java操作的标识符实际上是对象的一个“引用”
    • java操作的标识符实际上是对象的一个“引用”
    • java操作的标识符实际上是对象的一个“引用”

2.2 必须由你创建所有对象

  • new 关键字的意思是“给我一个新对象”。
  • 存储数据的地方:
    • 寄存器:最快的存储区,但由于寄存器的数量极其有限,所以根据需求进行分配。你不能直接控制,在程序中也感觉不到寄存器存在的任何迹象。

    • 堆栈:位于通用RAM(随机访问存储器)中。堆栈指针若往下移则分配新内存,若往上移动则释放内存。

      堆栈:就是STACK。实际上是只有一个出入口的队列,即后进先出(First In Last Out),先分配的内存必定后释放。一般由系统自动分配,存放函数的参数值,局部变量等,自动清除。

      堆栈是每个函数进入的时候分一小块,函数返回的时候就释放了

      局部变量放在堆栈中,所以函数返回,局部变量就全没了。

    • 堆:一种通用的内存池(位于RAM区), 用来存放所有的Java对象。

      当需要一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。

      用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间。

    • 常量存储:常量值通常是直接存放在程序代码内部。

    • 非RAM存储:如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行也可以存在。例如:流对象和持久化对象。

      【Java编程思想】【笔记】
  • 基本数据类型
    • 基本数据类型不用new来创建对象,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更加高效。
    • 所有数据类型都有正负号,没有无符号的数值类型。
    • 基本类型具有的包装器类,使得可以在堆中创建一个非基本对象,用来表示对应的及本类型。
    • java SE5的自动包装功能将自动地将基本数据类型转换成包装器类型。
    • java提供了两个用于高精度计算的类:BigInteger(支持任意精度的整数)和BigDecimal(支持任何精度的定点数)。
原文  https://juejin.im/post/5dbb7ac2f265da4d4b5fe497
正文到此结束
Loading...