《Java核心技术 卷1 基础知识》要点摘录

  1. 前言
  2. 第1章 Java 程序设计概述
  3. 第2章 Java 程序设计环境
  4. 第3章 Java 的基本程序设计结构
  5. 第4章 对象与类
  6. 第5章 继承
  7. 第6章 接口与内部类
  8. 第7章 图形程序设计
  9. 第8章 事件处理
  10. 第9章 Swing 用户界面组件
  11. 第10章 部署应用程序和 applet
  12. 第11章 异常、断言、日志和调试
  13. 第12章 泛型程序设计
  14. 第13章 集合
  15. 第14章 多线程

前言

1、面向对象程序设计(Object—Oriented Programming,OOP)是当今程序设计的主流,而 Java 是一种完全面向对象的语言。
2、继承可以使程序员使用现有的类,并根据需要进行修改。这是 Java 程序设计的基础。
3、Swing 工具箱允许建立一个跨平台的图形用户界面。
4、异常提供了一种将正常的处理代码与错误处理代码分开的有效手段。

第1章 Java 程序设计概述

1、Java 的目标之一是支持开发能够在小型机器上独立运行的软件。基本的解释器以及类支持大约仅为 40KB;再加上基础的标准类库和对线程的支持(基本上是一个自包含的微内核)大约需要增加 175KB。(3页)
2、面向对象设计是一种程序设计技术。它将重点放在数据(即对象)和对象的接口上。(3页)
3、Java 与 C++ 的主要不同点在于多继承,在 Java 中,取而代之的是简单的接口概念,以及 Java 的元类(metaclass)模型。(3页)
4、Java 的设计目标之一在于使得 Java 编写的程序具有多方面的可靠性。(4页)
5、Java 和 C++ 最大的不同在于 Java 采用的指针模型可以消除重写内存和损坏数据的可能性。(4页)
6、虚拟机有一个选项,可以将使用最频繁的字节码序列翻译成机器码,这一过程被称为即时编译。(5页)
7、Java 中的 int 永远为 32 位的整数。(5页)
8、在 Java 中,数据类型具有固定的大小,这消除了代码移植时令人头痛的主要问题。二进制数据以固定的格式进行存储和传输,消除了字节顺序的困扰。字符串是用标准的 Unicode 格式存储的。(5页)
9、多线程可以带来更好的交互响应和实时行为。(6页)
10、在网页中运行 Java 程序称为 applet。(6页)
11、Java 是一种程序设计语言,HTML 是一种描述网页结构的方式,JavaScript 是一种在网页中使用的脚本语言。(10页)
12、XML 是一种描述数据的方式,可以使用任何一种程序设计语言处理 XML 数据。(10页)

第2章 Java 程序设计环境

1、运行 JDK 的方法是在 shell 窗口中键入命令行。(13页)
2、JDK 是 Java Development Kit 的缩写。(14页)
3、执行路径是指操作系统搜索本地可执行文件的目录列表。(14页)
4、javac 程序是一个 Java 编译器。它将文件 ClassName.java 编译成 ClassName.class,并发送到 Java 虚拟机。虚拟机执行编译器放在 class 文件中的字节码。(18页)
5、编译时需要提供一个文件名(ClassName.java),而运行时,只需要指定类名(ClassName),不要带扩展名 .java 或 .class。(20页)
6、Java 区分大小写。(20页)
7、通常,Eclipse 错误报告会伴有一个灯泡的图标,点击这个图标可以得到一个建议解决这个错误的方案列表。(22页)

第3章 Java 的基本程序设计结构

