10.12 练习

本章的示例代码位于仓库 ThinkJavaCode 的目录 ch10 中,有关如何下载这个仓库,请参阅前言中的“使用示例代码”一节。做以下的练习前,建议你先编译并运行本章的示例。

练习10-1

这个练习旨在确保你明白将对象作为参数进行传递的机制。

(1) 为下面的程序绘制一个栈图,riddle 返回前,对方法 mainriddle 中的局部变量和形参进行描述。用箭头指出每个变量指向的对象。

(2) 这个程序的输出是什么?

(3) 对象 blank 是可修改的还是不可修改的?为什么?

  1. public static int riddle(int x, Point p) {
  2. x = x + 7;
  3. return x + p.x + p.y;
  4. }
  5. public static void main(String[] args) {
  6. int x = 5;
  7. Point blank = new Point(1, 2);
  8. System.out.println(riddle(x, blank));
  9. System.out.println(x);
  10. System.out.println(blank.x);
  11. System.out.println(blank.y);
  12. }

练习10-2

这个练习旨在确保你明白从方法返回对象的机制。

(1) 绘制一个栈图,指出下面这个程序在 distance 返回前的状态。列出这个栈图中的所有变量和形参,以及这些变量指向的对象。

(2) 这个程序的输出是什么?你能在不运行它的情况下确定这一点吗?

  1. public static double distance(Point p1, Point p2) {
  2. int dx = p2.x - p1.x;
  3. int dy = p2.y - p1.y;
  4. return Math.sqrt(dx * dx + dy * dy);
  5. }
  6. public static Point findCenter(Rectangle box) {
  7. int x = box.x + box.width / 2;
  8. int y = box.y + box.height / 2;
  9. return new Point(x, y);
  10. }
  11. public static void main(String[] args) {
  12. Point blank = new Point(5, 8);
  13. Rectangle rect = new Rectangle(0, 2, 4, 4);
  14. Point center = findCenter(rect);
  15. double dist = distance(center, blank);
  16. System.out.println(dist);
  17. }

练习10-3

这个练习与别名有关。前面说过,别名指的是两个指向同一个对象的变量。

(1) 绘制一个栈图,以显示下面程序在 main 结束前的状态,包括所有的局部变量及其指向的对象。

(2) 这个程序的输出是什么?

(3) main 结束时,p1p2 互为别名吗?为什么?

  1. public static void printPoint(Point p) {
  2. System.out.println("(" + p.x + ", " + p.y + ")");
  3. }
  4. public static Point findCenter(Rectangle box) {
  5. int x = box.x + box.width / 2;
  6. int y = box.y + box.height / 2;
  7. return new Point(x, y);
  8. }
  9. public static void main(String[] args) {
  10. Rectangle box1 = new Rectangle(2, 4, 7, 9);
  11. Point p1 = findCenter(box1);
  12. printPoint(p1);
  13. box1.grow(1, 1);
  14. Point p2 = findCenter(box1);
  15. printPoint(p2);
  16. }

练习10-4

你可能对 factorial 方法感到厌烦了,但你还得再编写一个版本。

(1) 新建一个命名为 Big.java 的程序,并编写(或重用)方法 factorial 的迭代版本。

(2) 以表格的方式显示整数 0~30 及其阶乘。你可能会发现,结果到 15 左右就不再正确了。这是为什么呢?

(3) BigInteger 是一个 Java 类,可用于表示任意大的整数,它可表示的最大整数只受制于内存量和处理速度。请花点时间阅读这个类的文档。可在网上搜索 Java BigInteger 来找这个文档。

(4) 要想使用 BigInteger,必须在程序开头导入 java.math.BigInteger

(5) 创建 BigInteger 对象的方式有很多种,但最简单的方式是使用 valueOf。下面的代码将一个整数转换为 BigInteger 对象:

  1. int x = 17;
  2. BigInteger big = BigInteger.valueOf(x);

(6) 因为 BigInteger 不是基本数据类型,所以对其执行数学运算时,不能用常规的数学运算符,而必须用 add 等方法。要想将两个 BigInteger 相加,可对其中一个调用方法 add,并将另一个作为实参:

  1. BigInteger small = BigInteger.valueOf(17);
  2. BigInteger big = BigInteger.valueOf(1700000000);
  3. BigInteger total = small.add(big);

请尝试使用其他方法,如 multiplypow

(7) 修改方法 factorial,在其中用 BigInteger 来执行计算,并将结果作为 BigInteger 返回。可保留其中的形参,因为其类型还是 int

(8) 修改方法 factorial 后,再次尝试以表格的方式显示 0~30 及其阶乘。计算出的 30 的阶乘正确吗?参数值最多可到多少结果依然是正确的?

(9) BigInteger 对象是可修改的还是不可修改的?你是怎么知道的?

练习10-5

很多加密算法需要计算大整数的幂,下面的方法实现了一种高效的整数幂算法:

  1. public static int pow(int x, int n) {
  2. if (n == 0) return 1;
  3. // 递归地计算x的n/2次幂
  4. int t = pow(x, n / 2);
  5. // 如果n为偶数,结果就是t的平方
  6. // 如果n为奇数,结果就是t的平方乘以x
  7. if (n % 2 == 0) {
  8. return t * t;
  9. } else {
  10. return t * t * x;
  11. }
  12. }

这个方法存在的问题是,仅当结果小到能够用 int 类型表示时,它才管用。请修改这个方法,在其中用 BigInteger 对象来存储结果,但形参可保留为 int 类型。

你应该使用 BigInteger 的方法 addmultiply,但不要使用方法 BigInteger.pow,不然就太没意思了。