JAVA THINGKING (一)

保存数据位置:
  (1) 寄存器。这是最快的保存区域,由于它位于和其余全部保存方式不一样的地方:处理器内部。
  (2) 堆栈。驻留于常规RAM(随机访问存储器)区域,这是一种特别快、特别有效的数据保存方式,仅次于寄存器。建立程序时,Java编译器必须准确地知道堆栈内保存的全部数据的“长度”以及“存在时间”。Java“对象句柄”以及“基本数据类型”也保存在堆栈,基本数据包括:boolean/char/byte /short/int/long/float/double但Java对象并不放到其中。
  (3) 堆。一种常规用途的内存池(也在RAM区域),其中保存了Java对象。和堆栈不一样,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器没必要知道要从堆里分配多少存储空间,也没必要知道存储的数据要在堆里停留多长的时间。所以,用堆保存数据时会获得更大的灵活性。要求建立一个对象时,只需用new命令编制相关的代码便可。执行这些代码时,会在堆里自动进行数据的保存。固然,为达到这种灵活性,必然会付出必定的代价:在堆里分配存储空间时会花掉更长的时间!
  (4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象自己永远都不会置入静态存储空间。
  (5) 常数存储。常数值一般直接置于程序代码内部。这样作是安全的,由于它们永远都不会改变。有的常数须要严格地保护,因此可考虑将它们置入只读存储器(ROM)。
  (6) 非RAM存储。若数据彻底独立于一个程序以外,则程序不运行时仍可存在,并在程序的控制范围以外。其中两个最主要的例子即是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,一般会发给另外一台机器。而对于固定对象,对象保存在磁盘中。即便程序停止运行,它们仍可保持本身的状态不变。程序员

抽象的进步
  全部编程语言的最终目的都是提供一种“抽象”方法。一种较有争议的说法是:解决问题的复杂程度直接取决于抽象的种类及质量。汇编语言是对基础机器的少许抽象。后来的许多“命令式”语言(如FORTRAN,BASIC和C)是对汇编语言的一种抽象。与汇编语言相比,这些语言已有了长足的进步,但它们的抽象原理依然要求咱们着重考虑计算机的结构,而非考虑问题自己的结构。在机器模型与实际解决的问题模型之间,程序员必须创建起一种联系。
  为机器建模的另外一个方法是为要解决的问题制做模型。面向对象的程序设计在此基础上则跨出了一大步,程序员可利用一些工具表达问题空间内的元素。因为这种表达很是广泛,因此没必要受限于特定类型的问题。咱们将问题空间中的元素以及它们在方案空间的表示物称做“对象”(Object)。固然,还有一些在问题空间没有对应体的其余对象。经过添加新的对象类型,程序可进行灵活的调整,以便与特定的问题配合。因此在阅读方案的描述代码时,会读到对问题进行表达的话语。与咱们之前见过的相比,这无疑是一种更加灵活、更增强大的语言抽象方法。总之,OOP容许咱们根据问题来描述问题,而不是根据方案。然而,仍有一个联系途径回到计算机。每一个对象都相似一台小计算机;它们有本身的状态,并且可要求它们进行特定的操做。与现实世界的“对象”或者“物体”相比,编程“对象”与它们也存在共通的地方:它们都有本身的特征和行为。数据库

  (1) 全部东西都是对象。
  (2) 程序是一大堆对象的组合;经过消息传递,各对象知道本身该作些什么。为了向对象发出请求,需向那个对象“发送一条消息”。
  (3) 每一个对象都有本身的存储空间,可容纳其余对象。
  (4) 每一个对象都有一种类型。根据语法,每一个对象都是某个“类”的一个“实例”。
  (5) 同一类全部对象都能接收相同的消息。编程

 

访问控制:数组

  一个缘由是防止程序员接触他们不应接触的东西——一般是内部数据类型的设计思想。若只是为了解决特定的问题,用户只需操做接口便可,毋需明白这些信息。浏览器

  第二个缘由是容许库设计人员修改内部结构,不用担忧它会对客户程序员形成什么影响。安全

继承:服务器

  咱们费尽心思作出一种数据类型后,假如不得不又新建一种类型,令其实现大体相同的功能,那会是一件很是使人灰心的事情。但若能利用现成的数据类型,对其进行“克隆”,再根据状况进行添加和修改,状况就显得理想多了。“继承”正是针对这个目标而设计的。但继承并不彻底等价于克隆。在继承过程当中,若原始类(正式名称叫做基础类、超类或父类)发生了变化,修改过的“克隆”类(正式名称叫做继承类或者子类)也会反映出这种变化。在Java语言中,继承是经过extends关键字实现的网络

  使用继承时,至关于建立了一个新类。这个新类不只包含了现有类型的全部成员,但更重要的是,它复制了基础类的接口。也就是说,可向基础类的对象发送的全部消息亦可原样发给衍生类的对象。根据能够发送的消息,咱们能知道类的类型。这意味着衍生类具备与基础类相同的类型!为真正理解面向对象程序设计的含义,首先必须认识到这种类型的等价关系。
  因为基础类和衍生类具备相同的接口,因此那个接口必须进行特殊的设计。也就是说,对象接收到一条特定的消息后,必须有一个“方法”可以执行。若只是简单地继承一个类,并不作其余任何事情,来自基础类接口的方法就会直接照搬到衍生类。这意味着衍生类的对象不只有相同的类型,也有一样的行为,这一后果一般是咱们不肯见到的。多线程

  有两种作法可将新得的衍生类与原来的基础类区分开。第一种作法十分简单:为衍生类添加新函数(功能)。这是一种最简单、最基本的继承用法。第二种方法是:改善基础类,即类的重写和多态。在C++中,这个关键字是virtual。在Java中,咱们则彻底没必要记住添加一个关键字,由于函数的动态绑定是自动进行的。编程语言

  另外,上溯造型确定是安全的,由于咱们是从一个更特殊的类型到一个更常规的类型。换言之,衍生类是基础类的一个超集。它能够包含比基础类更多的方法,但它至少包含了基础类的方法。进行上溯造型的时候,类接口可能出现的惟一一个问题是它可能丢失方法,而不是赢得这些方法。这即是在没有任何明确的造型或者其余特殊标注的状况下,编译器为何容许上溯造型的缘由所在。

 

代码重用-合成与继承:
  第一个最简单:在新类里简单地建立原有类的对象。咱们把这种方法叫做“合成”,由于新类由现有类的对象合并而成。咱们只是简单地重复利用代码的功能,而不是采用它的形式。
  第二种方法就是继承。

  不管合成仍是继承,都容许咱们将子对象置于本身的新类中。你们或许会奇怪二者间的差别,以及到底该如何选择。
  若是想利用新类内部一个现有类的特性,而不想使用它的接口,一般应选择合成。也就是说,咱们可嵌入一个对象,使本身能用它实现新类的特性。但新类的用户会看到咱们已定义的接口,而不是来自嵌入对象的接口。考虑到这种效果,咱们需在新类里嵌入现有类的private对象。有些时候,咱们想让类用户直接访问新类的合成。也就是说,须要将成员对象的属性变为public。成员对象会将自身隐藏起来,因此这是一种安全的作法。并且在用户知道咱们准备合成一系列组件时,接口就更容易理解。不然选择继承。

  在面向对象的程序设计中,建立和使用代码最可能采起的一种作法是:将数据和方法统一封装到一个类里,而且使用那个类的对象。有些时候,需经过“合成”技术用现成的类来构造新类。而继承是最少见的一种作法。所以,尽管继承在学习OOP的过程当中获得了大量的强调,但并不意味着应该尽量地处处使用它。相反,使用它时要特别慎重。只有在清楚知道继承在全部方法中最有效的前提下,才可考虑它。为判断本身到底应该选用合成仍是继承,一个最简单的办法就是考虑是否须要重新类上溯造型回基础类。若必须上溯,就须要继承。但若是不须要上溯造型,就应提醒本身防止继承的滥用。在下一章里(多形性),会向你们介绍必须进行上溯造型的一种场合。但只要记住常常问本身“我真的须要上溯造型吗”,对于合成仍是继承的选择就不该该是个太大的问题。

 

对象的建立和存在时间:
  最重要的问题之一是对象的建立及破坏方式。对象须要的数据位于哪儿,如何控制对象的“存在时间”呢?针对这个问题,解决的方案是各异其趣的。C++认为程序的执行效率是最重要的一个问题,因此它容许程序员做出选择。为得到最快的运行速度,存储以及存在时间可在编写程序时决定,只需将对象放置在堆栈(有时也叫做自动或定域变量)或者静态存储区域便可。这样便为存储空间的分配和释放提供了一个优先级。
  第二个方法是在一个内存池中动态建立对象,该内存池亦叫“堆”或者“内存堆”。若采用这种方式,除非进入运行期,不然根本不知道到底须要多少个对象,也不知道它们的存在时间有多长,以及准确的类型是什么。因为存储空间的管理是运行期间动态进行的,因此在内存堆里分配存储空间的时间比在堆栈里建立的时间长得多。
C++容许咱们决定是在写程序时建立对象,仍是在运行期间建立,这种控制方法更加灵活。程序员可用两种方法来破坏一个对象:用程序化的方式决定什么时候破坏对象,或者利用由运行环境提供的一种“垃圾收集器”特性,自动寻找那些再也不使用的对象,并将其清除。固然,垃圾收集器显得方便得多,但出于效率未能包括到C++里。但Java确实提供了一个垃圾收集器。

 集合与继承器:
  针对一个特定问题的解决,若是事先不知道须要多少个对象,或者它们的持续时间有多长,那么也不知道如何保存那些对象。既然如此,怎样才能知道那些对象要求多少空间呢?事先上根本没法提早知道,除非进入运行期。因此咱们事先没必要知道要在一个集合里容下多少东西。只需建立一个集合,之后的工做让它本身负责好了。在C++中,它们是以“标准模板库”(STL)的形式提供的。而Java也用本身的标准库提供了集合。

  全部集合都提供了相应的读写功能。

单根结构:
  在Java中(与其余几乎全部OOP语言同样),对这个问题的答案都是确定的,并且这个终级基础类的名字很简单,就是一个“Object”。这种“单根结构”具备许多方面的优势。单根结构中的全部对象都有一个通用接口,因此它们最终都属于相同的类型。

  另外一种方案(就象C++那样)是咱们不能保证全部东西都属于相同的基本类型。从向后兼容的角度看,这一方案可与C模型更好地配合,并且能够认为它的限制更少一些。但假期咱们想进行纯粹的面向对象编程,那么必须构建本身的结构,以期得到与内建到其余OOP语言里的一样的便利。需添加咱们要用到的各类新类库,还要使用另外一些不兼容的接口。
  JAVA单根结构中的全部对象均可以保证拥有一些特定的功能。在本身的系统中,咱们知道对每一个对象都能进行一些基本操做。一个单根结构,加上全部对象都在内存堆中建立,能够极大简化参数的传递(这在C++里是一个复杂的概念)。
  利用单根结构,咱们能够更方便地实现一个垃圾收集器。与此有关的必要支持可安装于基础类中,而垃圾收集器可将适当的消息发给系统内的任何对象。若是没有这种单根结构,并且系统经过一个句柄来操纵对象,那么实现垃圾收集器的途径会有很大的不一样,并且会面临许多障碍。
  因为运行期的类型信息确定存在于全部对象中,因此永远不会遇到判断不出一个对象的类型的状况。这对系统级的操做来讲显得特别重要,好比违例控制;并且也能在程序设计时得到更大的灵活性。
既然你把好处说得这么天花乱坠,为何C++没有采用单根结构呢?事实上,这是早期在效率与控制上权衡的一种结果。单根结构会带来程序设计上的一些限制。并且更重要的是,它加大了新程序与原有C代码兼容的难度。尽管这些限制仅在特定的场合会真的形成问题,但为了得到最大的灵活程度,C++最终决定放弃采用单根结构这一作法。而Java不存在上述的问题,它是全新设计的一种语言,没必要与现有的语言保持所谓的“向后兼容”。因此很天然地,与其余大多数面向对象的程序设计语言同样,单根结构在Java的设计方案中很快就落实下来。

集合库与方便使用集合-单根结构的便利
  因为集合是咱们常常都要用到的一种工具,因此一个集合库是十分必要的,它应该能够方便地重复使用
下溯造型与模板/通用性
  为了使这些集合可以重复使用,或者“再生”,Java提供了一种通用类型,之前曾把它叫做“Object”。单根结构意味着、全部东西归根结底都是一个对象”!因此容纳了Object的一个集合实际能够容纳任何东西。这使咱们对它的重复使用变得很是简便。为使用这样的一个集合,只需添加指向它的对象句柄便可,之后能够经过句柄从新使用对象。

  但因为集合只能容纳Object,因此在咱们向集合里添加对象句柄时,它会上溯造型成Object,这样便丢失了它的身份或者标识信息。再次使用它的时候,会获得一个Object句柄,而非指向咱们早先置入的那个类型的句柄。因此怎样才能归还它的原本面貌,调用早先置入集合的那个对象的有用接口呢?在这里,咱们再次用到了造型(Cast)。但这一次不是在分级结构中上溯造型成一种更“通用”的类型。而是下溯造型成一种更“特殊”的类型。这种造型方法叫做“下溯造型”(Downcasting)。举个例子来讲,咱们知道在上溯造型的时候,Circle(圆)属于Shape(几何形状)的一种类型,因此上溯造型是安全的。但咱们不知道一个Object究竟是Circle仍是Shape,因此很难保证下溯造型的安全进行,除非确切地知道本身要操做的是什么。但这也不是绝对危险的,由于假以下溯造型成错误的东西,会获得咱们称为“违例”(Exception)的一种运行期错误。但在从一个集合提取对象句柄时,必须用某种方式准确地记住它们是什么,以保证下溯造型的正确进行。下溯造型和运行期检查都要求花额外的时间来运行程序,并且程序员必须付出额外的精力。既然如此,咱们能不能建立一个“智能”集合,令其知道本身容纳的类型呢?这样作可消除下溯造型的必要以及潜在的错误。答案是确定的,咱们能够采用“参数化类型”,它们是编译器能自动定制的类,可与特定的类型配合。例如,经过使用一个参数化集合,编译器可对那个集合进行定制,使其只接受Shape,并且只提取Shape。
  参数化类型是C++一个重要的组成部分,这部分是C++没有单根结构的缘故。在C++中,用于实现参数化类型的关键字是template(模板)。Java目前还没有提供参数化类型,由于因为使用的是单根结构。

 

  C++语言的设计者曾经向C程序员发出请求(并且作得很是成功),不要但愿在可使用C的任何地方,向语言里加入可能对C++的速度或使用形成影响的任何特性。这个目的达到了,但代价就是C++的编程不可避免地复杂起来。Java比C++简单,但付出的代价是效率以及必定程度的灵活性。但对大多数程序设计问题来讲,Java无疑都应是咱们的首选。

垃圾收集器:
  在Java中,垃圾收集器在设计时已考虑到了内存的释放问题。垃圾收集器“知道”一个对象在何时再也不使用,而后会自动释放那个对象占据的内存空间。采用这种方式,另外加上全部对象都从单个根类Object继承的事实,并且因为咱们只能在内存堆中以一种方式建立对象,因此Java的编程要比C++的编程简单得多。咱们只须要做出少许的抉择,便可克服原先存在的大量障碍。
  既然这是如此好的一种手段,为何在C++里没有获得充分的发挥呢?咱们固然要为这种编程的方便性付出必定的代价,代价就是运行期的开销。正如早先提到的那样,在C++中,咱们可在堆栈中建立对象。在这种状况下,对象会得以自动清除。在堆栈中建立对象是为对象分配存储空间最有效的一种方式,也是释放那些空间最有效的一种方式。在内存堆(Heap)中建立对象可能要付出昂贵得多的代价。若是老是从同一个基础类继承,并使全部函数调用都具备“同质多形”特征,那么也不可避免地须要付出必定的代价。但垃圾收集器是一种特殊的问题,由于咱们永远不能肯定它何时启动或者要花多长的时间。这意味着在Java程序执行期间,存在着一种不连贯的因素。因此在某些特殊的场合,咱们必须避免用它——好比在一个程序的执行必须保持稳定、连贯的时候(一般把它们叫做“实时程序”,尽管并非全部实时编程问题都要这方面的要求。
  C++语言的设计者曾经向C程序员发出请求(并且作得很是成功),不要但愿在可使用C的任何地方,向语言里加入可能对C++的速度或使用形成影响的任何特性。这个目的达到了,但代价就是C++的编程不可避免地复杂起来。Java比C++简单,但付出的代价是效率以及必定程度的灵活性。但对大多数程序设计问题来讲,Java无疑都应是咱们的首选。

违例控制:解决错误
  这里的“违例”(Exception)属于一个特殊的对象,它会从产生错误的地方“扔”或“掷”出来。随后,这个违例会被设计用于控制特定类型错误的“违例控制器”捕获。在状况变得不对劲的时候,可能有几个违例控制器并行捕获对应的违例对象。Java的违例控制机制与大多数程序设计语言都有所不一样。由于在Java中,违例控制模块是从一开始就封装好的,因此必须使用它!若是没有本身写一些代码来正确地控制违例,就会获得一条编译期出错提示。这样可保证程序的连贯性,使错误控制变得更加容易。

多线程
  许多程序设计问题都要求程序可以停下手头的工做,改成处理其余一些问题,再返回主进程。能够经过多种途径达到这个目的。最开始的时候,那些拥有机器低级知识的程序员编写一些“中断服务例程”,主进程的暂停是经过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此形成了另外一类的代价高昂问题
  最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操做系统自己支持多个处理器,那么每一个线程均可分配给一个不一样的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操做最有价值的特性之一就是程序员没必要关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线程;假如机器自己安装了多个处理器,那么程序会运行得更快,毋需做出任何特殊的调校。
  根据前面的论述,你们可能感受线程处理很是简单。但必须注意一个问题:共享资源!若是有多个线程同时运行,并且它们试图访问相同的资源,就会遇到一个问题。举个例子来讲,两个进程不能将信息同时发送给一台打印机。为解决这个问题,对那些可共享的资源来讲(好比打印机),它们在使用期间必须进入锁定状态。因此一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其余线程能够接着使用一样的资源。
  Java的多线程机制已内建到语言中,这使一个可能较复杂的问题变得简单起来。对多线程处理的支持是在对象这一级支持的,因此一个执行线程可表达为一个对象。Java也提供了有限的资源锁定方案。它能锁定任何对象占用的内存(内存实际是多种共享资源的一种),因此同一时间只能有一个线程使用特定的内存空间。为达到这个目的,须要使用synchronized关键字。其余类型的资源必须由程序员明确锁定,这一般要求程序员建立一个对象,用它表明一把锁,全部线程在访问那个资源时都必须检查这把锁。

永久性:
  建立一个对象后,只要咱们须要,它就会一直存在下去。但在程序结束运行时,对象的“生存期”也会宣告结束。尽管这一现象表面上很是合理,但深刻追究就会发现,假如在程序中止运行之后,对象也能继续存在,并能保留它的所有信息,那么在某些状况下将是一件很是有价值的事情。

JAVA WEB
客户机/服务器计算
  客户机/服务器系统的基本思想是咱们能在一个统一的地方集中存放信息资源。通常将数据集中保存在某个数据库中,根据其余人或者机器的请求将信息投递给对方。将各类元素集中到一块儿,信息仓库、用于投递信息的软件以及信息及软件所在的那台机器(Server)。而后在远程机器上显示出来,这些就叫做“客户”(Client)。
  
Web实际就是一套规模巨大的客户机/服务器系统。但它的状况要复杂一些,由于全部服务器和客户都同时存在于单个网络上面。但咱们不必了解更进一步的细节,由于惟一要关心的就是一次创建同一个服务器的链接,并同它打交道(即便可能要在全世界的范围内搜索正确的服务器)。
最开始的时候,这是一个简单的单向操做过程。咱们向一个服务器发出请求,它向咱们回传一个文件,因为本机的浏览器软件(亦即“客户”或“客户程序”)负责解释和格式化,并在咱们面前的屏幕上正确地显示出来。但人们不久就不知足于只从一个服务器传递网页。

  Web最初采用的“服务器-浏览器”方案可提供交互式内容,但这种交互能力彻底由服务器提供,为服务器和因特网带来了不小的负担。服务器通常为客户浏览器产生静态网页,由后者简单地解释并显示出来。基本HTML语言提供了简单的数据收集机制:文字输入框、复选框、单选钮、列表以及下拉列表等,另外还有一个按钮,只能由程序规定从新设置表单中的数据,以便回传给服务器。用户提交的信息经过全部Web服务器均能支持的“通用网关接口”(CGI)回传到服务器。包含在提交数据中的文字指示CGI该如何操做。

  今天的许多Web站点都严格地创建在CGI的基础上,事实上几乎全部事情均可用CGI作到。惟一的问题就是响应时间。CGI程序的响应取决于须要传送多少数据,以及服务器和因特网两方面的负担有多重(并且CGI程序的启动比较慢)。Web的早期设计者并未预料到当初绰绰有余的带宽很快就变得不够用,这正是大量应用充斥网上形成的结果。原来的方法是咱们按下网页上的提交按钮(Submit);数据回传给服务器;服务器启动一个CGI程序,检查用户输入是否有错;格式化一个HTML页,通知可能遇到的错误,并将这个页回传给咱们;随后必须回到原先那个表单页,再输入一遍。这种方法不只速度很是慢,也显得很是繁琐。
  解决的办法就是客户端的程序设计。运行Web浏览器的大多数机器都拥有足够强的能力,可进行其余大量工做。与此同时,原始的静态HTML方法仍然能够采用,它会一直等到服务器送回下一个页。客户端编程意味着Web浏览器可得到更充分的利用,并可有效改善Web服务器的交互(互动)能力。

脚本编制语言
  插件形成了脚本编制语言的爆炸性增加。经过这种脚本语言,可将用于本身客户端程序的源码直接插入HTML页,而对那种语言进行解释的插件会在HTML页显示的时候自动激活。脚本语言通常都倾向于尽可能简化,易于理解。并且因为它们是从属于HTML页的一些简单正文,因此只需向服务器发出对那个页的一次请求,便可很是快地载入。缺点是咱们的代码所有暴露在人们面前。另外一方面,因为一般不用脚本编制语言作过份复杂的事情,因此这个问题暂且能够放在一边。
  脚本语言真正面向的是特定类型问题的解决,其中主要涉及如何建立更丰富、更具备互动能力的图形用户界面(GUI)。然而,脚本语言也许能解决客户端编程中80%的问题。你碰到的问题可能彻底就在那80%里面。
  
  若是说一种脚本编制语言能解决80%的客户端程序设计问题,那么剩下的20%又该怎么办呢?它们属于一些高难度的问题吗?目前最流行的方案就是Java。它不只是一种功能强大、高度安全、能够跨平台使用以及国际通用的程序设计语言,也是一种具备旺盛生命力的语言。对Java的扩展是不断进行的,提供的语言特性和库可以很好地解决传统语言不能解决的问题,好比多线程操做、数据库访问、连网程序设计以及分布式计算等等。Java经过“程序片”(Applet)巧妙地解决了客户端编程的问题。
  程序片(或“小应用程序”)是一种很是小的程序,只能在Web浏览器中运行。做为Web页的一部分,程序片代码会自动下载回来(这和网页中的图片差很少)。激活程序片后,它会执行一个程序。程序片的一个优势体如今:经过程序片,一旦用户须要客户软件,软件就可从服务器自动下载回来。它们能自动取得客户软件的最新版本,不会出错,也没有从新安装的麻烦。
安全
  自动下载和经过因特网运行程序听起来就象是一个病毒制造者的梦想。在客户端的编程中,ActiveX带来了最让人头痛的安全问题。点击一个Web站点的时候,可能会随同HTML网页传回任何数量的东西:GIF文件、脚本代码、编译好的Java代码以及ActiveX组件。有些是无害的;GIF文件不会对咱们形成任何危害,而脚本编制语言一般在本身可作的事情上有着很大的限制。Java也设计成在一个安全“沙箱”里在它的程序片中运行,这样可防止操做位于沙箱之外的磁盘或者内存区域。
ActiveX是全部这些里面最让人担忧的。用ActiveX编写程序就象编制Windows应用程序——能够作本身想作的任何事情。下载回一个ActiveX组件后,它彻底可能对咱们磁盘上的文件形成破坏。
  Java经过“沙箱”来防止这些问题的发生。Java解释器内嵌于咱们本地的Web浏览器中,在程序片装载时会检查全部有嫌疑的指令。特别地,程序片根本没有权力将文件写进磁盘,或者删除文件(这是病毒最喜欢作的事情之一)。

Java/C++
  Java特别象C++;由此很天然地会得出一个结论:C++彷佛会被Java取代。但我对这个逻辑存有一些疑问。不管如何,C++仍有一些特性是Java没有的。并且尽管已有大量保证,
我感受Java强大之处反映在与C++稍有不一样的领域。C++是一种绝对不会试图迎合某个模子的语言。特别是它的形式能够变化无穷,以解决不一样类型的问题。人们对Java作了大量的工做,使它能方便程序员解决应用级问题(如连网和跨平台UI等),因此它在本质上容许人们建立很是大型和灵活的代码主体。同时,考虑到Java还拥有我迄今为止还没有在其余任何一种语言里见到的最“健壮”的类型检查及错误控制系统,因此Java确实能大大提升咱们的编程效率。这一点是勿庸置疑的!
  但对于本身某个特定的项目,真的能够不假思索地将C++换成Java吗?除了Web程序片,还有两个问题须要考虑。首先,假如要使用大量现有的库(这样确定能够提升很多的效率),或者已经有了一个坚实的C或C++代码库,那么换成Java后,反映会阻碍开发进度,而不是加快它的速度。但若想从头开始构建本身的全部代码,那么Java的简单易用就能有效地缩短开发时间。
  最大的问题是速度。在原始的Java解释器中,解释过的Java会比C慢上20到50倍。尽管通过长时间的发展,这个速度有必定程度的提升,但和C比起来仍然很悬殊。计算机最注重的就是速度;假如在一台计算机上不能明显较快地干活,那么还不如用手作(有人建议在开发期间使用Java,以缩短开发时间。而后用一个工具和支撑库将代码转换成C++,这样可得到更快的执行速度)。

一切都是对象
  之因此说C++是一种杂合语言,是由于它支持与C语言的向后兼容能力。因为C++是C的一个超集,因此包含的许多特性都是后者不具有的,这些特性使C++在某些地方显得过于复杂。
Java语言首先便假定了咱们只但愿进行面向对象的程序设计。

用句柄操纵对象
  尽管将一切都“看做”对象,但操纵的标识符实际是指向一个对象的“句柄”(Handle)。只是因为拥有一个句柄,并不表示必须有一个对象同它链接。因此若是想容纳一个词或句子,可建立一个String句柄:String s;但这里建立的只是句柄,并非对象。若此时向s发送一条消息,就会得到一个错误(运行期)。这是因为s实际并未与任何东西链接(即“没有电视机”)。所以,一种更安全的作法是:建立一个句柄时,记住不管如何都进行初始化:
String s = new String("asdf");

  咱们建立类时会指出那个类的对象的外观与行为。除非用new建立那个类的一个对象,不然实际上并未获得任何东西。只有执行了new后,才会正式生成数据存储空间,并可以使用相应的方法。
  在类内做为字段使用的基本数据会初始化成零,但对象句柄会初始化成null。并且倘若试图为它们中的任何一个调用方法,就会产生一次“违例”。

  编译器并不仅是为每一个句柄建立一个默认对象,由于那样会在许多状况下招致没必要要的开销。如但愿句柄获得初始化,可在下面这些地方进行:
    (1) 在对象定义的时候。这意味着它们在构建器调用以前确定能获得初始化。
    (2) 在那个类的构建器中。
    (3) 紧靠在要求实际使用那个对象以前。这样作可减小没必要要的开销——假如对象并不须要建立的话。

 

保存到什么地方:
程序运行时,咱们最好对数据保存到什么地方作到心中有数。特别要注意的是内存的分配。有六个地方均可以保存数据:
(1) 寄存器。这是最快的保存区域,由于它位于和其余全部保存方式不一样的地方:处理器内部。然而,寄存器的数量十分有限,因此寄存器是根据须要由编译器分配。咱们对此没有直接的控制权,也不可能在本身的程序里找到寄存器存在的任何踪影。
(2) 堆栈。驻留于常规RAM(随机访问存储器)区域,但可经过它的“堆栈指针”得到处理的直接支持。堆栈指针若向下移,会建立新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。建立程序时,Java编译器必须准确地知道堆栈内保存的全部数据的“长度”以及“存在时间”。这是因为它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,因此尽管有些Java数据要保存在堆栈里——特别是对象句柄,但Java对象并不放到其中。
(3) 堆。一种常规用途的内存池(也在RAM区域),其中保存了Java对象。和堆栈不一样,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器没必要知道要从堆里分配多少存储空间,也没必要知道存储的数据要在堆里停留多长的时间。所以,用堆保存数据时会获得更大的灵活性。要求建立一个对象时,只需用new命令编制相关的代码便可。执行这些代码时,会在堆里自动进行数据的保存。固然,为达到这种灵活性,必然会付出必定的代价:在堆里分配存储空间时会花掉更长的时间!
(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象自己永远都不会置入静态存储空间。
(5) 常数存储。常数值一般直接置于程序代码内部。这样作是安全的,由于它们永远都不会改变。有的常数须要严格地保护,因此可考虑将它们置入只读存储器(ROM)。
(6) 非RAM存储。若数据彻底独立于一个程序以外,则程序不运行时仍可存在,并在程序的控制范围以外。其中两个最主要的例子即是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,一般会发给另外一台机器。而对于固定对象,对象保存在磁盘中。即便程序停止运行,它们仍可保持本身的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其余媒体中。一旦须要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。将来的版本甚至可能提供更完整的方案。

  Java决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java程序具备很强移植能力的缘由之一。

基本类型:

  可将它们想象成“基本”、“主”(Primitive)类型或者“内置”,进行程序设计时要频繁用到它们。之因此要特别对待,是因为用new建立对象(特别是小的、简单的变量)并非很是有效,由于new将对象置于“堆”里。对于这些类型,Java采纳了与C和C++相同的方法。也就是说,不是用new建立变量,而是建立一个并不是句柄的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,可以更高效地存取。Java不容许咱们建立本地(局部)对象——不管如何都要使用new。Java决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java程序具备很强移植能力的缘由之一。

Java的数组:
  在C和C++里使用数组是很是危险的,由于那些数组只是内存块。若程序访问本身内存块之外的数组,或者在初始化以前使用内存在C++里,应尽可能不要使用数组,换用标准模板库(Standard TemplateLibrary)里更安全的容器。
  Java的一项主要设计目标就是安全性。因此在C和C++里困扰程序员的许多问题都未在Java里重复。一个Java能够保证被初始化,并且不可在它的范围以外访问。建立对象数组时,实际建立的是一个句柄数组。并且每一个句柄都会自动初始化成一个特殊值,并带有本身的关键字:null(空)。一旦Java看到null,就知道该句柄并未指向一个对象。正式使用前,必须为每一个句柄都分配一个对象。若试图使用依然为null的一个句柄,就会在运行期报告问题。所以,典型的数组错误在Java里就获得了避免。也能够建立主类型数组。一样地,编译器可以担保对它的初始化,由于会将那个数组的内存划分红零。


对象的做用域:
Java对象不具有与主类型同样的存在时间。用new关键字建立一个Java对象的时候,它会超出做用域的范围以外。因此倘若使用下面这段代码:

{
String s = new String("a string");
} /* 做用域的终点 */

那么句柄s会在做用域的终点处消失。然而,s指向的String对象依然占据着内存空间。在上面这段代码里,咱们没有办法访问对象,由于指向它的惟一一个句柄已超出了做用域的边界。在后面的章节里,你们还会继续学习如何在程序运行期间传递和复制对象句柄。
这样形成的结果即是:对于用new建立的对象,只要咱们愿意,它们就会一直保留下去。这个编程问题在C和C++里特别突出。看来在C++里遇到的麻烦最大:因为不能从语言得到任何帮助,因此在须要对象的时候,根本没法肯定它们是否可用。并且更麻烦的是,在C++里,一旦工做完成,必须保证将对象清除。
这样便带来了一个有趣的问题。假如Java让对象依然故我,怎样才能防止它们大量充斥内存,并最终形成程序的“凝固”呢。在C++里,这个问题最令程序员头痛。但Java之后,状况却发生了改观。Java有一个特别的“垃圾收集器”,它会查找用new建立的全部对象,并辨别其中哪些再也不被引用。随后,它会自动释放由那些闲置对象占据的内存,以便能由新对象使用。这意味着咱们根本没必要操心内存的回收问题。只需简单地建立对象,一旦再也不须要它们,它们就会自动离去。这样作可防止在C++里很常见的一个编程问题:因为程序员忘记释放内存形成的“内存溢出”。

static关键字:
  建立类时会指出那个类的对象的外观与行为。除非用new建立那个类的一个对象,不然实际上并未获得任何东西。只有执行了new后,才会正式生成数据存储空间,并可以使用相应的方法。
  但在两种特殊的情形下,上述方法并不堪用。一种情形是只想用一个存储区域来保存一个特定的数据——不管要建立多少个对象,甚至根本不建立对象。另外一种情形是咱们须要一个特殊的方法,它没有与这个类的任何对象关联。也就是说,即便没有建立对象,也须要一个能调用的方法。为知足这两方面的要求,

 finalize()用途何在:
此时,你们可能已相信了本身应该将finalize()做为一种常规用途的清除方法使用。它有什么好处呢?
要记住的第三个重点是:垃圾收集只跟内存有关!
  也就是说,垃圾收集器存在的惟一缘由是为了回收程序再也不使用的内存。因此对于与垃圾收集有关的任何活动来讲,其中最值得注意的是finalize()方法,它们也必须同内存以及它的回收有关。
但这是否意味着假如对象包含了其余对象,finalize()就应该明确释放那些对象呢?答案是否认的——垃圾收集器会负责释放全部对象占据的内存,不管这些对象是如何建立的。它将对finalize()的需求限制到特殊的状况。在这种状况下,咱们的对象可采用与建立对象时不一样的方法分配一些存储空间。但你们或许会注意到,Java中的全部东西都是对象,因此这究竟是怎么一回事呢?
之因此要使用finalize(),看起来彷佛是因为有时须要采起与Java的普通方法不一样的一种方法,经过分配内存来作一些具备C风格的事情。这主要能够经过“固有方法”来进行,它是从Java里调用非Java方法的一种方式(固有方法的问题在附录A讨论)。C和C++是目前惟一得到固有方法支持的语言。但因为它们能调用经过其余语言编写的子程序,因此可以有效地调用任何东西。在非Java代码内部,也许能调用C的malloc()系列函数,用它分配存储空间。并且除非调用了free(),不然存储空间不会获得释放,从而形成内存“漏洞”的出现。固然,free()是一个C和C++函数,因此咱们须要在finalize()内部的一个固有方法中调用它。
读完上述文字后,你们或许已弄清楚了本身没必要过多地使用finalize()。这个思想是正确的;它并非进行普通清除工做的理想场所。那么,普通的清除工做应在何处进行呢?

多态-绑定:

  方法调用的绑定
  将一个方法调用同一个方法主体链接到一块儿就称为“绑定”(Binding)。若在程序运行之前执行绑定(由编译器和连接程序,若是有的话),就叫做“早期绑定”。C编译器只有一种方法调用,那就是“早期绑定”。
  “后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫做“动态绑定”。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能本身去调查,找到正确的方法主体。不一样的语言对后期绑定的实现方法是有所区别的。但咱们至少能够这样认为:它们都要在对象中安插某些特殊类型的信息。
  Java中绑定的全部方法都采用后期绑定技术,除非一个方法已被声明成final。这意味着咱们一般没必要决定是否应进行后期绑定——它是自动发生的。
  为何要把一个方法声明成final呢?正如上一章指出的那样,它能防止其余人覆盖那个方法。但也许更重要的一点是,它可有效地“关闭”动态绑定,或者告诉编译器不须要进行动态绑定。这样一来,编译器就可为final方法调用生成效率更高的代码。

抽象类-方法及接口:
  有些方法的做用仅仅是表达接口,而不是表达一些具体的实施细节。Java专门提供了一种机制,名为“抽象方法”。它属于一种不完整的方法,只含有一个声明,没有方法主体。下面是抽象方法声明时采用的语法:
abstract void X();
包含了抽象方法的一个类叫做“抽象类”。若是一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。不然,编译器会向咱们报告一条出错消息。
  若一个抽象类是不完整的,那么一旦有人试图生成那个类的一个对象,编译器又会采起什么行动呢?因为不能安全地为一个抽象类建立属于它的对象,因此会从编译器那里得到一条出错提示。经过这种方法,编译器可保证抽象类的“纯洁性”,咱们没必要担忧会误用它。若是从一个抽象类继承,并且想生成新类型的一个对象,就必须为基础类中的全部抽象方法提供方法定义。若是不这样作(彻底能够选择不作),则衍生类也会是抽象的,并且编译器会强迫咱们用abstract关键字标志那个类的“抽象”本质。即便不包括任何abstract方法,亦可将一个类声明成“抽象类”。若是一个类不必拥有任何抽象方法,并且咱们想禁止那个类的全部实例,这种能力就会显得很是有用。
  因此在实现一个接口的时候,来自接口的方法必须定义成public。不然的话,它们会默认为“友好的”,并且会限制咱们在继承过程当中对一个方法的访问——Java编译器不容许咱们那样作。

  接口只是比抽象类“更纯”的一种形式。它的用途并不止那些。因为接口根本没有具体的实施细节——也就是说,没有与存储空间与“接口”关联在一块儿——因此没有任何办法能够防止多个接口合并到一块儿。这一点是相当重要的,由于咱们常常都须要表达这样一个意思:“x从属于a,也从属于b,也从属于c”。在C++中,将多个类合并到一块儿的行动称做“多重继承”,并且操做较为不便,由于每一个类均可能有一套本身的实施细节。在Java中,咱们可采起一样的行动,但只有其中一个类拥有具体的实施细节。因此在合并多个接口的时候,C++的问题不会在Java中重演。

  在一个衍生类中,咱们并不必定要拥有一个抽象或具体(没有抽象方法)的基础类。若是确实想从一个非接口继承,那么只能从一个继承。剩余的全部基本元素都必须是“接口”。咱们将全部接口名置于implements关键字的后面,并用逗号分隔它们。可根据须要使用多个接口,并且每一个接口都会成为一个独立的类型,可对其进行上溯造型造成多态。

  使用接口最重要的一个缘由:能上溯造型至多个基础类。使用接口的第二个缘由与使用抽象基础类的缘由是同样的:防止客户程序员制做这个类的一个对象,以及规定它仅仅是一个接口。这样便带来了一个问题:到底应该使用一个接口仍是一个抽象类呢?若使用接口,咱们能够同时得到抽象类以及接口的好处。因此假如想建立的基础类没有任何方法定义或者成员变量,那么不管如何都愿意使用接口,而不要选择抽象类。事实上,若是事先知道某种东西会成为基础类,那么第一个选择就是把它变成一个接口。只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。

抽象类与接口设计层面上的区别

  1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类总体进行抽象,包括属性、行为,可是接口倒是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不一样类的事物,可是它们都有一个共性,就是都会飞。那么在设计的时候,能够将飞机设计为一个类Airplane,将鸟设计为一个类Bird,可是不能将 飞行 这个特性也设计为类,所以它只是一个行为特性,并非对一类事物的抽象描述。此时能够将 飞行 设计为一个接口Fly,包含方法fly( ),而后Airplane和Bird分别根据本身的须要实现Fly这个接口。而后至于有不一样种类的飞机,好比战斗机、民用飞机等直接继承Airplane便可,对于鸟也是相似的,不一样种类的鸟直接继承Bird类便可。从这里能够看出,继承是一个 "是否是"的关系,而 接口 实现则是 "有没有"的关系。若是一个类继承了某个抽象类,则子类一定是抽象类的种类,而接口实现则是有没有、具有不具有的关系,好比鸟是否能飞(或者是否具有飞行这个特色),能飞行则能够实现这个接口,不能飞行就不实现这个接口。

  2)设计层面不一样,抽象类做为不少子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,你们都用过ppt里面的模板,若是用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,若是它们的公共部分须要改动,则只须要改动模板A就能够了,不须要从新对ppt B和ppt C进行改动。而辐射式设计,好比某个电梯都装了某种报警器,一旦要更新报警器,就必须所有更新。也就是说对于抽象类,若是须要添加新的方法,能够直接在抽象类中添加具体的实现,子类能够不进行变动;而对于接口则不行,若是接口进行了变动,则全部实现这个接口的类都必须进行相应的改动。

相关文章
相关标签/搜索