1、关键字 public 称为访问修饰符(access modifier),它用于控制程序的其他部分对这段代码的访问级别。(29页)
2、关键字 class 表明 Java 程序中的全部内容都包含在类中。(29页)
3、关键字 class 后面紧跟类名。Java 中定义类名的规则很宽松。名字必须以字母开头,后面可以跟字母和数字的任意组合。长度基本上没有限制。但是不能使用 Java 保留字作为类名。(30页)
4、标准的命名规范为:类名是以大写字母开头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写(这种在一个单词中间使用大写字母的方式称为骆驼命名法)。(30页)
5、源代码的文件名必须与公共类的名字相同,并用 .java 作为扩展名。(30页)
6、在编译源代码之后就会得到一个包含这个类字节码的文件。(30页)
7、运行编译程序时,Java 虚拟机将从指定类中的 main 方法开始执行(这里的 “方法” 就是 Java 中所说的 “函数” ),因此为了代码能够执行,在类的源文件中必须包含一个 main 方法。(30页)
8、根据 Java 语言规范,main 方法必须声明为 public。(30页)
9、在 Java 中,像在 C/C++ 中一样,用花括号划分程序的各个部分(通常称为块)。Java 中任何方法的代码都用 “{” 开始,用 “}” 结束。(31页)
10、Java 中的所有函数都属于某个类的方法。(31页)
11、Java 中的 main 方法必须是静态的。(31页)
12、关键字 void 表示这个方法没有返回值。(31页)
13、如果 main 方法正常退出,那么 Java 应用程序的退出代码为 0,表示成功地运行了程序。如果希望在终止程序时返回其他的代码,那就需要调用 System.exit 方法。(31页)
14、在 Java 中,每个句子必须用分号结束。回车不是语句的结束标志,因此,如果需要可以将一条语句写在多行上。(31页)
15、点号(.)用于调用方法。Java 使用的通用语法是object.method(parameters),这等价于函数调用。(32页)
16、Java 与 C/C++ 一样,都采用双引号分隔字符串。(32页)
17、对于一个方法,即使没有参数也需要使用空括号。(32页)
18、System.out 的 print 方法在输出后不换行。(32页)
19、在 Java 中,有三种书写注释的方式。最常用的方式是使用 //,其注释内容从 // 开始到本行结尾。当需要长篇的注释时,既可以在每行的注释前面标记 //,也可以使用 /* 和 */ 将一段比较长的注释括起来。第三种注释可以用来自动地生成文档。这种注释以 /** 开始,以 */ 结束。(32页)
20、在 Java 中,/* */ 注释不能嵌套。也就是说,如果代码本身包含了一个 */,就不能用 /* 和 */ 将注释括起来。(33页)
21、Java 是一种强类型语言。这就意味着必须为每一个变量声明一种类型。在 Java 中,一共有 8 种基本类型(primitive type),其中有 4 种整型、2 种浮点类型、1 种用于表示 Unicode 编码的字符单元的字符类型 char 和一种用于表示真值的 boolean 类型。(33页)
22、整型用于表示没有小数部分的数值,它允许是负数(int:4 字节;short:2 字节;long:8 字节;byte:1 字节)。(33页)
23、在 Java 中,整型的范围与运行 Java 代码的机器无关。(33页)
24、长整型数值有一个后缀 L(如 4000000000L),十六进制数值有一个前缀 0x(如 0xCAFE),八进制有一个前缀 0(例如,010 对应八进制中的 8)。
25、从 Java 7 开始,加上前缀 0b 就可以写二进制数(例如,0b1001 就是 9)。(34页)
26、Java 没有任何无符号类型(unsigned)。(34页)
27、浮点类型用于表示有小数部分的数值(float:4 字节;double:8 字节)。(34页)
28、double 表示这种类型的数值精度是 float 类型的两倍。绝大部分应用程序都采用 double 类型。(34页)
29、float 类型的数值有一个后缀 F(例如,3.14F)。没有后缀 F 的浮点数值(如 3.14)默认为 double 类型。当然,也可以在浮点数值后面添加后缀 D(例如,3.14D)。
30、所有 “非数值” 的值都认为是不相同的。(35页)
31、char 类型用于表示单个字符。通常用来表示字符常量。(35页)
32、转义序列符 \u 可以出现在字符常量或字符串的引号之外(而其他所有转义序列符不可以)。(35页)
33、代码点(code point)是指与一个编码表中的某个字符对应的代码值。在 Unicode 标准中,代码点采用十六进制书写,并加上前缀 U+,例如 U+0041 就是字母 A 的代码点。Unicode 的代码点可以分成 17 个代码级别(code plane)。第一个代码级别称为基本的多语言级别(basic multilingual plane),代码点从 U+0000 到 U+FFFF,其中包括了经典的 Unicode 代码;其余的 16 个附加级别,代码点从 U+10000 到 U+10FFF,其中包括了一些辅助字符(supplementary character)。(36页)
34、boolean(布尔)类型有两个值:falsetrue,用来判定逻辑条件。整数值和布尔值之间不能进行相互转换。(36页)
35、在 Java 中,每一个变量属于一种类型(type)。在声明变量时,变量所属的类型位于变量名之前。(37页)
36、变量名必须是一个以字母开头的由字母或数字构成的序列。变量名中所有的字符都是有意义的,并且大小写敏感。变量名的长度没有限制。(37页)
37、如果想要知道哪些 Unicode 字符属于 Java 中的 “字母” ,可以使用 Character 类的 isJavaIdentifierStartisJavaIdentifierPart 方法进行检测。(37页)
38、声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用未被初始化的变量。(37页)
39、要想对一个已经声明过的变量进行赋值,就需要将变量名放在等号(=)左侧,相应取值的 Java 表达式放在等号的右侧。(38页)
40、在 Java 中,可以将声明放在代码中的任何地方,但推荐尽可能地靠近变量第一次使用的地方。(38页)
41、在 Java 中,利用关键字 final 指示常量。关键字 final 表示这个变量只能被赋值一次。一旦被赋值之后,就不能再更改了。习惯上,常量名使用全大写。(38页)
42、在 Java 中,经常希望某个常量可以在一个类中的多个方法中使用,通常将这些常量称为类常量。可以使用关键字 static final 设置一个类常量。(38页)
43、如果一个常量被声明为 public,那么其他类的方法也可以使用这个常量。(39页)
44、当参与 / 运算的两个操作数都是整数时,表示整数除法;否则,表示浮点除法。整数被 0 除将会产生一个异常,而浮点数被 0 除将会得到无穷大或 NaN 结果。(39页)
45、自增运算符和自减运算符的操作数不能是数值。这两个运算符有两种形式。前缀方式(++n)先进行加 1 计算,后缀运算(n++)则使用变量原来的值。(40页)
46、Java 用 && 表示逻辑 “与”、用 || 表示逻辑 “或”。&& 和 || 是按照 “短路” 方式求值的。如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。(41页)
47、Java 支持三元操作符?:。表达式condition?expression1:expression2,当条件 condition 为真时,计算第 1 个表达式,否则计算第 2 个表达式。(41页)
48、当使用两个数值进行二元操作时(例如,n+f,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算。

  • 如果其中一个操作数是 double 类型,另一个操作数就会转换为 double 类型。
  • 如果其中一个操作数是 float 类型,另一个操作数就会转换为 float 类型。
  • 如果其中一个操作数是 long 类型,另一个操作数就会转换为 long 类型。
  • 否则,两个操作数都将被转换为 int 类型。

49、强制类型转换的语法格式是在圆括号中给出想要转换的类型,后面紧跟待转换的变量名。(44页)
50、强制类型转换通过截断小数部分将浮点值转换为整型。如果想对浮点数进行舍入计算,以便得到最接近的整数,那就需要使用 Math.round 方法。round 方法返回的结果为 long 类型。(44页)
51、Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义类,叫作 String。(45页)
52、String 类的 substring 方法可以从一个较大的字符串提取出一个字串。substring 方法的第二个参数是不想复制的第一个位置。substring 的工作方式有一个优点:容易计算字串的长度。字符串 s.substring(a,b) 的长度为 b-a。(46页)
53、Java 语言允许使用 + 号连接(拼接)两个字符串。当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串。(46页)
54、String 类没有提供用于修改字符串的方法。由于不能修改 Java 字符串中的字符,所以在 Java 文档中将 String 类对象称为不可变字符串。不可变字符串却有一个优点:编译器可以让字符串共享。(46页)
55、可以使用equals方法检测两个字符串是否相等。对于表达式:s.equals(t),如果字符串 s 与字符串 t 相等,则返回 true;否则,返回 false。s 与 t 可以是字符串变量,也可以是字符串常量。(47页)
56、要想检测两个字符串是否相等,而不区分大小写,可以使用equalsIgnoreCase方法。一定不能使用==运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否放置在同一个位置上。(48页)
57、空串 “” 是长度为 0 的字符串。空串是一个 Java 对象,有自己的串长度(0)和内容(空)。可以调用以下代码检查一个字符串是否为空:if(str.length()==0)if(str.equals(""))。(48页)
58、String 变量还可以存放一个特殊的值,名为 null,这表示目前没有任何对象与该变量关联。要检查一个字符串是否为 null,可以使用以下条件:if(str==null)。(48页)
59、要检查一个字符串既不是 null 也不为空串,可以使用以下条件:if(str!=null&&str.length()!=0)。(48页)
60、Java 字符串由 char 序列组成。(49页)
61、如果需要用许多小段的字符串构建一个字符串,那么应该按照下列步骤进行:(53页)

  • 首先,构建一个空的字符串构建器:StringBuilder builder = new StringBuilder();
  • 当每次需要添加一部分内容时,就调用 append 方法:builder.append(ch);builder.append(str);
  • 在需要构建字符串时就调用 toString() 方法,将可以得到一个 String 对象,其中包含了构建器中的字符序列:String completedString = builder.toString();

62、要想通过控制台进行输入,首先需要构造一个 Scanner 对象,并与 “标准输入流” System.in 关联:Scanner in = new Scanner(System.in);(54页)
63、当使用的类不是定义在基本 java.lang 包中时,一定要使用 import 指示字将相应的包加载进来。(55页)
64、可以使用 System.out.print(x) 将数值 x 输出到控制台上。这条命令将以 x 对应的数据类型所允许的最大非 0 数字位数打印输出 x。(56页)
65、要想对文件进行读取,就需要用 File 对象构造一个 Scanner 对象;要想写入文件,就需要构造一个 PrintWriter 对象。(60页)
66、与任何程序设计语言一样,Java 使用条件语句和循环语句确定控制流程。(61页)
67、当需要对某个表达式的多个值进行检测时,可以使用 switch 语句。(61页)
68、块(即复合语句)是指由一对花括号括起来的若干条简单的 Java 语句,块确定了变量的作用域。一个块可以嵌套在另一个块中。不能在嵌套的两个块中声明同名的变量。使用块可以在 Java 程序结构中原本只能放置一条简单语句的地方放置多条语句。(62页)
69、当条件为 true 时,while 循环执行一条语句(也可以是一个语句块)。如果开始循环条件的值就为 false,则 while 循环体一次也不执行。(65页)
70、for 循环语句是支持迭代的一种通用结构,利用每次迭代之后更新的计数器或类似的变量来控制迭代次数。for 语句的第 1 部分通常用于对计数器初始化;第 2 部分给出每次新一轮循环执行前要检测的循环条件;第 3 部分指示如何更新计数器。(68页)
71、当在 for 语句的第 1 部分中声明了一个变量之后,这个变量的作用域就为 for 循环的整个循环体。如果在 for 语句内部定义一个变量,这个变量就不能在循环体之外使用。如果希望在 for 循环体之外使用循环体计数器的最终值,就要确保这个变量在循环语句的前面且在外部声明!(70页)
72、可以在各自独立的不同 for 循环中定义同名的变量。(70页)
73、switch 语句将从与选项值相匹配的 case 标签处开始执行直到遇到 break 语句,或者执行到 switch 语句的结束处为止。如果在 case 分支语句的末尾没有 break 语句,那么就会接着执行下一个 case 分支语句。如果没有相匹配的 case 标签,而有 default 子句,就执行这个子句。(73页)
74、当在 switch 语句中使用枚举常量时,不必在每个标签中指明枚举名,可以由 switch 的表达式值确定。(73页)
75、continue 语句将控制转移到最内层循环的首部。如果将 continue 语句用于 for 循环中,就可以跳到 for 循环的 “更新” 部分。(75页)
76、如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包中的两个很有用的类:BigInteger 和 BigDecimal。BigInteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。使用静态的 valueOf 方法可以将普通的数值转换为大数值:BigInteger a = BigInteger.valueOf(100);(76页)
77、数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。在声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量的名字。(78页)
78、应该使用 new 运算符创建数组,new int[n] 会创建一个长度为n的数组。(78页)
79、创建一个数字数组时,所有元素都初始化为 0。boolean 数组的元素会初始化为 false。对象数组的元素则初始化为一个特殊值 null,这表示这些元素(还)未存放任何对象。(79页)
80、如果创建了一个 100 个元素的数组,并且试图访问元素 a[100](或任何在 0 ~ 99 之外的下标),程序就会引发 “array index out of bounds” 异常而终止执行。(79页)
81、一旦创建了数组,就不能再改变它的大小(尽管可以改变每一个数组元素)。(79页)
82、在 Java 中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组。在 Java 中,允许数组长度为 0。(80页)
83、每一个 Java 应用程序都有一个带 String arg[] 参数的 main 方法。这个参数表明 main 方法将接收一个字符串数组,也就是命令行参数。在 Java 应用程序的 main 方法中,程序名并没有存储在 args 数组中。(81页)
84、Math.random 方法将返回一个 0 到 1 之间(包含 0、不包含 1)的随机浮点数。用 n 乘以这个浮点数,就可以得到从 0 到 n—1 之间的一个随机数。(82页)
85、多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。(85页)

第4章 对象与类

1、面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。(91页)
2、在 OOP 中,不必关心对象的具体实现,只要能够满足用户的需求即可。(91页)
3、类(class)是构造对象的模板或蓝图。由类构造(construct)对象的过程称为创建类的实例(instance)。(92页)
3、由 Java 编写的所有代码都位于某个类的内部。(92页)
4、从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。对象中的数据称为实例域(instance field),操纵数据的过程称为方法(method)。(92页)
5、实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。(92页)
6、在扩展一个已有的类时,这个扩展后的新类具有所扩展的类的全部属性和方法。在新类中,只需提供适用于这个新类的新方法和数据域就可以了。通过扩展一个类来建立另外一个类的过程称为继承(inheritance)。(93页)
7、要想使用OOP,一定要清楚对象的三个主要特性:(93页)

  • 对象的行为(behavior)——可以对对象施加哪些操作,或可以对对象施加哪些方法?
  • 对象的状态(state)——当施加那些方法时,对象如何让响应?
  • 对象的标识(identify)——如何辨别具有相同行为与状态的不同对象?

8、同一个类的所有对象实例,由于支持相同的行为而具有家族式的相似性。对象的行为是用可调用的方法定义的。(93页)
9、每个对象都保存着描述当前特征的信息。这就是对象的状态。对象的状态可能会随着时间而发生改变,但这种改变不会是自发的。对象状态的改变必须通过调用方法实现。(93页)
10、对象的状态并不能完全描述一个对象。每个对象都有一个唯一的身份(identify)。作为一个类的实例,每个对象的标识永远是不同的,状态常常也存在着差异。(93页)
11、识别类的简单规则是在分析问题的过程中寻找名词,而方法对应着动词。(93页)
12、在类之间,最常见的关系有依赖(“uses-a”)、聚合(“has-a”)、继承(“is-a”)。如果一个类的方法操纵另一个类的对象,我们就说一个类依赖于另一个类。聚合关系意味着类 A 的对象包含类 B 的对象。继承是一种用于表示特殊与一般关系的。一般而言,如果类 A 扩展类 B,类 A 不但包含从类 B 继承的方法,还会拥有一些额外的功能。(95页)
13、很多程序员采用 UML(Unified Modeling Language,统一建模语言)绘制类图,用来描述类之间的关系。类用矩形表示,类之间的关系用带有各种修饰的箭头表示。(95页)
14、要想使用对象,就必须首先构造对象,并指定其初始状态。然后,对对象应用方法。(95页)
15、在 Java 程序设计语言中,使用构造器(constructor)构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。构造器的名字应该与类名相同。(95页)
16、一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。(97页)
17、在 Java 中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new 操作符的返回值也是一个引用。(97页)
18、可以显示地将对象变量设置为 null,表明这个对象变量目前没有引用任何对象。(97页)
19、如果将一个方法应用于一个值为 null 的对象上,那么就会产生运行错误。(97页)
20、局部变量不会自动地初始化为 null,而必须通过调用 new 或将它们设置为 null 进行初始化。(98页)
21、时间是用距离一个固定时间点的毫秒数表示的,这个点就是所谓的纪元(epoch),它是 UTC(Coordinated Universal Time)时间 1970 年 1 月 1 日 00:00:00。(98页)
22、get 方法仅仅查看并返回对象的状态,而 set 和 add 方法却对对象的状态进行修改。对实例域做出修改的方法称为更改器方法(mutator method),仅访问实例域而不进行修改的方法称为访问器方法(accessor method)。(100页)
23、通常的习惯是在访问器方法名前面加上前缀 get,在更改器方法前面加上前缀 set。(100页)
24、在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。(107页)
25、关键字 public 意味着任何类的任何方法都可以调用这些方法。关键字 private 确保只有类自身的方法能够访问这些实例域,而其他类的方法不能够读写这些域。(109页)
26、类通常包括类型属于某个类类型的实例域。(109页)
27、构造器总是伴随着 new 操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。(110页)
28、构造器与类同名;每个类可以有一个以上的构造器;构造器可以有 0 个、1 个或多个参数;构造器没有返回值;构造器总是伴随着 new 操作一起调用。(110页)
29、不要在构造器中定义与实例域重名的局部变量。(110页)
30、方法用于操作对象以及存取它们的实例域。(111页)
31、在每一个方法中,关键字 this 表示隐式参数。(111页)
32、在 Java 程序设计语言中,所有的方法都必须在类的内部定义,但并不表示它们是内联方法。是否将某个方法设置为内联方法是 Java 虚拟机的任务。(112页)
33、一个方法可以访问所属类的所有对象的私有数据。(114页)
34、在 Java 中,为了实现一个私有的方法,只需将关键字 public 改为 private 即可。(114页)
35、对于私有方法,如果改用其他方法实现相应的操作,则不必保留原有的方法。(114页)
36、final 修饰符大都应用于基本(primitive)类型域,或不可变(immutable)类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类)。(115页)
37、如果将域定义为 static,每个类中只有一个这样的域。静态域属于类,而不属于任何独立的对象。(115页)
38、静态方法是一种不能向对象实施操作的方法。可以认为静态方法是没有 this 参数的方法。因为静态方法不能操作对象,所以不能在静态方法中访问实例域。但是,静态方法可以访问自身类中的静态域。(117页)
39、在下面两种情况下使用静态方法:(117页)

  • 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供。
  • 一个方法只需要访问类的静态域。

