十五、附录一:习题解答

从第 1 章到第 14 章的每一章都以“练习”部分结束,测试你对本章内容的理解。本附录提供了这些练习的答案。

第 1 章:Java 入门

  1. Java 是一种语言,也是一个平台。该语言部分模仿 C 和 C++语言,以缩短 C/C++开发人员的学习曲线。该平台由一个虚拟机和相关的执行环境组成。
  2. 虚拟机是一种基于软件的处理器,提供自己的指令集。
  3. Java 编译器的目的是将源代码翻译成由虚拟机执行的指令(和相关数据)。
  4. 答案是正确的:类文件的指令通常被称为字节码。
  5. 当虚拟机的解释器得知字节码指令序列被重复执行时,它通知虚拟机的实时(JIT)编译器将这些指令编译成本机代码。
  6. Java 平台通过提供底层平台的抽象来提高可移植性。因此,相同的字节码在基于 Windows、基于 Linux、基于 Mac OS X 和其他平台上运行时没有变化。
  7. Java 平台通过提供代码执行的安全环境来提高安全性。它通过使用字节码检验器来确保类文件的字节码是有效的,从而部分地完成了这项任务。
  8. 答案是错误的:Java SE 是开发应用和小程序的平台。
  9. JRE 实现了 Java SE 平台,使得运行 Java 程序成为可能。
  10. 公共 JRE 和私有 JRE 之间的区别在于,公共 JRE 独立于 JDK 而存在,而私有 JRE 是 JDK 的一个组件,它使得运行 Java 程序不受是否安装了公共 JRE 的影响。
  11. JDK 为开发 Java 程序提供了开发工具(包括编译器)。它还提供了运行这些程序的私有 JRE。
  12. JDK 的 javac 工具用于编译 Java 源代码。
  13. JDK 的 java 工具用于运行 java 应用。
  14. 标准 I/O 是一种由标准输入、标准输出和标准错误组成的机制,它可以从不同的源(键盘或文件)读取文本,将非错误文本写入不同的目的地(屏幕或文件),并将错误文本写入不同的目的地(屏幕或文件)。
  15. 您将 main() 方法的头指定为 public static void main(String[]args)。
  16. IDE 是一个开发框架,由一个管理项目文件的项目管理器、一个输入和编辑源代码的文本编辑器、一个定位错误的调试器和其他功能组成。Google 支持开发 Android 应用的 IDE 是 Eclipse。

第二章:学习语言基础

  1. Unicode 是一个计算行业标准,用于一致地编码、表示和处理世界上大多数书写系统中表达的文本。
  2. 注释是一种在源代码中嵌入文档的语言特性。
  3. Java 支持的三种注释是单行、多行和 Javadoc 。
  4. 标识符是一种语言特征,由字母(A-Z、A-Z 或其他人类字母表中的等效大写/小写字母)、数字(0-9 或其他人类字母表中的等效数字)、连接标点符号(例如下划线)和货币符号(例如美元符号$)组成。该名称必须以字母、货币符号或连接标点符号开头。并且它的长度不能超过它所在的行。
  5. 答案是错误的:Java 是一种区分大小写的语言。
  6. 类型是一种语言特性,它标识一组值(及其在内存中的表示)和一组将这些值转换为该组中其他值的操作。
  7. 原始类型是由语言定义的类型,其值不是对象。
  8. Java 支持布尔、字符、字节整数、短整数、整数、长整数、浮点和双精度浮点原语类型。
  9. 用户定义的类型是由开发人员使用类、接口、枚举或注释类型定义的类型,其值是对象。
  10. 数组类型是一种特殊的引用类型,表示一个数组,一个在大小相等的连续槽中存储值的内存区域,这些槽通常被称为元素。
  11. 变量是存储某种类型值的命名内存位置。
  12. 表达式是文字、变量名、方法调用和操作符的组合。在运行时,它计算出一个值,该值的类型称为表达式的类型。
  13. 两种表达式类别是简单表达式和复合表达式。
  14. 文字是逐字指定的值。
  15. 快速的棕色狐狸“跳过”懒惰的狗。是非法的,因为与 \" 、 \j 和 \ (反斜杠后跟空格字符)不是有效的转义序列。要使这个字符串合法,你必须避开这些反斜杠,如“敏捷的棕色狐狸\跳过\ \懒狗。。
  16. 操作符是源代码中用符号表示的一系列指令。
  17. 前缀运算符和后缀运算符的区别在于,前缀运算符位于操作数之前,后缀运算符位于操作数之后。
  18. cast 运算符的目的是将一种类型转换为另一种类型。例如,可以使用此运算符将浮点类型转换为 32 位整数类型。
  19. 优先级是指操作员的重要程度。
  20. 答案是正确的:Java 的大多数操作符都是从左到右关联的。
  21. 语句是一种语言特性,它为变量赋值,通过做出决策和/或重复执行另一条语句来控制程序流程,或者执行另一项任务。
  22. while 语句在循环的顶部评估其布尔表达式,而 do-while 语句在循环的底部评估其布尔表达式。因此,while 执行零次或多次,而 do-while 执行一次或多次。
  23. break 和 continue 语句的区别在于,break 将执行转移到 switch 语句或循环之后的第一条语句,而 continue 跳过当前循环迭代的剩余部分,重新计算循环的布尔表达式,并执行另一次迭代(当 true 时)或终止循环(当 false 时)。
  24. Listing A-1 presents an OutputGradeLetter application (the class is named OutputGradeLetter) whose main() method executes the grade letter code sequence presented while discussing the if-else statement.

    A-1 上市。划分等级

    ```java public class OutputGradeLetter { public static void main(String[] args) { char gradeLetter = 'u'; // unknown int testMark = 100;

      if (testMark >= 90)
      {
         gradeLetter = 'A';
         System.out.println("You aced the test.");
      }
      else
      if (testMark >= 80)
      {
         gradeLetter = 'B';
         System.out.println("You did very well on this test.");
      }
      else
      if (testMark >= 70)
      {
         gradeLetter = 'C';
         System.out.println("Not bad, but you need to study more for future tests.");
      }
      else
      if (testMark >= 60)
      {
         gradeLetter = 'D';
         System.out.println("Your test result suggests that you need a tutor.");
      }
      else
      {
         gradeLetter = 'F';
         System.out.println("Your test result is pathetic; you need summer school.");
      }
    

    } }

    ```

  25. Listing A-2 presents a Triangle application whose main() method uses a pair of nested for statements along with System.out.print() to output a 10-row triangle of asterisks, where each row contains an odd number of asterisks (1, 3, 5, 7, and so on).

    清单 A-2。打印三角形星号

    ```java public class Triangle { public static void main(String[] args) { for (int row = 1; row < 20; row += 2) { for (int col = 0; col < 19 - row / 2; col++) System.out.print(" "); for (int col = 0; col < row; col++) System.out.print("*"); System.out.print('\n'); } } }

    ```

第 3 章:发现类和对象

  1. 类是制造对象的模板。
  2. 您可以通过提供一个头部后跟一个主体来声明一个类。报头至少由保留字类和标识符组成。主体由放置在一对大括号字符之间的一系列声明组成。
  3. 对象是代码和数据的命名集合。
  4. 通过使用 new 操作符后跟一个构造函数来实例化一个对象。
  5. 构造函数是一个代码块,通过以某种方式初始化来构造一个对象。
  6. 答案是正确的:当一个类没有声明构造函数时,Java 会创建一个默认的无参数构造函数。
  7. 参数列表是一个圆括号分隔的逗号分隔的零个或多个参数声明的列表。参数是一个构造函数或方法变量,它在被调用时接收传递给构造函数或方法的表达式值。
  8. 参数列表是一个圆括号分隔的逗号分隔的零个或多个表达式的列表。参数是这些表达式之一,当调用构造函数或方法变量时,其值被传递给相应的参数。
  9. 答案是错误的:您通过指定 this 后跟一个参数列表来调用另一个构造函数。
  10. Arity 是传递给构造函数或方法的参数数量,或者是运算符操作数的数量。
  11. 局部变量是在构造函数或方法中声明的变量,不是构造函数或方法参数列表的成员。
  12. 寿命是变量的一个属性,它决定了变量存在的时间。例如,局部变量和参数在调用构造函数或方法时存在,而在构造函数或方法完成时被销毁。类似地,实例字段在对象被创建时存在,在对象被垃圾收集时被销毁。
  13. Scope 是变量的一个属性,它决定了代码对变量的访问程度。例如,参数只能由声明该参数的构造函数或方法中的代码访问。
  14. 封装指的是将状态和行为合并成一个源代码实体。与在结构化程序中分离状态和行为不同,状态和行为被组合成类和对象,这是基于对象的程序的焦点。例如,一个结构化的程序让你从独立的余额状态和存款/取款行为的角度来思考,而一个基于对象的程序让你从银行账户的角度来思考,银行账户通过封装将余额状态和存款/取款行为结合起来。
  15. 字段是在类体内声明的变量。
  16. 实例字段和类字段之间的区别在于,实例字段描述对象正在建模的真实世界实体的一些属性,并且对于每个对象是唯一的,而类字段标识由所有对象共享的一些数据项。
  17. 空白期末考试是只读的实例字段。它与真常量的不同之处在于,它有多个空白终结符副本(每个对象一个),只有一个真常量(每个类一个)。
  18. 通过更改同名局部变量或参数的名称,或者用 this 或类名后跟成员访问运算符来限定局部变量或参数的名称,可以防止字段被隐藏。
  19. 方法是在类体内声明的命名代码块。
  20. 实例方法和类方法的区别在于,实例方法描述对象正在建模的真实世界实体的一些行为,并且可以访问特定对象的状态,而类方法标识所有对象共有的一些行为,并且不能访问特定对象的状态。
  21. 递归是方法调用自身的动作。
  22. 通过将与现有方法同名但具有不同参数列表的方法引入到同一类中,可以重载方法。
  23. 一个类初始化器是一个静态的前缀的块,它被引入到类体中。实例初始化器是一个块,它被引入到类主体中,而不是作为方法或构造函数的主体引入。
  24. 垃圾收集器是在后台运行的代码,偶尔会检查未引用的对象。
  25. 答案为假:String[]letters = new String[2]{“A”,“B”};是不正确的语法。从方括号之间取下 2 使其正确。
  26. 不规则数组是一个二维数组,其中每行可以有不同数量的列。
  27. Calculating the greatest common divisor of two positive integers, which is the greatest positive integer that divides evenly into both positive integers, provides another example of tail recursion. Listing A-3 presents the source code.

    清单 A-3。递归计算最大公约数

    ```java public static int gcd(int a, int b) { // The greatest common divisor is the largest positive integer that // divides evenly into two positive integers a and b. For example, // GCD(12, 18) is 6.

    if (b == 0) // Base problem return a; else return gcd(b, a % b); }

    ```

  28. Listing A-4 presents the source code to a Book class with name, author, and International Standard Book Number (ISBN) fields and a suitable constructor and getter methods that return field values. Furthermore, a main() method is present that creates an array of Book objects and iterates over this array outputting each book’s name, author, and ISBN.

    A-4 上市。建立图书馆

    ```java public class Book { private String name; private String author; private String isbn;

    public Book(String name, String author, String isbn) { this.name = name; this.author = author; this.isbn = isbn; }

    public String getName() { return name; }

    public String getAuthor() { return author; }

    public String getISBN() { return isbn; }

    public static void main(String[] args) { Book[] books = new Book[] { new Book("Jane Eyre", "Charlotte Brontë", "0895772000"), new Book("A Kick in the Seat of the Pants", "Roger von Oech", "0060155280"), new Book("The Prince and the Pilgrim", "Mary Stewart", "0340649925") }; for (int i = 0; i < books.length; i++) System.out.println(books[i].getName() + " - " + books[i].getAuthor() + " - " + books[i].getISBN()); } }

    ```

