《Java黑皮书基础篇第10版》 第13章【习题】

Java语言程序设计 习题第十三章

13.1章节习题

13.1 在下面类的定义中,哪些定义了合法的抽象类?

a)

//错误,有只有抽象类中才能有抽象方法
class A {
    
    
abstract void unfinished() {
    
    
	} 
}

b)

// 关键字abstract写错了位置,应该写在class前面
// 习惯上会将访问修饰符(如 public、protected、private)放在前面,而将其他修饰符(如 abstract、final、static)放在后面
public class abstract A {
    
    
  abstract void unfinished();
} 

c)

// 错误,有只有抽象类中才能有抽象方法
class A {
    
    
	abstract void unfinished():
}

d)

// 错误,抽象类中的普通方法需要有{},不能省略
abstract class A {
    
    
  protected void unfinished();
}

e)

// 正确
abstract class A {
    
    
	abstract void unfinished();
}

f)

// 正确
abstract class A {
    
    
	abstract int unfinished();
}

13.2 getArea() 方法和 getPerimeter() 方法可以从 GeometricObject 类中删除。在 GeometricObject 类中将这两个方法定义为抽象方法的好处是什么?

在编译(compilation)期,一个声明为GeometricObject的对象可以使用这两个方法

否则,就没办法计算周长和面积

13.3 下面说法为真还是为假?

a. 除了不能使用 new 操作符创建抽象类的实例之外,一个抽象类可以像非抽象类一样使用。

b. 抽象类可以被继承

c. 非抽象的父类的子类不能是抽象的

d. 子类不能将父类中的具体方法重写,并定义为抽象的

e. 抽象方法必须是非静态的

13.3章节习题

13.4 为什么下面两行代码可以编译成功,但是会导致运行错误?

// 第二行等号右侧想要把Integer类型的numberRef转换为Double类型,而Integer和Double没有继承关系,如果Double是Integer的父类,就会运行成功
Number numberRef = new Integer(0);
Double doubleRef = (Double)numberRef;

13.5 为什么下面两行代码可以编译成功,但是会导致运行错误?

//和上一题类似,不能把Double转换成Integer
Number[] numberArray = new Integer[2];
numberArray[0] = new Double(1.5);

13.6 给出下面代码的输出。

public class Test {
    
    
  public static void main(String[] args) {
    
    
    Number x = 3;
    System.out.println(x.intValue());
    System.out.println(x.doubleValue());
  }
}
3
3.0

13.7 下面代码有什么错误? (注意,Integer和Double类的compareTo方法在第10.7节中进行了介绍)

// 很好的例子,因为compareTo只是Integer和Double的方法,并不是Number的方法
public class Test {
    
    
  public static void main(String[] args) {
    
    
    Number x = new Integer(3);
    System.out.println(x.intValue());
    System.out.println(x.compareTo(new Integer(4)));
  }
}

13.8 下面代码中有什么错误?

// 很好的例子,11章讲过,“.”是成员对象访问符,运算顺序优先于类型转化。所以编译器会先检查x.compareTo,自然不通过,理由在上一题
//应该改成((Integer)x).compareTo(new Integer(4))
public class Test {
    
    
  public static void main(String[] args) {
    
    
    Number x = new Integer(3);
    System.out.println(x.intValue());
    System.out.println((Integer)x.compareTo(new Integer(4)));
  }
}

13.4 章节习题

13.9 可以使用Calendar类来创建一个Calendar对象吗?

不可以,Calendar是一个抽象类

13.10 Calendar类中哪个方法是抽象的?

add方法

13.11 如何为当前时间创建一个 Calendar 对象?

GregorianCalendar的无参构造方法

13.12 对于一个 Calendar 对象 c 而言,如何得到它的年、月、日期 、小时 、分钟以及秒钟?

package learning;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class Test {
    
    
  public static void main(String[] args) {
    
    
	  Calendar c = new GregorianCalendar();
	  System.out.println(c.get(Calendar.YEAR));
	  System.out.println(c.get(Calendar.MONTH));
	  System.out.println(c.get(Calendar.DAY_OF_MONTH));
	  System.out.println(c.get(Calendar.HOUR));
	  System.out.println(c.get(Calendar.MINUTE));
	  System.out.println(c.get(Calendar.SECOND)); 
  }
}

输出结果:

需要注意的是,Calendar类中的MONTH是从0计数的

2023
4
15
9
35
25

13.5章节习题

13.13 假设A是一个接口,可以使用newA()创建一个实例吗?

不可以

13.14 假设A是一个接口,可以如下声明一个类型A的引用变量x吗?

A x;

可以

13.15 下面哪个是正确的接口?

// 在抽象类中,既可以有抽象方法(没有方法体),也可以有具体的方法(有方法体),子类继承抽象类后,必须实现抽象方法,可以选择性的实现具体的方法
// 在旧版本的Java中,接口只能包含抽象方法,这些方法没有方法体。但是,自Java 8开始,接口也可以包含默认方法和静态方法
(a)
// 错误,接口不能有方法体
interface A {
    
        
  void print() {
    
     }
} 

(b)   
// 错误,接口不能用abstract修饰,接口不能有方法体
abstract interface A {
    
        
  abstract void print() {
    
     }
}

(c)       
// 错误,接口不能用abstract修饰
abstract interface A {
    
        
  print();
} 

(d)       
//正确
interface A {
    
       
  void print();
}

(e)
//正确
interface A {
    
        
  default void print() {
    
    
  }
} 

(f)
//正确
interface A {
    
        
  static int get() {
    
    
    return 0;
  }
} 

13.16 指出下面代码中的错误

// 在接口中声明的方法默认是public的,因此在实现接口方法时,必须使用public访问修饰符进行修饰
interface A {
    
    
  void m1();
}

class B implements A {
    
    
  void m1() {
    
    
    System.out.println("m1");
  }
}

13.6章节习题

13.17 下面说法为真还是为假? 如果一个类实现了Comparable, 那么该类的对象可以调用 compareTo 方法

正确

13.18 下面哪个是正确的 String 类中的 compareTo 方法的方法头?

// compareTo比较当前对象
public int compareTo(String o)
public int compareTo(Object o)

13.19 下面代码可以被编译吗? 为什么?

// 不可以,因为对象类型不一致
Integer n1 = new Integer(3);
Object n2 = new Integer(4);
System.out.println(n1.compareTo(n2));

13.20 可以在类中定义 compareTo 方法而不实现 Comparable 接口。实现 Comparable 接口的好处是 什么?

通过实现 Comparable 接口,可以将类的对象传递给需要 Comparable 类型的方法。

13.21 下面的代码有什么错误?

// Person类没有实现Comparable接口,而sort方法是依赖Camparable的
public class Test {
    
    
  public static void main(String[] args) {
    
    
    Person[] persons = {
    
    new Person(3), new Person(4), new Person(1)};
    java.util.Arrays.sort(persons);
  }
}

class Person {
    
    
  private int id;

  Person(int id) {
    
    
    this.id = id;         
  }
}

13.7章节习题

13.22 如果一个对象的类没有实现 java.lang.Cloneable, 可以调用 clone() 方法来克隆这个对象吗? Date 类实现了 Cloneable 接口吗?

在编译阶段不会报错,可以正常编译。但是在运行阶段会报异常。因此,如果在main方法想要调用a.clone(),a对象所在的类必须实现Cloneable

实现了。如果没有实现,克隆时会报异常

13.23 如果 House 类(在程序清单 13-11 中定义)没有覆盖 clone() 方法,或者如果 House 类没有实现java.lang.Cloneable, 会发生什么?

如果没有覆盖clone(),编译器会报错

如果没有实现Cloneable,会报异常

13.24 给出下面代码的输出结果

java.util.Date date = new java.util.Date(); 
java.util.Date date1 = date;
java.util.Date date2 = (java.util.Date)(date.clone()); System.out.println(date == date1); 
System.out.println(date == date2); System.out.println(date.equals(date2));
true
false
true

13.25 给出下面代码的输出结果

ArrayList<String> list = new ArrayList<>();
		  list.add("New York");
		  ArrayList<String> list1 = list;
		  ArrayList<String> list2 = (ArrayList<String>)(list.clone()); 
		  list.add("Atlanta");
		  System.out.println(list == list1); 
		  System.out.println(list == list2); 
		  System.out.println("list is " + list); 
		  System.out.println("list1 is " + list1); 
		  System.out.println("list2.get(0) is " + list2.get(0)); 
		  System.out.println("list2.size() is " + list2.size());