40、不需要使用对象调用静态方法。(118页)
41、main 方法也是一个静态方法。main 方法不对任何对象进行操作。静态的 main 方法将执行并创建程序所需要的对象。(118页)
42、按值调用(call by value)表示方法接收的是调用者提供的值。而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。(121页)
43、Java 程序设计语言总是采用按值调用。(121页)
44、如果多个方法有相同的名字、不同的参数,便产生了重载。编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,或者找出多个可能的匹配,就会产生编译时错误(这个过程被称为重载解析(overloading resolution))。(126页)
45、Java 允许重载任何方法,而不只是构造器方法。因此,要完整地描述一个方法,需要指出方法名以及参数类型。这叫做方法的签名(signature)。(126页)
46、不能有两个名字相同、参数类型也相同却返回不同类型值的方法。(126页)
47、如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋予默认值:数值为 0、布尔值为 false、对象引用为 null。(127页)
48、如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数构造器。这个构造器将所有的实例域设置为默认值。(127页)
49、如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时,如果没有提供参数就会被视为不合法。(127页)
50、如果构造器的第一个语句形如 this(…),这个构造器将调用同一个类的另一个构造器。(129页)
51、Java 有自动的垃圾回收器,不需要人工回收内存,所以 Java 不支持析构器。(134页)
52、Java 允许使用包(package)将类组织起来。使用包的主要原因是确保类名的唯一性。(134页)
53、一个类可以使用所属包中的所有类,以及其他包中的公有类(public class)。(134页)
54、可以采用两种方式访问另一个包中的公有类。第一种方式是在每个类名之前添加完整的包名。第二种方式是使用 import 语句。import 语句是一种引用包含在包中的类的简明描述。一旦使用了 import 语句,在使用类时,就不必写出包的全名了。可以使用 import 语句导入一个特定的类或者整个包。import 语句应该位于源文件的顶部(但位于 package 语句的后面)。(135页)
55、只能使用星号(*)导入一个包,而不能使用 import java.* 或 import java.*.* 导入以 java 为前缀的所有包。(135页)
56、import 语句不仅可以导入类,还增加了导入静态方法和静态域的功能。(136页)
57、要想将一个类放入包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。(136页)
58、如果没有在源文件中放置 package 语句,这个源文件中的类就被放置在一个默认包(default package)中。默认包是一个没有名字的包。(137页)
59、将包中的文件放到与完整的包名匹配的子目录中。(137页)
60、如果没有指定 public 或 private,这个部分(类、方法或变量)可以被同一个包中的所有方法访问。(139页)
61、类存储在文件系统的子目录中。类的路径必须与包名匹配。(140页)
62、类文件也可以存储在 JAR(Java 归档)文件中。在一个 JAR 文件中,可以包含多个压缩形式的类文件和子目录,这样既可以节省又可以改善性能。在程序中用到第三方(third-party)的库文件时,通常会给出一个或多个需要包含的 JAR 文件。(140页)
63、类注释必须放在 import 语句之后,类定义之前。(144页)
64、每一个方法注释必须放在所描述的方法之前。(144页)
65、可以直接将类、方法和变量的注释放置在 Java 源文件中,只要用 /**…*/ 文档注释界定就可以了。但是,要想产生包注释,就需要在每一个包目录中添加一个单独的文件。(146页)

第5章 继承

1、利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上,还可以添加一些新的方法和域,以满足新的需求。(150页)
2、反射是指在程序运行期间发现更多的类及其属性的能力。(150页)
3、在 Java 中,所有的继承都是共有继承。(150页)
4、关键字 extends 表示继承。关键字 extends 表明正在构造的新类派生于一个已经存在的类。已存在的类称为超类(superclass)、基类(base class)或父类(parent class);新类称为子类(subclass)、派生类(derived class)或孩子类(child class)。(151页)
5、子类比超类拥有的功能更加丰富。(151页)
6、在通过扩展超类定义子类的时候,仅需要指出子类与超类的不同之处。因此在设计类的时候,应该将通用的方法放在超类中,而将具有特殊用途的方法放在子类中,这种将通用的功能放到超类的做法,在面向对象程序设计中十分普遍。(151页)
7、在子类中可以增加域、增加方法或覆盖超类的方法,然而绝对不能删除继承的任何域和方法。(153页)
8、在 Java 中使用关键字 super 调用超类的方法。使用 super 调用构造器的语句必须是子类构造器的第一条语句。(153页)
9、如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中又没有显式地调用超类的其他构造器,则 Java 编译器将报告错误。(153页)
10、关键字 this 有两个用途:一是引用隐式参数,二是调用该类其他的构造器。super 关键字也有两个用途;一是调用超类的方法,二是调用超类的构造器。在调用构造器的时候,这两个关键字的使用方式很相似。调用构造器的语句只能作为另一个构造器的第一条语句出现。构造参数既可以传递给本类(this)的其他构造器,也可以传递给超类(super)的构造器。(153页)
11、一个对象变量(例如,变量 e)可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamic binding)。(154页)
12、由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy)。在继承层次中,从某个特定类到其祖先的路径被称为该类的继承链。(inheritance chain)。通常,一个祖先类可以拥有多个子孙继承链。(156页)
13、Java 不支持多继承。(156页)
14、有一个用来判断是否应该设计为继承关系的简单规则,这就是 “is—a” 规则,它表明子类的每个对象也是超类的对象。“is—a” 规则的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。(157页)
15、在 Java 程序设计语言中,对象变量是多态的。一个 Employee 变量既可以引用一个 Employee 类对象,也可以引用一个 Employee 类的任何一个子类的对象。不能将一个超类的引用赋给子类变量。(157页)
16、在 Java 中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。(158页)
17、如果在子类中定义了一个与超类签名相同的方法,那么子类中的这个方法就覆盖了超类中的这个相同签名的方法。(159页)
18、每次调用方法都要进行搜索,时间开销相当大。因此,虚拟机预先为每个类创建了一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。这样一来,在真正调用方法的时候,虚拟机仅查找这个表就行了。(159页)
19、在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。特别是,如果超类方法是 public,子类方法一定要声明为 public。(160页)
20、不允许扩展的类被称为 final 类。如果在定义类的时候使用了 final 修饰符就表明这个类是 final 类。类中的特定方法也可以被声明为 final。如果这样做,子类就不能覆盖这个方法(final 类中的所有方法自动地称为 final 方法)。如果将一个类声明为 final,只有其中的方法自动地称为 final,而不包括域。(160页)
21、将方法或类声明为 final 的主要目的是:确保它们不会在子类中改变语义。(161页)
22、如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程称为内联(inlining)。(161页)
23、将一个子类的引用赋给一个超类变量,编译器是允许的。但将一个超类的引用赋给一个子类变量,必须进行类型转换,这样才能够通过运行时的检查。(162页)
24、只能在继承层次内进行类型转换;在将超类转换成子类之前,应该使用 instanceof 进行检查。(162页)
25、如果自下而上在类的继承层次结构中上移,位于上层的类更具有通用性,甚至可能更加抽象。从某种角度看,祖先类更加通用,人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。(163页)
26、包含一个或多个抽象方法的类本身必须被声明为抽象的。(164页)
27、除了抽象方法之外,抽象类还可以包含具体数据和具体方法。(164页)
28、建议尽量将通用的域和方法(不管是否是抽象的)放在超类(不管是否是抽象类)中。(164页)
29、抽象方法充当着占位的角色,它们的具体实现在子类中。扩展抽象类可以有两种选择。一种是在子类中定义部分抽象方法或不定义抽象方法,这样就必须将子类也标记为抽象类;另一种是定义全部的抽象方法,这样一来,子类就不是抽象的了。(164页)
30、类即使不含抽象方法,也可以将类声明为抽象类。抽象类不能被实例化。如果将一个类声明为 abstract,就不能创建这个类的对象。可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。(165页)
31、Java用于控制可见性的 4 个访问修饰符:(168页)

  • 仅对本类可见——private。
  • 对所有类可见——public。
  • 对本包和所有子类可见——protected。
  • 对本包可见——默认,不需要修饰符。

