Java编程思想第三版摘录
=========== <!----> <o:p> </o:p>
<o:p> </o:p>
Chap1 对象简介 <o:p> </o:p>
<o:p> </o:p>
1. 抽象的过程 <o:p> </o:p>
Alan Kay 总结了 Smalltalk 的五项基本特征。这些特征代表了纯的面向对象的编程方法: <o:p> </o:p>
(1). 万物皆对象。将对象想成一种特殊的变量;它存储数据,而且还可以让你“提要求”,命令它进行某些操作。从理论上讲,你可以把所有待解决的问题中的概念性组件(狗,建筑,服务等)都标识成程序里的对象。 <o:p> </o:p>
(2). 程序就是一组相互之间传递消息的对象。你只要向那个对象“发一个消息”,就能向它提出要求。更确切的说,你可以这样认为,消息是调用专属某个对象的方法的请求。 <o:p> </o:p>
(3). 每个对象都利用别的对象来组建它自己的记忆。换言之,你通过将已有的对象打成一个包,来创建新的对象。由此,你可以将程序的复杂性,隐藏在对象的简单性之下。 <o:p> </o:p>
(4). 对象都有类型。任何对象都是某个类的实例 (instance of a class) 。用以区分类的最突出的特点就是“你能传给它什么消息?” <o:p> </o:p>
(5). 所有属于同一类型的对象能接受相同的消息。这种互换性 (substitutability) 是 OOP 最强大的功能之一。 <o:p> </o:p>
Booch 还给对象下了个更为简洁的定义: <o:p> </o:p>
对象有状态,行为和标识。 <o:p> </o:p>
这就是说,对象可以有内部数据(状态),有方法(因而产生了行为),以及每个对象都能同其它对象区分开来--具体而言,每个对象在内存里都有唯一的地址。 <o:p> </o:p>
这句话或许有点太过了。因为对象还能存在于另一台及其上以及不同的内存空间中,此外还能保存在硬盘上。在这种情况下,对象的身份就不能用内存地址,而必须要用别的方法来确定。 <o:p> </o:p>
<o:p> </o:p>
2. 可凭借多态性相互替换的对象 <o:p> </o:p>
非 OOP 的编译器的做法称为前绑定 (early binding) 。编译器会产生那个名字的函数的调用,而连接器负责将这个调用解析成须执行的代码的绝对地址。在 OOP 中,不到运行的时候,程序没法确定代码的地址,所以向泛型对象发送一个消息的时候,就要用到一些特别的手段。 <o:p> </o:p>
OOP 语言用了后绑定 (late binding) 的概念。当你向某个对象送了一个消息后,不到运行时,系统不能确定到底该调用哪段代码。编译器只保证这个方法存在,并且检查参数和返回值的类型(不这么做的语言属于弱类型 weakly typed ),但是它并不知道具体执行的是哪段代码。 <o:p> </o:p>
在有些语言中,你必须明确申明,某个方法要用到后绑定的灵活性( C++ 用 virtual 关键字)。在这些语言中,方法不是默认地动态绑定的。而动态绑定是 Java 的缺省行为,因此无需添加什么额外的关键词就能获得多态性。 <o:p> </o:p>
将派生类当作它的基类来用的过程称为上传( upcast ),反之称为下传( downcast )。下传所需的运行时检查会引起程序运行效率的降低,也加重了编程的负担。解决方案就是参数化类型 (parameterized type) 机制,即泛型。 <o:p> </o:p>
<o:p> </o:p>
3.Collection 和迭代器 <o:p> </o:p>
ArrayList 和 LinkedList ,都是简单的线性序列,具有相同的接口和外部行为。对于 ArrayList ,随机访问是一种时间恒定的操作。然而对于 LinkedList ,随机访问和选取元素的代价会很大。另一方面,如果要在序列中插入元素, LinkedList 的效率会比 ArrayList 的高出许多。 <o:p> </o:p>
<o:p> </o:p>
<o:p> </o:p>
<o:p> </o:p>
============= <o:p> </o:p>
Chap2 万物皆对象 <o:p> </o:p>
<o:p> </o:p>
1. 数据存在哪里 <o:p> </o:p>
数据可以存储在以下六个地方: <o:p> </o:p>
(1). 寄存器 (registers) 。这是反应最快的存储,因为它处在 CPU 里。但寄存器数量有限,由编译器分配,你不能直接控制。 <o:p> </o:p>
(2). 栈 (stack) 。位于常规内存区里, CPU 可以通过栈指针对它进行直接访问。栈指针下移就创建新的存储空间,上移就释放内存空间。这是仅次于寄存器的最快、最有效率的分配内存的方法。由于 Java 编译器必须生成能控制栈指针上下移的代码,所以程序编译的时候,那些将被存储在栈中的数据的大小和生命周期必须是已知的。 Java 把对象的 reference 存放在栈里。 <o:p> </o:p>
(3). 堆 (heap) 。这是一段多用途的内存池,所有 Java 对象都保存在这里。在堆中分配空间时,编译器无需知道该分配多少空间,或数据会在堆里待多长时间。但是其速度比分配栈的慢些。 <o:p> </o:p>
(4). 静态存储 (static storage) 。这里“静态”的意思是“在固定的位置” ( 尽管还是在 RAM 里面 ) 。静态存储里面的数据在整个程序运行期间都能访问到。可以用 static 关键词指明对象的某个元素是静态的,但是 Java 对象本身是决不会放到静态存储中去的。 <o:p> </o:p>
(5). 固定存储 (constant storage) 。常量值通常直接放在程序里。有时常量还能为自己设置界限,这样在嵌入式系统中,就能选择是不是把它们放到 ROM 里面去。 <o:p> </o:p>
(6). 非内存的存储 (Non-RAM storage) 。如果数据完全独立于程序,那么即使程序不运行,它也应该还在。对象被转化成某种能保存在其它介质上的东西,要用的时候,又能在内存里重建。 Java 提供了轻量级 persistence 的支持。 <o:p> </o:p>
<o:p> </o:p>
特例: primitive 类型 <o:p> </o:p>
primitive( 原始 ) 类型的变量直接保存值,并且存储在栈中。 <o:p> </o:p>
<o:p> </o:p>
高精度的数值 <o:p> </o:p>
Java 还包括两个能进行高精度算术运算的类: BigInteger 和 BigDecimal 。 <o:p> </o:p>
<o:p> </o:p>
作用域 <o:p> </o:p>
int x = 12;<o:p></o:p>
{<o:p></o:p>
int x = 100;//illegal<o:p></o:p>
}<o:p></o:p>
<o:p> </o:p>
2. 创建新的数据类型:类 <o:p> </o:p>
只有在“变量被用作类的成员”时, Java 才能确保它获得默认值。本地变量,没有这种保障。 <o:p> </o:p>
不管在哪种情况下, Java 在传递对象的时候,实际上是在传递 reference 。 <o:p> </o:p>
<o:p> </o:p>
<o:p> </o:p>
<o:p> </o:p>
============ <o:p> </o:p>
Chap3 控制程序流程 <o:p> </o:p>
<o:p> </o:p>
1. 运算符 <o:p> </o:p>
逗号运算符 <o:p> </o:p>
Java 里面,唯一一个把逗号当运算符用的地方是 for 循环。 <o:p> </o:p>
<o:p> </o:p>
String 的 + 运算符 <o:p> </o:p>
加号 (+) 用在 String 上的时候,如果表达式中有 String ,那么 Java 编译器会把其他的操作数都转换成 String 。 <o:p> </o:p>
<o:p> </o:p>
Java 没有 sizeof<o:p></o:p>
C 和 C++ 的 sizeof() 用于获取数据要占用多少字节的内存,需要 sizeof 的主要原因是为了移植。相同的数据类型在不同的机器上占用的内存长度可能会不一样。 <o:p> </o:p>
Java 没有移植的问题,因此不需要 sizeof ,所有数据类型在所有的机器上都是相同的。 <o:p> </o:p>
<o:p> </o:p>
运算符的总结 <o:p> </o:p>
在进行数学运算或混和赋值的时候, char , byte , short ,都会先进行提升,运算结果也是 int 。如果要把结果赋给原先那个变量,就必须明确地进行类型转换。 <o:p> </o:p>
除了 boolean 之外,所有的 primitive 类型都能被转换成其它的 primitive 类型。 <o:p> </o:p>
<o:p> </o:p>
2. 执行控制 <o:p> </o:p>
Java 不允许把数字当作 boolean 用,尽管 C 和 C++ 允许这么做(非零值表示 true ,零表示 false )。 <o:p> </o:p>
<o:p> </o:p>
Java 里,唯一能放标签的地方,就是在循环语句的外面。而且必须直接放--在循环语句和标签之间不能有任何东西。而这么做的唯一理由就是,你会嵌套多层循环或选择。因为通常情况下 break 和 continue 关键词只会中断当前循环,而用了标签后,就会退到 label 所在的地方。 <o:p> </o:p>
label1:<o:p></o:p>
outer-iteration{<o:p></o:p>
inner-iteration{<o:p></o:p>
break;// 中断内循环,退到外循环 <o:p> </o:p>
continue;// 中断本次内循环,重新移到内循环开始处,执行下次内循环 <o:p> </o:p>
continue label1;// 中断本次外循环,移到外循环开始处,重新执行下次外循环 <o:p> </o:p>
break label1;// 退出外循环,执行循环以后的语句 <o:p> </o:p>
}<o:p></o:p>
}<o:p></o:p>
如果退出循环或选择的同时,还要退出方法,可以直接使用 return 。 <o:p> </o:p>
continue , break 以及 label 的规则: <o:p> </o:p>
(1). 普通的 continue 会退到内部循环的最开始,然后继续执行内部循环。 <o:p> </o:p>
(2). 带标签的 continue 会跳转到标签,并且重新进入直接跟在标签后面的循环。 <o:p> </o:p>
(3).break 会从循环的“底部溜出去”。 <o:p> </o:p>
(4). 带标签的 break 会从由这个标签标识的循环的“底部溜出去”。 <o:p> </o:p>
在 Java 里能使用标签的唯一理由就是,在嵌套循环的同时要用 break 和 continue 退出多层循环。 <o:p> </o:p>
<o:p> </o:p>
3.switch<o:p></o:p>
switch 会根据整数表达式的值(可以是 char )决定应该运行哪些代码。 <o:p> </o:p>
找到匹配的值后,就会执行相应的 case 语句,不会再进行比较。通常 case 语句应该以 break 结束。否则会直接执行下一个 case 语句,而不会再次进行匹配。如果没有匹配的 case ,则执行 default 语句。 <o:p> </o:p>
<o:p> </o:p>
计算细节 <o:p> </o:p>