《Java编程思想》--Bruce Eckel 读书笔记

java编程思想, Bruce Eckel




所有编程语言的最终目的都是提供一种“抽象”方法。一



将一条消息发给对象时,如果并不知道对方的具体类型是什么,但采取的行动同样是正确的,这种情况就叫作“多形性”(Polymorphism)




单根结构




所有类最终是否都应从单独一个基础类继承。在Java中(与其他几乎所有OOP语言一样),对这个问题的答案都是肯定的,而且这个终级基础类的名字很简单,就是一个“Object”。




Java比C++简单,但付出的代价是效率以及一定程度的灵活性。




违例控制:解决错误 从最古老的程序设计语言




程序,但其中最常见的是Perl。这是由于Perl是专为文字的处理及解释而设计的,所以能在任何服务器上安装和使用,无论采用的处理




一直等到服务器送回下一个页。客户端编程意味着Web浏览器可获得更充分的利用,并可有效改善Web服务器的交互(互动)能力。




脚本语言也许能解决客户端编程中80%的问题。你碰到的问题可能完全就在那80%里面。而且由于脚本编制语言的宗旨是尽可能地简化与快速,所以在考虑其他更复杂的方案之前(如Java及ActiveX),首先应想




Java通过“程序片”(Applet)巧妙地解决了客户端编程的问题。 程序片(或“小应用程序”)是一种非常小的程序,只能在Web浏览器中运行。




安全问题将我们引入客户端编程领域一个似乎是自动形成的分支。若程序是在因特网上运行,由于无从知晓它会在什么平台上运行,所以编程时要特别留意,防范可能出现的编程错误。




在传统意义上,服务器端编程是用Perl和CGI脚本进行的,但更复杂的系统已经出现。其中包括基于Java的Web服务器,它允许我们用Java进行所有服务器端编程,写出的程序就叫




在传统意义上,服务器端编程是用Perl和CGI脚本进行的,但更复杂的系统已经出现。其中包括基于Java的Web服务器,它允许我们用Java进行所有服务器端编程,写出的程序就叫作“小服务程序”(Servlet)。




与Java有关的大多数争论都是与程序片有关的。Java实际是一种常规用途的程序设计语言,可解决任何类型的问题,至少理论上如此。




我们为此也要付出一些代价。其中最明显的是执行速度放慢了(尽管可对此进行多方面的调整)。




1.12.1 不要迷失 在整个开发过程中,最重要的事情就是:不要将自己迷失!




想周全,能满足尽可能多的要求。给我的感觉,编程更象一门艺术,不能只是作为技术活来看待。所有付出最终都会得到回报。作为真正的程序员,




栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一




boolean 1-bit – – Boolean char 16-bit Unicode 0 Unicode 216- 1 Character byte 8-bit -128 +127 Byte[11] short 16-bit -215 +215 – 1




那么句柄s会在作用域的终点处消失。




但Java以后,情况却发生了改观。Java有一个特别的“垃圾收集器”,它会查找用new创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由那些闲置对象占据的内存,以便能由新对象使用。




一旦将变量作为类成员使用,就要特别注意由Java分配的默认值。




对象类型,以及每个对象的名字。正如在Java其他地方处理对象时一样,我们实际传递的是“句柄”(注释④)。然而,句柄的类型必须正确。




(=)进行的。它的意思是“取得右边的值,把它复制到左边”。




为主类型使用“A=B”,那么B处的内容就复制到A。若接着又修改了A,那么B根本不会受这种修改的影响。作为一名程序员,这应




实际就是将句柄从一个地方复制到另一个地方。这意味着假若为对象使用“C=D”,那么C和D最终都会指向最初只有D才指向的那个对象。下面这个例子将向大家阐示这一点。




际内容是否相同,又该如何操作呢?此时,必须使用所有对象都适用的特殊方法equals()。




这是由于equals()的默认行为是比较句柄。所




while(布尔表达式) 语句 在循环刚开始时,会计算一次“布尔表达式”的值。而对于后来每一次额外的循环,都会在开始前重新计算一次。 下面这个简单的例子可产生随机数,直到符合特定的条件为止:




do 语句 while(布尔表达式) while和do-while唯一的区别就是do-while肯定会至少执行一次;也就是说,至少会将其中的语句“过一遍”——即便表达式第一次便计算为false。而在while循环结构中,若条件第一次就为false,那么其中的语句根本不会执行。在实际应用中,while比do-while更常用一些。




break用于强行退出循环,不执行循环中剩余的语句。而continue则停止执行当前的反复,然后退回循环起始和,开始新的反复。




Java没有goto。




在标签和循环之间置入任何语句都是不明智的。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环或者一个开关。这是由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。如下所示:




做完全没有问题。然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因




以前述的两个方法调用就变成了下面这样的形式: Banana.f(a,1); Banana.f(b,2);




但在这里有必要区分一下C++和Java的区别,因为C++的对象肯定会被清除(排开编程错误的因素),而Java对象并非肯定能作为垃圾被“收集”去。或者换句话说: 垃圾收集并不等于“破坏”!




释放那些对象呢?答案是否定的——垃圾收集器会负责释放所有对象占据的内存,无论这些对象是如何创建的。




判断一个程序效率如何,关键是看是否由于变量的初始化不正确而造成了严重的编程错误(臭虫)




进行面向对象的设计时,一项基本的考虑是:如何将发生变化的东西与保持不变的东西分隔开。




“Java引人注目的一项特性是代码的重复使用或者再生。但最具革命意义的是,除代码的复制和修改以外,我们还能做多得多的其他事情。”