第 4 章:发现继承、多态和接口

  1. 实现继承是通过类扩展的继承。
  2. Java 通过提供保留字扩展来支持实现继承。
  3. 一个子类只能有一个超类,因为 Java 不支持多实现继承。
  4. 通过将类声明为 final ,可以防止类被子类化。
  5. 答案是假的: super() 调用只能出现在构造函数中。
  6. 如果超类声明一个带有一个或多个参数的构造函数,并且如果子类构造函数不使用 super() 调用该构造函数,编译器会报告一个错误,因为子类构造函数试图调用超类中不存在的无参数构造函数。(当一个类没有声明任何构造函数时,编译器会为该类创建一个不带参数的构造函数[无参数构造函数]。因此,如果超类没有声明任何构造函数,将为超类创建一个无参数构造函数。继续,如果子类构造函数没有使用 super() 来调用超类构造函数,编译器会插入调用,不会有错误。)
  7. 不可变类是其实例不能被修改的类。
  8. 答案是错误的:类不能继承构造函数。
  9. 覆盖一个方法意味着用另一个方法替换一个继承的方法,这个方法提供相同的签名和相同的返回类型,但是提供一个新的实现。
  10. 要从超类方法覆盖的子类方法中调用超类方法,在方法调用中用保留字 super 和成员访问操作符作为超类方法名的前缀。
  11. 通过将方法声明为 final ,可以防止方法被覆盖。
  12. 您不能使覆盖的子类方法比它覆盖的超类方法更难访问,因为如果子类方法变得更难访问,子类型多态性将不能正常工作。假设您通过将实例的引用分配给超类类型的变量,将子类实例向上转换为超类类型。现在假设您在变量上指定了一个超类方法调用。如果该方法被子类覆盖,则调用该方法的子类版本。但是,如果对子类的覆盖方法的访问可以是私有的,那么调用这个方法会破坏封装——私有方法不能从它们的类外部直接调用。
  13. 您告诉编译器,一个方法覆盖了另一个方法,方法是在覆盖方法的头部加上前缀 @Override 注释。
  14. Java 不支持多实现继承,因为这种形式的继承会导致歧义。
  15. Java 的终极超类的名字是对象。这个类位于 java.lang 包中。
  16. clone() 方法的目的是在不调用构造函数的情况下复制一个对象。
  17. 对象的 clone() 方法在要浅克隆实例的类没有实现可克隆接口时抛出 CloneNotSupportedException。
  18. 浅层复制和深层复制的区别在于,浅层复制将每个原语或引用字段的值复制到其在克隆中的对应物,而深层复制为每个引用字段创建一个新对象,并将其引用分配给该字段。对于这些新创建的对象,这个深度复制过程递归地继续。
  19. == 运算符 不能用于确定两个对象是否逻辑等价,因为该运算符只比较对象引用,而不比较这些对象的内容。
  20. 对象的等于()方法 将当前对象的 this 引用与作为参数传递给该方法的引用进行比较。(当我引用对象的 equals() 方法时,我指的是对象类中的 equals() 方法。)
  21. 表达式 "abc" == "a" + "bc" 返回 true。这样做是因为字符串类包含特殊支持,允许通过 == 比较文字字符串和字符串值常量表达式。
  22. 您可以优化一个耗时的 equals() 方法,首先使用 == 来确定该方法的引用参数是否标识当前对象(在源代码中通过保留字 this 来表示)。
  23. finalize() 方法
  24. 你不应该依赖于 finalize() 来关闭打开的文件,因为文件描述符是一个有限的资源,应用可能无法打开额外的文件,直到 finalize() 被调用,这个方法可能很少被调用(或者可能根本不被调用)。
  25. 散列码是通过对潜在的大量数据应用数学函数而产生的一个小值。
  26. 答案是正确的:无论何时重写 equals() 方法,都应该重写 hashCode() 方法。
  27. 对象的 toString() 方法 返回当前对象的字符串表示,由对象的类名、后跟 @ 符号、后跟对象的哈希码的十六进制表示组成。(当我引用对象的 toString() 方法时,我指的是对象类中的 toString() 方法。)
  28. 您应该覆盖 toString() 来提供一个简洁但有意义的对象描述,以便于通过 System.out.println() 方法调用进行调试。对于 toString() 来说,显示对象状态比显示类名、后跟符号 @ 以及十六进制表示的对象散列码更能说明问题。
  29. Composition 是一种重用代码的方法,它基于类之间的“has-a”关系,将其他类组合成类。
  30. 答案是错误的:组合用于描述“有-a”关系,实现继承用于描述“是-a”关系。
  31. 实现继承的根本问题是它打破了封装。解决这个问题的方法是,确保对超类及其子类拥有控制权,确保超类是为扩展而设计和记录的,或者在扩展超类时使用包装类代替子类。
  32. 子类型多态性是一种多态性,其中子类型实例出现在超类型上下文中,对子类型实例执行超类型操作会导致该操作的子类型版本执行。
  33. 子类型多态性是通过将子类型实例向上转换为其父类型来实现的;通过将实例的引用赋给该类型的变量;并且,通过这个变量,调用已经在子类中被覆盖的超类方法。
  34. 您将使用抽象类和抽象方法来描述一般概念(例如,形状、动物或车辆)和一般操作(例如,绘制一般形状)。抽象类不能被实例化,抽象方法不能被调用,因为它们没有代码体。
  35. 抽象类可以包含具体的方法。
  36. 向下转换的目的是访问子类型特征。例如,您可以将包含对 Circle 类型的 Circle 实例引用的 Point 变量向下转换,以便您可以在实例上调用 Circle 的 getRadius() 方法 。
  37. RTTI 有两种形式,一种是虚拟机验证强制转换是否合法,另一种是使用操作符的实例来确定一个实例是否是某个类型的成员。
  38. 协变返回类型是一种方法返回类型,在超类的方法声明中,它是子类的覆盖方法声明中返回类型的超类型。
  39. 通过指定至少一个保留字接口,后跟一个名称,再跟一个用大括号分隔的常量体和/或方法头,正式声明一个接口。
  40. 答案是正确的:你可以在接口声明之前加上抽象保留字。然而,这样做是多余的。
  41. 标记接口是一个没有声明成员的接口。
  42. 接口继承是通过接口实现或者接口扩展的继承。
  43. 通过将 implements 子句(由保留字 implements 后跟接口名称组成)附加到类头,并在类中覆盖接口的方法头,可以实现接口。
  44. 当您实现多个接口时,可能会遇到一个或多个名称冲突。
  45. 通过在接口头上附加保留字扩展,后跟接口名,可以形成接口的层次结构。
  46. Java 的接口特性非常重要,因为它给了开发人员在设计应用时最大的灵活性。
  47. 接口和抽象类描述抽象类型。
  48. 接口和抽象类的不同之处在于,接口只能声明抽象方法和常数,并且可以由任何类层次结构中的任何类实现。相反,抽象类可以声明常数和非常数字段;可以声明抽象和具体的方法;和只能出现在类层次结构的上层,在那里它们用于描述抽象概念和行为。
  49. Listings A-5 through A-11 declare the Animal, Bird, Fish, AmericanRobin, DomesticCanary, RainbowTrout, and SockeyeSalmon classes that were called for in Chapter 4.

    清单 A-5。动物类抽象出鸟类和鱼类(以及其他生物)

    ```java public abstract class Animal { private String kind; private String appearance;

    public Animal(String kind, String appearance) { this.kind = kind; this.appearance = appearance; }

    public abstract void eat();

    public abstract void move();

    @Override public final String toString() { return kind + " -- " + appearance; } }

    ```

    清单 A-6。鸟类掠过美洲知更鸟、家养金丝雀和其他种类的鸟

    ```java public abstract class Bird extends Animal { public Bird(String kind, String appearance) { super(kind, appearance); }

    @Override public final void eat() { System.out.println("eats seeds and insects"); }

    @Override public final void move() { System.out.println("flies through the air"); } }

    ```

    A-7 上市。鱼类提取虹鳟鱼、红鲑和其他种类的鱼

    ```java public abstract class Fish extends Animal { public Fish(String kind, String appearance) { super(kind, appearance); }

    @Override public final void eat() { System.out.println("eats krill, algae, and insects"); }

    @Override public final void move() { System.out.println("swims through the water"); } }

    ```

    清单 A-8。美洲知更鸟类 T5】表示胸部为红色的鸟

    ```java public final class AmericanRobin extends Bird { public AmericanRobin() { super("americanrobin", "red breast"); } }

    ```

    清单 A-9。家金丝雀类 T5】表示各种颜色的鸟

    ```java public final class DomesticCanary extends Bird { public DomesticCanary() { super("domestic canary", "yellow, orange, black, brown, white, red"); } }

    ```

    A-10 上市。虹鳟鱼类 T5】表示一种彩虹色的鱼

    ```java public final class RainbowTrout extends Fish { public RainbowTrout() { super("rainbowtrout", "bands of brilliant speckled multicolored " + "stripes running nearly the whole length of its body"); } }

    ```

    清单 A-11。SockeyeSalmon 类 T5】表示红绿相间的鱼

    ```java public final class SockeyeSalmon extends Fish { public SockeyeSalmon() { super("sockeyesalmon", "bright red with a green head"); } }

    ```

    Animal’s toString() method is declared final because it doesn’t make sense to override this method, which is complete in this example. Also, each of Bird’s and Fish’s overriding eat() and move() methods is declared final because it doesn’t make sense to override these methods in this example, which assumes that all birds eat seeds and insects; all fish eat krill, algae, and insects; all birds fly through the air; and all fish swim through the water.

    The AmericanRobin, DomesticCanary, RainbowTrout, and SockeyeSalmon classes are declared final because they represent the bottom of the Bird and Fish class hierarchies, and it doesn’t make sense to subclass them. 50. Listing A-12 declares the Animals class that was called for in Chapter 4.

    清单 A-12。动物类让动物进食和移动

    ```java public class Animals { public static void main(String[] args) { Animal[] animals = { new AmericanRobin(), new RainbowTrout(), new DomesticCanary(), new SockeyeSalmon() }; for (int i = 0; i < animals.length; i++) { System.out.println(animals[i]); animals[i].eat(); animals[i].move(); System.out.println(); } } }

    ```

  50. Listings A-13 through A-15 declare the Countable interface, the modified Animal class, and the modified Animals class that were called for in Chapter 4.

    清单 A-13。可计数的接口,用于进行动物普查

    ```java public interface Countable { String getID(); }

    ```

    清单 A-14。重构后的动物类 T5】用于人口普查

    ```java public abstract class Animal implements Countable { private String kind; private String appearance;

    public Animal(String kind, String appearance) { this.kind = kind; this.appearance = appearance; }

    public abstract void eat();

    public abstract void move();

    @Override public final String toString() { return kind + " -- " + appearance; }

    @Override public final String getID() { return kind; } }

    ```

    清单 A-15。修改后的动物类进行普查

    ```java public class Animals { public static void main(String[] args) { Animal[] animals = { new AmericanRobin(), new RainbowTrout(), new DomesticCanary(), new SockeyeSalmon(), new RainbowTrout(), new AmericanRobin() }; for (int i = 0; i < animals.length; i++) { System.out.println(animals[i]); animals[i].eat(); animals[i].move(); System.out.println(); }

      Census census = new Census();
      Countable[] countables = (Countable[]) animals;
      for (int i = 0; i < countables.length; i++)
         census.update(countables[i].getID());
    
      for (int i = 0; i < Census.SIZE; i++)
         System.out.println(census.get(i));
    

    } }

    ```

