Thinking in java 学习笔记(1)

来源:互联网 发布:武汉理工网络教育 编辑:程序博客网 时间:2024/05/16 02:36




  1. 面向对象程序设计的挑战之一,就是在问题空间的元素和解空间的对象之间创建一对一的映射。

  2. 将对象看成是服务提供者,作为好处之一是提高对象的内聚性。

  3. 将程序开发人员按照角色分为类创建者和客户端程序员。

  4. 访问控制存在的第一个原因就是让客户端程序员无法触及他们不应触及的部分--这些部分对数据类型的内部操作来说是必须的,但并不是用户解决特定问题所需的借口的一部分。访问控制的第二个存在原因就是允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员。

  5. JAVA有一种缺省的访问权限,当没有使用public,private,protected这些访问制定词(access specifier)时,它将发挥作用,这种权限通常被称为包访问权限,因为在这种权限下,类可以访问在同一个包中的其他类成员,但是在包之外,这些成员如同指定了private一样。

  6. 因为是在使用现有的类合成新的类,所以这种概念被称为组合(composition),如果组合是动态发生的,那么它通常被称为聚合(aggregation)。组合经常被视为“has-a”(拥有)关系,就像我们常说的“汽车拥有引擎”。

  7. 组合带来了极大的灵活性。新类的成员对象通常都被声明为private,使得使用新类的客户端程序员不能访问它们。这也使得你可以在不干扰现有客户端代码的情况下,修改这些成员。也可以在运行时修改这些成员对象,以实现动态修改程序的行为。而继承不具备这样的灵活性,因为编译器必须对通过继承而创建的类施加编译时的限制。但由于继承在面向对象程序设计中如此重要,所以它经常被高度强调,于是程序员新手就会有这样的印象:处处都应该使用继承。这会导致难以使用并过分复杂的设计。实际上,在创建新类时,应该首先考虑组合,因为它更加灵活。如果采用这种方式,设计会变得更加清晰。一旦有了一些经验以后,便能够看出必须使用继承的场合了。

  8. JAVA编译器不可能产生传统意义上的函数调用。一个非面向对象编程的编译器产生的函数调用会引起所谓的“前期邦定”,这么做意味着编译器将产生对一个具体函数名字的调用,而链接器将这个调用解析到将要被执行的代码的绝对地址。然而OOP中,程序直到运行时才能够确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的方法。为了解决这个问题,面向对象程序设计语言使用了后期邦定的概念。当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为是弱类型的),但并不知道将被执行的确切代码。为了执行后期邦定,JAVA使用一小段特殊的代码来替代绝对地址调用。这段代码使用对象中存储的信息来计算方法体的地址。在C++中如果希望声明某个方法具备后期邦定属性所带来的灵活性,那么用virtual来声明,方法在缺省情况下部是动态邦定的。而在JAVA中,动态邦定时时缺省行为,不需要添加额外的关键字来实现多态。

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

  10. 通常在一个设计中,你会希望基类仅仅表示其导出类的接口,也就是说,你不希望任何人创建基类的实际对象,而只是希望他们将对象向上转型到基类,所以它的接口将派上用场。这是通过abstract关键字把类标识成抽象类来实现的。如果有人试图创建一个abstract类的对象,编译器就会加以阻止。也可以使用abstract关键字来描述尚未被实现的方法,就像一个存根,用来表示“这是一个自此类继承来的所有类型都具有的接口方法,但是此刻还没有为它实际任何具体实现”。抽象法方只能在抽象类内部创建,当该类被继承时,抽象方法必须被事先,否则继承类仍然是一个抽象类。创建抽象方法可以使得你可以将一个方法置于接口中而不必为此方法提供任何可能毫无意义的方法体。

  11. Interface关键字比抽象类的概念更进了一步,它压根不允许有任何方法定义。接口是一个非常方便而通用的工具,因为它提供了接口与实现的完美分离。此外,只要愿意,就可以将多个接口组合到一起,而要从多个普通类或抽象类中继承却是不可能的。

  12. 在堆栈中创建存储空间和释放存储空间通常各需要一条汇编指令即可,分别对应将栈顶指针向上移动和向下移动。而创建堆存储空间的时间依赖于存储机制的实现。JAVA采用的是在被称为堆(heap)的内存池中动态的创建对象。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。然而如果是在堆上创建对象,编译器就会对他的生命周期一无所知。在象C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄露。JAVA提供被称为“垃圾回收器”的机制,它可以自动的发现对象何时不再被使用,并继而销毁它。

  13. 迭代器,它是一个用来选取容器中的元素,并把它呈现给迭代器用户的对象。作为一个类,他也提供了某种层次的抽象。这种抽象可以用来把容器的细节从访问容器的代码中分离出来。容器通过迭代器被简单的抽象为一个序列。迭代器允许遍历这个序列而不用担心底层的结构,也就是说,不用关心它是一个ArrayList,LinkedList,Stack还是其他什么东西。这提供了极大的灵活性,使得不用影响程序代码就可以十分方便的修改底层数据结构。JAVA1.1版本有一个为所有容器类设计的称为Enumeration的标准迭代器,JAVA2增加了一个完备的多的容器类库,其中包含一个称为Iterator的迭代器,它比老式的Enumeration有更多的功能。

  14. 除了C++以外的所有OOP语言都选在采用单根继承结构,JAVA的终极基类名称是Object。单根继承结构保证所有对象具备某些功能。因此你知道,在你的系统中你可以在每个对象上执行某些基本操作。单根继承结构以及在堆上创建所有对象,极大的简化了参数传递(这在C++中是十分复杂的话题之一)。同时单根继承结构是垃圾回收机制的实现变得容易的多。其必须的支持功能可以置于基类中,这样,垃圾回收器就可以发送恰当的消息给系统中的每一个对象。如果没有单根继承结构以及通过引用来操作对象的系统特性,要实现垃圾回收器将非常困难。由于所有对象都保证具有运行时类型信息,因此不会因无法确定对象的类型而陷入僵局。这对于系统级操作(如异常处理)显得尤其重要,并且给编程带来了更大的灵活性。

  15. 容器在使用时,把对象的引用置入其中,然后可以取出。但是由于容器只能存储Object,所以对象引用置入容器时,必须向上转型为Object,因此它会丢失其身份。当把它取回时,就获得了一个对Object对象的引用,这时需要向下转型,向上转型是安全的,向下则是非安全的(除非知道所要处理的对象的类型)。如果向下转型为错误的类型,会得到被称为异常的运行时错误。向下转型和运行时的检查需要额外的程序运行时间。C++采用参数化类型机制,即通过参数化类型,编译器可以定制一个只接纳和取出某一特定类型的对象的容器。C++中实现参数化类型的关键字是template(模板)。

  16. 异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中。异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。这使得代码编写变得简单,因为不需要被迫定期检查错误。此外,异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。异常不能够被忽略,所以它保证一定会在某处得到处理。异常提供了一种从错误状况进行可靠恢复的途径。现在不再只能是退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出健壮的程序。异常处理不是面向对象的特征--尽管在面向对象语言中异常经常被表示成对象,异常处理在面向对象语言出现之前就已经存在了。

  17. JAVA的线程是内置于语言中的,它使一个复杂的课题变得简单多了。线程机制被对象层次所支持,因此线程的执行可以用对象来表示。JAVA同时也提供了限制性资源锁定功能,它可以锁定任何对象所占用的内存(毕竟这也算某种共享资源),使得同一时刻只能由一个线程在使用它。这是通过synchronized关键字来实现的。其他类型的资源必须有程序员显示的锁定,通常是创建一个表示锁的对象,所有线程在访问资源之前应该先检查这个对象。

  18. JAVA提供对“轻量级持久性”的支持,这意味着可以很容易的将对象存储在磁盘上,并在以后取回。称之为轻量级是因为仍然要创建显示的调用来执行存储和取回操作。轻量级持久性可以通过对象序列化或者Java数据对象(JDO)来实现。