true
false
list is [New York, Atlanta]
list1 is [New York, Atlanta]
list2.get(0) is New York
list2.size() is 1

13.26 下面的代码有什么错误?

// 实现接口,重写方法
public class Test {
    
    
	public static void main(String[] args) {
    
    
		GeometricObject x = new Circle(3); 
    GeometricObject y = x.clone(); 
    System.out.println(x == y);
	} 
}

13.8章节习题

13.27 给出一个例子显示接口相对于抽象类的优势。

多重继承

13.28 给出抽象类和接口的定义。抽象类和接口之间的相同点和不同点是什么?

  1. 定义:接口是一种完全抽象的类,只包含方法的声明而没有实现;抽象类是一个可以包含抽象方法和具体方法实现的类。
  2. 实现方式:一个类可以有多个接口,但只能继承一个类。
  3. 构造函数:接口没有构造方法,而抽象类可以有构造方法。
  4. 成员变量:接口中只能定义常量,不能定义实例变量;抽象类可以包含实例变量。
  5. 接口之间的继承:接口可以继承多个接口,形成接口之间的继承关系;抽象类之间只能通过类继承实现继承关系。

13.29 真或者假?

a。接口被编译为独立的字节码文件。

b。接口可以有静态方法。

c。接口可以扩展一个或多个接口。

d。接口可以扩展抽象类。

e。抽象类可以扩展接口。

13.9章节习题

13.30 给出下面代码的输出。

Rational r1 = new Rational(-2, 6); 
System.out.println(r1.getNumerator()); 
System.out.println(r1.getDenominator()); 
System.out.println(r1.intValue()); 
System.out.println(r1.doubleValue());
-1
3
0
-0.3333333333333333

13.31 下面的代码错在何处?

// Object类没有compareTo方法
Rational r1 = new Rational(-2, 6); 
Object r2 = new Rational(1, 45); System.out.println(r2.compareTo(r1));

13.32 下面的代码错在何处?

//调用方和参数必须都是Rational类型
Object r1 = new Rational(-2, 6); 
Rational r2 = new Rational(1, 45); System.out.println(r2.compareTo(r1));

13.33 如何使用一行代码,而不使用if语句来简化程序清单13-13中82 - 85行的代码?

public boolean equals(Object o) {
    
    
  return (this.subtract((Rational)(other)))
    .getNumerator() == 0;
}
            
public int compareTo(Rational o) {
    
    
  return this.subtract(o).getNumerator() > 0 ? 1 :
    (this.subtract(o).getNumerator() == 0 ? 0 : 1);
}    

13.34 仔细地跟踪程序的执行,给出下面代码的输出。

Rational r1 = new Rational(1, 2); 
Rational r2 = new Rational(1, -2); System.out.println(r1.add(r2));
0/4

13.10章节习题

13.35 描述类的设计原则。

13.10.1 内聚性

13.10.2 — 致性

13.10.3 封装性

13.10.4 清晰性

13.10.5 完整性

13.10.6 实例和静态

13.10.7 继承与聚合

13.10.8 接口和抽象类

编程练习题

**13.1(三角形类)

设计一个扩展自抽象类GeometricObject的新的Triangle类。绘制Triangle类和GeometricObject类的UML图并实现Triangle类。编写一个测试程序,提示用户输入三角形的三条边、一种颜色以及一个表明该三角形是否填充的布尔值。程序应该根据用户的输入, 使用这些边以及颜色和是否填充的信息,创建一个Triangle对象。程序应该显示面积、周长、 颜色以及真或者假来表明是否被填充。

Test.java

package learning;

import java.util.Scanner;

public class  Test {
    
    
	public static void main(String[] args) {
    
    
		System.out.print("Input 3 sides of a triangle accordingly: ");
		Scanner scanner = new Scanner(System.in);
		double side1 = scanner.nextDouble();
		double side2 = scanner.nextDouble();
		double side3 = scanner.nextDouble();
		System.out.print("Input the color: ");
		String color = scanner.next();
		System.out.print("Input the fill status(true / false): ");
		boolean filled = scanner.nextBoolean();
		Triangle test1 = new Triangle(side1, side2, side3, color, filled);
		System.out.print(test1.toString());
		scanner.close();
	}
} 

GeometricObject.java

package learning;

abstract class GeometricObject {
    
    
	String color;
	boolean filled;
	
	abstract double getPerimeter();
	abstract double getArea();
}

Triangle.java

package learning;

//__________________________UML DIAGRAM_____________________________*
/*																	|
* 						 /GeometricObject/							|
*-------------------------------------------------------------------|
*   color: String													|
*   filled: boolean													|
*-------------------------------------------------------------------|		
*   /getArea()/: double/											|
*   /getPerimeter(): double/										|
*___________________________________________________________________| 
					  		    ^
					   		    ^
							    ^
//______________________________________________________________________________________________|
/*																  								|
* 					                      Triangle							    			    |
* ----------------------------------------------------------------------------------------------|			
*    side1: double         		 								    							|
*    side2: double              																|
* 	 side3: double	      										    							| 	  	    		
* ----------------------------------------------------------------------------------------------|
*	 Triangle()											            							|
*	 Triangle(side1:double, side2:double, side3:double, color:String, filled:boolean)			|		
* 	 getPerimeter(): double																		|
* 	 getArea(): double																			|
*    +toString(): String																		|			
*_______________________________________________________________________________________________|*/

class Triangle extends GeometricObject {
    
    
	double side1;
	double side2;
	double side3;
	
	Triangle() {
    
    
		
	}
	
	Triangle(double side1, double side2, double side3, String color, boolean filled) {
    
    
		this.side1 = side1;
		this.side2 = side2;
		this.side3 = side3;
		this.color = color;
		this.filled = filled;
	}
	
	@Override
	double getPerimeter() {
    
    
		return side1 + side2 + side3;
	}
	
	@Override
	double getArea() {
    
    
		return Math.pow((getPerimeter() * 0.5) * (getPerimeter() - side1) * (getPerimeter() - side2) * (getPerimeter() - side3), 0.5);
	}
	
	@Override
	public String toString() {
    
    
		if (filled == true) {
    
    
			return "The Perimeter is " + getPerimeter() + " and the area is " + getArea() + ".\nThis triangle is " + color + " and filled.";
		}
		return "The Perimeter is " + getPerimeter() + " and the area is " + getArea() + ".\nThis triangle is " + color + " and not filled.";
		}
}

输出结果:

Input 3 sides of a triangle accordingly: 3 4 5
Input the color: blue
Input the fill status(true / false): true
The Perimeter is 12.0 and the area is 54.99090833947008.
This triangle is blue and filled.
*13.2 (打乱ArrayList)

编写以下方法,打乱ArrayList里面保存的数字

public static void shuffle(ArrayList<Number> list)
package learning;

import java.util.ArrayList;

// 代码的大意是,把初始的list数组创建为1-10的顺序数列,再使用temp数组随机生成0-9的乱序数列,用temp的元素作为list的参数,依次传递给afterShuffle数组,所以打乱完成
// 例如,temp的第一个元素是5,那么就把原数组list中的第5号元素,挪动到新数组afterShuffle中,相当于temp数组充当了参数
public class  Test {
    
    
	// 用户可以设置希望打乱多大的数组,本例中设置为10
	private final static int SIZE = 10;
	
	public static void main(String[] args) {
    
    
		// 把1-10按顺序加进初始list数组
		ArrayList<Number> list = new ArrayList<>();
		for (int i = 1; i <= SIZE; i++) {
    
    
			list.add(i);
		}
		System.out.print("The original array is: ");
		System.out.println(list.toString());
		// 执行打乱方法
		shuffle(list);
	}
	
	public static void shuffle(ArrayList<Number> list) {
    
    
		// 创建一个暂时的数组temp,用于储存随机的参数
		ArrayList<Number> temp = new ArrayList<>();
		// 只要这个temp的长度没有超过用户设置的“10”,就执行循环,添加新的随机参数到数组中
		while (temp.size() < SIZE) {
    
    
			// 随机生成一个0 - 10的数字,如果这个数字没有被添加过,就把这个数字添加进去
			// 循环结束后,temp数组由0-9的乱序数列组成
			int index = (int)(Math.random() * SIZE);
			if (!temp.contains(index)) {
    
    
				temp.add(index);
			}
		}
		ArrayList<Number> afterShuffle = new ArrayList<>();
		/* 循环解释:
		 * 假设
		 * list = [1, 2, 3, 4, 5]
		 * temp = [3, 1, 4, 5, 2]
		 * 那么
		 * 把temp[0]元素3拿出来,执行list.get(3),结果是4,把结果赋值给afterShuffle的第一位元素,因此afterShuffle的第一位元素是4
		 * 把temp[1]元素1拿出来,执行list.get(1),结果是2,把结果赋值给afterShuffle的第一位元素,因此afterShuffle的第二位元素是2
		 * ......以此类推
		 */
		for (int i = 0; i < SIZE; i++) {
    
    
			//这里需要类型转换的原因是,内圈temp.get(i)得到的数字类型是Number,而外圈list.get()方法需要一个Integer参数
			afterShuffle.add(list.get((int) temp.get(i)));
		}
		System.out.print("The shuffled array is: ");
		System.out.println(afterShuffle.toString());
	}
} 