第 5 章:掌握高级语言功能第 1 部分

  1. 嵌套类是被声明为另一个类或范围的成员的类。
  2. 四种嵌套类是静态成员类、非静态成员类、匿名类和本地类。
  3. 非静态成员类 、匿名类和局部类也被称为内部类。
  4. 答案是错误的:静态成员类没有封闭实例。
  5. 通过首先实例化封闭类,然后在实例化封闭类时用封闭类实例作为 new 操作符的前缀,从封闭类之外实例化非静态成员类。例如: new EnclosingClass()。。
  6. 当被匿名类或局部类的实例访问时,有必要声明局部变量和参数 final 。
  7. 答案是正确的:一个接口可以在一个类中声明,也可以在另一个接口中声明。
  8. 包是一个惟一的名称空间,可以包含顶级类、其他顶级类型和子包的组合。
  9. 通过将反向互联网域名指定为顶级包名,可以确保包名是唯一的。
  10. package 语句是标识源文件类型所在的包的语句。
  11. 答案是错误的:不能在一个源文件中指定多个 package 语句。
  12. import 语句是一条语句,它通过告诉编译器在编译期间在哪里寻找非限定类型名来从包中导入类型。
  13. 您通过指定通配符( * )来表明您想要通过单个 import 语句导入多个类型。
  14. 在运行时搜索期间,当虚拟机找不到类文件时,它会报告“没有找到类定义”错误。
  15. 通过用于启动虚拟机的 -classpath 选项,或者当不存在时,通过 CLASSPATH 环境变量,为虚拟机指定用户类路径。
  16. 常量接口是只导出常量的接口。
  17. 常量接口用于避免用它们的类来限定它们的名字。
  18. 常量接口之所以不好,是因为它们的常量只不过是一个实现细节,不允许泄露到类的导出接口中,因为它们可能会使类的用户感到困惑(这些常量的目的是什么?).此外,它们代表了未来的承诺:即使当类不再使用这些常量时,接口也必须保留以确保二进制兼容性。
  19. 静态导入语句是 import 语句的一个版本,它允许您导入一个类的静态成员,这样您就不必用它们的类名来限定它们。
  20. 您将静态导入语句指定为 import ,后跟 static ,后跟成员访问操作符分隔的包和子包名称列表,后跟成员访问操作符、类名、成员访问操作符、单个静态成员名称或星号通配符,例如 import static Java . lang . math . cos;(从 Math 类中导入 cos() 静态方法)。
  21. 异常是与应用正常行为的背离。
  22. 对象优于错误代码来表示异常,因为错误代码布尔值或整数值没有对象名称有意义,并且因为对象可以包含导致异常的信息。这些细节有助于找到合适的解决方法。此外,错误代码很容易被忽略。
  23. 可抛出的是可抛出的的一个实例或者它的一个子类。
  24. getCause() 方法返回一个包装在另一个异常中的异常。
  25. 异常描述由外部因素(例如,无法打开文件)和有缺陷的代码(例如,向方法传递非法参数)导致的异常。错误描述了面向虚拟机的异常,比如内存不足或无法加载类文件。
  26. 已检查的异常是一种异常,它表示存在恢复可能性的问题,开发人员必须提供解决方法。
  27. 运行时异常是代表编码错误的异常。
  28. 当标准类库中没有现有的异常类满足您的需求时,您可以引入自己的异常类。
  29. 答案是错误的:通过将 throws 子句附加到方法的头,使用该子句来标识从方法中抛出的异常。
  30. try 语句的目的是提供一个作用域(通过它的大括号分隔的主体),在这个作用域中呈现可以抛出异常的代码。catch 块的目的是接收引发的异常,并通过提供解决方法来提供处理该异常的代码(通过其大括号分隔的主体)。
  31. finally 块的目的是提供清理代码,无论是否抛出异常,都会执行该代码。
  32. Listing A-16 presents the G2D class that was called for in Chapter 5.

    清单 A-16。G2D 类 T5】及其矩阵非静态成员类

    ```java public class G2D { private Matrix xform;

    public G2D() { xform = new Matrix(); xform.a = 1.0; xform.e = 1.0; xform.i = 1.0; }

    private class Matrix { double a, b, c; double d, e, f; double g, h, i; } }

    ```

  33. To extend the logging package (presented in Chapter 5’s discussion of packages) to support a null device in which messages are thrown away, first introduce Listing A-17’s NullDevice package-private class.

    清单 A-17 。实现众所周知的“比特桶”类

    ```java package logging;

    class NullDevice implements Logger { private String dstName;

    NullDevice(String dstName) { }

    public boolean connect() { return true; }

    public boolean disconnect() { return true; }

    public boolean log(String msg) { return true; } }

    ```

    Continue by introducing, into the LoggerFactory class, a NULLDEVICE constant and code that instantiates NullDevice with a null argument—a destination name is not required—when newLogger()’s dstType parameter contains this constant’s value. Check out Listing A-18.

    清单 A-18。一个被重构的记录器工厂类

    ```java package logging;

    public abstract class LoggerFactory { public final static int CONSOLE = 0; public final static int FILE = 1; public final static int NULLDEVICE = 2;

    public static Logger newLogger(int dstType, String...dstName) { switch (dstType) { case CONSOLE : return new Console(dstName.length == 0 ? null : dstName[0]); case FILE : return new File(dstName.length == 0 ? null : dstName[0]); case NULLDEVICE: return new NullDevice(null); default : return null; } } }

    ```

  34. Modifying the logging package (presented in Chapter 5’s discussion of packages) so that Logger’s connect() method throws a CannotConnectException instance when it cannot connect to its logging destination, and the other two methods each throw a NotConnectedException instance when connect() was not called or when it threw a CannotConnectException instance, results in Listing A-19’s Logger interface.

    清单 A-19。一个记录器接口,其方法抛出异常

    ```java package logging;

    public interface Logger { void connect() throws CannotConnectException; void disconnect() throws NotConnectedException; void log(String msg) throws NotConnectedException; }

    ```

    Listing A-20 presents the CannotConnectException class.

    A-20 上市。一个不复杂的不能连接异常类

    ```java package logging;

    public class CannotConnectException extends Exception { }

    ```

    NotConnectedException 类具有相同的结构,但名称不同。

    清单 A-21 展示了控制台级。

    清单 A-21。控制台类 T5】满足记录器的契约而不抛出异常

    ```java package logging;

    class Console implements Logger { private String dstName;

    Console(String dstName) { this.dstName = dstName; }

    public void connect() throws CannotConnectException { }

    public void disconnect() throws NotConnectedException { }

    public void log(String msg) throws NotConnectedException { System.out.println(msg); } }

    ```

    清单 A-22 给出了文件类。

    清单 A-22。文件类 满足记录器的契约,必要时抛出异常

    ```java package logging;

    class File implements Logger { private String dstName;

    File(String dstName) { this.dstName = dstName; }

    public void connect() throws CannotConnectException { if (dstName == null) throw new CannotConnectException(); }

    public void disconnect() throws NotConnectedException { if (dstName == null) throw new NotConnectedException(); }

    public void log(String msg) throws NotConnectedException { if (dstName == null) throw new NotConnectedException(); System.out.println("writing " + msg + " to file " + dstName); } }

    ```

  35. When you modify TestLogger to respond appropriately to thrown CannotConnectException and NotConnectedException objects, you end up with something similar to Listing A-23.

    清单 A-23。处理抛出异常的一个测试记录器类 T5】

    ```java import logging.*;

    public class TestLogger { public static void main(String[] args) { try { Logger logger = LoggerFactory.newLogger(LoggerFactory.CONSOLE); logger.connect(); logger.log("test message #1"); logger.disconnect(); } catch (CannotConnectException cce) { System.err.println("cannot connect to console-based logger"); } catch (NotConnectedException nce) { System.err.println("not connected to console-based logger"); }

      try
      {
         Logger logger = LoggerFactory.newLogger(LoggerFactory.FILE, "x.txt");
         logger.connect();
         logger.log("test message #2");
         logger.disconnect();
      }
      catch (CannotConnectException cce)
      {
         System.err.println("cannot connect to file-based logger");
      }
      catch (NotConnectedException nce)
      {
         System.err.println("not connected to file-based logger");
      }
    
      try
      {
         Logger logger = LoggerFactory.newLogger(LoggerFactory.FILE);
         logger.connect();
         logger.log("test message #3");
         logger.disconnect();
      }
      catch (CannotConnectException cce)
      {
         System.err.println("cannot connect to file-based logger");
      }
      catch (NotConnectedException nce)
      {
         System.err.println("not connected to file-based logger");
      }
    

    } }

    ```

第 6 章:掌握高级语言特性第 2 部分

  1. 断言是一个让你通过布尔表达式表达程序正确性假设的语句。
  2. 您可以使用断言来验证内部不变量、控制流不变量、前置条件、后置条件和类不变量。
  3. 答案是错误的:指定不带参数的 -ea 命令行选项将启用除系统断言之外的所有断言。
  4. 注释是注释类型的实例,将元数据与应用元素相关联。它在源代码中通过在类型名前面加上 @ 符号来表达。
  5. 可以对构造函数、字段、局部变量、方法、包、参数和类型(注释、类、枚举和接口)进行注释。
  6. 三种编译器支持的注释类型是覆盖 、弃用 和抑制警告 。
  7. 通过指定 @ 符号,紧接着是保留字接口,然后是类型名,最后是主体,来声明注释类型。
  8. 标记注释是一个注释类型的实例,除了它的名称之外不提供任何数据——类型的主体是空的。
  9. 元素是出现在注释类型主体中的方法头。它不能有参数或 throws 子句。它的返回类型必须是原语(例如, int )、 String 、 Class 、enum 类型、annotation 类型或前面类型的数组。它可以有一个默认值。
  10. 通过指定 default 后跟值,可以为元素分配默认值,默认值的类型必须与元素的返回类型相匹配。例如, String developer()默认“未赋值”;。
  11. 元注释是注释注释类型的注释。
  12. Java 的四种元注释类型是目标、保留、文档化,以及继承。
  13. 泛型可以定义为一套语言特性,用于声明和使用与类型无关的类和接口。
  14. 你可以使用泛型来确保你的代码是类型安全的,避免抛出 ClassCastException
  15. 泛型和参数化类型的区别泛型是一个类或者接口,通过声明一个形式类型参数表来引入一族参数化类型,参数化类型是泛型的一个实例。
  16. 匿名类不能是泛型的,因为它们没有名字。
  17. 五种实际类型参数是具体类型、具体参数化类型、数组类型、类型参数和通配符。
  18. 答案是正确的:你不能指定一个原始类型的名(例如, double 或 int )作为实际的类型参数。
  19. 原始类型是没有类型参数的泛型类型。
  20. 当编译器检测到涉及类型参数的显式强制转换时,它会报告一条未检查的警告消息。编译器担心向下转换为传递给类型参数的任何类型可能会导致违反类型安全。
  21. 通过在包含未检查代码的构造函数或方法前面加上@ suppress warnings(" unchecked ")注释,可以隐藏未检查的警告消息。
  22. 答案是真的:列表T1】的 E 类型参数无界。
  23. 您通过保留字扩展后跟类型名来指定一个上限。
  24. 递归类型界限是包括类型参数的类型参数界限。
  25. 通配符类型实参是必要的,因为通过接受任何实际的类型实参,它们提供了一种类型安全的解决方法,解决了多态行为不适用于多个参数化类型的问题,这些参数化类型的区别仅在于一个类型实参是另一个类型实参的子类型。例如,因为 List < String > 不是一种 List < Object > ,所以不能将类型为 List < String > 的对象传递给类型为 List < Object > 的方法参数。但是,你能把一个列表<字符串>对象传递给列表<吗?> 假设你不打算把列表<字符串> 对象添加到列表<?>T16】。
  26. 泛型方法是一个具有类型通用实现的类或实例方法。
  27. Although you might think otherwise, Listing 6-36’s methodCaller() generic method calls someOverloadedMethod(Object o). This method, instead of someOverloadedMethod(Date d), is called because overload resolution happens at compile time, when the generic method is translated to its unique bytecode representation, and erasure (which takes care of that mapping) causes type parameters to be replaced by their leftmost bound or Object (when there is no bound). After erasure, you are left with Listing A-24’s nongeneric methodCaller() method.

    清单 A-24。擦除产生的非泛型方法调用方()方法

    ```java public static void methodCaller(Object t) { someOverloadedMethod(t); }

    ```

  28. 具体化 将抽象表现为具体。

  29. 答案是错误的:类型参数没有具体化。
  30. 擦除是在编译之后丢弃类型参数,以便它们在运行时不可用。擦除还包括用类型变量的上限(例如, Object )替换其他类型变量的使用,并在生成的代码类型不正确时插入对适当类型的强制转换。
  31. 枚举类型是将相关常数的命名序列指定为合法值的类型。
  32. 当您使用常量基于 int 的枚举类型时,可能会出现三个问题:缺乏编译时类型安全性、脆弱的应用以及无法将 int 常量转换为有意义的基于字符串的描述。
  33. 枚举是通过保留字枚举表示的枚举类型。
  34. 通过将枚举常量指定为语句的选择器表达式,并将常量名称指定为事例值,可以将 switch 语句与枚举一起使用。
  35. 您可以通过添加字段、构造函数和方法来增强 enum——您甚至可以让 enum 实现接口。此外,您可以覆盖 toString() 来提供一个更有用的常量值描述,并子类化常量来分配不同的行为。
  36. 抽象 枚举类的目的是作为所有基于 Java 语言的枚举类型的公共基类。
  37. Enum 的 name() 和 toString() 方法的区别在于 name() 总是返回一个常量的名称,但是 toString() 可以被覆盖以返回一个更有意义的描述,而不是常量的名称。
  38. 答案是对的: Enum 的泛型是 Enum < E 扩展 Enum < E > > 。
  39. Listing A-25 presents a ToDo marker annotation type that annotates only type elements and that also uses the default retention policy.

    清单 A-25。待办事项标注类型为需要完成的标注类型

    ```java import java.lang.annotation.ElementType; import java.lang.annotation.Target;

    @Target(ElementType.TYPE) public @interface ToDo { }

    ```

  40. Listing A-26 presents a rewritten StubFinder application that works with Listing 6-13’s Stub annotation type (with appropriate @Target and @Retention annotations) and Listing 6-14’s Deck class.

    A-26 上市。通过新版本的 StubFinder 报告存根的 ID、截止日期和开发者

    ```java import java.lang.reflect.Method;

    public class StubFinder { public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("usage: java StubFinder classfile"); return; } Method[] methods = Class.forName(args[0]).getMethods(); for (int i = 0; i < methods.length; i++) if (methods[i].isAnnotationPresent(Stub.class)) { Stub stub = methods[i].getAnnotation(Stub.class); System.out.println("Stub ID = " + stub.id()); System.out.println("Stub Date = " + stub.dueDate()); System.out.println("Stub Developer = " + stub.developer()); System.out.println(); } } }

    ```

  41. Listing A-27 presents the generic Stack class and the StackEmptyException and StackFullException helper classes that were called for in Chapter 6.

    清单 A-27。 堆栈及其 StackEmptyException 和 StackFullException 助手类证明并非所有助手类都需要嵌套

    ```java public class Stack { private E[] elements; private int top;

    @SuppressWarnings("unchecked") Stack(int size) { if (size < 2) throw new IllegalArgumentException("" + size); elements = (E[]) new Object[size]; top = -1; }

    void push(E element) throws StackFullException { if (top == elements.length - 1) throw new StackFullException(); elements[++top] = element; }

    E pop() throws StackEmptyException { if (isEmpty()) throw new StackEmptyException(); return elements[top--]; }

    boolean isEmpty() { return top == -1; }

    public static void main(String[] args) throws StackFullException, StackEmptyException { Stack stack = new Stack(5); assert stack.isEmpty(); stack.push("A"); stack.push("B"); stack.push("C"); stack.push("D"); stack.push("E"); // Uncomment the following line to generate a StackFullException. //stack.push("F"); while (!stack.isEmpty()) System.out.println(stack.pop()); // Uncomment the following line to generate a StackEmptyException. //stack.pop(); assert stack.isEmpty(); } }

    class StackEmptyException extends Exception { }

    class StackFullException extends Exception { }

    ```

  42. Listing A-28 presents the Compass enum that was called for in Chapter 6.

    清单 A-28。一个有四个方向常数的罗盘枚举

    ```java enum Compass { NORTH, SOUTH, EAST, WEST }

    ```

    清单 A-29 展示了第 6 章中要求的使用罗盘级

    清单 A-29。使用指南针防止迷路

    ```java public class UseCompass { public static void main(String[] args) { int i = (int) (Math.random() * 4); Compass[] dir = { Compass.NORTH, Compass.EAST, Compass.SOUTH, Compass.WEST }; switch(dir[i]) { case NORTH: System.out.println("heading north"); break; case EAST : System.out.println("heading east"); break; case SOUTH: System.out.println("heading south"); break; case WEST : System.out.println("heading west"); break; default : assert false; // Should never be reached. } } }

    ```