程序开发是一个不断递增或者累积的过程,就象人们学习知识一样。




类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。




对于面向对象的程序设计语言,多型性是第三种最基本的特征(前两种是数据抽象和继承。”




接口这样描述自己:“对于实现我的所有类,看起来都应该象我现在这个样子”。




的方法声明明确定义为“public”。但即便不明确定义,它们也会默认为public。




由于置入一个接口的所有字段都自动具有static和final属性,所以接口是对常数值进行分组的一个好




通过内部类完美地解决了GUI的问题。 为理解内部类如何




通过内部类完美地解决了GUI的问题。 为理解内部类如何简化控制框架的创建与使用,可认为一个控制框架的工作就是在事件“就绪”以后执行它们。




里正是内部类大显身手的地方。它们允许我们做两件事情: (1) 在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。内部类用于表达多种不同类型的action(),它们用于解决实际的问题。除此以外,




内部类使我们具体的实施变得更加巧妙,因为能方便地访问外部类的任何成员。若不具备这种能力,代码看起来就可能没那么使人舒




然,一种更灵活的做法是避免进行“硬编码”,而是从一个文件里读入它们(第10章的一个练习会要求大家修改这个例子,从而达到这个目标)




意味着对于一个复杂的对象,构建器的调用遵照下面的顺序: (1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。 (2) 按声明顺序调用成员初始化模块。 (3) 调用衍生构建器的主体。




的任何消息,因为两者拥有完全一致的接口。我们要做的全部事情就是从衍生上溯造型,而且永远不需要回过头来检查对象的准确类型是什么。所有细节都已通过多形性获得了完美的控




——好象它们没有特定的类型。换言之,它们将其当作Object类型处理(Object类型是Java中所有类的“根”类)。




疑惑,它看起来似乎根本没有封装类Integer的功能。为什么不用int或Integer呢?事实上,由于所有集合能容纳的仅有对象句柄,所以根本不可以使用整数。学过集合后,封装类的概念对大家来说就可能更容易










在ArrayList中进行随机访问(即get())以及循环反复是最划得来的;但对于LinkedList却是一个不小的开销。但另一方面,在列表中部进行插入和删除操作对于LinkedList来说却比ArrayList划算得多。我们最好的做法也许是先选择一个ArrayList作为自己的默认起点。以后若发现由于大量的插入和删除造成了性




进行add()以及contains()操作时,HashSet显然要比ArraySet出色得多,而且性能明显与元素的多寡关系不大。一般编写程序的




由于Map的大小是最严重的问题,所以程序的计时测试按Map的大小(或容量)来分割时间,以便得到令人信服的测试结果。下面列出一系列结果(在你的机器上可能不同):




      throw new Exception("Here's my Exception");     } catch(Exception e) {       System.out.println("Caught Exception");       System.out.println(         "e.getMessage(): " + e.getMessage());       System.out.println(         "e.toString(): " + e.toString());       System.out.println("e.printStackTrace():");       e.printStackTrace();     }




因此,违例堆栈路径无论如何都会记住它的真正起点,无论自己被重复“掷”了好几次。 若将第17行标注(




at NeverCaught.main(NeverCaught.java:15) 所以答案就是:假若一个RuntimeException获得到达main()的所有途径,同时不被捕获,那么当程序退出时,会为那个违例调用printStackTrace()




对语言设计人员来说,创建好的输入/输出系统是一项特别困难的任务。




用层次化对象动态和透明地添加单个对象的能力的做法叫作“装饰器”(Decorator)方案——“方案”




每个对象调用finalize()。然而,最安全的方法还是为文件明确调用close()。




Object Serialization)。它面向那些实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子




测试。它建立了许多链接对象的一个“Worm”(蠕虫),




正式的序列化操作是在下面这行代码里发生的: o.writeObject(sc);




每次调用randomFactory()方法时,它都会创建一个不同的Shape(Shape值采用随机值)。




不要试图在单个类里做太多的事情”!




每次写一个新类时,同时也会创建一个Class对象(更恰当地说,是保存在一个完全同名的.class文件中)




所以利用Class对象,我们几乎能将一个对象的祖宗十八代都调查出来。




反射:运行期类信息 如果不知道一个对象的准




换句话说,我们可以用“普通”方式调用一个对象的所有方法;但对“反射”来说,.class文件在编译期间是不可使用的,而是由运行期环境打开和检查。




几乎肯定要调用super.clone(),以及注意将克隆设为public。




的按位复制。 不管我们要做什么,克隆过程的第一个部分通常都应该是调用super.clone()。通过进行一次准确的复制,这样做可为后续的克隆进程建立起一个良好的基础




存储空间和系统开销。 下面列出有关StringBuffer(字串缓冲)

Thinking in Java, Bruce Eckel




任何事件都可以发生在组件上




因为一个Bean就是一个类。




调用start(),如下所示: selfThread.start(); 它的作用是执行常规初始化操作,然后调用run()。




每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。




线程可以运行,但有某种东西阻碍了它。若线程处于堵塞状态,调度机制可以简单地跳过它,不给它分配任何CPU时间。除非线程再次进入“可运行”状态,否则不会采取任何操作。




无法使用。 亦可调用yield()(Thread类的一个方法)自动放弃CPU,以便其他线程能够运行。然而,假




DNS(域名服务)形式




Design Patterns》书内所有方案的组织都围绕“程序进化时会发生什么变化”这个问题展开。对于




住多形性只能通过方法调用才能表现出来,所以假如想使双重派遣正确进行,必须执行两个方法调






你可能感兴趣的:(读书笔记,学习记录)