输出结果:

The original array is: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The shuffled array is: [9, 5, 2, 3, 8, 1, 4, 6, 7, 10]
*13.3 (排序ArrayList)

编写以下方法,对 ArrayList 里面保存的数字进行排序。

public static void sort(ArrayList<Number> list)
package learning;

import java.util.ArrayList;
import java.util.Arrays;

//使用冒泡排序,在方法内先把ArrayList转化成int[]
public class  Test {
    
    
	public static void main(String[] args) {
    
    
		ArrayList<Number> list = new ArrayList<>();
		list.add(5);
		list.add(9);
		list.add(2);
		list.add(7);
		list.add(3);
		System.out.print("初始排序 :"); 
		System.out.println(list.toString());	
		sort(list);
	}
	
	public static void sort(ArrayList<Number> list) {
    
    
		int[] arr = new int[list.size()];
		for (int i = 0; i < list.size(); i++) {
    
    
			arr[i] = (int)list.get(i);
		}
		for (int i = arr.length - 1; i > 0; i--) {
    
     //外圈决定循环次数:规律是(length-1)次
            for (int j = 0; j < i; j++) {
    
     // 内圈决定交换次数,规律一个length为n的数组只需交换(n-1)次(i次,j初始值为0所以无“=”)即可实现最大值冒泡
                
            	if (arr[j] > arr[j + 1]) {
    
    
                    // 交换
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
    	}
    	    System.out.println("最终排序 :" + Arrays.toString(arr)); //for外只输出最终数组
	}
} 

输出结果:

初始排序 :[5, 9, 2, 7, 3]
最终排序 :[2, 3, 5, 7, 9]
**13.4 (显示日历)

重写程序清单6-12中的PrintCalendar类,使用Calendar和GregorianCalendar类显示一个给定月份的日历。你的程序从命令行得到月份和年份的输入,例如:

java Exercisel3_04 5 2016

也可以不输入年份来运行程序。这种情况下,年份就是当前年份。 如果不指定月份和年份来运行程序,那么就是指当前月份

package learning;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class Test {
    
    
  /** Main method */
  public static void main(String[] args) {
    
    
	  Calendar calendar = new GregorianCalendar();
	  if (args.length == 2) {
    
    
		  printMonth(Integer.parseInt(args[1]), Integer.parseInt(args[0]));
	  }
	  
	  if (args.length == 1) {
    
    
		  printMonth(calendar.get(Calendar.YEAR), Integer.parseInt(args[0]));
	  }
	  
	  if (args.length == 0) {
    
    
		  printMonth(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
	  }
  }

  /** Print the calendar for a month in a year */
  public static void printMonth(int year, int month) {
    
    
    // Print the headings of the calendar
    printMonthTitle(year, month);

    // Print the body of the calendar
    printMonthBody(year, month);
  }

  /** Print the month title, e.g., May, 1999 */
  public static void printMonthTitle(int year, int month) {
    
    
    System.out.println("         " + getMonthName(month)
      + " " + year);
    System.out.println("-----------------------------");
    System.out.println(" Sun Mon Tue Wed Thu Fri Sat");
  }

  /** Get the English name for the month */
  public static String getMonthName(int month) {
    
    
    String monthName = "";
    switch (month) {
    
    
      case 1: monthName = "January"; break;
      case 2: monthName = "February"; break;
      case 3: monthName = "March"; break;
      case 4: monthName = "April"; break;
      case 5: monthName = "May"; break;
      case 6: monthName = "June"; break;
      case 7: monthName = "July"; break;
      case 8: monthName = "August"; break;
      case 9: monthName = "September"; break;
      case 10: monthName = "October"; break;
      case 11: monthName = "November"; break;
      case 12: monthName = "December";
    }

    return monthName;
  }

  /** Print month body */
  public static void printMonthBody(int year, int month) {
    
    
    // Get start day of the week for the first date in the month
    int startDay = getStartDay(year, month);

    // Get number of days in the month
    int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month);

    // Pad space before the first day of the month
    int i = 0;
    for (i = 0; i < startDay; i++)
      System.out.print("    ");

    for (i = 1; i <= numberOfDaysInMonth; i++) {
    
    
      System.out.printf("%4d", i);

      if ((i + startDay) % 7 == 0)
        System.out.println();
    }

    System.out.println();
  }

  /** Get the start day of month/1/year */
  public static int getStartDay(int year, int month) {
    
    
    final int START_DAY_FOR_JAN_1_1800 = 3;
    // Get total number of days from 1/1/1800 to month/1/year
    int totalNumberOfDays = getTotalNumberOfDays(year, month);

    // Return the start day for month/1/year
    return (totalNumberOfDays + START_DAY_FOR_JAN_1_1800) % 7;
  }

  /** Get the total number of days since January 1, 1800 */
  public static int getTotalNumberOfDays(int year, int month) {
    
    
    int total = 0;

    // Get the total days from 1800 to 1/1/year
    for (int i = 1800; i < year; i++)
      if (isLeapYear(i))
        total = total + 366;
      else
        total = total + 365;

    // Add days from Jan to the month prior to the calendar month
    for (int i = 1; i < month; i++)
      total = total + getNumberOfDaysInMonth(year, i);

    return total;
  }

  /** Get the number of days in a month */
  public static int getNumberOfDaysInMonth(int year, int month) {
    
    
    if (month == 1 || month == 3 || month == 5 || month == 7 ||
      month == 8 || month == 10 || month == 12)
      return 31;

    if (month == 4 || month == 6 || month == 9 || month == 11)
      return 30;

    if (month == 2) return isLeapYear(year) ? 29 : 28;

    return 0; // If month is incorrect
  }

  /** Determine if it is a leap year */
  public static boolean isLeapYear(int year) {
    
    
    return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
  }
}

输出结果:

// 不指定年月
java /Users/kevinwang/eclipse-workspace/test/src/learning/Test.java
         May 2023
-----------------------------
 Sun Mon Tue Wed Thu Fri Sat
       1   2   3   4   5   6
   7   8   9  10  11  12  13
  14  15  16  17  18  19  20
  21  22  23  24  25  26  27
  28  29  30  31
*13.5 (将 GeometricObject 类变成可比较的)

修改 GeometricObject 类以实现 Comparable 接口,并且在 GeometricObject 类中定义一个静态的求两个 GeometricObject 对象中较大者的 max 方法。画出 UML 图并实现这个新的 GeometricObject 类。编写一个测试程序,使用 max 方法求两个圆中的较大者和两个矩形中的较大者。

Test.java

package learning;

public class Test {
    
    
	public static void main(String[] args) {
    
    
		GeometricObject circle1 = new Circle(1);
		GeometricObject circle2 = new Circle(10);
		// max方法会利用多态,将声明为GeometricObject类型的变量匹配到Circle类型的方法
		GeometricObject.max(circle1, circle2);
		GeometricObject rec1 = new Rectangle(3, 5);
		GeometricObject rec2 = new Rectangle(10, 20);
		// max方法会利用多态,将声明为GeometricObject类型的变量匹配到Rectangle类型的方法
		GeometricObject.max(rec1, rec2);
	}
}

GeometricObject.java

package learning;

//__________________________UML DIAGRAM_____________________________*
/*																	|
* 						 /GeometricObject/							|
*-------------------------------------------------------------------|
*	color: String													|
* 	filled: boolean													|
*-------------------------------------------------------------------|
* 	/getArea: double/												|
* 	+compareTo(o: GeometricObject): int								|
* 	+___max(obj1: GeometricObject, obj2: GeometricObject): void___	|
* 	+toString(): String												|
* __________________________________________________________________| */

abstract class GeometricObject implements Comparable<GeometricObject>{
    
    
	String color;
	boolean filled;
	
	abstract double getArea();

	// Comparable接口实现了compareTo方法,实现这个接口的抽象类GeometricObject需要覆写,如果当前抽象类没有覆写,继承抽象类的常规子类就必须覆写
	@Override
	public int compareTo(GeometricObject o) {
    
    
		if (getArea() > o.getArea())
			return 1;
		else if (getArea() < o.getArea())
			return -1;
		else	
			return 0;
	}
	
	// max方法会先调用上面的compareTo方法,得到返回值,根据返回值,调用toString方法进行输出
	public static void max(GeometricObject obj1, GeometricObject obj2) {
    
    
		if (obj1.compareTo(obj2) == 1)
			System.out.println(obj1.toString());
		else if (obj1.compareTo(obj2) == -1)
			System.out.println(obj2.toString());
		else
			System.out.println("Equal");
	}
	
	// 需要注意的是,如果GeometricObject类,Circle类和Rectangle类都覆写了toString,那么调用哪个toString,会根据多态,取决于变量的实际类型
	// 如果Circle类和Rectangle类没有覆写toString,那么就会调用GeometricObject类的toString
	// 例如,GeometricObject类,Circle类和Rectangle类都覆写了toString,那么下面的toString就不会被调用
	@Override
	public String toString() {
    
    
		return "I will not be invoked";
	}
}

Circle.java

package learning;

class Circle extends GeometricObject{
    
    
	double radius;
	
	Circle(double radius) {
    
    
		this.radius = radius;
	}

	// GeometricObject实现了抽象方法getArea,子类必须强制调用
	@Override
	double getArea() {
    
    
		return Math.PI * radius * radius;
	}
	
	@Override
	public String toString() {
    
    
		return "The " + radius + " radius object is larger";
	}
}

Rectangle.java

package learning;

class Rectangle extends GeometricObject{
    
    
	double width;
	double height;
	
	Rectangle(double width, double height) {
    
    
		this.width = width;
		this.height = height;
	}

	// GeometricObject实现了抽象方法getArea,子类必须强制调用
	@Override
	double getArea() {
    
    
		return Math.PI * width * height;
	}	
	
	@Override
	public String toString() {
    
    
		return "The " + width + " width and " + height + " height object is larger";
	}
}

输出结果:

The 10.0 radius object is larger
The 10.0 width and 20.0 height object is larger
*13.6 (ComparableCircle类)

创建名为 ComparableCircle 的类,它继承自 Circle 类,并实现 Comparable 接口。画出 UML 图并实现 compareTo 方法,使其根据面积比较两个圆。编写一个测试程序求出 ComparableCircle 对象的两个实例中的较大者。

Test.java

package learning;

//___________________________UML DIAGRAM____________________________|
/*																	|
* 						         Circle							    |
*-------------------------------------------------------------------|
*    radius: double													|
*-------------------------------------------------------------------|
* 	 Circle()														|	
* 	 Circle(radius: double)											|
* 	 getArea(): double												|
* __________________________________________________________________| */
/*							  ^
					   		  ^
					  		  ^
//_____________________UML DIAGRAM_________________________________|
/*													   		  	   |
* 					  ComparableCircle    					       |
*------------------------------------------------------------------|                    //_______________________________________________|
*    radius: double						    			           |                    *	             <<interface>>                   *
*    ComparableCircle()	   										   |<<<<<<<<<<<<<<<<<<<<* 	               Comparable                    |
* 	 ComparableCircle(radius: double)				     		   |                    *      +compareTo(circle: ComparableCircle): int |
*    getArea(): double											   |					//_______________________________________________|
*    +compareTo(o: comparableCircle): int						   |
*__________________________________________________________________| */                    


		
public class Test {
    
    
	public static void main(String[] args) {
    
    
		ComparableCircle test1 = new ComparableCircle(5);
		ComparableCircle test2 = new ComparableCircle(10);
		System.out.print(test1.compareTo(test2));
	}
}

Circle.java

package learning;

class Circle {
    
    
	double radius;
	
	Circle() {
    
    
		radius = 1;
	}
	
	Circle(double radius) {
    
    
		this.radius = radius;
	}

	double getArea() {
    
    
		return Math.PI * radius * radius;
	}
}

ComparableCircle.java

package learning;

class ComparableCircle extends Circle implements Comparable<ComparableCircle> {
    
    
	double radius;
	
	ComparableCircle() {
    
    
		super();
	}
	
	ComparableCircle(double radius) {
    
    
		this.radius = radius;
	}
	
	@Override
	double getArea() {
    
    
		return Math.PI * radius * radius;
	}
	
	@Override
	public int compareTo(ComparableCircle o) {
    
    
		if (getArea() > o.getArea())
			return 1;
		else if (getArea() < o.getArea())
			return -1;
		else
			return 0;
	}
}

输出结果:

-1
*13.7 (可着色接口Colorable)

设计一个名为 Colorable 的接口,其中有名为 howToColor() 的 void 方法。可着色对象的每个类必须实现 Colorable 接口。设计一个名为 Square 的类,继承自 CeometricObject 类并实现 Colorable 接口。实现 howToColor 方法,显示一个消息 Color all four sides(给所有的四条边着色)。

画出包含 Colorable、Square 和 CeometricObject 的 UML 图。编写一个测试程序,创建有五个 GeometricObject 对象的数组。对于数组中的每个对象而言,如果对象是可着色的, 那就调用 howToColor 方法。

Test.java

package learning;

//___________________________UML DIAGRAM____________________________|
/*																	|
* 						    /GeometricObject/						|
*-------------------------------------------------------------------|
*    color: String													|
*    filled: boolean												|
*-------------------------------------------------------------------|
* 	 GeometricObject()												|	
* __________________________________________________________________| */
/*							  ^
					   		  ^
					  		  ^
//_____________________UML DIAGRAM_________________________________|
/*													   		  	   |
* 					       Square    					       	   |
*------------------------------------------------------------------|                    
*    sides: double						    			           | 					//_______________________________________________|
*------------------------------------------------------------------|                    *	             <<interface>>                   *
*    Square()	   										   		   |<<<<<<<<<<<<<<<<<<<<* 	               Colorable                     |
* 	 Squaree(sides: double)				     		   			   |                    *                howToColor(): void	         	 |		
*    +howToColor(): void						   				   |					//_______________________________________________|
*__________________________________________________________________| */                    


		
public class Test {
    
    
	public static void main(String[] args) {
    
    
		Square square = new Square(5);
		square.howToColor();
	}
}

GeometricObject.java

package learning;

abstract class GeometricObject {
    
    
	String color;
	boolean filled;
	
	GeometricObject() {
    
    
	}
}

Square.java

package learning;

class Square extends GeometricObject implements Colorable {
    
    
	double sides;
	
	Square() {
    
    
	}
	
	Square(double sides){
    
    
		this.sides = sides;
	}
	
	@Override
	public void howToColor() {
    
    
		System.out.println("Color all four sides");
	}
}

Colorable.java

package learning;

interface Colorable {
    
    
	void howToColor();
}
*13.8 (修改MyStack类)

重写程序清单11-10中的MyStack类, 执行list域的深度复制

Test.Java

package learning;

public class Test {
    
    

    public static void main(String[] args) throws CloneNotSupportedException {
    
    

        MyStack start = new MyStack();

        start.push(new String("One"));
        start.push(new String("Two"));
        start.push(new String("Three"));

        // 返回的stackCloned返回声明类型是Object,但是实际类型是MyStack,所以可以进行转换
        MyStack cloned = (MyStack) start.clone();
        // 通过==判断这两个对象其实是不共享内存的,后续的追加数据也是独立的
        System.out.println(cloned == start);
        start.push(new String("Four"));
        System.out.println("Cloned " + cloned.toString());
        System.out.println("Original " + start.toString());
    }

}

MyStack.java

package learning;

import java.util.ArrayList;

public class MyStack implements Cloneable {
    
    

    private ArrayList<Object> list = new ArrayList<Object>();

    public int getSize() {
    
    
        return list.size();
    }

    public Object peek() {
    
    
        return list.get(getSize() - 1);
    }

    public Object pop() {
    
    
        Object obj = list.get(getSize() - 1);
        list.remove(getSize() - 1);
        return obj;
    }

    public void push(Object obj) {
    
    
        list.add(obj);

    }

    public int search(Object obj) {
    
    

        return list.indexOf(obj);

    }

    @Override
    public Object clone() throws CloneNotSupportedException {
    
    
    	// 深拷贝的逻辑是,创建一个新的内存空间,储存新对象,新旧对象互不干涉,所以就需要新建一个实例变量,用来储存新的对象
    	// 创建一个新的实例变量stackCloned,先对start进行浅拷贝,将浅拷贝的结果储存进stackCloned
    	// 其中,super.clone()相当于start.clone(),调用Object类的clone()方法进行克隆,start的实际类型是MyStack,所以转型可以成功
    	MyStack stackCloned = (MyStack)super.clone();
    	// 调用deepClone()方法,将深拷贝储存到stackCloned中,在这里我们需要深拷贝的是list字段,所以deepClone()方法针对list字段进行深拷贝
    	stackCloned.list = deepClone();
    	// 返回深拷贝后的stackCloned
    	// 由于方法返回值类型是Object,所以可以返回任意子类类型
    	// 但是需要注意的是,返回的stackCloned还是一个Object类型(实际类型是MyStack),后续如果需要,就要类型转换
    	return stackCloned;
    }
    
    // 由于stackCloned.list = deepClone()语句中,我们是想要深拷贝list,而list的类型是ArrayList<Object>,所以下面方法的返回值也应该是ArrayList<Object>
    // 相当于在stackCloned对象中,储存了一个ArrayList<Object>字段
    ArrayList<Object> deepClone() {
    
    
    	// 方法的逻辑是,新建一个ArrayList<Object>数组,一个一个元素复制对象的list字段,最后返回这个新建的数组
    	ArrayList<Object> temp = new ArrayList<>();
    	for (int i = 0; i < list.size(); i++) 
    		temp.add(list.get(i));
    	return temp;
    }

    @Override
    public String toString() {
    
    
        return "Stack: " + list.toString();

    }
}

输出结果:

false
Cloned Stack: [One, Two, Three]
Original Stack: [One, Two, Three, Four]
*13.9 (将 Circle 类改成可比较的)

改写程序清单13-2中的 Circle 类,它继承自 GeometricObject 类并实现 Comparable 接口。覆盖 Object 类中的 equals 方法。当两个 Circle 对象半径相等时,则这两个 Circle 对象是相同的。画出包括 Circle 、GeometricObject 和 Comparable 的 UML图。

Test.java

package learning;

//___________________________UML DIAGRAM____________________________|
/*																	|
* 						    /GeometricObject/						|
*-------------------------------------------------------------------|
*    color: String													|
*    filled: boolean												|
*-------------------------------------------------------------------|
* 	 GeometricObject()												|	
* __________________________________________________________________| */
/*							  ^
					   		  ^
					  		  ^
//_____________________UML DIAGRAM_________________________________|
/*													   		  	   |
* 					       Circle    					       	   |
*------------------------------------------------------------------|                    
*    -radius: double						    			       | 					//_______________________________________________|
*------------------------------------------------------------------|                    *	             <<interface>>                   *
*    +Circle()	   										   		   |<<<<<<<<<<<<<<<<<<<<* 	               Comparable                    |
* 	 +Circle(radius: double)				     		   		   |                    *                +compareTo(other T): int	     |		
*    +getArea(): double						   				       |					//_______________________________________________|
*	 +compareTo(c: Circle): int									   |
*	 +equals(ob: Object): boolean								   |
*__________________________________________________________________| */                    



public class Test {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
    	Circle c1 = new Circle(5);
    	Circle c2 = new Circle(5);
    	System.out.println(c1.equals(c2));
    	System.out.println(c1.compareTo(c2));
    }
}

Circle.java

package learning;

// 通常情况下,Comparable接口的范型参数应该是当前的类名,以指定要比较的对象是当前类的实例,但是其实类型可以是当前类的任意子类或父类
// Comparable接口的范型参数应该和compareTo方法的范型参数保持一致
public class Circle extends GeometricObject implements Comparable<Circle> {
    
    
  private double radius;