第 7 章:探索基本 API 第 1 部分

  1. Math 声明 double 常量 E 和 PI 分别代表自然对数底值(2.71828。。。)和圆的周长与其直径的比值(3.14159。。。). E 初始化为 2.718281828459045 并且 PI 初始化为 3.141592653589793 。
  2. math . ABST3】(整数。【T4 最小值】)等于整数。MIN_VALUE 因为不存在与 MIN_VALUE 等价的 32 位正整数。(整数。MIN_VALUE 等于-2147483648 和整数。MAX_VALUE 等于 2147483647。)
  3. Math 的方法返回一个介于 0.0(含)和 1.0(不含)之间的伪随机数。表达式 (int) Math.random() * limit 不正确,因为该表达式始终返回 0。 (int) cast 运算符的优先级高于 * ,这意味着 cast 是在乘法之前执行的。 random() 返回一个小数值,强制转换将该值转换为 0,然后乘以 limit 的值,得到的总数值为 0。
  4. 浮点计算期间可能出现的五个特殊值是+无穷大、–无穷大、NaN、+0.0 和–0.0。
  5. 数学和严格数学 在以下方面有所不同:
    • StrictMath 的方法在所有平台上返回完全相同的结果。相比之下,一些 Math 的方法可能会返回因平台不同而略有不同的值。
    • image因为 StrictMath 不能利用平台特定的特性,如扩展精度数学协处理器,所以 StrictMath 的实现可能不如 Math 的实现有效。
  6. strictfp 的目的是限制浮点计算,保证可移植性。这个保留字在中间浮点表示和上溢/下溢(生成太大或太小而不适合表示的值)的上下文中实现了可移植性。此外,它可以应用于方法级别或类级别。
  7. BigDecimal 是一个不可变的类,它表示具有任意精度(位数)的有符号十进制数(例如,23.653)以及相关联的小数位数(指定小数点后位数的整数)。您可以使用这个类来准确地存储表示货币值的浮点值,并适当地舍入每个货币计算的结果。
  8. 描述学校普遍教授的舍入形式的舍入模式常数为半上。
  9. BigInteger 是一个不可变的类,表示任意精度的有符号整数。它以二进制补码格式存储其值(所有位都翻转,从 1 到 0 和从 0 到 1,并且在结果中添加了 1,以便与 Java 的字节整数、短整数、整数和长整数类型使用的二进制补码格式兼容)。
  10. 答案是正确的:字符串文字是一个字符串对象。
  11. 字符串的实习生()方法的目的是在字符串对象的内部表中存储字符串对象的唯一副本。 intern() 可以通过引用和 == 或比较字符串!= 。这些运算符是比较字符串的最快方法,在对大量字符串进行排序时尤其有用。
  12. String 和 StringBuffer 的区别在于 String 对象包含不可变的字符序列,而 StringBuffer 对象包含可变的字符序列。
  13. StringBuffer 和 StringBuilder 的不同之处在于 StringBuffer 的方法是同步的,而 StringBuilder 的等价方法是不同步的。因此,您可以在多线程情况下使用线程安全但较慢的 StringBuffer 类,在单线程情况下使用非线程安全但较快的 StringBuilder 类。
  14. 包的 isSealed() 方法的目的是指示一个包是否被密封(作为包一部分的所有类都归档在同一个 JAR 文件中)。当封装被密封时,此方法返回 true。
  15. 答案是正确的: getPackage() 在返回一个描述该包的 Package 对象之前,需要从包中加载至少一个 classfile。
  16. Listing A-30 presents the PrimeNumberTest application that was called for in Chapter 7.

    清单 A-30。检查一个正整数参数以发现它是否是质数

    ```java public class PrimeNumberTest { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java PrimeNumberTest integer"); System.err.println("integer must be 2 or higher"); return; } try { int n = Integer.parseInt(args[0]); if (n < 2) { System.err.println(n + " is invalid because it is less than 2"); return; } for (int i = 2; i <= Math.sqrt(n); i++) if (n % i == 0) { System.out.println (n + " is not prime"); return; } System.out.println(n + " is prime"); } catch (NumberFormatException nfe) { System.err.println("unable to parse " + args[0] + " into an int"); } } }

    ```

  17. 下面的循环使用 string bufferT3】来最小化对象创建:

    ```java String[] imageNames = new String[NUM_IMAGES]; StringBuffer sb = new StringBuffer(); for (int i = 0; i < imageNames.length; i++) { sb.append("image"); sb.append(i); sb.append(".png"); imageNames[i] = sb.toString(); sb.setLength(0); // Erase previous StringBuffer contents. }

    ```

  18. Listing A-31 presents the DigitsToWords application that was called for in Chapter 7.

    清单 A-31。将整数值转换为其文本表示

    ```java public class DigitsToWords { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java DigitsToWords integer"); return; } System.out.println(convertDigitsToWords(Integer.parseInt(args[0]))); }

    static String convertDigitsToWords(int integer) { if (integer < 0 || integer > 9999) throw new IllegalArgumentException("Out of range: " + integer); if (integer == 0) return "zero"; String[] group1 = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; String[] group2 = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; String[] group3 = { "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" }; StringBuffer result = new StringBuffer(); if (integer >= 1000) { int tmp = integer / 1000; result.append(group1[tmp - 1] + " thousand"); integer -= tmp * 1000; if (integer == 0) return result.toString(); result.append(" "); } if (integer >= 100) { int tmp = integer / 100; result.append(group1[tmp - 1] + " hundred"); integer -= tmp * 100; if (integer == 0) return result.toString(); result.append(" and "); } if (integer >= 10 && integer <= 19) { result.append(group2[integer - 10]); return result.toString(); } if (integer >= 20) { int tmp = integer / 10; result.append(group3[tmp - 2]); integer -= tmp * 10; if (integer == 0) return result.toString(); result.append("-"); } result.append(group1[integer - 1]); return result.toString(); } }

    ```

第 8 章:探索基本 API 第 2 部分

  1. 原始类型包装类是一个类,它的实例将自己包装在原始类型的值周围。
  2. Java 的原语类型包装类包括布尔、字节、字符、双精度、浮点、整数、长短。
  3. Java 提供了原始类型包装类,这样原始类型的值可以存储在集合中,并提供了一个很好的地方将有用的常量和类方法与原始类型相关联。
  4. 答案是错误的:布尔 是最小的原始类型包装类。
  5. 您应该使用 Character 类方法而不是像 ch>= ' 0 '&&ch<= ' 9 '这样的表达式来确定一个字符是否是一个数字、一个字母等等,因为很容易在表达式中引入一个 bug,表达式不能很好地描述它们所测试的内容,并且表达式偏向于拉丁数字(0–9)和字母(A–Z 和 A–Z)。
  6. 通过将此变量作为参数传递给 Double 的 boolean is infinit(Double d)class 方法,可以确定 double 变量 d 是否包含+infinity 或–infinity,当此参数为+infinity 或–infinity 时,该方法返回 true。
  7. 号是字节、字符的超类,以及其他原始类型包装类。
  8. 线程是应用代码的独立执行路径。
  9. Runnable 接口的目的是识别那些为线程提供代码的对象,以便通过该接口的唯一 void run() 方法执行。
  10. 线程类的目的是为底层操作系统的线程架构提供一致的接口。它提供了方法,使得将代码与线程相关联以及启动和管理那些线程成为可能。
  11. 答案是错误的:一个线程对象与一个单独的线程相关联。
  12. 竞争条件是多个线程同时或几乎同时更新同一对象的场景。对象的一部分存储由一个线程写入的值,对象的另一部分存储由另一个线程写入的值。
  13. 线程同步是指一次只允许一个线程在一个方法或块中执行代码的行为。
  14. 根据监视器和锁实现同步。
  15. 同步的工作原理是要求一个想要进入监视器控制的临界区的线程首先获得一个锁。当线程退出临界区时,锁会自动释放。
  16. 答案是正确的:类型为 long 或 double 的变量在 32 位虚拟机上不是原子的。
  17. 保留字 volatile 的目的是让运行在多处理器或多核机器上的线程访问实例字段或类字段的单个副本。如果没有 volatile ,每个线程可能会访问其缓存的字段副本,并且不会看到其他线程对其副本所做的修改。
  18. 答案是错误的:对象的 wait() 方法不能从同步方法或块的外部调用。
  19. 死锁是一种情况,其中锁被多个线程获取,没有一个线程持有自己的锁,而是持有其他线程所需的锁,并且没有一个线程可以进入并在稍后退出其临界区以释放其持有的锁,因为其他线程持有该临界区的锁。
  20. ThreadLocal 类的目的是将每线程数据(例如,用户 ID)与线程相关联。
  21. inheritable thread local 与 ThreadLocal 的不同之处在于,前者让子线程从其父线程继承线程本地值。
  22. 第八章中讨论的四个 java.lang 包系统类分别是系统、运行时、进程和进程构建器。
  23. 您调用 system . array copy()将一个数组复制到另一个数组。
  24. exec(字符串程序)方法在单独的本地进程中执行名为程序的程序。新进程继承了方法调用者的环境,并且返回一个进程对象以允许与新进程通信。发生 I/O 错误时,抛出 IOException 。
  25. 进程的 getInputStream() 方法返回一个 InputStream 引用,用于读取新进程写入其输出流的字节。
  26. Listing A-32 presents the MultiPrint application that was called for in Chapter 8.

    清单 A-32。多次打印一行文本

    ```java public class MultiPrint { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java MultiPrint text count"); return; } String text = args[0]; int count = Integer.parseInt(args[1]); for (int i = 0; i < count; i++) System.out.println(text); } }

    ```

  27. Listing A-33 presents the revised CountingThreads application that was called for in Chapter 8.

    清单 A-33。通过守护线程计数

    ```java public class CountingThreads { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { String name = Thread.currentThread().getName(); int count = 0; while (true) System.out.println(name + ": " + count++); } }; Thread thdA = new Thread(r); thdA.setDaemon(true); Thread thdB = new Thread(r); thdB.setDaemon(true); thdA.start(); thdB.start(); } }

    ```

    当您运行这个应用时,两个守护线程开始执行,您可能会看到一些输出。但是,一旦默认主线程离开 main() 方法并终止,应用就会结束。

  28. Listing A-34 presents the StopCountingThreads application that was called for in Chapter 8.

    清单 A-34。按回车键时停止计数线程

    ```java import java.io.IOException;

    public class StopCountingThreads { private static volatile boolean stopped = false;

    public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { String name = Thread.currentThread().getName(); int count = 0; while (!stopped) System.out.println(name + ": " + count++); } }; Thread thdA = new Thread(r); Thread thdB = new Thread(r); thdA.start(); thdB.start(); try { System.in.read(); } catch (IOException ioe) {} stopped = true; } }

    ```

  29. Listing A-35 presents the EVDump application that was called for in Chapter 8.

    清单 A-35。将所有环境变量转储到标准输出

    ```java public class EVDump { public static void main(String[] args) { System.out.println(System.getenv()); // System.out.println() calls toString() // on its object argument and outputs this // string } }

    ```

