2023年3月

《Java编程思想第四版完整中文高清版.pdf》-笔记

附录 B 对比 C++和 Java

“作为一名 C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是非常熟悉的。事实上,Java 本来就是从 C++衍生出来的。”
然而,C++和Java 之间仍存在一些显著的差异。可以这样说,这些差异代表着技术的极大进步。一旦我们弄清楚了这些差异,就会理解为什么说Java 是一种优秀的程序设计语言。本附录将引导大家认识用于区分
Java 和C++的一些重要特征。
(1) 最大的障碍在于速度:解释过的Java 要比C 的执行速度慢上约 20倍。无论什么都不能阻止Java 语言进行编译。写作本书的时候,刚刚出现了一些准实时编译器,它们能显著加快速度。当然,我们完全有理由认为会出现适用于更多流行平台的纯固有编译器,但假若没有那些编译器,由于速度的限制,必须有些问题是Java 不能解决的。
(2) 和C++一样,Java 也提供了两种类型的注释。
(3) 所有东西都必须置入一个类。不存在全局函数或者全局数据。如果想获得与全局函数等价的功能,可考虑将static方法和 static 数据置入一个类里。注意没有象结构、枚举或者联合这一类的东西,一切只有“类”(Class)!
(4) 所有方法都是在类的主体定义的。所以用C++的眼光看,似乎所有函数都已嵌入,但实情并非如何(嵌入的问题在后面讲述)。
(5) 在Java 中,类定义采取几乎和 C++一样的形式。但没有标志结束的分号。没有class foo 这种形式的类声明,只有类定义。
class aType()
void aMethod() {/* 方法主体 */}
}
(6) Java 中没有作用域范围运算符“::”。Java 利用点号做所有的事情,但可以不用考虑它,因为只能在一个类里定义元素。即使那些方法定义,也必须在一个类的内部,所以根本没有必要指定作用域的范围。我们注意到的一项差异是对 static方法的调用:使用 ClassName.methodName()。除此以外,package(包)的名字是用点号建立的,并能用import关键字实现 C++的“#include”的一部分功能。例如下面这个语句:
import java.awt.*;
(#include 并不直接映射成import,但在使用时有类似的感觉。)
(7) 与C++类似,Java 含有一系列“主类型”(Primitive type),以实现更有效率的访问。在Java 中,这些类型包括 boolean,char,byte,short,int,long,float 以及double。所有主类型的大小都是固有的,且与具体的机器无关(考虑到移植的问题)。这肯定会对性能造成一定的影响,具体取决于不同的机器。对类型的检查和要求在Java 里变得更苛刻。例如:
条件表达式只能是boolean(布尔)类型,不可使用整数。
■必须使用象X+Y这样的一个表达式的结果;不能仅仅用“X+Y”来实现“副作用”。
(8) char(字符)类型使用国际通用的 16位 Unicode字符集,所以能自动表达大多数国家的字符。
(9) 静态引用的字串会自动转换成 String对象。和C 及 C++不同,没有独立的静态字符数组字串可供使用。
(10) Java 增添了三个右移位运算符“>>>”,具有与“逻辑”右移位运算符类似的功用,可在最末尾插入零值。“>>”则会在移位的同时插入符号位(即“算术”移位)。
(11) 尽管表面上类似,但与 C++相比,Java 数组采用的是一个颇为不同的结构,并具有独特的行为。有一个只读的length 成员,通过它可知道数组有多大。而且一旦超过数组边界,运行期检查会自动丢弃一个异常。所有数组都是在内存“堆”里创建的,我们可将一个数组分配给另一个(只是简单地复制数组句柄)。数组标识符属于第一级对象,它的所有方法通常都适用于其他所有对象。
(12) 对于所有不属于主类型的对象,都只能通过new命令创建。和 C++不同,Java 没有相应的命令可以“在堆栈上”创建不属于主类型的对象。所有主类型都只能在堆栈上创建,同时不使用new命令。所有主要的类都有自己的“封装(器)”类,所以能够通过 new创建等价的、以内存“堆”为基础的对象(主类型数组是一个例外:它们可象C++那样通过集合初始化进行分配,或者使用 new)。
(13) Java 中不必进行提前声明。若想在定义前使用一个类或方法,只需直接使用它即可——编译器会保证使用恰当的定义。所以和在C++中不同,我们不会碰到任何涉及提前引用的问题。
(14) Java 没有预处理机。若想使用另一个库里的类,只需使用import 命令,并指定库名即可。不存在类似于预处理机的宏。
(15) Java 用包代替了命名空间。由于将所有东西都置入一个类,而且由于采用了一种名为“封装”的机制,它能针对类名进行类似于命名空间分解的操作,所以命名的问题不再进入我们的考虑之列。数据包也会在单独一个库名下收集库的组件。我们只需简单地“import”(导入)一个包,剩下的工作会由编译器自动完成。
(16) 被定义成类成员的对象句柄会自动初始化成null。对基本类数据成员的初始化在Java 里得到了可靠的保障。若不明确地进行初始化,它们就会得到一个默认值(零或等价的值)。可对它们进行明确的初始化(显式初始化):要么在类内定义它们,要么在构建器中定义。采用的语法比C++的语法更容易理解,而且对于static和非 static成员来说都是固定不变的。我们不必从外部定义 static成员的存储方式,这和 C++是不同的。
(17) 在Java 里,没有象 C 和C++那样的指针。用new创建一个对象的时候,会获得一个引用(本书一直将其称作“句柄”)。例如:
String s = new String("howdy");
然而,C++引用在创建时必须进行初始化,而且不可重定义到一个不同的位置。但Java 引用并不一定局限于创建时的位置。它们可根据情况任意定义,这便消除了对指针的部分需求。在C 和C++里大量采用指针的另一个原因是为了能指向任意一个内存位置(这同时会使它们变得不安全,也是Java 不提供这一支持的原因)。指针通常被看作在基本变量数组中四处移动的一种有效手段。Java 允许我们以更安全的形式达到相同的目标。解决指针问题的终极方法是“固有方法”(已在附录 A讨论)。将指针传递给方法时,通常不会带来太大的问题,因为此时没有全局函数,只有类。而且我们可传递对对象的引用。Java 语言最开始声称自己“完全不采用指针!”但随着许多程序员都质问没有指针如何工作?于是后来又声明“采用受到限制的指针”。大家可自行判断它是否“真”的是一个指针。但不管在何种情况下,都不存在指针“算术”。
(18) Java 提供了与C++类似的“构建器”(Constructor)。如果不自己定义一个,就会获得一个默认构建器。而如果定义了一个非默认的构建器,就不会为我们自动定义默认构建器。这和C++是一样的。注意没有复制构建器,因为所有自变量都是按引用传递的。
(19)Java 中没有“破坏器”(Destructor)。变量不存在“作用域”的问题。一个对象的“存在时间”是由对象的存在时间决定的,并非由垃圾收集器决定。有个 finalize()方法是每一个类的成员,它在某种程度上类似于C++的“破坏器”。但 finalize()是由垃圾收集器调用的,而且只负责释放“资源”(如打开的文件、套接字、端口、URL等等)。如需在一个特定的地点做某样事情,必须创建一个特殊的方法,并调用它,不能依赖finalize()。而在另一方面,C++中的所有对象都会(或者说“应该”)破坏,但并非 Java 中的所有对象都会被当作“垃圾”收集掉。由于 Java 不支持破坏器的概念,所以在必要的时候,必须谨慎地创建一个清除方法。而且针对类内的基础类以及成员对象,需要明确调用所有清除方法。
(20) Java 具有方法“过载”机制,它的工作原理与C++函数的过载几乎是完全相同的。
(21) Java 不支持默认自变量。
(22) Java 中没有goto。它采取的无条件跳转机制是“break 标签”或者“continue 标准”,用于跳出当前的多重嵌套循环。
(23) Java 采用了一种单根式的分级结构,因此所有对象都是从根类Object 统一继承的。而在 C++中,我们可在任何地方启动一个新的继承树,所以最后往往看到包含了大量树的“一片森林”。在Java 中,我们无论如何都只有一个分级结构。尽管这表面上看似乎造成了限制,但由于我们知道每个对象肯定至少有一个Object接口,所以往往能获得更强大的能力。C++目前似乎是唯一没有强制单根结构的唯一一种 OO语言。
(24) Java 没有模板或者参数化类型的其他形式。它提供了一系列集合:Vector(向量),Stack(堆栈)以及Hashtable(散列表),用于容纳 Object 引用。利用这些集合,我们的一系列要求可得到满足。但这些集合并非是为实现象C++“标准模板库”(STL)那样的快速调用而设计的。Java 1.2 中的新集合显得更加完整,但仍不具备正宗模板那样的高效率使用手段
(25) “垃圾收集”意味着在 Java 中出现内存漏洞的情况会少得多,但也并非完全不可能(若调用一个用于
分配存储空间的固有方法,垃圾收集器就不能对其进行跟踪监视)。然而,内存漏洞和资源漏洞多是由于编
写不当的finalize()造成的,或是由于在已分配的一个块尾释放一种资源造成的(“破坏器”在此时显得特
别方便)。垃圾收集器是在C++基础上的一种极大进步,使许多编程问题消弥于无形之中。但对少数几个垃
圾收集器力有不逮的问题,它却是不大适合的。但垃圾收集器的大量优点也使这一处缺点显得微不足道。
(26) Java 内建了对多线程的支持。利用一个特殊的Thread 类,我们可通过继承创建一个新线程(放弃了run()方法)。若将synchronized(同步)关键字作为方法的一个类型限制符使用,相互排斥现象会在对象这一级发生。在任何给定的时间,只有一个线程能使用一个对象的synchronized方法。在另一方面,一个synchronized方法进入以后,它首先会“锁定”对象,防止其他任何synchronized方法再使用那个对象。只有退出了这个方法,才会将对象“解锁”。在线程之间,我们仍然要负责实现更复杂的同步机制,方法是创建自己的“监视器”类。递归的 synchronized方法可以正常运作。若线程的优先等级相同,则时间的“分片”不能得到保证
(27) 我们不是象 C++那样控制声明代码块,而是将访问限定符(public,private 和protected)置入每个
类成员的定义里。若未规定一个“显式”(明确的)限定符,就会默认为“友好的”(friendly)。这意味
着同一个包里的其他元素也可以访问它(相当于它们都成为C++的“friends”——朋友),但不可由包外的
任何元素访问。类——以及类内的每个方法——都有一个访问限定符,决定它是否能在文件的外部“可
见”。private关键字通常很少在 Java 中使用,因为与排斥同一个包内其他类的访问相比,“友好的”访问
通常更加有用。然而,在多线程的环境中,对 private的恰当运用是非常重要的。Java 的 protected 关键字
意味着“可由继承者访问
,亦可由包内其他元素访问”。注意 Java 没有与C++的protected 关键字等价的元素,后者意味着“只能由继承者访问”(以前可用“private protected”实现这个目的,但这一对关键字的组合已被取消了)。
(28) 嵌套的类。在C++中,对类进行嵌套有助于隐藏名称,并便于代码的组织(但 C++的“命名空间”已使
名称的隐藏显得多余)。Java 的“封装”或“打包”概念等价于 C++的命名空间,所以不再是一个问题。
Java 1.1 引入了“内部类”的概念,它秘密保持指向外部类的一个句柄 ??——创建内部类对象的时候需要用
到。这意味着内部类对象也许能访问外部类对象的成员,毋需任何条件——就好象那些成员直接隶属于内部
类对象一样。这样便为回调问题提供了一个更优秀的方案——C++是用指向成员的指针解决的。
(29) 由于存在前面介绍的那种内部类,所以Java 里没有指向成员的指针。
(30) Java 不存在“嵌入”(inline)方法。Java 编译器也许会自行决定嵌入一个方法,但我们对此没有更
多的控制权力。在Java 中,可为一个方法使用 final 关键字,从而“建议”进行嵌入操作。然而,嵌入函数
对于C++的编译器来说也只是一种建议。
(31) Java 中的继承具有与C++相同的效果,但采用的语法不同。Java 用 extends关键字标志从一个基础类
的继承,并用super 关键字指出准备在基础类中调用的方法,它与我们当前所在的方法具有相同的名字(然
而,Java 中的super 关键字只允许我们访问父类的方法——亦即分级结构的上一级)。通过在 C++中设定基
础类的作用域,我们可访问位于分级结构较深处的方法。亦可用super 关键字调用基础类构建器。正如早先
指出的那样,所有类最终都会从Object 里自动继承。和C++不同,不存在明确的构建器初始化列表。但编译
器会强迫我们在构建器主体的开头进行全部的基础类初始化,而且不允许我们在主体的后面部分进行这一工
作。通过组合运用自动初始化以及来自未初始化对象句柄的异常,成员的初始化可得到有效的保证。
public class Foo extends Bar {
public Foo(String msg) {
super(msg); // Calls base constructor
}
public baz(int i) { // Override
super.baz(i); // Calls base method
}
}
(32) Java 中的继承不会改变基础类成员的保护级别。我们不能在Java 中指定 public,private或者
protected 继承,这一点与C++是相同的。此外,在衍生类中的优先方法不能减少对基础类方法的访问。例
如,假设一个成员在基础类中属于 public,而我们用另一个方法代替了它,那么用于替换的方法也必须属于
public(编译器会自动检查)。
(33) Java 提供了一个 interface 关键字,它的作用是创建抽象基础类的一个等价物。在其中填充抽象方
法,且没有数据成员。这样一来,对于仅仅设计成一个接口的东西,以及对于用extends 关键字在现有功能
基础上的扩展
,两者之间便产生了一个明显的差异。不值得用 abstract 关键字产生一种类似的效果,因为我
们不能创建属于那个类的一个对象。一个abstract(抽象)类可包含抽象方法(尽管并不要求在它里面包含
什么东西),但它也能包含用于具体实现的代码。
因此,它被限制成一个单一的继承。通过与接口联合使
用,这一方案避免了对类似于C++虚拟基础类那样的一些机制的需要。
为创建可进行“例示”(即创建一个实例)的一个 interface(接口)的版本,需使用implements 关键字。
它的语法类似于继承的语法,如下所示:
675
public interface Face {
public void smile();
}
public class Baz extends Bar implements Face {
public void smile( ) {
System.out.println("a warm smile");
}
}
(34) Java 中没有virtual 关键字,因为所有非static 方法都肯定会用到动态绑定。在Java 中,程序员不
必自行决定是否使用动态绑定。C++之所以采用了 virtual,是由于我们对性能进行调整的时候,可通过将其
省略,从而获得执行效率的少量提升(或者换句话说:“如果不用,就没必要为它付出代价”)。virtual
经常会造成一定程度的混淆,而且获得令人不快的结果。final关键字为性能的调整规定了一些范围——它
向编译器指出这种方法不能被取代,所以它的范围可能被静态约束(而且成为嵌入状态,所以使用C++非
virtual调用的等价方式)。这些优化工作是由编译器完成的。
(35) Java 不提供多重继承机制(MI),至少不象 C++那样做。与 protected 类似,MI表面上是一个很不错
的主意,但只有真正面对一个特定的设计问题时,才知道自己需要它。由于Java 使用的是“单根”分级结
构,所以只有在极少的场合才需要用到MI
。interface 关键字会帮助我们自动完成多个接口的合并工作。
(36) 运行期的类型标识功能与C++极为相似。例如,为获得与句柄 X有关的信息,可使用下述代码:
X.getClass().getName();
为进行一个“类型安全”的紧缩造型,可使用:
derived d = (derived)base;
这与旧式风格的C 造型是一样的。编译器会自动调用动态造型机制,不要求使用额外的语法。尽管它并不象
C++的“new casts”那样具有易于定位造型的优点,但 Java 会检查使用情况,并丢弃那些“异常”,所以它
不会象C++那样允许坏造型的存在。
(37) Java 采取了不同的异常控制机制,因为此时已经不存在构建器。可添加一个finally从句,强制执行
特定的语句
,以便进行必要的清除工作。Java 中的所有异常都是从基础类 Throwable里继承而来的,所以可
确保我们得到的是一个通用接口。
public void f(Obj b) throws IOException {
myresource mr = b.createResource();
try {
mr.UseResource();
} catch (MyException e) {
// handle my exception
} catch (Throwable e) {
// handle all other exceptions
} finally {
mr.dispose(); // special cleanup
}
}
(38) Java 的异常规范比C++的出色得多。丢弃一个错误的异常后,不是象C++那样在运行期间调用一个函
数,Java 异常规范是在编译期间检查并执行的。除此以外,被取代的方法必须遵守那一方法的基础类版本的
异常规范:它们可丢弃指定的异常或者从那些异常衍生出来的其他异常。这样一来,我们最终得到的是更为
“健壮”的异常控制代码。??
(39) Java 具有方法过载的能力,但不允许运算符过载。String类不能用+和+=运算符连接不同的字串,而且
String表达式使用自动的类型转换,但那是一种特殊的内建情况。
(40) 通过事先的约定,C++中经常出现的const 问题在 Java 里已得到了控制。我们只能传递指向对象的句
,本地副本永远不会为我们自动生成。若希望使用类似 C++按值传递那样的技术,可调用clone(),生成自
变量的一个本地副本(尽管clone()的设计依然尚显粗糙——参见第12 章)。根本不存在被自动调用的副本
构建器。为创建一个编译期的常数值,可象下面这样编码:
static final int SIZE = 255
static final int BSIZE = 8 * SIZE
(41) 由于安全方面的原因,“应用程序”的编程与“程序片”的编程之间存在着显著的差异。一个最明显的
问题是程序片不允许我们进行磁盘的写操作,因为这样做会造成从远程站点下载的、不明来历的程序可能胡
乱改写我们的磁盘。随着Java 1.1 对数字签名技术的引用,这一情况已有所改观。根据数字签名,我们可确
切知道一个程序片的全部作者,并验证他们是否已获得授权。Java 1.2 会进一步增强程序片的能力。
(42) 由于Java 在某些场合可能显得限制太多,所以有时不愿用它执行象直接访问硬件这样的重要任务。
Java 解决这个问题的方案是“固有方法”,允许我们调用由其他语言写成的函数(目前只支持C 和C++)。
这样一来,我们就肯定能够解决与平台有关的问题(采用一种不可移植的形式,但那些代码随后会被隔离起
来)。程序片不能调用固有方法,只有应用程序才可以。
(43) Java 提供对注释文档的内建支持,所以源码文件也可以包含它们自己的文档。通过一个单独的程序,
这些文档信息可以提取出来,并重新格式化成 HTML。这无疑是文档管理及应用的极大进步。
(44) Java 包含了一些标准库,用于完成特定的任务。C++则依靠一些非标准的、由其他厂商提供的库。这些
任务包括(或不久就要包括):
■连网
■数据库连接(通过JDBC)
■多线程
■分布式对象(通过RMI和 CORBA)
■压缩
■商贸
由于这些库简单易用,而且非常标准,所以能极大加快应用程序的开发速度。
(45) Java 1.1 包含了 Java Beans标准,后者可创建在可视编程环境中使用的组件。由于遵守同样的标准,
所以可视组件能够在所有厂商的开发环境中使用。由于我们并不依赖一家厂商的方案进行可视组件的设计,
所以组件的选择余地会加大,并可提高组件的效能。除此之外,Java Beans 的设计非常简单,便于程序员理
解;而那些由不同的厂商开发的专用组件框架则要求进行更深入的学习。
(46) 若访问Java 句柄失败,就会丢弃一次异常。这种丢弃测试并不一定要正好在使用一个句柄之前进行。
根据Java 的设计规范,只是说异常必须以某种形式丢弃。许多C++运行期系统也能丢弃那些由于指针错误造
成的异常。
(47) Java 通常显得更为健壮,为此采取的手段如下:
■对象句柄初始化成null(一个关键字)
■句柄肯定会得到检查,并在出错时丢弃异常
■所有数组访问都会得到检查,及时发现边界违例情况
■自动垃圾收集,防止出现内存漏洞
■明确、“傻瓜式”的异常控制机制
■为多线程提供了简单的语言支持
■对网络程序片进行字节码校验

在由Gamma,Helm 和 Johnson 编著的《Design Patterns》一书中被定义成一个“里程碑”。那本书列出了解决这个问题的 23 种不同的方法

16.1.2 范式分类
《Design Patterns》一书讨论了 23种不同的范式,并依据三个标准分类(所有标准都涉及那些可能发生变
化的方面)。这三个标准是:
(1) 创建:对象的创建方式。这通常涉及对象创建细节的隔离,这样便不必依赖具体类型的对象,所以在新
添一种对象类型时也不必改动代码。
(2) 结构:设计对象,满足特定的项目限制。这涉及对象与其他对象的连接方式,以保证系统内的改变不会
影响到这些连接。
(3) 行为:对程序中特定类型的行动进行操纵的对象。这要求我们将希望采取的操作封装起来,比如解释一
种语言、实现一个请求、在一个序列中遍历(就象在继承器中那样)或者实现一种算法。本章提供了“观察
器”(Observer)和“访问器”(Visitor)的范式的例子。
《Design Patterns》为所有这 23种范式都分别使用了一节,随附的还有大量示例,但大多是用 C++编写
的,少数用 Smalltalk 编写(如看过这本书,就知道这实际并不是个大问题,因为很容易即可将基本概念从
两种语言翻译到Java 里)。现在这本书并不打算重复《Design Patterns》介绍的所有范式,因为那是一本
独立的书,大家应该单独阅读。相反,本章只准备给出一些例子,让大家先对范式有个大致的印象,并理解
它们的重要性到底在哪里
16.1.1 单子
或许最简单的设计范式就是“单子”(Singleton),它能提供对象的一个(而且只有一个)实例。单子在
Java 库中得到了应用,但下面这个例子显得更直接一些:
//: SingletonPattern.java
// The Singleton design pattern: you can
// never instantiate more than one.
589
package c16;
// Since this isn't inherited from a Cloneable
// base class and cloneability isn't added,
// making it final prevents cloneability from
// being added in any derived classes:
final class Singleton {
private static Singleton s = new Singleton(47);
private int i;
private Singleton(int x) { i = x; }
public static Singleton getHandle() {
return s;
}
public int getValue() { return i; }
public void setValue(int x) { i = x; }
}
public class SingletonPattern {
public static void main(String[] args) {
Singleton s = Singleton.getHandle();
System.out.println(s.getValue());
Singleton s2 = Singleton.getHandle();
s2.setValue(9);
System.out.println(s.getValue());
try {
// Can't do this: compile-time error.
// Singleton s3 = (Singleton)s2.clone();
} catch(Exception e) {}
}
} ///:~
创建单子的关键就是防止客户程序员采用除由我们提供的之外的任何一种方式来创建一个对象。必须将所有
构建器都设为private(私有),而且至少要创建一个构建器以防止编译器帮我们自动同步一个默认构建
(它会自做聪明地创建成为“友好的”——friendly,而非 private)。
此时应决定如何创建自己的对象。在这儿,我们选择了静态创建的方式。但亦可选择等候客户程序员发出一
个创建请求,然后根据他们的要求动态创建。不管在哪种情况下,对象都应该保存为“私有”属性。我们通
过公用方法提供访问途径。在这里,getHandle()会产生指向 Singleton 的一个句柄。剩下的接口(getValue()和 setValue())属于普通的类接口。
Java 也允许通过克隆(Clone)方式来创建一个对象。在这个例子中,将类设为 final 可禁止克隆的发生
16.2 观察器范式(观察者)
观察器(Observer)范式解决的是一个相当普通的问题:由于某些对象的状态发生了改变,所以一组对象都
需要更新
,那么该如何解决?在Smalltalk 的MVC(模型-视图-控制器)的“模型-视图”部分中,或在
几乎等价的“文档-视图结构”中,大家可以看到这个问题。现在我们有一些数据(“文档”)以及多个视
图,假定为一张图(Plot)和一个文本视图。若改变了数据,两个视图必须知道对自己进行更新,而那正是
“观察器”要负责的工作。这是一种十分常见的问题,它的解决方案已包括进标准的 java.util 库中。
在Java 中,有两种类型的对象用来实现观察器范式。其中,Observable 类用于跟踪那些当发生一个改变时
希望收到通知的所有个体——无论“状态”是否改变。如果有人说“好了,所有人都要检查自己,并可能要
进行更新”
,那么Observable 类会执行这个任务——为列表中的每个“人”都调用 notifyObservers()方
法。notifyObservers()方法属于基础类Observable 的一部分。
在观察器范式中,实际有两个方面可能发生变化:观察对象的数量以及更新的方式。也就是说,观察器范式
允许我们同时修改这两个方面,不会干扰围绕在它周围的其他代码。
下面这个例子类似于第 14章的ColorBoxes 示例。箱子(Boxes)置于一个屏幕网格中,每个都初始化一种随
机的颜色。此外,每个箱子都“实现”(implement)了“观察器”(Observer)接口,而且随一个
Observable对象进行了注册。若点击一个箱子,其他所有箱子都会收到一个通知,指出一个改变已经发生。
这是由于Observable 对象会自动调用每个Observer 对象的 update()方法。在这个方法内,箱子会检查被点
中的那个箱子是否与自己紧邻。若答案是肯定的,那么也修改自己的颜色,保持与点中那个箱子的协调。

个人理解:一个对象改变了,通知所有已经关联的对象,并做相关的处理。
---待续

D.2.1安插自己的测试代码
插入下述“显式”计时代码,对程序进行评测:
long start =System.currentTimeMillis();//要计时的运算代码放在这儿 long time = System.currentTimeMillis() - start;


目录
前言
简介
第1章 对象导论
1.1抽象过程1.2每个对象都有一个接口1.3每个对象都提供服务1.4被隐藏的具体实现1.5复用具体实现1.6继承1.6.1 “是一个”(is-a)与“像是一个”(is-like-a)关系1.7伴随多态的可互换对象1.8单根继承结构1.9容器1.9.1参数化类型(范型)1.10对象的创建和生命期1.11异常处理:处理错误1.12并发编程1.13java与internet1.13.1web是什么

.
1.13.2客户端编程1.13.3服务器端编程1.22总结
第2章 一切都是对象
2.1用引用操纵对象2.2必须由你创建所有对象2.2.1存储到什么地方2.2.2特例:基本类型2.2.3java中的数组2.3永远不需要销毁对象2.3.1作用域2.3.2对象的作用域2.4创建新的数据类型:类2.4.1域和方法2.4.2基本成员默认值2.5方法、参数和返回值2.5.1参数列表2.6构建一个java程序2.6.1名字可见性2.6.2运用其他构件2.6.3 static关键字2.7你的第一个java程序
编译和运行
2.8注释和嵌入式文档2.8.1注释文档2.8.2语法2.8.3嵌入式html2.8.4一些标签示例2.8.5文档示例2.9编码风格2.10总结2.11练习
第3章 操作符
3.1更简单的打印语句3.2使用java操作符3.3优先级3.4赋值3.4.1方法调用中的别名问题3.5算术操作符3.5.1一元加、减操作符3.6自动递增和递减3.7关系操作符3.7.1测试对象的等价性3.8逻辑操作符3.8.1短路3.9直接常量3.9.1指数记数法3.10按位操作符3.11移位操作符3.12 三元操作符 if-else 3.13 字符串操作符 + 和 += 3.14使用操作符时常犯的错误3.15类型转换操作符3.15.1截尾和舍入3.15.2提升3.16java没有“sizeof”3.17操作符小结3.18总结
第4章 控制执行流程
4.1true和false4.2 if-else 4.3迭代4.3.1 do-while 4.3.2 for 4.3.3逗号操作符4.4foreach语法4.5 return 4.6 break和 continue 4.7 臭名昭著的“goto4.8 switch 4.9总结
第5章 初始化与清理
5.1用构造器确保初始化5.2方法重载5.2.1区分重载方法5.2.2涉及基本类型的重载5.2.3以返回值区分重载方法5.3缺省构造器5.4this关键字5.4.1在构造器中调用构造器5.4.2static的含义5.5清理:终结处理和垃圾回收5.5.1finalize()的用途何在5.5.2你必须实施清理5.5.3终结条件5.5.4垃圾回收器如何工作5.6成员初始化5.6.1指定初始化5.7构造器初始化5.7.1初始化顺序5.7.2. 静态数据的初始化5.7.3. 显式的静态初始化5.7.4. 非静态实例初始化5.8数组初始化5.8.1可变参数列表5.9枚举类型5.10总结
第6章 访问权限控制
第7章 复用类
第8章 多态
第9章 接口
第10章 内部类
第11章 持有对象
第12章 通过异常处理错误
第13章 字符串
第14章 类型信息
第15章 泛型
第16章 数组
第17章 容器深入研究
第18章 java i
/o系统
第19章 枚举类型
第20章 注解
第21章 并发
第22章 图形化用户界面
附录a 补充材料
可下载的补充材料
thinking in c:java的基础
java编程思想 研讨课
hands
-on java研讨课cd
thinking in objects研讨课
thinking in enterprise java
thinking in patterns(with java)
thinking in patterns研讨课
设计咨询与复审
附录b 资源
软件
编辑器与ide
书籍
分析与设计
python
我的著作列表
索引

书本目录

广州尚学堂官网-|广州Java培训|Java培训机构|人工智能+Python培训|PHP培训|全栈工程师培训|UI设计培训|前端移动开发培训
http://www.gzsxt.cn/

==Iterator迭代

这个课程是2009年的

==10课 自己写一个 ArrayList

==11课 一个链表 Node 和 LikedList


== 12课,考虑容器的可替换性

如果他们都继承一个基类  collection,针对接口编程,如果是写在配置文件,代码都不用编译。

==13课时  数组遍历 下标可以,链表遍历 指针(引用)可以,二叉树遍历 左序遍历右序中序遍历

java内部类,
Iterator
<
T
> iterator()

==14课时  泛型

==15

Strut2 可以实现Strut1的功能,但是有些老系统还用strut1,还得学。

工作流 ?? JBPN系统上

==章节2Thinking_in_oo面向对象思维 课程提到的笔记:

教学,教与学的比重:重点在学

面向对象:的核心就是多态。封装 继承 多态。多态 --》可扩展性 Extensibility

有时候知识跟那牛吃草似的,先囫囵吞下去,是不是翻出来嚼一下,慢慢就会了。

geter seter 好处:防止无条件开发读写属性,很危险;函数里面可以添加筛选条件。所以属性一般是private。

先学会用,再研究原理;

Over Design也是一种罪过。设计过头,还是要实事求是。

一首诗:OO思想慢慢来,封装继承和多态,设计层层无止境,适可而止乐开怀。

---el表达式保留两位小数、使用jstl <fmt:formatNumber>标签保留两位小数

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<fmt:formatNumber type="number" value="${688.87 * 0.8 }" pattern="0.00" maxFractionDigits="2"/>

<td><fmt:formatNumbertype="number"value="${item.lakecoin}"pattern="0.00" /></td>

<fmt:formatNumber type="number" value="${lb.monBzfj }" maxFractionDigits="2"/>这里的maxFractionDigits="2"是什么意思啊

max最大 fraction小数 digits位数
最多保留小数位数 = 2

---jsp页面判定某个变量为负值的时候显示为红色
<tdnowrap class="tdjj"align="center">
    <c:iftest="${list.profitmoney < '0'}">
        <fontcolor="red">${list.profitmoney}</font>
    </c:if>
    <c:iftest="${list.profitmoney >= '0'}">
        <fontcolor="black">${list.profitmoney}</font>
    </c:if>
</td>

${list.profitmoney}
是jsp页面查询出来的某个变量的值。

上面代码是为了实现如果为负值,字体颜色标红。如果为正值或者0,则字体颜色为黑。

<td>//if的方法
  <c:iftest="${item.anxcoin<0}">
        <fontcolor="red"><fmt:formatNumbertype="number"value="${item.anxcoin}"pattern="0.00" /></font>
  </c:if>
  <c:iftest="${item.anxcoin>=0}">
        <fmt:formatNumbertype="number"value="${item.anxcoin}"pattern="0.00" />
  </c:if>
</td>

choose  when otherwise 的方法

<td><c:choose>
    <c:whentest="${item.anxcoin<0}">
        <fontcolor="red"><fmt:formatNumbertype="number"value="${item.anxcoin}"pattern="0.00" /></font>
    </c:when>
    <c:otherwise><fmt:formatNumbertype="number"value="${item.anxcoin}"pattern="0.00" /></c:otherwise>
</c:choose></td>