  public Circle() {
    
    
  }

  public Circle(double radius) {
    
    
    this.radius = radius;
  }

  public double getArea() {
    
    
    return radius * radius * Math.PI;
  }

  @Override
  public int compareTo(Circle c) {
    
    
	  if (getArea() > c.getArea())
		  return 1;
	  else if (getArea() < c.getArea())
		  return -1;
	  else 
		  return 0;
  }
  
  @Override
  public boolean equals(Object ob) {
    
    
	  Circle circle = (Circle) ob;
	    return this.radius == circle.radius;
	}
}

GeometricObject.java

package learning;

abstract class GeometricObject {
    
    
	String color;
	boolean filled;
	
	GeometricObject() {
    
    
	}
}

输出结果:

true
0
*13.10(将Rectangle类变成可比较的)

改写程序清单13-3的Rectangle类,它继承自GeometricObject类并实现Comparable接口。覆盖Object类中的equals方法。当两个Rectangle对象面积相同时,则这两个对象是相同的。画出包括Rectangle,GeometricObject和Comparable的UML图

Test.java

package learning;

//___________________________UML DIAGRAM____________________________|
/*																	|
* 						    /GeometricObject/						|
*-------------------------------------------------------------------|
*    color: String													|
*    filled: boolean												|
*-------------------------------------------------------------------|
* 	 GeometricObject()												|	
* __________________________________________________________________| */
/*							  ^
					   		  ^
					  		  ^
//_____________________UML DIAGRAM_________________________________|
/*													   		  	   |
* 					     Rectangle    					       	   |
*------------------------------------------------------------------|                    
*    -width: double			-height: double						   | 					//_______________________________________________|
*------------------------------------------------------------------|                    *	             <<interface>>                   *
*    +Rectangle()	   										   	   |<<<<<<<<<<<<<<<<<<<<* 	               Comparable                    |
* 	 +Rectangle(height: double, width: double)				       |                    *                +compareTo(other T): int	     |		
*    +getArea(): double						   				       |					//_______________________________________________|
*	 +compareTo(obj: Rectangle): int						       |
*	 +equals(ob: Object): boolean								   |
*__________________________________________________________________| */                    



public class Test {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
    	Rectangle c1 = new Rectangle(5, 10);
    	Rectangle c2 = new Rectangle(5, 10);
    	System.out.println(c1.equals(c2));
    	System.out.println(c1.compareTo(c2));
    }
}