第 9 章:探索集合框架

  1. 集合是一组对象,存储在为此目的而设计的类的实例中。
  2. 集合框架是一组类型,为表示和操作集合提供了标准架构。
  3. 集合框架主要由核心接口、实现类和工具类组成。
  4. comparable 是一个对象,它的类实现了 Comparable 接口。
  5. 当您希望根据对象的自然排序来比较对象时,您可以让一个类实现 Comparable 接口。
  6. 比较器是一个对象,它的类实现了比较器接口。其目的是允许对象按照不同于其自然排序的顺序进行比较。
  7. 答案是错误的:集合使用 comparable(其类实现了 Comparable 接口的对象)来定义其元素的自然排序。
  8. Iterable 接口描述了任何可以以某种顺序返回其包含对象的对象。
  9. 集合接口表示被称为元素的对象集合。
  10. 当集合的 add() 方法抛出 UnsupportedOperationException 类的一个实例时,就是试图向不可修改的集合中添加元素。
  11. Iterable 的迭代器()方法返回实现迭代器接口的类的实例。该接口提供了一个 hasNext() 方法来确定迭代是否已经结束,一个 next() 方法来返回集合的下一个元素,一个 remove() 方法来从集合中删除由 next() 返回的最后一个元素。
  12. 增强的 for 循环语句的目的是简化集合或数组迭代。
  13. 增强的 for 循环语句表示为 for ( 类型 id : 集合 ) 或 for ( 类型 id : 数组 ) ,并读取“对于集合中的每个类型对象,将此对象分配给 id 或者“对于数组中的每个类型对象,在循环迭代开始时将该对象分配给 id
  14. 答案是正确的:增强的 for 循环适用于数组。比如, int[] x = { 1,2,3 };for(int I:x)system . out . println(I);声明数组 x 并输出其所有基于 int 的元素。
  15. 自动装箱是指每当指定了原始类型但需要引用时,将原始类型值包装在原始类型包装类的对象中的行为。该特性使开发人员在集合中存储原始值时不必显式实例化包装类。
  16. Unboxing 是每当指定引用但需要原始类型时,从其包装对象中解开原始类型值的行为。这一特性使开发人员不必显式调用对象上的方法(例如, intValue() )来检索包装的值。
  17. 列表是有序的集合,也称为序列。元素可以通过整数索引存储在特定的位置,也可以从特定的位置访问。
  18. 一个 ListIterator 实例使用光标在列表中导航。
  19. 视图是由另一个列表支持的列表。对视图所做的更改会反映在此支持列表中。
  20. 您可以使用 subList() 方法 以紧凑的方式对集合执行范围视图操作。比如 list.subList(fromIndex,toIndex)。clear();从列表中删除一系列元素,其中第一个元素位于索引的处,最后一个元素位于索引的 - 1 处。
  21. ArrayList 类 提供了一个基于内部数组的列表实现。
  22. LinkedList 类 提供了基于链接节点的列表实现。
  23. 节点是值和链接存储器位置的固定序列(即,特定数量的值和链接的排列,例如一个值位置后跟一个链接位置)。从面向对象的角度来看,它是一个对象,其字段存储值和对其他节点对象的引用。这些引用也称为链接。
  24. 答案是错误的: ArrayList 比 LinkedList 提供更慢的元素插入和删除。
  25. 集合是不包含重复元素的集合。
  26. 树集类 提供了一个基于树数据结构的集合实现。因此,元素按排序顺序存储。
  27. HashSet 类 提供了一个由散列表数据结构支持的集合实现。
  28. 答案是正确的:为了避免 hashset 中的重复元素,您自己的类必须正确地覆盖 equals() 和 hashCode() 。
  29. HashSet 和 LinkedHashSet 的区别在于 LinkedHashSet 使用链表存储其元素,导致其迭代器按照元素插入的顺序返回元素。
  30. 枚举集类 提供了基于位集的集实现。
  31. 有序集合是一个以升序维护其元素的集合,按照元素的自然顺序或创建有序集合时提供的比较器进行排序。此外,集合的实现类必须实现 SortedSet 接口。
  32. 可导航集合是一个排序集合,可以按照降序和升序进行迭代,并且可以报告给定搜索目标的最接近匹配。
  33. 答案是假的: HashSet 不是一个有序集合的例子。然而,树集是有序集的一个例子。
  34. 当您试图向排序集添加元素时,排序集的 add() 方法会抛出 ClassCastException ,因为元素的类没有实现 Comparable 。
  35. 队列是一个集合,其中的元素以特定的顺序存储和检索。大多数队列分为“先进先出”、“后进先出”或优先级。
  36. 答案是真的:队列的元素()方法在调用空队列 时抛出 NoSuchElementException。
  37. PriorityQueue 类 提供了一个优先级队列的实现,这是一个根据元素的自然排序或通过队列实例化时提供的比较器对其元素进行排序的队列。
  38. 映射是一组键/值对(也称为条目)。
  39. TreeMap 类 提供了基于红黑树的地图实现。因此,条目按其键的排序顺序存储。
  40. HashMap 类 提供了基于哈希表数据结构的映射实现。
  41. 哈希表使用哈希函数将键映射到整数值。
  42. 继续前面的练习,得到的整数值被称为散列码;它们标识哈希表数组元素,这些元素被称为桶或槽。
  43. 哈希表的容量是指桶的数量。
  44. 哈希表的加载因子指的是存储条目数除以存储桶数的比率。
  45. HashMap 和 LinkedHashMap 的区别在于 LinkedHashMap 使用链表存储其条目,导致其迭代器按照条目插入的顺序返回条目。
  46. IdentityHashMap 类 提供了一个映射实现,它在比较键和值时使用引用相等( == )而不是对象相等( equals() )。
  47. 枚举映射类 提供了一个映射实现,其键是同一个枚举的成员。
  48. 排序映射是一个以升序维护其条目的映射,根据键的自然排序或根据创建排序映射时提供的比较器进行排序。此外,地图的实现类必须实现 SortedMap 接口。
  49. 可导航地图是一个排序的地图,可以按照降序和升序进行迭代,并且可以报告给定搜索目标的最接近匹配。
  50. 答案是正确的: TreeMap 就是排序地图的一个例子。
  51. 数组的用途 类的静态< T >列表< T > asList(T...array) 方法是返回一个由指定的数组支持的固定大小的列表。(改变返回列表“直写”到数组。)
  52. 答案是错误的:二分搜索法比线性搜索更快。
  53. 您可以使用集合 ' 静态Setsynchronized Set(Sets)方法来返回一个 hashset 的同步变体。
  54. 七个面向遗产集合的类型是向量、枚举、堆栈、字典、哈希表、属性和位集。
  55. Listing A-36 presents the JavaQuiz application that was called for in Chapter 9.

    清单 A-36。你对 Java 了解多少?参加测验并找出答案!

    ```java import java.util.ArrayList; import java.util.Iterator; import java.util.List;

    public class JavaQuiz { private static class QuizEntry { private String question; private String[] choices; private char answer;

      QuizEntry(String question, String[] choices, char answer)
      {
         this.question = question;
         this.choices = choices;
         this.answer = answer;
      }
    
      String[] getChoices()
      {
         // Demonstrate returning a copy of the choices array to prevent clients
         // from directly manipulating (and possibly screwing up) the internal
         // choices array.
         String[] temp = new String[choices.length];
         System.arraycopy(choices, 0, temp, 0, choices.length);
         return temp;
      }
    
      String getQuestion()
      {
         return question;
      }
    
      char getAnswer()
      {
         return answer;
      }
    

    }

    static QuizEntry[] quizEntries = { new QuizEntry("What was Java's original name?", new String[] { "Oak", "Duke", "J", "None of the above" }, 'A'), new QuizEntry("Which of the following reserved words is also a literal?", new String[] { "for", "long", "true", "enum" }, 'C'), new QuizEntry("The conditional operator (?:) resembles which statement?", new String[] { "switch", "if-else", "if", "while" }, 'B') };

    public static void main(String[] args) { // Populate the quiz list. List quiz = new ArrayList(); for (QuizEntry entry: quizEntries) quiz.add(entry); // Perform the quiz. System.out.println("Java Quiz"); System.out.println("---------\n"); Iterator iter = quiz.iterator(); while (iter.hasNext()) { QuizEntry qe = iter.next(); System.out.println(qe.getQuestion()); String[] choices = qe.getChoices(); for (int i = 0; i < choices.length; i++) System.out.println(" " + (char) ('A' + i) + ": " + choices[i]); int choice = -1; while (choice < 'A' || choice > 'A' + choices.length) { System.out.print("Enter choice letter: "); try { choice = System.in.read(); // Remove trailing characters up to and including the newline // to avoid having these characters automatically returned in // subsequent System.in.read() method calls. while (System.in.read() != '\n'); choice = Character.toUpperCase((char) choice); } catch (java.io.IOException ioe) { } } if (choice == qe.getAnswer()) System.out.println("You are correct!\n"); else System.out.println("You are not correct!\n"); } } }

    ```

  56. 在哈希代码生成算法中使用(int)(f ^(f>>>32))而不是 (int) (f ^ (f > > 32)) ,因为 > > > 总是向右移动 0,这不会影响哈希代码,而 > > 向右移动 0 或 1(无论符号位中的值是什么),这都会影响哈希代码

  57. Listing A-37 presents the FrequencyDemo application that was called for in Chapter 9.

    清单 A-37。报告上一个命令行参数在之前的命令行参数中出现的频率

    ```java import java.util.Collections; import java.util.LinkedList; import java.util.List;

    public class FrequencyDemo { public static void main(String[] args) { List listOfArgs = new LinkedList(); String lastArg = (args.length == 0) ? null : args[args.length - 1]; for (int i = 0; i < args.length - 1; i++) listOfArgs.add(args[i]); System.out.println("Number of occurrences of " + lastArg + " = " + Collections.frequency(listOfArgs, lastArg)); } }

    ```

