10.12 练习
本章的示例代码位于仓库 ThinkJavaCode 的目录 ch10 中,有关如何下载这个仓库,请参阅前言中的“使用示例代码”一节。做以下的练习前,建议你先编译并运行本章的示例。
练习10-1
这个练习旨在确保你明白将对象作为参数进行传递的机制。
(1) 为下面的程序绘制一个栈图,riddle 返回前,对方法 main 和 riddle 中的局部变量和形参进行描述。用箭头指出每个变量指向的对象。
(2) 这个程序的输出是什么?
(3) 对象 blank 是可修改的还是不可修改的?为什么?
public static int riddle(int x, Point p) {x = x + 7;return x + p.x + p.y;}public static void main(String[] args) {int x = 5;Point blank = new Point(1, 2);System.out.println(riddle(x, blank));System.out.println(x);System.out.println(blank.x);System.out.println(blank.y);}
练习10-2
这个练习旨在确保你明白从方法返回对象的机制。
(1) 绘制一个栈图,指出下面这个程序在 distance 返回前的状态。列出这个栈图中的所有变量和形参,以及这些变量指向的对象。
(2) 这个程序的输出是什么?你能在不运行它的情况下确定这一点吗?
public static double distance(Point p1, Point p2) {int dx = p2.x - p1.x;int dy = p2.y - p1.y;return Math.sqrt(dx * dx + dy * dy);}public static Point findCenter(Rectangle box) {int x = box.x + box.width / 2;int y = box.y + box.height / 2;return new Point(x, y);}public static void main(String[] args) {Point blank = new Point(5, 8);Rectangle rect = new Rectangle(0, 2, 4, 4);Point center = findCenter(rect);double dist = distance(center, blank);System.out.println(dist);}
练习10-3
这个练习与别名有关。前面说过,别名指的是两个指向同一个对象的变量。
(1) 绘制一个栈图,以显示下面程序在 main 结束前的状态,包括所有的局部变量及其指向的对象。
(2) 这个程序的输出是什么?
(3) main 结束时,p1 和 p2 互为别名吗?为什么?
public static void printPoint(Point p) {System.out.println("(" + p.x + ", " + p.y + ")");}public static Point findCenter(Rectangle box) {int x = box.x + box.width / 2;int y = box.y + box.height / 2;return new Point(x, y);}public static void main(String[] args) {Rectangle box1 = new Rectangle(2, 4, 7, 9);Point p1 = findCenter(box1);printPoint(p1);box1.grow(1, 1);Point p2 = findCenter(box1);printPoint(p2);}
练习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 对象:
int x = 17;BigInteger big = BigInteger.valueOf(x);
(6) 因为 BigInteger 不是基本数据类型,所以对其执行数学运算时,不能用常规的数学运算符,而必须用 add 等方法。要想将两个 BigInteger 相加,可对其中一个调用方法 add,并将另一个作为实参:
BigInteger small = BigInteger.valueOf(17);BigInteger big = BigInteger.valueOf(1700000000);BigInteger total = small.add(big);
请尝试使用其他方法,如 multiply 和 pow。
(7) 修改方法 factorial,在其中用 BigInteger 来执行计算,并将结果作为 BigInteger 返回。可保留其中的形参,因为其类型还是 int。
(8) 修改方法 factorial 后,再次尝试以表格的方式显示 0~30 及其阶乘。计算出的 30 的阶乘正确吗?参数值最多可到多少结果依然是正确的?
(9) BigInteger 对象是可修改的还是不可修改的?你是怎么知道的?
练习10-5
很多加密算法需要计算大整数的幂,下面的方法实现了一种高效的整数幂算法:
public static int pow(int x, int n) {if (n == 0) return 1;// 递归地计算x的n/2次幂int t = pow(x, n / 2);// 如果n为偶数,结果就是t的平方// 如果n为奇数,结果就是t的平方乘以xif (n % 2 == 0) {return t * t;} else {return t * t * x;}}
这个方法存在的问题是,仅当结果小到能够用 int 类型表示时,它才管用。请修改这个方法,在其中用 BigInteger 对象来存储结果,但形参可保留为 int 类型。
你应该使用 BigInteger 的方法 add 和 multiply,但不要使用方法 BigInteger.pow,不然就太没意思了。