GeometricObject.java

package learning;

abstract class GeometricObject {
    
    
	String color;
	boolean filled;
	
	GeometricObject() {
    
    
	}
}

Rectangle.java

package learning;

public class Rectangle extends GeometricObject implements Comparable<Rectangle> {
    
    

    private double width;
    private double height;

    public Rectangle() {
    
    
    }

    public Rectangle(double width, double height) {
    
    
        this.width = width;
        this.height = height;
    }

    public double getArea() {
    
    
        return width * height;
    }

    @Override
    public int compareTo(Rectangle obj) {
    
    
        if (this.getArea() > obj.getArea()) {
    
    
            return 1;
        } else if (this.getArea() < obj.getArea()) {
    
    
            return -1;
        } else
            return 0;

    }

    @Override
    public boolean equals(Object ob) {
    
    
        Rectangle r = (Rectangle) ob;
        double thisArea = this.getArea();
        double rArea = r.getArea();
        // 等号的左边必须是变量,因此前面新声明了一个变量表示面积
        return thisArea == rArea;

    }
}
*13.11 (八边形类Octagon)

编写一个名为Octagon的类,它继承自GeometricObject类并实现Comparable和Cloneable接口。假设八边形八条边的边长都相等。它的面积可以使用下面的公式计算:

面积 = ( 2 + 4 / √ 2 ) × 边长 × 边长 面积=(2+4/√2)×边长×边长 面积=(2+4/√2)×边长×边长

画出包括Octagon、GeometricObject、Comparable和Cloneable的UML图。编写一个测试程序,创建一个边长值为5的Octagon对象,然后显示它的面积和周长。使用clone方法创建一个新对象,并使用compareTo方法比较这两个对象。

Test.java