第 10 章:探索附加的实用 API

  1. 任务是一个对象,它的类实现了可运行的接口(一个可运行的任务)或者可调用的接口(一个可调用的任务)。
  2. 执行器 是一个对象,它的类直接或间接地实现了执行器接口,该接口将任务提交与任务执行机制相分离。
  3. 执行器接口专门关注可运行,这意味着可运行任务没有便捷的方式向其调用者返回值(因为可运行的 run() 方法不返回值); Executor 没有提供方法来跟踪执行可运行任务的进度,取消正在执行的可运行任务,或者确定可运行任务何时完成执行;执行者无法执行可运行任务的集合;并且 Executor 没有为应用提供关闭执行器的方法(更不用说正确关闭执行器了)。
  4. 通过提供 ExecutorService 接口,克服了 Executor 的局限性。
  5. Runnable 的 run() 方法和 Callable 的 call() 方法的区别在于: run() 不能返回值,而 call() 可以返回值;并且 run() 不能抛出已检查的异常,而 call() 可以抛出已检查的异常。
  6. 答案是错误的:您可以从可调用的的 call() 方法中抛出已检查和未检查的异常,但只能从 Runnable 的 run() 方法中抛出未检查的异常。
  7. 未来是一个对象,它的类实现了未来接口。它表示异步计算,并提供取消任务、返回任务值以及确定任务是否已完成的方法。
  8. Executors 类的 newFixedThreadPool() 方法创建了一个线程池,该线程池重用固定数量的在共享无界队列外运行的线程。最多有 n 个线程 个线程在主动处理任务。如果在所有线程都处于活动状态时提交了额外的任务,它们将在队列中等待一个可用的线程。如果在执行器关闭之前,任何线程由于执行过程中的故障而终止,那么在需要执行后续任务时,一个新的线程将取代它的位置。在明确关闭执行器之前,线程池中的线程将一直存在。
  9. 同步器是一个促进同步的通用形式的类。
  10. 四种常用的同步器是倒计时锁、循环屏障、交换器和信号量。倒计时锁让一个或多个线程在一个“门”等待,直到另一个线程打开这个门,此时这些其他线程可以继续。循环障碍让一组线程等待彼此到达一个公共障碍点。交换器让一对线程在同步点交换对象。信号量维护一组许可证,用于限制可以访问有限资源的线程数量。
  11. 并发工具提供的对集合框架的面向并发的扩展是 ArrayBlockingQueue 、blocking queue、 BlockingQueue 、 ConcurrentHashMap 、 ConcurrentMap 、 ConcurrentNavigableMap 、 ConcurrentLinkedQueue 、 ConcurrentSkipListMap 、concurrentskistmap
  12. 锁是实现锁接口的类的实例,它提供了比通过同步保留字所能实现的更广泛的锁定操作。锁还通过关联的条件对象支持等待/通知机制。
  13. Lock 对象控制线程进入临界区时获得的隐式锁(通过 synchronized 保留字控制)的最大优势是它们能够退出获取锁的尝试。
  14. 原子变量是一个类的实例,它封装了单个变量,并支持对该变量进行无锁、线程安全的操作,例如, AtomicInteger 。
  15. 日期类用一个相对于 Unix 纪元的长整数来描述日期。
  16. 格式化程序类是 printf() 风格格式字符串的解释器。此类为布局对齐和对齐提供支持;数字、字符串和日期/时间数据的通用格式。以及更多。支持常用的 Java 类型(如 byte 和 BigDecimal )。
  17. Random 类的实例从一个被称为种子的特殊 48 位值开始生成随机数序列。该值随后由数学算法修改,该算法被称为线性同余发生器。
  18. 在正则表达式的控制下,扫描器类将输入的字符流解析为原始类型、字符串和大整数/小数。
  19. 您调用扫描器的 hasNext 方法中的一个来确定一个字符序列是否代表一个整数或其他类型的值,然后再扫描该序列。
  20. ZipFile 和 ZipInputStream 的两个区别是 ZipFile 允许随机访问 ZIP 条目,而 ZipInputStream 允许顺序访问;并且 ZipFile 在内部缓存 ZIP 条目以提高性能,而 ZipInputStream 不缓存条目。
  21. Listing A-38 presents the ZipList application that was called for in Chapter 10.

    清单 A-38。列出归档内容

    ```java import java.io.FileInputStream; import java.io.IOException;

    import java.util.Date;

    import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream;

    public class ZipList { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("usage: java ZipList zipfile"); return; } ZipInputStream zis = null; try { zis = new ZipInputStream(new FileInputStream(args[0])); ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { System.out.println(ze.getName()); System.out.println(" Compressed Size: " + ze.getCompressedSize()); System.out.println(" Uncompressed Size: " + ze.getSize()); if (ze.getTime() != -1) System.out.println(" Modification Time: " + new Date(ze.getTime())); System.out.println(); zis.closeEntry(); } } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { if (zis != null) try { zis.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } } } }

    ```

第 11 章:执行经典 I/O

  1. File 类的目的是提供对底层平台可用文件系统的访问。
  2. File 类的实例包含文件和目录的路径名,这些路径名可能存在,也可能不存在于它们的文件系统中。
  3. File 的 listRoots() 方法 返回一个数组 File 对象,表示可用文件系统的根目录(根)。
  4. 路径是目录的层次结构,必须遍历它才能定位文件或目录。路径名是路径的字符串表示;与平台相关的分隔符(例如,Windows 反斜杠[ \ ]字符)出现在连续名称之间。
  5. 绝对路径名和相对路径名的区别如下:绝对路径名是以根目录符号开头的路径名,而相对路径名是不以根目录符号开头的路径名;它是通过从其他路径名获取的信息来解释的。
  6. 通过指定 system . getproperty(" user . dir ")获得当前用户(也称为工作)目录。
  7. 父路径名是由除了最后一个名称之外的所有路径名组成的字符串。
  8. Normalize 意味着用默认的名称分隔符替换分隔符,以便路径名与底层文件系统兼容。
  9. 通过访问文件的分隔符 和分隔符类字段,可以获得默认的名称分隔符。第一个字段将字符存储为一个字符,第二个字段将其存储为一个字符串。
  10. 规范路径名是一个绝对且唯一的路径名,并且每次都以相同的方式格式化。
  11. 文件的 getParent() 和 getName() 方法的区别在于 getParent() 返回父路径名, getName() 返回路径名的名称序列中的最后一个名称。
  12. 答案是假的:文件的 exists() 方法 决定一个文件或目录是否存在。
  13. 普通文件不是目录,而是满足其他平台相关标准的文件:例如,它不是符号链接或命名管道。由 Java 应用创建的任何非目录文件都保证是普通文件。
  14. 文件的 lastModified() 方法 返回由这个文件对象的路径名表示的文件最后一次被修改的时间,或者当文件不存在或者在这个方法调用期间发生 I/O 错误时返回 0。从 Unix 纪元(00:00:00 GMT,1970 年 1 月 1 日)开始,返回值以毫秒为单位。
  15. 答案是正确的: File 的 list() 方法 返回一个由 String 组成的数组,其中每个条目都是一个文件名,而不是一个完整的路径。
  16. FilenameFilter 和 FileFilter 接口的区别如下: FilenameFilter 声明一个布尔接受(文件目录,字符串名称)方法,而 FileFilter 声明一个布尔接受(字符串路径名)方法。这两种方法都完成了相同的任务,即接受(通过返回 true)或拒绝(通过返回 false)包含由目录列表中的参数标识的文件或目录。
  17. 答案是假的:文件的 createNewFile() 方法检查文件是否存在,并在文件不存在时创建文件,该操作相对于所有其他可能影响文件的文件系统活动是原子的。
  18. 通过读取 java.io.tmpdir 系统属性可以定位到文件的 createTempFile(String,String) 方法创建临时文件的默认临时目录。
  19. 您可以通过调用 File 的 deleteOnExit() 方法来注册临时文件以便删除,从而确保在虚拟机正常结束时删除临时文件(不会崩溃,也不会断电)。
  20. 通过首先在每个文件对象上调用文件的 getCanonicalFile() 方法 ,然后比较返回的文件对象,可以准确地比较两个文件对象。
  21. RandomAccessFile 类的目的是创建和/或打开随机访问的文件,在文件关闭之前,可以混合进行写和读操作。
  22. “rwd”和“rws”模式参数 的目的是确保对位于本地存储设备上的文件的任何写入都被写入该设备,这保证了当系统崩溃时关键数据不会丢失。当文件不在本地设备上时,不做任何保证。
  23. 文件指针是标识下一个要写入或读取的字节位置的光标。当打开一个现有文件时,文件指针被设置为它的第一个字节,偏移量为 0。创建文件时,文件指针也被设置为 0。
  24. 答案是假的:当你调用 RandomAccessFile 的 seek(long) 方法来设置文件指针的值,如果这个值大于文件的长度,文件的长度不变。文件长度将仅在偏移被设置为超出文件结尾之后通过写入来改变。
  25. 平面文件数据库是组织成记录和字段的单个文件。记录存储单个条目(例如,零件数据库中的零件),字段存储条目的单个属性(例如,零件号)。
  26. 流是任意长度的有序字节序列。字节通过输出流从应用流向目的地,并通过输入流从源流向应用。
  27. OutputStream 的 flush() 方法 的目的是将任何缓冲的输出字节写入目的地。如果该输出流的预期目的地是由底层平台提供的抽象(例如,文件),则刷新该流仅保证先前写入该流的字节被传递到底层平台以供写入;它不能保证它们实际上被写入到物理设备,如磁盘驱动器。
  28. 答案是对的: OutputStream 的 close() 方法自动刷新输出流。如果应用在调用 close() 之前结束,输出流将自动关闭,其数据将被刷新。
  29. InputStream 的 mark(int) 和 reset() 方法 的目的是重读流的一部分。 mark(int) 标记输入流中的当前位置。对 reset() 的后续调用将该流重新定位到最后标记的位置,以便后续读取操作重新读取相同的字节。别忘了调用 markSupported() 来了解子类是否支持 mark() 和 reset() 。
  30. 你可以通过调用 ByteArrayOutputStream 的 toByteArray() 方法来访问 ByteArrayOutputStream 实例的内部字节数组的副本。
  31. 答案是假的: FileOutputStream 和 FileInputStream 不提供内部缓冲区来提高读写操作的性能。
  32. 您可以使用 PipedOutputStream 和 PipedInputStream 在一对执行线程之间传递数据。
  33. 过滤流是在输入流到达目的地之前对其字节序列进行缓冲、压缩/解压缩、加密/解密或其他操作的流。
  34. 当一个流实例被传递给另一个流类的构造函数时,两个流被链接在一起。
  35. 通过将一个 BufferedOutputStream 实例链接到一个 FileOutputStream 实例,并调用 BufferedOutputStream 实例的 write() 方法,可以提高文件输出流的性能,以便数据在流向文件输出流之前得到缓冲。您可以通过将 BufferedInputStream 实例链接到 FileInputStream 实例来提高文件输入流的性能,以便通过调用此实例的 read() 方法在从 BufferedInputStream 实例返回数据之前缓冲来自文件输入流的数据。
  36. DataOutputStream 和 DataInputStream 通过提供以平台无关的方式读写原语类型值和字符串的方法,支持 FileOutputStream 和 FileInputStream 。相比之下, FileOutputStream 和 FileInputStream 只提供写/读字节和字节数组的方法。
  37. 对象序列化是一种虚拟机机制,用于将对象状态序列化为字节流。它的反序列化对应物是一个虚拟机机制,用于从字节流中反序列化该状态。
  38. Java 支持的三种形式的序列化和反序列化是默认序列化和反序列化,自定义序列化和反序列化,以及外部化。
  39. Serializable 接口的目的是告诉虚拟机可以序列化实现类的对象。
  40. 当序列化机制遇到一个其类没有实现 Serializable 的对象时,它抛出一个 NotSerializableException 类的实例。
  41. Java 不支持无限制序列化的三个原因如下:安全性、性能和不适合序列化的对象。
  42. 您通过创建一个 ObjectOutputStream 实例并调用其 writeObject() 方法来启动序列化。通过创建一个 ObjectInputStream 实例并调用其 readObject() 方法来初始化反序列化。
  43. 答案是错误的:类字段不会自动序列化。
  44. 瞬态保留字的目的是标记不参与默认序列化和默认反序列化的实例字段。
  45. 反序列化机制导致 readObject() 在尝试反序列化类已更改的对象时抛出 InvalidClassException 类的实例。
  46. 反序列化机制检测到序列化对象的类发生了如下变化:每个序列化对象都有一个标识符。反序列化机制将被反序列化的对象的标识符与其类的序列化标识符进行比较(所有可序列化的类都会自动获得唯一的标识符,除非它们显式指定了自己的标识符),并在检测到不匹配时引发 InvalidClassException 。
  47. 通过引入一个 long serialVersionUID =long integer 值 ,可以将一个实例字段添加到一个类中,并避免在反序列化一个在添加实例字段之前已序列化的对象时出现问题;申报入班。长整数值必须是唯一的,并且被称为流唯一标识符(SUID) 。您可以使用 JDK 的 serialver 工具来帮助完成这项任务。
  48. 通过在类中声明私有的 void writeObject(object output stream)和 void read object(ObjectInputStream)方法,可以在不使用外部化的情况下定制默认的序列化和反序列化机制。
  49. 通过首先调用 writeObject(object output stream)中 ObjectOutputStream 的 defaultWriteObject() 方法以及首先调用 ObjectInputStream 的 defaultReadObject()read object(ObjectInputStream)中方法
  50. 外部化不同于默认和自定义的序列化和反序列化,因为它提供了对序列化和反序列化任务的完全控制。
  51. 一个类通过实现可外部化的接口而不是可序列化的,并通过声明 void write external(object output)和 void read external(object input in)方法而不是 void writeObject(object output stream)和 void readObject(object input stream)方法来表明它支持外部化。
  52. 答案是正确的:在外部化过程中,当反序列化机制没有检测到公共无参数构造函数时,它会抛出 InvalidClassException 和“无有效构造函数”消息。
  53. PrintStream 的 print() 和 println() 方法之间的区别在于 print() 方法不在它们的输出中添加行结束符,而 println() 方法 添加行结束符。
  54. PrintStream 的无参数 void println() 方法 输出 line.separator 系统属性的值,以确保行以可移植的方式终止(例如,在 Windows 上回车后跟换行符/换行符,或者在 Unix/Linux 上仅换行/换行符)。
  55. Java 的流类不擅长流字符,因为字节和字符是两回事:一个字节代表 8 位数据项,一个字符代表 16 位数据项。此外,字节流不知道字符集及其字符编码。
  56. 当谈到字符 I/O 时,Java 提供了 writer 类和 reader 类作为 stream 类的首选替代。
  57. 答案是假的:读者没有声明一个可用()方法 。
  58. OutputStreamWriter 类的目的是作为传入的字符序列和传出的字节流之间的桥梁。根据默认或指定的字符编码,写入此编写器的字符被编码为字节。 InputStreamReader 类的目的是充当传入字节流和传出字符序列之间的桥梁。从该读取器读取的字符根据默认或指定的字符编码从字节解码。
  59. 您可以通过读取文件的值来识别默认的字符编码。编码系统属性。
  60. FileWriter 类的目的是使用默认字符编码方便地连接到底层文件输出流。 FileReader 类的目的是使用默认字符编码方便地连接到底层文件输入流。
  61. Listing A-39 presents the Touch application that was called for in Chapter 11.

    清单 A-39。将文件或目录的时间戳设置为当前时间

    ```java import java.io.File;

    import java.util.Date;

    public class Touch { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Touch pathname"); return; } new File(args[0]).setLastModified(new Date().getTime()); } }

    ```

  62. Listing A-40 presents the Copy application that was called for in Chapter 11.

    清单 A-40。使用缓冲 I/O 将源文件复制到目标文件

    ```java import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;

    public class Copy { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java Copy srcfile dstfile"); return; } BufferedInputStream bis = null; BufferedOutputStream bos = null; try { FileInputStream fis = new FileInputStream(args[0]); bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(args[1]); bos = new BufferedOutputStream(fos); int b; // I chose b instead of byte because byte is a reserved word. while ((b = bis.read()) != -1) bos.write(b); } catch (FileNotFoundException fnfe) { System.err.println(args[0] + " could not be opened for input, or " + args[1] + " could not be created for output"); } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { if (bis != null) try { bis.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context }

         if (bos != null)
            try
            {
               bos.close();
            }
            catch (IOException ioe)
            {
               assert false; // shouldn't happen in this context
            }
      }
    

    } }

    ```

  63. Listing A-41 presents the Split application that was called for in Chapter 11.

    清单 A-41。将一个大文件分割成许多较小的部分文件

    ```java import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;

    public class Split { static final int FILESIZE = 1400000; static byte[] buffer = new byte[FILESIZE];

    public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Split pathname"); return; } File file = new File(args[0]); long length = file.length(); int nWholeParts = (int) (length / FILESIZE); int remainder = (int) (length % FILESIZE); System.out.printf("Splitting %s into %d parts%n", args[0], (remainder == 0) ? nWholeParts : nWholeParts + 1); BufferedInputStream bis = null; BufferedOutputStream bos = null; try { FileInputStream fis = new FileInputStream(args[0]); bis = new BufferedInputStream(fis); for (int i = 0; i < nWholeParts; i++) { bis.read(buffer); System.out.println("Writing part " + i); FileOutputStream fos = new FileOutputStream("part" + i); bos = new BufferedOutputStream(fos); bos.write(buffer); bos.close(); bos = null; } if (remainder != 0) { int br = bis.read(buffer); if (br != remainder) { System.err.println("Last part mismatch: expected " + remainder + " bytes"); System.exit(0); } System.out.println("Writing part " + nWholeParts); FileOutputStream fos = new FileOutputStream("part" + nWholeParts); bos = new BufferedOutputStream(fos); bos.write(buffer, 0, remainder); } } catch (IOException ioe) { ioe.printStackTrace(); } finally { if (bis != null) try { bis.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } if (bos != null) try { bos.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } } } }

    ```

  64. Listing A-42 presents the CircleInfo application that was called for in Chapter 11.

    清单 A-42。从标准输入中读取代表圆半径的文本行,并根据当前半径输出周长和面积

    ```java import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException;

    public class CircleInfo { public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); while (true) { System.out.print("Enter circle's radius: "); String str = br.readLine(); double radius; try { radius = Double.valueOf(str).doubleValue(); if (radius <= 0) System.err.println("radius must not be 0 or negative"); else { System.out.println("Circumference: " + Math.PI * 2.0 * radius); System.out.println("Area: " + Math.PI * radius * radius); System.out.println(); } } catch (NumberFormatException nfe) { System.err.println("not a number: " + nfe.getMessage()); } } } }

    ```