32、Object 类是 Java 中所有类的始祖,在 Java 中每个类都是由它扩展而来的。如果没有明确地指出超类,Object 就被认为是这个类的超类。可以使用 Object 类型的变量引用任何类型的对象。(169页)
33、在 Java 中,只有基本类型(primitive types)不是对象,例如,数值、字符和布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展于 Object 类。(169页)
34、Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。在 Object 类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。(169页)
35、如果两个参数都为 null,Object.equals(a,b) 调用将返回 true;如果其中一个参数为 null,则返回 false;否则,如果两个参数都不为 null,则调用 a.equals(b)。(170页)
36、Java 语言规范要求 equals 方法具有下面的特性:

  • 自反性:对于任何非空引用 x,x.equals(x) 应该返回 true。
  • 对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 true,x.equals(y) 也应该返回 true。
  • 传递性:对于任何引用 x、y 和 z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,x.equals(z) 也应该返回 true。
  • 一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
  • 对于任意非空引用 x,x.equals(null) 应该返回 false。

37、散列码(hash code)是由对象导出的一个整数值。散列码是没有规律的。如果 x 和 y 是两个不同的对象,x.hashCode() 与 y.hashCode() 基本上不会相同。(173页)
38、每个对象都有一个默认的散列码,其值为对象的存储地址。(174页)
39、hashCode 方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。(174页)
40、Object 中的 toString 方法用于返回表示对象值的字符串。(175页)
41、只要对象与一个字符串通过操作符 “+” 连接起来,Java 编译就会自动地调用 toString 方法,以便获得这个对象的字符串描述。(176页)
42、在调用 x.toString() 的地方可以用 “+x” 替代。这条语句将一个空串与 x 的字符串表示相连接。这里的 x 就是 x.toString()。(176页)
43、在 Java 中,允许在运行时确定数组的大小。(181页)
44、ArrayList 是一个采用类型参数(type parameter)的泛型类(genetic class)。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面。尖括号中的类型参数不允许是基本类型。(181页)
45、使用 add 方法可以将元素添加到数组列表中。(181页)
46、数组列表管理着对象引用的一个内部数组。最终,数组的全部空间有可能被用尽。如果调用 add 且内部数组已经满了,数组列表就将自动地创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中。(182页)
47、size() 方法将返回数组列表中包含的实际元素数目。(182页)
48、一旦能够确认数组列表的大小不再发生变化,就可以调用 trimToSize 方法。这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。垃圾回收器将回收多余的存储空间。一旦整理了数组列表的大小,添加新元素就需要花时间再次移动存储块,所以应该在确认不会添加任何元素时,再调用 trimToSize。(182页)
49、使用 get 和 set 方法实现访问或改变数组元素的操作。(183页)
50、使用 add 方法为数组添加新元素,而不要使用 set 方法,它只能替换数组中已经存在的元素内容。(183页)
51、除了在数组列表的尾部追加元素之外,还可以在数组列表的中间插入元素,使用带索引参数的 add 方法。如果插入新元素后,数组列表的大小超过了容量,数组列表就会被重新分配存储空间。(184页)
52、所有的基本类型都有一个与之对应的类。通常,这些类称为包装器(wrapper)。对象包装器类时不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是 final,因此不能定义它们的子类。(187页)
53、在比较两个枚举类型的值时,永远不需要调用 equals,而直接使用 “==” 就可以了。(191页)
54、所有的枚举类型都是 Enum 类的子类。它们继承了这个类的许多方法。其中最有用的一个是 toString,这个方法能够返回枚举常量名。toString 的逆方法是静态方法 valueOf。每个枚举类型都有一个静态的 values 方法,它将返回一个包含全部枚举值的数组。ordinal 方法返回 enum 声明中枚举常量的位置,位置从 0 开始计数。(191页)
55、 当程序运行过程中发生错误时,就会 “抛出异常”。抛出异常比终止程序要灵活得多,这是因为可以提供一个 “捕获” 异常的处理器(handler)对异常情况进行处理。如果没有提供处理器,程序就会终止,并在控制台上打印出一条信息,其中给出了异常的类型。(195页)
56、异常有两种类型:未检查异常和已检查异常。对于已检查异常,编译器将会检查是否提供了处理器。对于未检查异常,编译器不会查看是否为这些错误提供了处理器。(195页)
57、将可能抛出已检查异常的一个或多个方法调用代码放在 try 块中,然后再 catch 子句中提供处理器代码。(195页)
58、如果抛出异常,则将跳过 try 块中的剩余代码,程序直接进入 catch 子句(利用 Throwable 类的 printStackTrace 方法打印出栈的轨迹。Throwable 是 Exception 类的超类)。如果 try 块中没有抛出任何异常,那么会跳过 catch 子句的处理器代码。(196页)
59、在 java.lang.reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、方法和构造器。这三个类都有一个叫做 getName 的方法,用来返回项目的名称。Field 类有一个 getType 方法,用来返回描述域所属类型的 Class 对象。Method 和 Constructor 类有能够报告参数类型的方法,Method 类还有一个可以报告返回类型的方法。这三个类还有一个叫做 getModifiers 的方法,它将返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况。(196页)
60、java.lang.reflect 包中的 Array 类允许动态地创建数组。(206页)
61、可以通过调用 Array.getLength(a) 获得数组 a 的长度,也可以通过 Array 类的静态 getLength 方法的返回值得到任意数组的长度。(207页)
62、反射机制允许调用任意方法。(209页)
63、设计继承关系的建议:(212页)

  • 将公共操作和域放在超类。
  • 不要使用受保护的域。
  • 使用继承实现 “is-a” 关系。
  • 除非所有继承的方法都有意义,否则不要使用继承。
  • 在覆盖方法时,不要改变预期的行为。
  • 使用多态,而非类型信息。使用多态方法或接口编写的代码比使用对多种类型进行检测的代码更加易于维护和扩展。
  • 不要过多地使用反射。

第6章 接口与内部类

1、接口(interface)技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现。一个类可以实现(implement)一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。(215页)
2、对象的克隆是指创建一个新对象,且新对象的状态与原始对象的状态相同。当对克隆的新对象进行修改时,不会影响原始对象的状态。(215页)
3、内部类(inner class)定义在另外一个类的内部,其中的方法可以访问包含它们的外部类的域。内部类技术主要用于设计具有相互协作关系的类集合。特别是在编写处理 GUI 事件的代码时,使用它将可以让代码看起来更加简练专业。(215页)
4、代理(proxy)是一种实现任意接口的对象。代理是一种非常专业的构造工具,它可以用来构建系统级的工具。(215页)
5、在 Java 程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。(215页)
6、接口中的所有方法自动地属于 public。因此,在接口中声明方法时,不必提供关键字 public。(216页)
7、在接口中还可以定义常量。(216页)
8、接口绝不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务应该由实现接口的那个类来完成。因此,可以将接口看成是没有实例域的抽象类。(216页)
9、为了让类实现一个接口,通常需要下面两个步骤:

  • 将类声明为实现给定的接口。
  • 对接口中的所有方法进行定义。

10、要将类声明为实现某个接口,需要使用关键字 implements。(216页)
11、在实现接口时,必须把方法声明为 public;否则,编译器将认为这个方法的访问属性是包可见性,即类的默认访问属性,之后编译器就会给出试图提供更弱的访问权限的警告信息。(217页)
12、Java 程序设计语言是一种强类型(strongly typed)语言。在调用方法的时候,编译器将会检查这个方法是否存在。(217页)
13、“sgn” 是一个数值的符号:如果 n 是负值,sgn(n) 等于 —1;如果 n 是 0,sgn(n) 等于 0;如果 n 是正值,sgn(n) 等于 1。(220页)
14、接口不是类,尤其不能使用 new 运算符实例化一个接口。尽管不能构造接口的对象,却能声明接口的变量,接口变量必须引用实现了接口的类对象。(220页)
15、如同使用 instanceof 检查一个对象是否属于某个特定类一样,也可以使用 instanceof 检查一个对象是否实现了某个特定的接口。(221页)
16、与可以建立类的继承关系一样,接口也可以被扩展。允许存在多条从具有较高通用性的接口到较高专用性的接口的链。(221页)
17、虽然在接口中不能包含实例域或静态方法,但却可以包含常量。(221页)
18、与接口中的方法都自动地被设置为 public 一样,接口中的域将被自动设为 public static final。(221页)
19、尽管每个类只能够拥有一个超类,但却可以实现多个接口。(221页)
20、如果希望自己设计的类拥有克隆和比较的能力,只要实现 CloneableComparable 这两个接口就可以了。使用逗号将实现的各个接口(描述你想提供的特性)分隔开。(221页)
21、有些程序设计语言允许一个类有多个超类,例如 C++。我们将此特性称为多继承(multiple inheritance)。而 Java 的设计者选择了不支持多继承, 其主要原因是多继承会让语言本身变得非常复杂,效率也会降低。(222页)
22、当拷贝一个变量时,原始变量与拷贝变量引用同一个对象。这就是说,改变一个变量所引用的对象将会对另一个变量产生影响。(222页)
23、clone 方法是 Object 类的一个 protected 方法,在用户编写的代码中不能直接调用它。(223页)
24、对于每一个类,都需要做出下列判断:(224页)

  • 默认的 clone 方法是否满足要求。
  • 默认的 clone 方法是否能够通过调用可变子对象的 clone 得到修补。
  • 是否不应该使用 clone。