package learning;

										//___________________________UML DIAGRAM____________________________|
										/*																	|
										 * 						    /GeometricObject/						|
										 *------------------------------------------------------------------|
										 *    color: String													|
										 *    filled: boolean												|
										 *------------------------------------------------------------------|
										 * 	 GeometricObject()												|
										 * 	 /#getPerimeter(): double/										|
										 * 	 /#getArea(): double/											|
										 * _________________________________________________________________| */
										/*					                ^
           				      												^
           			    	  												^
           					  												^
           											/*_________________UML Diagram__________________*
            										*												*
            										*                   Octagon  	        	    *
            										*-----------------------------------------------*
            										* 							  			 		*
            										*   side: double 								*
            										*-----------------------------------------------*  			
_____________________________________           	* 	Octagon()									*			     _____________________________________
*	        <<interface>>           *           	*   Octagon(side:double)				  		*			    *	        <<interface>>            *
*      java.lang.Cloneable          *           	*                          						* 			    * 	java.lang.Comparable<Octagon>    *
*___________________________________*   <<<<<<<<< 	*  												*	>>>>>>>>>   *____________________________________*
*	    #clone():Object             *           	* 	#getArea(): double							*			    *	+compareTo(obj: T):int           *
*___________________________________*           	* 	#getPerimeter(): double						*			    *____________________________________*
            										* 	#clone(): Object                     		*
            										*  	+compareTo(o: Octagon): int			        *	
            										* ______________________________________________* */
            										

public class Test {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
    	Octagon o1 = new Octagon(5);
    	System.out.println(o1.getPerimeter());
    	System.out.println(o1.getArea());
    	Octagon o2 = (Octagon)o1.clone();
    	System.out.print(o1.compareTo(o2));
    }
}

GeometricObject.java

package learning;

abstract class GeometricObject{
    
    
	String color;
	boolean filled;
	
	GeometricObject() {
    
    
	}
	
	protected abstract double getPerimeter();
	protected abstract double getArea();
}

Octagon.java

package learning;

class Octagon extends GeometricObject implements Cloneable, Comparable<Octagon>{
    
    
	private double side;
	
	Octagon() {
    
    
		this(1.0);
	}
	
	Octagon(double side) {
    
    
		this.side = side;
	}
	
	@Override
	protected double getPerimeter() {
    
    
		return side * 8;
	}
	
	@Override
	protected double getArea() {
    
    
		return (2 + 4 / Math.pow(2, 0.5) * side * side);
	}
	
	@Override
	public int compareTo(Octagon o) {
    
    
		if (getArea() > o.getArea())
			return 1;
		else if (getArea() < o.getArea())
			return -1;	
		else 
			return 0;
	}
	
	@Override
	// 在覆写中,子类必须要选择可见性更高的修饰符,部分关键字不需要覆写(如abstract,native),父类方法声明的异常也不需要覆写
	protected Object clone() throws CloneNotSupportedException {
    
    
		// Object类中的clone方法是经过关键字native由虚拟机完成的,无法在Java源代码直接访问到
		return super.clone();
	}
}

输出结果:

40.0
72.71067811865474
0
*13.12(求几何对象的面积之和)

编写一个方法,求数组中所有几何对象的面积之和。方法签名如下:

public static double sumArea(Geometricobject[] a)

编写测试程序,创建四个对象(两个圆和两个矩形)的数组,然后使用sumArea方法求它们的总面积。

Test.java

package learning;

										//___________________________UML DIAGRAM____________________________|
										/*																	|
										 * 						    /GeometricObject/						|
										 *------------------------------------------------------------------|
										 *    color: String													|
										 *    filled: boolean												|
										 *------------------------------------------------------------------|
										 * 	 GeometricObject()												|
										 * 	 /#getArea(): double/											|
										 * _________________________________________________________________| 
														                    ^
           				      												^
           			    	  												^
           							^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^	  		
           					  		^																			  ^
           					  		^																		      ^
           /*_________________UML Diagram___________________*							  /*_________________UML Diagram____________________*
            *												*							   *												*
            *                  Rectangle  	        	    *                              *                   Circle  	        	        *
            *-----------------------------------------------*                              *------------------------------------------------*
            * 	-width: double						  		*							   *    -radius: double								*
            *   -height: double 							*							   *------------------------------------------------*
            *-----------------------------------------------*  							   *    Circle()     								*
            *   Rectangle()									*							   *    Circle(radius: double)						*
            *   Rectangle(width: double, height: double)	*							   *    #getArea(): double						    *
            *   #getArea(): double							*							   *												*
            * ______________________________________________*    						   * _______________________________________________* */
            										

public class Test {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
    	GeometricObject r1 = new Rectangle(3, 4);
    	GeometricObject r2 = new Rectangle(4, 5);
    	GeometricObject c1 = new Circle(3);
    	GeometricObject c2 = new Circle(4);
    	GeometricObject[] go = new GeometricObject[4];
    	go[0] = r1;
    	go[1] = r2;
    	go[2] = c1;
    	go[3] = c2;
    	System.out.print(sumArea(go));
    }
    
    public static double sumArea(GeometricObject[] a) {
    
    
    	double sum = 0;
    	for (int i = 0; i < a.length; i++) {
    
    
    		double area = a[i].getArea();
    		sum = sum + area;
    	}
    	return sum;
    }
}

GeometricObject.java

package learning;

abstract class GeometricObject{
    
    
	String color;
	boolean filled;
	
	GeometricObject() {
    
    
	}
	
	protected abstract double getArea();
}

Circle.java

package learning;

class Circle extends GeometricObject {
    
    
	private double radius;
	
	Circle() {
    
    
		this(1);
	}
	
	Circle(double radius) {
    
    
		this.radius = radius;
	}
	
	@Override
	protected double getArea() {
    
    
		return Math.PI * radius * radius;
	}
	
}

Rectangle.java

package learning;

class Rectangle extends GeometricObject {
    
    
	private double width;
	private double height;
	
	Rectangle() {
    
    
		this(1, 1);
	}
	
	Rectangle(double width, double height) {
    
    
		this.width = width;
		this.height = height;
	}
	
	@Override
	protected double getArea() {
    
    
		return width * height;
	}
	
}

输出结果:

110.53981633974483
*13.13 (使得 Course 类可复制)

重写程序淸单10-6中的 Course 类,增加一个 clone 方法,执行students 域上的深度复制。

Test.java

package learning;									

public class Test {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
    	Course c1 = new Course("English");
    	c1.addStudent("A");
    	c1.addStudent("B");
    	c1.addStudent("C");
    	c1.addStudent("D");
    	Course c2 = (Course)c1.clone();
    	System.out.println("c1 == c2 is " + (c1.getStudents() == c2.getStudents()));
    }
}

Course.java

package learning;

public class Course implements Cloneable {
    
    
  private String courseName;
  private String[] students = new String[4];
  private int numberOfStudents;
    
  public Course(String courseName) {
    
    
    this.courseName = courseName;
  }
  
  public void addStudent(String student) {
    
    
    students[numberOfStudents] = student;
    numberOfStudents++;
  }
  
  public String[] getStudents() {
    
    
    return students;
  }

  public int getNumberOfStudents() {
    
    
    return numberOfStudents;
  }  

  public String getCourseName() {
    
    
    return courseName;
  }  
  
  @Override
  protected Object clone() throws CloneNotSupportedException{
    
    
	  Course studentCloned = (Course)super.clone();
	  studentCloned.students = deepCloned();
	  return studentCloned;
  }
  
  protected String[] deepCloned() {
    
    
	  String[] temp = new String[4];
	  for (int i = 0; i < students.length; i++) {
    
    
		  temp[i] = students[i];
	  }
	  return temp;
  }
}

输出结果:

c1 == c2 is false
*13.14(演示封装的好处)

使用新的分子分母的内部表达改写13.13节中的Rational类。创建有两个整数的数组,如下所示:

private long[] r = new 1ong[2]:

使用r[0]表示分子,使用r[1]表示分母。在Rational类中的方法签名没有改变,因此,无须重新编译,前一个Rational类的客户端应用程序可以继续使用这个新的Rational类。

// 替换字段处即可
private long[] r = new long[2];
private long numerator = r[0];
private long denominator = r[1];
13.15(在 Rational 类中使用 Biglnteger)

使用 Biglnteger 表示分子和分母,重新设计和实现

package learning;

import java.math.BigInteger;

public class Rational extends Number implements Comparable<Rational> {
    
    
    // Data fields for numerator and denominator
    private BigInteger numerator = BigInteger.ZERO;
    private BigInteger denominator = BigInteger.ONE;

    public Rational() {
    
    
        this(0, 1);
    }

    public Rational(long numerator) {
    
    
        this(numerator, 1);
    }

    public Rational(long numerator, long denominator) {
    
    
        BigInteger gcd = gcd(numerator, denominator);
        this.numerator = BigInteger.valueOf(((denominator > 0) ? 1 : -1) * numerator).divide(gcd);
        this.denominator = BigInteger.valueOf(Math.abs(denominator)).divide(gcd);
    }

    private static BigInteger gcd(long n, long d) {
    
    
        BigInteger b1 = BigInteger.valueOf(n);
        BigInteger b2 = BigInteger.valueOf(d);
        return b1.gcd(b2);
    }