第 12 章:访问网络

  1. 网络是一组可以在网络用户之间共享的互连节点。
  2. 内部网 是位于组织内部的网络,互联网 是将组织相互连接的网络。
  3. 内部网和互联网通常使用 TCP/IP 在节点之间进行通信。传输控制协议(TCP) 是面向连接的协议,用户数据报协议(UDP) 是无连接协议,互联网协议(IP) 是 TCP 和 UDP 执行其任务的基本协议。
  4. 主机是基于计算机的 TCP/IP 节点。
  5. 套接字是两个进程之间通信链路的端点。
  6. 套接字由标识主机的 IP 地址和标识该主机上运行的进程的端口号来标识。
  7. IP 地址 是 32 位或 128 位无符号整数,唯一标识网络主机或某个其他网络节点。
  8. 数据包是一个可寻址的消息块。数据包通常被称为 IP 数据报。
  9. 套接字地址由 IP 地址和端口号组成。
  10. 用于表示 IPv4 和 IPv6 地址的 InetAddress 子类是 Inet4Address 和 Inet6Address 。
  11. 环回接口是一个基于软件的网络接口,输出数据作为输入数据环回。
  12. 答案是错误的:在网络字节顺序中,最重要的字节排在最前面。
  13. 本地主机由主机名 local host 或 IP 地址表示,通常表示为 127.0.0.1 (IPv4)或::1 (IPv6)。
  14. 套接字选项是用于配置套接字行为的参数。
  15. 套接字选项由在套接字选项接口中声明的常量描述。
  16. 答案是假的:你没有通过调用 void setOption(int optID,Object value) 方法来设置 socket 选项。取而代之的是,您调用一个在套接字后缀的类中声明的类型安全套接字选项方法。
  17. 基于套接字类的套接字通常被称为流套接字,因为套接字与输入流 和输出流类相关联。
  18. 在套接字实例的上下文中,绑定使得客户端套接字地址对服务器套接字可用,以便服务器进程可以通过服务器套接字与客户端进程通信。
  19. 代理是出于安全目的位于内部网和互联网之间的主机。Java 通过 java.net.Proxy 类的实例来表示代理设置。
  20. 答案是假的: ServerSocket() 构造函数 创建一个未绑定的服务器套接字。
  21. DatagramSocket 和 MulticastSocket 类之间的区别如下: DatagramSocket 让您在一对主机之间执行基于 UDP 的通信,而 MulticastSocket 让您在许多主机之间执行基于 UDP 的通信。
  22. 数据报包是与数据报包类的一个实例相关联的字节数组。
  23. 单播和组播 的区别如下:单播是服务器向单个客户端发送消息的动作,而组播是服务器向多个客户端发送消息的动作。
  24. URL 是指定资源(例如,网页)位于基于 TCP/IP 的网络(例如,互联网)上何处的字符串。此外,它还提供了检索该资源的方法。
  25. URN 是命名资源的字符串,但不提供访问该资源的方法(该资源可能不可用)。
  26. 答案是正确的:网址和骨灰盒也是 URIs。
  27. 当你将 null 传递给 s 时, URL(字符串 s) 构造函数抛出 malformedurexception。
  28. 相当于 openStream() 就是执行 openConnection()。getInputStream() 。
  29. 答案是假的:你不需要调用 URLConnection 的 void setDoInput(boolean doInput)方法并以 true 作为参数,就可以从 web 资源输入内容。默认设置为 true。
  30. 当遇到空格字符时, URLEncoder 将其转换为加号。
  31. NetworkInterface 类 将网络接口表示为名称和分配给该接口的 IP 地址列表。此外,它还用于标识加入多播组的本地接口。
  32. MAC 地址 是包含网络接口硬件地址的字节数组。
  33. MTU 代表最大传输单位。此大小表示无需将消息分割成多个 IP 数据报就可以装入 IP 数据报的最大消息长度。
  34. 答案是错误的: NetworkInterface 的 getName() 方法返回一个网络接口的名称(如 eth0 或 lo ),而不是一个人类可读的显示名称。
  35. InterfaceAddress 的 getNetworkPrefixLength() 方法 返回 IPv4 下的子网掩码。
  36. HTTP cookie (简称 cookie)是一个状态对象。
  37. 最好将 cookie 存储在客户端而不是服务器上,因为可能会有数百万个 cookie(取决于网站的受欢迎程度)。
  38. 用于处理 cookies 的四种 java.net 类型是 CookieHandler 、 CookieManager 、 CookiePolicy 和 CookieStore 。
  39. Listing A-43 presents the enhanced EchoClient application that was called for in Chapter 12.

    清单 A-43。向服务器回显数据并从服务器接收数据,并显式关闭套接字

    ```java import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter;

    import java.net.Socket; import java.net.UnknownHostException;

    public class EchoClient { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage : java EchoClient message"); System.err.println("example: java EchoClient \"This is a test.\""); return; } Socket socket = null; try { socket = new Socket("localhost", 9999); OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); PrintWriter pw = new PrintWriter(osw); pw.println(args[0]); pw.flush(); InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); System.out.println(br.readLine()); } catch (UnknownHostException uhe) { System.err.println("unknown host: " + uhe.getMessage()); } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { if (socket != null) try { socket.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } } } }

    ```

  40. Listing A-44 presents the enhanced EchoServer application that was called for in Chapter 12.

    清单 A-44。从客户端接收数据并将其回显给客户端,并在出现 Kill 文件后显式关闭套接字

    ```java import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter;

    import java.net.ServerSocket; import java.net.Socket;

    public class EchoServer { public static void main(String[] args) { System.out.println("Starting echo server..."); ServerSocket ss = null; try { ss = new ServerSocket(9999); File file = new File("kill"); while (!file.exists()) { Socket s = ss.accept(); // waiting for client request try { InputStream is = s.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String msg = br.readLine(); System.out.println(msg); OutputStream os = s.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); PrintWriter pw = new PrintWriter(osw); pw.println(msg); pw.flush(); } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { try { s.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } } } } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { if (ss != null) try { ss.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context } } } }

    ```