实际上,第 3 项是默认的。如果要选择 1 或 2,类必须:

  • 实现 Cloneable 接口。
  • 使用 public 访问修饰符重新定义 clone 方法。

25、如果一个对象需要克隆,而没有实现 Cloneable 接口,就会产生一个已检验异常(checked exception)。(224页)
26、Cloneable 接口是 Java 提供的几个标记接口之一。通常使用接口的目的是为了确保类实现某个特定的方法或一组特定的方法,而标记接口没有方法,使用它的唯一目的是可以用 instanceof 进行类型检查。建议在自己编写程序时,不要使用这种技术。(225页)
27、所有的数组类型均包含一个 clone 方法,这个方法被设为 public,而不是 protected。可以利用这个方法创建一个包含所有数据元素拷贝的一个新数组。(226页)
28、回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。(228页)
29、在任何使用 C++ 函数指针的地方,都应该考虑使用 Java 中的接口。(229页)
30、内部类(inner class)是定义在另一个类中的类。使用内部类的主要原因有以下三点:(231页)

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏起来。
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。

31、内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。(233页)
32、内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用 $ (美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。(236页)
33、局部类不能用 public 或 private 访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。局部类有一个优势,即对外部世界可以完全地隐藏起来。(239页)
34、与其他内部类相比较,局部类还有一个优点。它们不仅能够访问包含它们的外部类,还可以访问局部变量。不过,那些局部变量必须被声明为 final。(239页)
35、final 关键字可以应用于局部变量、实例变量和静态变量。在所有这些情况下,它们的含义都是:在创建这个变量之后,只能够为之赋值一次。此后,再也不能修改它的值了,这就是 final。不过,在定义 final 变量的时候,不必进行初始化。定义时没有初始化的 final 变量通常被称为空 final(blank final)变量。(240页)
36、数组变量被声明为 final,仅仅表示不可以让它引用另外一个数组,数组中的数据元素可以自由地更改。(241页)
37、假如只创建局部内部类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous inner class)。(241页)
38、由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类(superclass)构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。(242页)
39、如果构造参数的闭圆括号跟一个开花括号,正在定义的就是匿名内部类。(242页)
40、有时候,有些内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为 static,以便取消产生的引用。(244页)
41、利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。(247页)
42、代理类是在程序运行过程中创建的。然而,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。(251页)
43、所有的代理类都扩展于 Proxy 类。一个代理类只有一个实例域——调用处理器,它定义在 Proxy 的超类中。为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。(251页)

第7章 图形程序设计

1、名为 Swing 的用户界面库是不对等基于 GUI 工具箱的正式名字。它已是 Java 基础类库(Java Foundation Class,JFC)的一部分。(254页)
2、Swing 是指 “被绘制的” 用户界面类;AWT 是指像事件处理这样的窗口工具箱的底层机制。(254页)
3、在 Java 中,顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架(frame)。在 AWT 库中有一个称为 Frame 的类,用于描述顶层窗口。这个类的 Swing 版本名为 JFrame,它扩展于 Frame 类。JFrame 是极少数几个不绘制在画布上的 Swing 组件之一。因此,它的修饰部件(按钮、标题栏、图标等)由用户的窗口系统绘制,而不是由 Swing 绘制。(257页)
4、所有的 Swing 组件必须由事件分派线程(event dispatch thread)进行配置,线程将鼠标点击和按键控制转移到用户接口组件。(258页)
5、在包含多个框架的程序中,不能在用户关闭其中的一个框架时就让程序退出。在默认情况下,用户关闭窗口时只是将框架隐藏起来,而程序并没有终止。(259页)
6、事件分派线程保持程序处于激活状态,直到关闭框架或调用 System.exit 方法终止程序。(259页)
7、组件类的很多方法是以获取/设置方法对形式出现的。这样的一个获取/设置方法对被称为一种属性。属性包括属性名和类型。将 get 或 set 之后的第一个字母改为小写字母就可以得到相应的属性名。(261页)
8、针对 get/set 约定有一个例外:对于类型为 boolean 的属性,获取方法由 is 开头。(261页)
9、如果没有明确地指定框架的大小,所有框架的默认值为 0 × 0 像素。(262页)
10、在 Java 中,框架被设计为放置组件的容器,可以将菜单栏和其他的用户界面元素放置在其中。在通常情况下,应该在另一组件上绘制信息,并将这个组件添加到框架中。(265页)
11、在 Java 中,所有的绘制都必须使用 Graphics 对象,其中包含了绘制图案、图像和文本的方法。(266页)
12、一定不要自己调用 paintComponent 方法。在应用程序需要重新绘图的时候,这个方法将被自动地调用,不要人为地干预这个自动的处理过程。(267页)
13、使用 Graphics2D 类的 setPaint 方法可以为图形环境上的所有后续的绘制操作选择颜色。要想绘制多种颜色,就需要按照选择颜色、绘制图形、再选择另外一种颜色、再绘制图形的过程实施。(277页)
14、Color 类用于定义颜色。在 java.awt.Color 类中提供了 13 个预定义的常量,它们分别表示 13 种标准颜色:BLACK,BLUE,CYAN(蓝绿色),DARK_GRAY(灰黑色),GREEN,LIGHT_GRAY(浅灰色),MAGENTA(洋红色),ORANGE,PINK,RED,WHITE,YELLOW(277页)
15、可以通过提供红、绿和蓝三色成分来创建一个 Color 对象,以达到定制颜色的目的。三种颜色都是用 0 ~ 255 (也就是一个字节)之间的整型数值表示。(278页)
16、要想设置背景颜色,就需要使用 Component 类中的 setBackground 方法。Component 类是 JComponent 类的祖先。(278页)
17、要想使用某种字体绘制字符,必须首先利用指定的字体名、字体风格和字体大小来创建一个 Font 类对象。(281页)
18、点数目是排版中普遍使用的表示字体大小的单位,每英寸包含 72 个点。(281页)
19、基线(baseline)是一条虚构的线,例如,字母 “e” 所在的底线。上坡度(ascent)是从基线到坡顶(ascenter)的距离。例如,“b” 和 “k” 以及大写字母的上面部分。下坡度(descent)是从基线到坡底(descenter)的距离,坡底是 “p” 和 “g” 这种字母的底线。行间距(leading)是某一行的坡底与其下一行的坡顶之间的空隙。字体的高度是连续两个基线之间的距离,它等于下坡度 + 行间距 + 上坡度。(282页)

第8章 事件处理

1、任何支持 GUI 的操作环境都要不断地监视按键或点击鼠标这样的事件。操作环境将这些事件报告给正在运行的应用程序。如果有事件发生,每个应用程序将决定如何对它们做出响应。(291页)
2、在 AWT 所知的事件范围内,完全可以控制事件从事件源(event source)(例如,按钮或滚动条)到事件监听器(event listener)的传递过程,并将任何对象指派给事件监听器。(291页)
3、事件源有一些向其注册事件监听器的方法。当某个事件源产生事件时,事件源会向为事件注册的所有事件监听器对象发送一个通告。(291页)
4、像 Java 这样的面向对象语言,都将事件的相关信息封装在一个事件对象(event object)中。在 Java 中,所有的事件对象都最终派生于 java.util.EventObject 类。(291页)
5、不同的事件源可以产生不同类别的事件。(292页)
6、AWT 事件处理机制的概要:

  • 监听器对象是一个实现了特定监听器接口(listener interface)的类的实例。
  • 事件源是一个能够注册监听器对象并发送事件对象的对象。
  • 当事件发生时,事件源将事件对象传递给所有注册的监听器。
  • 监听器对象将利用事件对象中的信息决定如何对事件做出响应。

7、事件监听器对象通常需要执行一些对其他对象可能产生影响的操作。可以策略性地将监听器放置在需要修改状态的那个类中。(295页)
8、每个含有多个方法的 AWT 监听器接口都配有一个适配器(adapter)类,这个类实现了接口中的所有方法,但每个方法没有做任何事情。这意味着适配器类自动地满足了 Java 实现相关监听器接口的技术需求。可以通过扩展适配器类来指定对某些事件的响应动作,而不必实现接口中的每个方法。(304页)
9、Swing 包提供了一种非常实用的机制来封装命令,并将它们连接到多个事件源,这就是 Action 接口。一个动作是一个封装下列内容的对象:

  • 命令的说明(一个文本字符串和一个可选图标);
  • 执行命令所需要的参数。