    public long getNumerator() {
    
    
        return numerator.longValue();
    }

    public long getDenominator() {
    
    
        return denominator.longValue();
    }

    public Rational add(Rational secondRational) {
    
    
        long n = numerator.longValue() * secondRational.getDenominator() +
                denominator.longValue() * secondRational.getNumerator();
        long d = denominator.longValue() * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational subtract(Rational secondRational) {
    
    
        long n = numerator.longValue() * secondRational.getDenominator()
                - denominator.longValue() * secondRational.getNumerator();
        long d = denominator.longValue() * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational multiply(Rational secondRational) {
    
    
        long n = numerator.longValue() * secondRational.getNumerator();
        long d = denominator.longValue() * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational divide(Rational secondRational) {
    
    
        long n = numerator.longValue() * secondRational.getDenominator();
        long d = denominator.longValue() * secondRational.numerator.longValue();
        return new Rational(n, d);
    }

    @Override
    public String toString() {
    
    
        if (denominator.intValue() == 1)
            return numerator + "";
        else
            return numerator + "/" + denominator;
    }

    @Override 
    public boolean equals(Object other) {
    
    
        if ((this.subtract((Rational) (other))).getNumerator() == 0)
            return true;
        else
            return false;
    }

    @Override 
    public int intValue() {
    
    
        return (int) doubleValue();
    }

    @Override 
    public float floatValue() {
    
    
        return (float) doubleValue();
    }

    @Override
    public double doubleValue() {
    
    
        return numerator.longValue() * 1.0 / denominator.longValue();
    }

    @Override 
    public long longValue() {
    
    
        return (long) doubleValue();
    }

    @Override 
    public int compareTo(Rational o) {
    
    
        if (this.subtract(o).getNumerator() > 0)
            return 1;
        else if (this.subtract(o).getNumerator() < 0)
            return -1;
        else
            return 0;
    }
}
*13.16(创建一个有理数的计算器)

编写一个类似于程序清单7-9的程序。这里不使用整数,而是使用有理数,如图13-10a所示。需要使用在10.10.3节中介绍的String类中的split方法来获取分子字符串和分母字符串,并使用Integer.parseInt方法将字符串转换为整数

Test.java

package learning;									

public class Test {
    
    
    public static void main(String[] args) {
    
    
    	
      String num1 = "1/3";
      String opera = "*";
      String num2 = "1/3";
      String[] sepNum1 = new String[2];
      String[] sepNum2 = new String[2];
      sepNum1 = num1.split("/");
  	  sepNum2 = num2.split("/");
  	  long numerator1 = Integer.parseInt(sepNum1[0]);
  	  long denominator1 = Integer.parseInt(sepNum1[1]);
  	  long numerator2 = Integer.parseInt(sepNum2[0]);
  	  long denominator2 = Integer.parseInt(sepNum2[1]);

      switch(opera) {
    
    
         case "+":
         	System.out.println(new Rational(numerator1, denominator1).add(new Rational(numerator2, denominator2)));
         	break;
         case "-":
         	System.out.println(new Rational(numerator1, denominator1).subtract(new Rational(numerator2, denominator2)));
         	break;
         case "*":
         	System.out.println(new Rational(numerator1, denominator1).multiply(new Rational(numerator2, denominator2)));
         	break;
         case "/":
         	System.out.println(new Rational(numerator1, denominator1).divide(new Rational(numerator2, denominator2)));
         }
    }
}

Rational.java

package learning;

public class Rational extends Number implements Comparable<Rational> {
    
    
	  // Data fields for numerator and denominator
	  private long numerator = 0;
	  private long denominator = 1;

	  /** Construct a rational with default properties */
	  public Rational() {
    
    
	    this(0, 1);
	  }

	  /** Construct a rational with specified numerator and denominator */
	  public Rational(long numerator, long denominator) {
    
    
	    long gcd = gcd(numerator, denominator);
	    this.numerator = (denominator > 0 ? 1 : -1) * numerator / gcd;
	    this.denominator = Math.abs(denominator) / gcd;
	  }

	  /** Find GCD of two numbers */
	  private static long gcd(long n, long d) {
    
    
	    long n1 = Math.abs(n);
	    long n2 = Math.abs(d);
	    int gcd = 1;
	    
	    for (int k = 1; k <= n1 && k <= n2; k++) {
    
    
	      if (n1 % k == 0 && n2 % k == 0) 
	        gcd = k;
	    }

	    return gcd;
	  }

	  /** Return numerator */
	  public long getNumerator() {
    
    
	    return numerator;
	  }

	  /** Return denominator */
	  public long getDenominator() {
    
    
	    return denominator;
	  }

	  /** Add a rational number to this rational */
	  public Rational add(Rational secondRational) {
    
    
	    long n = numerator * secondRational.getDenominator() +
	      denominator * secondRational.getNumerator();
	    long d = denominator * secondRational.getDenominator();
	    return new Rational(n, d);
	  }

	  /** Subtract a rational number from this rational */
	  public Rational subtract(Rational secondRational) {
    
    
	    long n = numerator * secondRational.getDenominator()
	      - denominator * secondRational.getNumerator();
	    long d = denominator * secondRational.getDenominator();
	    return new Rational(n, d);
	  }

	  /** Multiply a rational number to this rational */
	  public Rational multiply(Rational secondRational) {
    
    
	    long n = numerator * secondRational.getNumerator();
	    long d = denominator * secondRational.getDenominator();
	    return new Rational(n, d);
	  }

	  /** Divide a rational number from this rational */
	  public Rational divide(Rational secondRational) {
    
    
	    long n = numerator * secondRational.getDenominator();
	    long d = denominator * secondRational.numerator;
	    return new Rational(n, d);
	  }

	  @Override  
	  public String toString() {
    
    
	    if (denominator == 1)
	      return numerator + "";
	    else
	      return numerator + "/" + denominator;
	  }

	  @Override // Override the equals method in the Object class 
	  public boolean equals(Object other) {
    
    
	    if ((this.subtract((Rational)(other))).getNumerator() == 0)
	      return true;
	    else
	      return false;
	  }

	  @Override // Implement the abstract intValue method in Number 
	  public int intValue() {
    
    
	    return (int)doubleValue();
	  }

	  @Override // Implement the abstract floatValue method in Number 
	  public float floatValue() {
    
    
	    return (float)doubleValue();
	  }

	  @Override // Implement the doubleValue method in Number 
	  public double doubleValue() {
    
    
	    return numerator * 1.0 / denominator;
	  }

	  @Override // Implement the abstract longValue method in Number
	  public long longValue() {
    
    
	    return (long)doubleValue();
	  }

	  @Override // Implement the compareTo method in Comparable
	  public int compareTo(Rational o) {
    
    
	    if (this.subtract(o).getNumerator() > 0)
	      return 1;
	    else if (this.subtract(o).getNumerator() < 0)
	      return -1;
	    else
	      return 0;
	  }
	}

输出结果:

1/9
*13.17(數学: Complex类)

一个复数是一个形式为a+bi加的数,这里的a和b都是实数,i是 − 1 \sqrt{-1} 1 的平方根。数字a和b分别称为复数的实部和虚部。可以使用下面的公式完成复数的加、减、乘、除:

a + bi + c + di = (a + c) + (b + d)i
a + bi - (c + di) = (a - c) + (b - d)i
(a + bi)*(c + di) = (ac - bd) + (bc + ad)i
(a + bi)/(c + di) = (ac + bd)/(c2 + d2) + (bc - ad)i/(c^2 + d^2)

还可以使用下面的公式得到复数的绝对值:

∣ a + b i ∣ = a 2 + b 2 |a + bi| = \sqrt{a^2 + b^2} a+bi=a2+b2

(复数可以解释为一个平面上的点,将(a,b)值作为该点的坐标。复数的绝对值是该点到原点的距离,如图13-10b所示。)

设计一个名为Complex的复数来表示复数以及完成复数运算的add、substract、 multiply、divide和abs方法,并且覆盖toString方法以返回一个表示复数的字符串。 方法toString返回字符串a+bi。如果b是0,那么它只返回a。Complex类应该也实现Cloneable接口。

提供三个构造方法Complex(a,b)、Complex(a)和Complex()。Complex()创建数字0的Complex对象,而Complex(a)创建一个b为0的Complex对象。还提供getRealPart() 和getImaginaryPart()方法以分别返回复数的实部和虚部。

编写一个测试程序,提示用户输人两个复数,然后显示它们做加、减、乘、除之后的结果。下面是一个运行示例:

Test.java

package learning;									

public class Test {
    
    
    public static void main(String[] args) {
    
    
    	Complex c1 = new Complex(3.5, 5.5);
    	Complex c2 = new Complex(-3.5, 1);
    	System.out.println(c1.add(c2));
    	System.out.println(c1.sub(c2));
    	System.out.println(c1.mul(c2));
    	System.out.println(c1.div(c2));
    	System.out.println(c1.abs());
    }
}

Complex.java

package learning;

class Complex {
    
    
	  public double a;
	  public double b;
	  
	  //会依次调用之后的两个构造方法,注意如果没有传入值,默认0.0
	  public Complex() {
    
    
	        this(0);
	  }

	  public Complex(double a) {
    
    
	        this(a, 0);
	  }

	  public Complex(double a, double b) {
    
    
	        this.a = a;
	        this.b = b;
	  }
	  
	  public Complex add(Complex c2) {
    
    
		  double c = c2.a;
		  double d = c2.b;
		  double complex1 = a + c;
		  double complex2 = b + d;
		  return new Complex(complex1, complex2);
	  }
	  
	  public Complex sub(Complex c2) {
    
    
		  double c = c2.a;
		  double d = c2.b;
		  double complex1 = a - c;
		  double complex2 = b - d;
		  return new Complex(complex1, complex2);
	  }
	
	  public Complex mul(Complex c2) {
    
    
		  double c = c2.a;
		  double d = c2.b;
		  double complex1 = a * c - b * d;
		  double complex2 = b * c + a * d;
		  return new Complex(complex1, complex2);
	  }
	
	  public Complex div(Complex c2) {
    
    
		  double c = c2.a;
		  double d = c2.b;
		  double complex1 = (a * c + b * d) / (c * c + d * d);
		  double complex2 = (b * c - a * d) / (c * c + d * d);
		  return new Complex(complex1, complex2);
	  }
	  
	  public double abs() {
    
    
		  return Math.pow(a * a + b * b, 0.5);
	  }

	  public double getRealPart() {
    
    
		  return a;
	  }
	  
	  public double getImaginaryPart() {
    
    
		  return b;
	  }
	  
	  @Override
	  // 输出对象时,会自动调用toString
	  public String toString() {
    
    
		  if (b == 0)
			  return a + "";
		  else
			  return a + " + " + b + "i";
	  }
}

输出结果:

0.0 + 6.5i
7.0 + 4.5i
-17.75 + -15.75i
-0.5094339622641509 + -1.7169811320754718i
6.519202405202649
13.18(使用 Rational 类)

编写程序,使用 Rational 类计算下面的求和数列:

1 2 + 2 3 + 3 4 + ⋅ ⋅ ⋅ + 99 100 \frac{1}{2} + \frac{2}{3} + \frac{3}{4} + ··· + \frac{99}{100} 21+32+43+⋅⋅⋅+10099

你将会发现输出是不正确的 ,因为整数溢出(太大了)。为了解决这个问题 ,参见编程练习題13.15。

直接复制13.15的Rational代码,下面只给出Test测试类

Test.java

package learning;									

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Rational resultRational = new Rational();
        long i = 2;
        while (i <= 100) {
    
    
            Rational rational = new Rational(i - 1, i);
            System.out.print(rational.getNumerator() + "/" + rational.getDenominator());

            if (i == 100) System.out.print(" = ");
            else if (i % 10 == 0) System.out.println(" + ");
            else System.out.print(" + ");

            resultRational = resultRational.add(rational);
            i++;
        }
        System.out.println(resultRational.getNumerator() + " / " + resultRational.getDenominator());

    }
}

输出结果:

1/2 + 2/3 + 3/4 + ... + 97/98 + 98/99 + 99/100 = 1349247664274259951 / 1822963237492290880
13.19 (将十进制數转化为分教)

编写一个程序,提示用户输入一个十进制数,然后以分数的形式显示该数字。提示 :将十进制数以字符串的形式读入 ,从字符串中抽取其整数部分和小数部分 ,然后运用编程练习题 13.15 中使用 Biglnteger 实现的 Rational 类,来获得该十进制数的有理数。这里是一些运行示例:

最后3题比较考察数学功底,编程逻辑不难,但是数学难哈哈哈。。。

直接复制13.15的Rational代码,下面只给出Test测试类

Test.java

package learning;									

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a decimal number: ");
        String decVal = in.next();
        try {
    
    
            System.out.println("The fraction number is " + decimalToFraction(decVal));
        } catch (Exception e) {
    
    
            System.out.println(e.getMessage());
        }