第 13 章:迁移到新的 I/O

  1. 新的 I/O 是一种支持内存映射文件 I/O、就绪选择、文件锁定等的架构。这个架构由缓冲区、通道、选择器、正则表达式和字符集组成。
  2. 缓冲器是存储要发送到 I/O 服务或从 I/O 服务接收的固定量数据的对象(用于执行输入/输出的装置)。
  3. 缓冲器的四个属性是容量、极限、位置和标记。
  4. 当您在由只读数组支持的缓冲区上调用 Buffer 的 array() 方法时,该方法抛出 ReadOnlyBufferException。
  5. 当您在一个缓冲区上调用 Buffer 的 flip() 方法时,限制被设置为当前位置,然后该位置被设置为零。当标记被定义时,它被丢弃。缓冲液现在可以被排空了。
  6. 当你在一个没有设置标记的缓冲区上调用 Buffer 的 reset() 方法时,这个方法抛出 InvalidMarkException 。
  7. 答案是错误的:缓冲区不是线程安全的。
  8. 扩展抽象 Buffer 类的类有 ByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 和 ShortBuffer 。此外,这个包包括 MappedByteBuffer 作为抽象 ByteBuffer 子类 。
  9. 您可以通过调用其 allocate() 、 allocateDirect() 或 wrap() 类方法之一来创建一个字节缓冲区。
  10. 视图缓冲器是管理另一个缓冲器的数据的缓冲器。
  11. 通过调用 Buffer 子类的 duplicate() 方法来创建视图缓冲区。
  12. 通过调用 Buffer 子类方法,比如 byte Buffer asReadOnlyBuffer()或 char Buffer asReadOnlyBuffer(),可以创建一个只读视图缓冲区。
  13. ByteBuffer 在字节缓冲区存储单个字节的方法有 ByteBuffer put(int index,byte b) 和 ByteBuffer put(byte b) 。 ByteBuffer 从字节缓冲区取出单个字节的方法有 byte get(int index) 方法和 byte get() 。
  14. 当当前位置大于或等于限制时,试图使用相对 put() 方法或相对 get() 方法会导致 bufferoverflow exception 或 bufferunderflow exception 发生。
  15. 相当于执行 buffer . flip();是执行的 buffer.limit(buffer.position())。位置(0);。
  16. 答案是假的:调用 flip() 两次并不能让你回到原来的状态。相反,缓冲区的大小为零。
  17. Buffer 的 clear() 和 reset() 方法的区别在于: clear() 方法将一个缓冲区标记为空,而 reset() 将缓冲区的当前位置更改为先前设置的标记,或者在没有先前设置的标记时抛出 invalidmackexception。
  18. ByteBuffer 的 compact() 方法将当前位置和限制之间的所有字节复制到缓冲区的开头。索引 p = position() 处的字节复制到索引 0,索引 p + 1 处的字节复制到索引 1,依此类推,直到索引 limit() - 1 处的字节复制到索引 n = limit() - 1 - p 。然后,缓冲器的当前位置被设置为 n + 1 ,其极限被设置为其容量。定义后的标记将被丢弃。
  19. ByteOrder 类的目的是帮助你在向/从多字节缓冲区写入/读取多字节值时处理字节顺序问题。
  20. 直接字节缓冲区是与通道和本机代码交互以执行 I/O 的字节缓冲区。直接字节缓冲区尝试将字节元素存储在通道用来通过本机代码执行直接(原始)访问的内存区域中,本机代码告诉操作系统直接清空或填充内存区域。
  21. 你通过调用 ByteBuffer 的 allocated direct()方法获得一个直接字节缓冲区。
  22. 通道是一个对象,表示与硬件设备、文件、网络套接字、应用组件或其他能够执行写、读和其他 I/O 操作的实体的开放连接。通道在字节缓冲区和 I/O 服务源或目的地之间高效地传输数据。
  23. 通道接口提供的功能是关闭一个通道(通过 close() 方法)和确定一个通道是否打开(通过 isOpen() 方法)。
  24. 直接扩展通道的三个接口分别是可写字节通道 T5、可读字节通道 T8、中断字节通道 T11】。
  25. 答案是正确的:实现了 InterruptibleChannel 的通道是异步关闭的。
  26. 获取通道的两种方式是调用 Channels 类方法,如 writable bytechannel new channel(output stream output stream),以及调用经典 I/O 类上的通道方法,如 RandomAccessFile 的 FileChannel getChannel() 方法。
  27. 分散/集中 I/O 是跨多个缓冲区执行单个 I/O 操作的能力。
  28. 散射通道 和收集通道接口用于实现散射/收集 I/O
  29. 文件通道 是到底层文件的通道。
  30. 答案是错误的:文件通道支持分散/聚集 I/O。
  31. 文件通道提供了 MappedByteBuffer 映射(文件通道。将文件的一个区域映射到内存的方法。
  32. FileChannel 的 lock() 和 tryLock() 方法的根本区别在于 lock() 方法 可以阻塞而 tryLock() 方法 从不阻塞。
  33. 正则表达式(也称为 regex 或 regexp)是一种基于字符串的模式,表示匹配该模式的一组字符串。
  34. 模式类 的实例通过编译后的正则表达式表示模式。出于性能原因编译正则表达式;通过编译后的正则表达式进行模式匹配比不编译正则表达式要快得多。
  35. Pattern 的 compile() 方法在正则表达式参数中发现非法语法时抛出 PatternSyntaxException 类的实例。
  36. 匹配器类 的实例试图将编译后的正则表达式与输入文本进行匹配。
  37. Matcher 的 matches() 和 lookingAt() 方法 的区别在于,与 matches()looking at()不需要匹配整个区域。
  38. 人物类 是出现在和之间的一组人物。
  39. 有六种字符类:简单、否定、范围、并集、交集和减法。
  40. 捕获组保存匹配的字符,供以后在模式匹配期间调用。
  41. 零长度匹配 是起始和结束索引相等的零长度匹配。
  42. 量词是隐式或显式绑定到模式的数值。量词被分为贪婪的、不情愿的和占有的。
  43. 贪婪量词和勉强量词的区别在于,贪婪量词试图寻找最长匹配,而勉强量词试图寻找最短匹配。
  44. 所有格量词和贪婪量词的区别在于,所有格量词只能尝试一次寻找最长匹配,而贪婪量词可以尝试多次。
  45. Listing A-45 presents the enhanced Copy application that was called for in Chapter 13.

    清单 A-45。通过字节缓冲区和文件通道复制文件

    ```java import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;

    import java.nio.ByteBuffer;

    import java.nio.channels.FileChannel;

    public class Copy { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java Copy srcfile dstfile"); return; } FileChannel fcSrc = null; FileChannel fcDest = null; try { FileInputStream fis = new FileInputStream(args[0]); fcSrc = fis.getChannel(); FileOutputStream fos = new FileOutputStream(args[1]); fcDest = fos.getChannel(); ByteBuffer buffer = ByteBuffer.allocateDirect(2048); while ((fcSrc.read(buffer)) != -1) { buffer.flip(); while (buffer.hasRemaining()) fcDest.write(buffer); buffer.clear(); } } catch (FileNotFoundException fnfe) { System.err.println(args[0] + " could not be opened for input, or " + args[1] + " could not be created for output"); } catch (IOException ioe) { System.err.println("I/O error: " + ioe.getMessage()); } finally { if (fcSrc != null) try { fcSrc.close(); } catch (IOException ioe) { assert false; // shouldn't happen in this context }

         if (fcDest != null)
            try
            {
               fcDest.close();
            }
            catch (IOException ioe)
            {
               assert false; // shouldn't happen in this context
            }
      }
    

    } }

    ```

  46. Listing A-46 presents the ReplaceText application that was called for in Chapter 13.

    清单 A-46。用替换文本替换模式的所有匹配项

    ```java import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException;

    public class ReplaceText { public static void main(String[] args) { if (args.length != 3) { System.err.println("usage: java ReplaceText text oldText newText"); return; } try { Pattern p = Pattern.compile(args[1]); Matcher m = p.matcher(args[0]); String result = m.replaceAll(args[2]); System.out.println(result); } catch (PatternSyntaxException pse) { System.err.println(pse); } } }

    ```

第 14 章:访问数据库

  1. 数据库是有组织的数据集合。
  2. 关系数据库是一种将数据组织成可以相互关联的表格的数据库。
  3. 另外两个数据库类别是层次数据库和面向对象数据库。
  4. 数据库管理系统是一组程序,它使你能够存储、修改和提取数据库中的信息。它还为用户提供了添加、删除、访问、修改和分析存储在一个位置的数据的工具。
  5. Java DB 是 Apache 的开源 Derby 产品的发行版,它基于 IBM 的 Cloudscape RDBMS 代码库。
  6. 答案是错误的:Java DB 的嵌入式驱动程序使数据库引擎与应用运行在同一个虚拟机中。
  7. setEmbeddedCP 将 derby.jar 和 derbytools.jar 添加到类路径中,以便您可以从应用中访问 Java DB 的嵌入式驱动程序。
  8. 答案是错误的:您运行 Java DB 的 sysinfo 命令行工具来查看 Java 环境/Java DB 配置。
  9. SQLite 是一个非常简单和流行的 RDBMS,它实现了一个独立的、无服务器的、零配置的事务 SQL 数据库引擎,被认为是世界上部署最广泛的数据库引擎。
  10. 清单类型化 是将任何数据类型的任何值存储到任何列中的能力,而不管该列声明的类型。
  11. sqlite 提供了用于访问和修改 SQLite 数据库的工具。
  12. JDBC 是一个以独立于 RDBMS 的方式与 RDBMS 通信的 API。
  13. 数据源 是一种数据存储工具,范围从简单的文件到由 RDBMS 管理的复杂关系数据库。
  14. 一个 JDBC 驱动实现了 java.sql.Driver 接口。
  15. 答案是错误的:有四种 JDBC 司机。
  16. 第三类 JDBC 驱动程序不依赖于本机代码,通过 RDBMS 独立协议与中间件服务器通信。然后,中间件服务器将客户机的请求传递给数据源。
  17. JDBC 提供了与数据源通信的 java.sql.DriverManager 类和 javax.sql.DataSource 接口。
  18. 通过传递一个形式为 JDBC:derby:databaseName的 URL,您可以通过嵌入式驱动程序获得一个到 Java DB 数据源的连接; URLAttributes 属于 DriverManager 的 getConnection() 方法之一。
  19. 答案为假: int getErrorCode() 返回特定于供应商的错误代码。
  20. SQL 状态错误代码是一个五个字符的字符串,由两个字符的类值和三个字符的子类值组成。
  21. sqlnontransienexception 和 sqltransienexception 的区别在于:sqlnontransienexception 描述的是在不改变应用源代码或数据源某个方面的情况下无法重试的失败操作,sqltransienexception 描述的是可以立即重试的失败操作。
  22. JDBC 的三种报表类型分别是报表、准备报表和可调用报表。
  23. 您调用执行 SQL SELECT 语句的语句方法是 ResultSet execute query(String SQL)。
  24. 结果集的游标提供对特定数据行的访问。
  25. SQL FLOAT 类型映射到 Java 的 double 类型。
  26. 预处理语句表示预编译的 SQL 语句。
  27. 答案是真的: CallableStatement 扩展 prepared statementT5】。
  28. 存储过程是执行特定任务的 SQL 语句列表。
  29. 调用存储过程时,首先获取与 escape 子句关联的 CallableStatement 实现实例(通过连接的 prepareCall() 方法之一),然后执行 CallableStatement 方法,如 void setInt(Stringparameter name,int x) 将参数传递给 escape 子句参数,最后调用布尔执行
  30. 转义子句是独立于 RDBMS 的语法。
  31. 元数据是关于数据的数据。
  32. 元数据包括目录、基表、视图、索引、模式和附加信息的列表。
  33. Listing A-47 presents the enhanced JDBCDemo application that was called for in Chapter 14.

    清单 A-47。为 SQLite 或 Java DB 嵌入式驱动程序输出数据库元数据

    ```java import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;

    public class JDBCDemo { final static String URL1 = "jdbc:derby:employee;create=true"; final static String URL2 = "jdbc:sqlite:employee";

    public static void main(String[] args) { String url = null; if (args.length != 1) { System.err.println("usage 1: java JDBCDemo javadb"); System.err.println("usage 2: java JDBCDemo sqlite"); return; } if (args[0].equals("javadb")) url = URL1; else if (args[0].equals("sqlite")) url = URL2; else { System.err.println("invalid command-line argument"); return; } Connection con = null; try { if (args[0].equals("sqlite")) Class.forName("org.sqlite.JDBC"); con = DriverManager.getConnection(url); dump(con.getMetaData()); } catch (ClassNotFoundException cnfe) { System.err.println("unable to load sqlite driver"); } catch (SQLException sqlex) { while (sqlex != null) { System.err.println("SQL error : " + sqlex.getMessage()); System.err.println("SQL state : " + sqlex.getSQLState()); System.err.println("Error code: " + sqlex.getErrorCode()); System.err.println("Cause: " + sqlex.getCause()); sqlex = sqlex.getNextException(); } } finally { if (con != null) try { con.close(); } catch (SQLException sqle) { sqle.printStackTrace(); } } }

    static void dump(DatabaseMetaData dbmd) throws SQLException { System.out.println("DB Major Version = " + dbmd.getDatabaseMajorVersion()); System.out.println("DB Minor Version = " + dbmd.getDatabaseMinorVersion()); System.out.println("DB Product = " + dbmd.getDatabaseProductName()); System.out.println("Driver Name = " + dbmd.getDriverName()); System.out.println("Numeric function names for escape clause = " + dbmd.getNumericFunctions()); System.out.println("String function names for escape clause = " + dbmd.getStringFunctions()); System.out.println("System function names for escape clause = " + dbmd.getSystemFunctions()); System.out.println("Time/date function names for escape clause = " + dbmd.getTimeDateFunctions()); System.out.println("Catalog term: " + dbmd.getCatalogTerm()); System.out.println("Schema term: " + dbmd.getSchemaTerm()); System.out.println(); System.out.println("Catalogs"); System.out.println("--------"); ResultSet rsCat = dbmd.getCatalogs(); while (rsCat.next()) System.out.println(rsCat.getString("TABLE_CAT")); System.out.println(); System.out.println("Schemas"); System.out.println("-------"); ResultSet rsSchem = dbmd.getSchemas(); while (rsSchem.next()) System.out.println(rsSchem.getString("TABLE_SCHEM")); System.out.println(); System.out.println("Schema/Table"); System.out.println("------------"); rsSchem = dbmd.getSchemas(); while (rsSchem.next()) { String schem = rsSchem.getString("TABLE_SCHEM"); ResultSet rsTab = dbmd.getTables(null, schem, "%", null); while (rsTab.next()) System.out.println(schem + " " + rsTab.getString("TABLE_NAME")); } } }

    ```