10、用户界面中可以包含许多按钮、菜单、滚动栏以及其他的组件。当用户敲击键盘时,这个动作会被发送给拥有焦点的组件。通常具有焦点的组件可以明显地察觉到。(308页0
11、用同一个动作响应按钮、菜单项或按键的方式:

  • 实现一个扩展于 AbstractAction 类的类。多个相关的动作可以使用同一个类。
  • 构造一个动作类的对象。
  • 使用动作对象创建按钮或菜单项。构造器将从动作对象中读取标签文本和图标。
  • 为了能够通过按钮触发动作,必须额外地执行几步操作。首先定位顶层窗口组件(例如,包含所有其他组件的面板)。
  • 然后,得到顶层组件的 WHEN_ANCESTOR_OF_FOCUS_COMPONENT 输入映射。为需要的按键创建一个 KeyStrike 对象。创建一个描述动作字符串这样的动作键对象。将(按键,动作键)对添加到输入映射中。
  • 最后,得到顶层组件的动作映射。将(动作键,动作对象)添加到映射中。

12、如果只希望用户能够点击按钮或菜单,那么就不需要显式地处理鼠标事件。鼠标操作将由用户界面中的各种组件内部处理。然而,如果希望用户使用鼠标画图,就需要捕获鼠标移动点击和拖动事件。(312页)
13、当用户点击鼠标按钮时,将会调用三个监听器方法:鼠标第一次被按下时调用 mousePressed;鼠标被释放时调用 mouseReleased;最后调用 mouseClicked。(312页)
14、AWT 将事件分为低级(low-level)事件和语义(semantic)事件。语义事件是表示用户动作的事件;低级事件是形成那些事件的事件。(319页)

第9章 Swing 用户界面组件

1、在解决一个问题时,不需要从头做起,而是借鉴过去的经验,或者向做过相关工作的专家请教。设计模式就是一种方法,这种方法以一种结构化的方式展示专家们的心血。(322页)
2、在 “模型-视图-控制器” 模式中,背景是显示信息和接收用户输入的用户界面系统。(323页)
3、模型-视图-控制器(model-view-controller)这种设计模式同其他许多设计模式一样,都遵循面向对象设计中的一个基本原则:限制一个对象拥有的功能数量。不要用一个按钮类完成所有的事情,而是应该让一个对象负责组件的观感,另一个对象负责存储内容。模型-视图-控制器(MVC)模式告诉我们如何实现这种设计,实现三个独立的类:(324页)

  • 模型(model):存储内容。
  • 视图(view):显示内容。
  • 控制器(controller):处理用户输入。

4、模型存储内容,它没有用户界面。模型必须实现改变内容和查找内容的方法。模型是完全不可见的。显示存储在模型中的数据时视图的工作。(324页)
5、模型-视图-存储器模式的一个特点是一个模型可以有多个视图,其中每个视图可以显示全部内容的不同部分或不同形式。(325页)
6、控制器负责处理用户输入事件,如点击鼠标和敲击键盘。然后决定是否把这些事件转化成对模型或视图的改变。(325页)
7、每个用户界面元素都有一个包装器类来保存模型和视图。当需要查询内容时,包装器类会向模型询问并且返回所要的结果。当想改变视图时,包装器类会把此请求转发给视图。(325页)
8、对于大多数组件来说,模型类将实现一个名字以 Model 结尾的接口。(326页)
9、流布局管理器(flow layout manager)是面板的默认布局管理器。(328页)
10、在流布局中,按钮总是位于面板的中央,即使用户对框架进行缩放也是如此。(328页)
11、通常,组件放置在容器中,布局管理器决定容器中的组件具体放置的位置和大小。容器也可以放置在另一个容器中。(328页)
12、每个容器都有一个默认的布局管理器,但可以重新进行设置。(329页)
13、边框布局管理器(border layout manager)是每个 JFrame 的内容窗格的默认布局管理器。它允许为每个组件选择一个放置位置。可以选择把组件放在内容窗格的中部、北部、南部、东部或者西部。并非需要占用所有的位置,如果没有提供任何值,系统默认为 CENTER。(329页)
14、边框布局会扩展所有组件的尺寸以便填满可用空间。(330页)
15、网格布局像电子数据表一样,按行列排列所有的组件。不过,它的每个单元大小都是一样的。在网格布局对象的构造器中,需要指定行数和列数。添加组件,从第一行的第一列开始,然后是第一行的第二列,以此类推。(331页)
16、文本域(JTextField)和文本区(JTextArea)组件用于获取文本输入。文本域只能接收单行文本的输入,而文本区能够接收多行文本的输入。JPassword 也只能接收单行文本的输入,但不会将输入的内容显示出来。(334页)
17、把文本域添加到窗口的常用办法是将它添加到面板或者其他容器中。如果希望文本域最多能够输入 n 个字符,就应该把宽度设置为 n 列。在实际中,这样做效果并不理想,应该将最大输入长度再多设 1 ~ 2 个字符。(334页)
18、如果布局管理器需要缩放这个文本域,它会调整文本域的大小。在 JTextField 的构造器中设定的宽度并不是用户能输入的字符个数的上限。用户可以输入一个更长的字符串,但是当文本长度超过文本域长度时输入就会滚动。(335页)
19、标签是容纳文本的组件,它们没有任何的修饰(例如没有边缘),也不能响应用户输入。可以利用标签标识组件。与其他组件一样,标签也可以放置在容器中。(336页)
20、密码域是一种特殊类型的文本域。为了避免有不良企图的人看到密码,用户输入的字符不显示出来。每个输入的字符都用回显字符(echo character)表示,典型的回显字符是星号(*)。(337页)
21、当在程序中放置一个文本区组件时,用户就可以输入多行文本,并用 ENTER 键换行。每行都以一个 “\n” 结尾。(338页)
22、在 Swing 中,文本区没有滚动条。如果需要滚动条,可以将文本区插入到滚动窗格(scroll pane)中。如果文本超出了文本区可以显示的范围,滚动条就会自动地出现,并且在删除部分文本后,当文本能够显示在文本区范围内时,滚动条会再次自动地消失。滚动是由滚动窗格内部处理的,编写程序时无需处理滚动事件。(338页)
23、要想为组件添加滚动条,只需将它们放入一个滚动窗格中即可。(338页)
24、如果想要接收的输入只是 “是” 或 “非”,就可以使用复选框组件。用户通过点击某个复选框来选择相应的选项,再点击则取消选取。(340页)
25、对于两个复选框,用户既可以选择一个、两个,也可以两个都不选。在很多情况下,我们需要用户只选择几个选项当中的一个。当用户选择另一项的时候,前一项就自动地取消选择。这样一组选框通常称为单选按钮组(Radio Button Group)。(342页)
26、单选按钮与复选框的外观是不一样的。复选框为正方形,并且如果被选择,这个正方形中会出现一个对勾的符号。单选按钮是圆形,选择以后圈内出现一个圆点。(343页)
27、如果在一个窗口中有多组单选按钮,就需要用可视化的形式指明哪些按钮属于同一组。Swing 提供了一组很有用的边框(borders)来解决这个问题。可以在任何继承了 JComponent 的组件上应用边框。最常用的用途是在一个面板周围放置一个边框,然后用其他用户界面元素(如单选按钮)填充面板。(345页)
28、如果有多个选择项,使用单选按钮就不太适宜了,其原因是占据的屏幕空间太大。这时就可以选择组合框。当用户点击这个组件时,选择列表就会下拉出来,用户可以从中选择一项。(349页)
29、滑动条允许进行连续值的选择,例如,从 1 ~ 100 之间选择任意数值。当用户滑动滑动条时,滑动条的值就会在最小值和最大值之间变化。(352页)
30、可以通过显示标尺(tick)对滑动条进行修饰。可以强制滑动条对齐标尺。这样一来,只要用户完成拖放滑动条的操作,滑动条就会立即自动地移到最接近的标尺处。(353页)
31、位于窗口顶部的菜单栏(menu bar)包括了下拉菜单的名字。点击一下名字就可以打开包含菜单项(menu items)和子菜单(submenus)的菜单。当用户点击菜单项时,所有的菜单都会被关闭并且将一条消息发送给程序。(357页)
32、复选框和单选按钮菜单项在文本旁边显示了一个复选框或一个单选按钮。当用户选择一个菜单项时,菜单项就会自动地在选择和未选择间进行切换。(360页)
33、弹出菜单(pop-up menu)是不固定在菜单栏中随处浮动的菜单。弹出菜单没有标题。(361页)
34、加速器是在不打开菜单的情况下选择菜单项的快捷键。加速器只能关联到菜单项上,不能关联到菜单上。加速器键并不实际打开菜单。它将直接地 激活菜单关联的动作事件。(363页)
35、在有些时候,某个特定的菜单项可能只能够在某种特定的环境下才可用。可以将这个菜单项设为禁用状态,以便屏蔽掉这些暂时不适用的命令。被禁用的菜单项被显示为灰色,不能被选择。(364页)
36、工具栏是在程序中提供的快速访问常用命令的按钮栏。工具栏的特殊之处在于可以将它随处移动,可以将它拖拽到框架的四个边框上。释放鼠标按钮后,工具栏将会停靠在新的位置上。(368页)
37、工具栏只有位于采用边框布局或者任何支持 North、East、South 和 West 约束布局管理器的容器内才能够被拖拽。(368页)
38、工具栏可以完全脱离框架。这样的工具栏将包含在自己的框架中。当关闭包含工具栏的框架时,它会回到原始的框架中。(368页)
39、当光标停留在某个按钮上片刻时,工具提示就会被激活。工具提示文本显示在一个有颜色的矩形里。当用户移开鼠标时,工具提示就会自动地消失。(369页)
40、网格组布局(grid bag layout)将组件按行和列排列。行和列的大小可以灵活改变,并且组件可以横跨多行多列。(371页)
41、网格组布局是所有布局管理器之母。可以将网格组布局看成是没有任何限制的网格布局。在网格组布局中,行和列的尺寸可以改变。可以将相邻的单元合并以适应较大的组件。组件不需要填充整个单元格区域,并可以指定它们在单元格内的对齐方式。(372页)
42、将一个组件定位到某个绝对定位的步骤:(388页)

  • 将布局管理器设置为 null。
  • 将组件添加到容器中。
  • 指定想要放置的位置和大小。

43、与大多数的窗口系统一样,AWT 也分为模式对话框和无模式对话框。所谓模式对话框是指在结束对它的处理之前,不允许用户与应用程序的其余窗口进行交互。模式对话框主要用于在程序继续运行之前获取用户提供的信息。所谓无模式对话框是指允许用户同时在对话框和应用程序的其他窗口中输入信息。使用无模式对话框的最好例子就是工具栏。工具栏可以停靠在任何地方,并且用户可以在需要的时候,同时与应用程序窗口和工具栏进行交互。(393页)

第10章 部署应用程序和 applet

1、在将应用程序进行打包时,使用者一定希望仅提供给其一个单独的文件,而不是一个含有大量类文件的目录,Java 归档(JAR)文件就是为此目的而设计的。一个 JAR 文件既可以包含类文件,也可以包含诸如图像和声音这些其他类型的文件。此外,JAR 文件是压缩的,它使用了 ZIP 压缩格式。(426页)
2、除了类文件、图像和其他资源外,每个 JAR 文件还包含一个用于描述归档特征的清单文件(manifest)。清单文件的最后一行必须以换行符结束。否则,清单文件将无法被正确地读取。(428页)
3、applet 是一种包含在 HTML 网页中的 Java 应用程序。HTML 网页必须告诉浏览器要加载哪个 applet 以及每个 applet 放置在页面的哪个位置。(445页)

第11章 异常、断言、日志和调试

1、对于异常情况,Java 使用一种称为异常处理(exception handing)的错误捕获机制处理。(471页)
2、如果由于出现错误而使得某些操作没有完成,程序应该:返回到一种安全状态,并能够让用户执行一些其他的命令或者允许用户保存所有操作的结果,并以适当的方式终止程序。(472页)
3、异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。(472页)
4、在 Java 中,如果某个方法不能够采用正常的途径完成它的任务,就可以通过另外一个路径退出方法。在这种情况下,方法并不返回任何值,而是抛出(throw)一个封装了错误信息的对象。需要注意的是,这个方法将会立刻退出,并不返回任何值。此外,调用这个方法的代码也将无法继续执行,而是异常处理机制开始搜索能够处理这种异常状况的异常处理器(exception handler)。(472页)
5、在 Java 程序设计语言中,异常对象都是派生于 Throwable 类的一个实例。(473页)
6、所有的异常都是由 Throwable 继承而来,但在下一层立即分解为两个分支:Error 和 Exception。

  • Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了。
  • Exception 层次结构又分解为两个分支:一个分支派生于 RuntimeException;另一个分支包含其他异常。划分两个分支的规则是:由程序错误导致的异常属于 RuntimeException;而程序本身没有问题,但由于像 I/O 错误这类问题导致的异常属于其他异常。(473页)

7、派生于 RuntimeException 的异常包含下面几种情况:(473页)

  • 错误的类型转换
  • 数组访问越界
  • 访问空指针

不是派生于 RuntimeException 的异常包括:

  • 试图在文件尾部后面读取数据
  • 试图打开一个不存在的文件
  • 试图根据给定的字符串查找 Class 对象,而这个字符串表示的类并不存在

8、应该通过检测数组下标是否越界来避免 ArrayIndexOutOfBoundsException 异常;应该通过在使用变量之前检测是否为空来杜绝 NullPointerException 异常的发生。(473页)
9、Java 语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为未检查(unchecked)异常,所有其他的异常称为已检查(checked)异常。(474页)
10、如果遇到了无法处理的情况,那么 Java 的方法是抛出一个异常。一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。(474页)
11、方法应该在其首部声明所有可能抛出的异常。这样可以从首部反映出这个方法可能抛出哪类已检查异常。(474页)
12、在自己编写方法时,不必将所有可能抛出的异常都进行声明。遇到下面 4 种情况时应该抛出异常:(474页)

  • 调用一个抛出已检查异常的方法。
  • 程序运行过程中发现错误,并且利用 throw 语句抛出一个已检查异常。
  • 程序出现错误。
  • Java 虚拟机和运行时库出现的内部错误。

13、任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常,当前执行的线程就会结束。(475页)
14、对于那些可能被他人使用的 Java 方法,应该根据异常规范(exception specification),在方法的首部声明这个方法可能抛出的异常。(475页)
15、如果一个方法有可能抛出多个已检查异常,那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开。(475页)
16、不需要声明 Java 的内部错误,即从 Error 继承的错误。任何程序代码都具有抛出那些异常的潜能,而我们对其没有任何控制能力。也不应该声明从 RuntimeException 继承的那些未检查异常。(475页)
17、一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误消息。(476页)
18、如果在子类中覆盖了超类的一个方法,子类方法中声明的已检查异常不能比超类方法中声明的异常更通用(也就是说,子类方法中可以抛出更特定的异常,或者根本不抛出任何异常)。特别需要说明的是,如果超类方法没有抛出任何已检查异常,子类也不能抛出任何已检查异常。(476页)
19、如果类中的一个方法声明将会抛出一个异常,而这个异常是某个特定类的实例,则这个方法就有可能抛出一个这个类的异常或者这个类的任意一个子类的异常。(476页)
20、对于一个已经存在的异常类,将其抛出非常容易。在这种情况下:(477页)

  • 找到一个合适的异常类。
  • 创建这个类的一个对象。
  • 将对象抛出。

21、如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容。对于图形界面程序,在捕获异常之后,也会打印出堆栈的信息,但程序将返回到用户界面的处理循环中。(478页)
22、要想捕获一个异常,必须设置 try/catch 语句块。(479页)
23、如果在 try 语句块中的任何代码抛出了一个在 catch 子句中说明的异常类,那么程序将跳过 try 语句块的其余代码,执行 catch 子句中的处理器代码。如果在 try 语句块中的代码没有抛出任何异常,那么程序将跳过 catch 子句。如果方法中的任何代码抛出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出。(479页)
24、如果调用了一个抛出已检查异常的方法,就必须对它进行处理,或者将它继续进行传递。(479页)
25、通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递。如果想传递一个异常,就必须在方法的首部添加一个 throws 说明符,以便告知调用者这个方法可能会抛出异常。(480页)
26、如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个已检查异常。不允许在子类的 throws 说明符中出现超过超类方法所列出的异常类范围。(480页)
27、在一个 try 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。(480页)
28、在 Java SE 7 中,同一个 catch 子句中可以捕获多个异常类型。(481页)
29、捕获多个异常时,异常变量隐含为 final 变量。(481页)
30、在 catch 子句中可以抛出一个异常,这样做的目的是改变异常的类型。(481页)
31、如果在一个方法中发生了一个已检查异常,而不允许抛出它,那么包装技术就十分有用。我们可以捕获这个已检查异常,并将它包装成一个运行时异常。(482页)
32、当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新抛出所有的异常。但是,这种解决方案比较乏味,这是因为需要在两个地方清除所分配的资源。一个在正常的代码中;另一个在异常代码中。(482页)
33、不管是否有异常被捕获,finally 子句中的代码都被执行。(483页)
34、下列 3 种情况会执行 finally 子句:(483页)

  • 代码没有抛出异常。在这种情况下,程序首先执行 try 语句块中的全部代码,然后执行 finally 子句中的代码。随后,继续执行 try 语句块之后的第一条语句。
  • 抛出一个在 catch 子句中捕获的异常。在这种情况下,程序将执行 try 语句块中的所有代码,直到发生异常为止。此时,将跳过 try 语句块中的剩余代码,转去执行与该异常匹配的 catch 子句中的代码,最后执行 finally 子句中的代码。如果 catch 子句没有抛出异常,程序将执行 try 语句块之后的第一条语句。如果 catch 子句抛出了一个异常,异常将被抛回这个方法的调用者。
  • 代码抛出了一个异常,但这个异常不是由 catch 子句捕获的。在这种情况下,程序将执行 try 语句块中的所有语句,直到有异常被抛出为止。此时,将跳过 try 语句块中的剩余代码,然后执行 finally 子句中的语句,并将异常抛给这个方法的调用者。

35、try 语句可以只有 finally 子句,而没有 catch 子句。(483页)
36、带资源的 try 语句(try-with-resources)的最简形式为:(486页)

try(Resource res = ...)
{
work with res
}

try 块退出时,会自动调用 res.close()。
37、堆栈跟踪(stack trace)是一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置。当 Java 程序正常终止,而没有捕获异常时,这个列表就会显示出来。(487页)
38、使用异常机制的几个技巧:(490页)

  • 异常处理不能代替简单地测试。使用异常的基本规则是:只在异常情况下使用异常机制。
  • 不要过分地细化异常。
  • 利用异常层次结构。不要只抛出 RuntimeException 异常。应该寻找更加适当的子类或创建自己的异常类。不要只捕获 Throwable 异常,否则,会使程序代码更难读、更难维护。将一种异常转换成另一种更加适合的异常时不要犹豫。
  • 不要压制异常。
  • 在检测错误时,“苛刻” 要比放任更好。
  • 不要羞于传递异常。

39、断言机制允许在测试期间向代码中插入一些检查语句。当代码发布时,这些插入的检测语句将会被自动地移走。(493页)
40、Java 语言引入了关键字 assert。这个关键字有两种形式:assert 条件;assert 条件 :表达式;。这两种形式都会对条件进行检测,如果结果为 false,则抛出一个 AssertionError 异常。在第二种形式中,表达式将被传入 AssertionError 的构造器,并转换成一个消息字符串。(493页)
41、在默认情况下,断言被禁用。可以在运行程序时用 -enableassertions-ea 选项启用它:java -enableassertions MyApp。需要注意的是,在启用或禁用断言时不必重新编译程序。启用或禁用断言是类加载器(class loader)的功能。当断言被禁用时,类加载器将跳过断言代码,因此,不会降低程序运行的速度。(493页)
42、可以用选项 -disableassertions-da 禁用某个特定类和包的断言。(494页)
43、启用和禁用所有断言的 -ea-da 开关不能应用到那些没有类加载器的 “系统类” 上。对于这些系统类来说,需要使用 -enableassertions/-esa 开关启用断言。(494页)
44、在 Java 语言中,给出了 3 种处理系统错误的机制:抛出一个异常、日志和使用断言。(494页)
45、断言是一种测试和调试阶段所使用的战术性工具;而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。(495页)

第12章 泛型程序设计

1、泛型正是我们需要的程序设计手段。使用泛型机制编写的程序代码要比那些杂乱地使用 Object 变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。(527页)
2、泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。(527页)
3、泛型程序设计划分为 3 个能力级别。基本级别是仅仅使用泛型类,不必考虑它们的工作方式与原因。(528页)
4、一个泛型类(generic class)就是具有一个或多个类型变量的类。(529页)
5、泛型方法可以定义在普通类中,也可以定义在泛型类中。(531页)

第13章 集合

1、队列接口指出可以在队列的尾部添加元素,在队列的头部删除元素,并且可以查找队列中元素的个数。当需要收集对象,并按照 “先进先出” 的规则检索对象时就应该使用队列。(560页)
2、队列通常有两种实现方式:一种是使用循环数组;另一种是使用链表。(561页)
3、当在程序中使用队列时,一旦构建了集合就不需要知道究竟使用了哪种实现。(562页)
4、循环数组要比链表更高效,因此多数人优先选择循环数组。循环数组是一个有界集合,即容量有限。如果程序中要收集的对象数量没有上限,就最好使用链表来实现。(562页)
5、在 Java 程序设计语言中,所有链表实际上都是双向链接的(doubly linked)——即每个结点还存放着指向前驱结点的引用。(568页)
6、从链表中间删除一个元素是一个很轻松的操作,即需要对被删除元素附近的结点更新一下即可。(568页)
7、链表不支持快速地随机访问。如果要查看链表中第 n 个元素,就必须从头开始,越过 n—1 个元素。(572页)
8、使用链表的唯一理由是尽可能地减少在列表中间插入或删除元素所付出的代价。如果列表中只有少数几个元素,就完全可以使用 ArrayList。(572页)
9、散列表(hash table)这种数据结构可以快速地查找所需要的对象。散列表为每个对象计算一个整数,称为散列码(hash code)。散列码是由对象的实例域产生的一个整数。更准确地说,具有不同数据域的对象将产生不同的散列码。(576页)
10、在 Java 中,散列表用链表数组实现。每个列表被称为桶(bucket)。要想查找表中对象的位置,就要先计算它的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引。(576页)
11、如果想要更多地控制散列表的运行性能,就要指定一个初始的桶数。桶数是指用于收集具有相同散列值的桶的数目。如果要插入到散列表中的元素太多,就会增加冲突的可能性,降低运行性能。通常,将桶数设置为预计元素个数的 75% ~ 150%。标准类库使用的桶数是 2 的幂,默认值为 16。(577页)
12、如果散列表太满,就需要再散列(rehashed)。如果要对散列表再散列,就需要创建一个桶数更多的表,并将所有元素插入到这个新表中,然后丢弃原来的表。装填因子(load factor)决定何时对散列表进行再散列。(577页)
13、树集是一个有序集合(sorted collection)。可以以任意顺序将元素插入到集合中。在对集合进行遍历时,每个值将自动地按照排序后的顺序呈现。(579页)
14、将一个元素添加到树中要比添加到散列表中慢,但是,与将元素添加到数组或链表的正确位置上相比还是快很多的。(579页)
15、有两个端头的队列,即双端队列,可以让人们有效地在头部和尾部同时添加或删除元素。不支持在队列中间添加元素。(585页)
16、优先级队列(priority queue)中的元素可以按照任意的顺序插入,却总是按照排序的顺序进行检索。(586页)
17、堆(heap)是一个可以自我调整的二叉树,对树执行添加(add)和删除(remove)操作,可以让最小的元素移动到根,而不必花费时间对元素进行排序。(586页)
18、映射表(map)用来存放键/值对。如果提供了键,就能够查找到值。键必须是唯一的。不能对同一个键存放两个值。如果对同一个键两次调用 put 方法,第二个值就会取代第一个值。(588页)
19、框架(framework)是一个类的集,它奠定了创建高级功能的基础。框架包含很多超类,这些超类拥有非常有用的功能、策略和机制。框架使用者创建的子类可以扩展超类的功能,而不必重新创建这些基本的机制。(595页)

第14章 多线程

1、多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常,每一个任务称为一个线程(thread),它是线程控制的简称。可以同时执行一个以上线程的程序称为多线程程序(multithreaded)。(620页)
2、多进程与多线程本质的区别在于每个进程拥有自己的一整套变量,而线程则共享数据。(620页)
3、调用 Thread.sleep 不会创建一个新线程,sleep 是 Thread 类的静态方法,用于暂停当前线程的活动。sleep 方法可以抛出一个 InterruptedException 异常。(621页)
4、如果需要执行一个比较耗时的任务,应该使用独立的线程。(626页)
5、不要调用 Thread 类或 Runnable 对象的 run 方法。直接调用 run 方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用 Thread.start 方法。这个方法将创建一个执行 run 方法的新线程。(627页)
6、当线程的 run 方法执行方法体中的最后一条语句后,并经由执行 return 语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。(630页)
7、当对一个线程调用 interrupt 方法时,线程的中断状态将被置位。这是每一个线程都具有的 boolean 标志。每个线程都应该不时地检查这个标志,以判断线程是否被中断。(630页)
8、如果线程被阻塞,就无法检测中断状态。这是产生 InterruptedException 异常的地方。当在一个被阻塞的线程(调用 sleep 或 wait)上调用 interrupt 方法时,阻塞调用将会被 Interrupted Exception 异常中断。(631页)
9、没有任何语言方面的需求要求一个被中断的线程应该终止。中断一个线程不过是引起它的注意。被中断的线程可以决定如何响应中断。某些线程是如此重要以至于应该处理完异常后,继续执行,而不理会中断。(631页)
10、线程可以有如下 6 种状态:New(新创建)、Runnable(可运行)、Blocked(被阻塞)、Waiting(等待)、Timed waiting(计时等待)、Terminated(被终止)。要确定一个线程的当前状态,可调用 getState 方法。(633页)
11、当用 new 操作符创建一个新线程时,该线程还没有开始运行。这意味着它的状态是 new。当一个线程处于新创建状态时,程序还没有开始运行中的代码。一旦调用 start 方法,线程处于 runnable 状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。(633页)
12、一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。(634页)
13、现在所有的桌面以及服务器操作系统都使用抢占式调度。但是,像手机这样的小型设备可能使用协作式调度。在这样的设备中,一个线程只有在调用 yield 方法、或者被阻塞或等待时,线程才失去控制权。(634页)
14、在具有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程并行运行。当然,如果线程的数目多于处理器的数目,调度器依然采用时间片机制。(634页)
15、当线程处于被阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。(634页)
16、当一个线程试图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变成非阻塞状态。(634页)
17、当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。(634页)
18、当一个线程被阻塞或等待时(或终止时),另一个线程被调度为运行状态。当一个线程被重新激活,调度器检查它是否具有比当前运行线程更高的优先级。如果是这样,调度器从当前运行线程中挑选一个,剥夺其运行权,选择一个新的线程运行。(634页)
19、线程因如下两个原因之一而被终止:因为 run 方法正常退出而自然死亡或因为一个没有捕获的异常终止了 run 方法而意外死亡。(634页)
20、在 Java 程序设计语言中,每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用 setPriority 方法提高或降低任何一个线程的优先级。可以将优先级设置为 MIN_PRIORITY(在 Thread 类中定义为 1)与 MAX_PRIORITY(定义为 10)之间的任何值。NORM_PRIORITY 被定义为 5。(636页)
21、每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。(636页)
22、可以通过调用 t.setDaemon(true); 将线程转换为守护线程(daemon thread)。守护线程的唯一用途是为其他线程提供服务。守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。(637页)
23、线程的 run 方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。(637页)
24、线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也可能会建立其他的组。(637页)
25、在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取。根据各线程访问数据的次序,可能会产生讹误的对象。这样一个情况通常称为竞争条件(race condition)。(638页)
26、通常,线程进入临界区,却发现在某一条件满足之后它才能执行。要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程。(647页)
27、当一个线程调用 await 时,它没有办法重新激活自身。它寄希望于其他线程。如果没有其他线程来重新激活等待的线程,它就永远不再运行了。这将导致令人不快的死锁(deadlock)现象。如果所有其他线程被阻塞,最后一个活动线程在解除其他线程的阻塞状态之前就调用 await 方法,那么它也被阻塞。没有任何线程可以解除其他线程的阻塞,那么该程序就挂起了。(648页)
28、当一个线程拥有某个条件的锁时,它仅仅可以在该条件上调用 await、signalAll 或 signal 方法。(649页)
29、锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。锁可以管理试图进入被保护代码段的线程。锁可以拥有一个或多个相关的条件对象。每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。(651页)
30、Java 中的每一个对象都有一个内部锁。如果一个方法用 synchronized 关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。(651页)
31、如果向一个变量写入值,而这个变量接下来可能会被另一个线程读取,或者从一个变量读值,而这个变量可能是之前被另一个线程写入的,此时必须使用同步。(657页)
32、Java 编程语言中没有任何东西可以避免或打破死锁现象。必须仔细设计程序,以确保不会出现死锁。(660页)
33、对于许多线程问题,可以通过使用一个或多个队列以优雅且安全的方式将其形式化。生产者线程向队列插入元素,消费者线程则取出它们。使用队列,可以安全地从一个线程向另一个线程传递数据。(665页)
34、当试图向队列添加元素而队列已满,或是想从队列移出元素而队列为空的时候,阻塞队列(blocking queue)导致线程阻塞。(666页)
35、Runnable 封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。(675页)
36、如果程序中创建了大量的生命期很短的线程,应该使用线程池(thread pool)。一个线程池中包含许多准备运行的空闲线程。将 Runnable 对象交给线程池,就会有一个线程调用 run 方法。当 run 方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。(679页)
37、创建大量线程会大大降低性能甚至使虚拟机崩溃。如果有一个会创建许多线程的算法,应该使用一个线程数 “固定的” 线程池以限制并发线程的总数。(679页)
38、当用完一个线程池的时候,调用 shutdown。该方法启动该池的关闭序列。被关闭的执行器不再接受新的任务。当所有任务都完成以后,线程池中的线程死亡。另一种方法是调用 shutdownNow。该池取消尚未开始的所有任务并试图中断正在运行的线程。(680页)
39、当程序需要做某些耗时的工作时,应该启动另一个工作器线程而不是阻塞用户接口。(691页)