        in.close();
    }

    private static String decimalToFraction(String val) throws Exception {
    
    
        boolean isNegative = val.startsWith("-");

        String[] decimalNumberParts = val.split("\\.");
        if (decimalNumberParts.length < 2) {
    
    
            throw new Exception("You must enter a decimal number like: 123.12");
        }
        if (val.startsWith("-")) {
    
    
            isNegative = true;
        }
        Rational leftSideOfDecimal = new Rational(Long.parseLong(decimalNumberParts[0]));
        String denominatorRightSide = "1";
        for (int i = 0; i < decimalNumberParts[1].length(); i++) {
    
    
            denominatorRightSide += "0";
        }
        Rational rightSideOfDecimal = new Rational(Long.parseLong(decimalNumberParts[1]), Long.parseLong(denominatorRightSide));
        Rational result = leftSideOfDecimal.add(rightSideOfDecimal);
        return (isNegative ? "-" : "") + result.toString();

    }
}

输出结果:

Enter a decimal number: 3.25
The fraction number is 13/4
13.20 (數学:求解二元方程)

重写编程练习题3.1,如果行列式小于0,则使用编程练习题13.17中的 Complex 类来得到虚根。这里是一些运行示例:

最后3题比较考察数学功底,编程逻辑不难,但是数学难哈哈哈。。。

直接复制13.17的Complex代码,下面只给出Test测试类

Test.java

package learning;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    

        try (Scanner input = new Scanner(System.in)) {
    
    

            System.out.print("Enter a, b, c: ");

            double a = input.nextDouble();
            double b = input.nextDouble();
            double c = input.nextDouble();

            double discriminant = b * b - 4 * a * c;

            if (discriminant > 0) {
    
    
                double r1 = ((-1 * b) + Math.sqrt(discriminant)) / (2 * a);
                double r2 = ((-1 * b) - Math.sqrt(discriminant)) / (2 * a);
                System.out.printf("Two real roots: %2.5f and %2.5f", r1, r2);

            } else if (discriminant == 0) {
    
    
                double r = ((-1 * b) + Math.sqrt(discriminant)) / (2 * a);
                System.out.print("One real root: " + r);

            } else {
    
    
                Complex complexR1 = new Complex(-b / (2 * a), Math.sqrt(2 * a));
                Complex complexR2 = new Complex(-b / (2 * a), -Math.sqrt(2 * a));
                System.out.println("The roots are " + complexR1 + " and " + complexR2);
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

    }
}

输出结果:

Enter a, b, c: 1 2 3
The roots are -1.0 + 1.4142135623730951i and -1.0 + -1.4142135623730951i
13.21(代数:顶点式方程)

抛物线方程可以表达为标准形式(y=ax2+bx+c)或者顶点式(y=a(x-h)2+k)。编写一个程序,提示用户输人标准形式下的整数a、b和c值,显示顶点式下面的h和k值。这里是一些运行示例:

最后3题比较考察数学功底,编程逻辑不难,但是数学难哈哈哈。。。

直接复制13.15的Rational代码,下面只给出Test测试类

Test.java

package learning;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
    		Scanner in = new Scanner(System.in);
            System.out.print("Enter a, b, c (as integers): ");
            int a = in.nextInt();
            int b = in.nextInt();
            int c = in.nextInt();
            Rational aRational = new Rational(a); 
            Rational bRational = new Rational(b); 
            Rational cRational = new Rational(c); 
            Rational h = new Rational(bRational.multiply(new Rational(-1)).longValue(),
                    aRational.multiply(new Rational(2)).longValue());
            Rational k = aRational.multiply(h.multiply(h)).add(bRational.multiply(h)).add(cRational);
            System.out.println("h is " + h + ", k is " + k);
    }
}

输出结果:

Enter a, b, c (as integers): 1 3 1
h is -3/2, k is -5/4

猜你喜欢

转载自blog.csdn.net/weixin_40020256/article/details/130799494
今日推荐