Java 言語プログラミング演習 第 13 章
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 クラスから削除できます。これら 2 つのメソッドを GeometricObject クラスの抽象メソッドとして定義する利点は何ですか?
GeometricObject として宣言されたオブジェクトは、コンパイル中にこれら 2 つのメソッドを使用できます。
そうしないと、周長と面積を計算する方法がありません。
13.3 次の記述は真ですか、それとも偽ですか?
a. 抽象クラスは、new 演算子を使用して抽象クラスのインスタンスを作成できないことを除いて、非抽象クラスと同様に使用できます。
本物
b. 抽象クラスの継承が可能
本物
c. 非抽象親クラスのサブクラスを抽象にすることはできません
偽
d. サブクラスは、親クラスの特定のメソッドをオーバーライドして抽象メソッドとして定義することはできません。
偽
e. 抽象メソッドは非静的でなければなりません
本物
13.3 章の演習
13.4 次の 2 行のコードは正常にコンパイルできるのに、実行時エラーが発生するのはなぜですか?
// 第二行等号右侧想要把Integer类型的numberRef转换为Double类型,而Integer和Double没有继承关系,如果Double是Integer的父类,就会运行成功
Number numberRef = new Integer(0);
Double doubleRef = (Double)numberRef;
13.5 次の 2 行のコードは正常にコンパイルできるのに、実行時エラーが発生するのはなぜですか?
//和上一题类似,不能把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 クラスのどのメソッドが抽象ですか?
メソッドの追加
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 次のステートメントは true ですか、それとも false? クラスが 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() を呼び出したい場合は、オブジェクトが配置されているクラスで 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 つだけです。
- コンストラクター: インターフェイスにはコンストラクターがありませんが、抽象クラスにはコンストラクターがあります。
- メンバー変数: インターフェイスでは定数のみを定義でき、インスタンス変数は定義できません。抽象クラスにはインスタンス変数を含めることができます。
- インターフェイス間の継承: インターフェイスは複数のインターフェイスを継承してインターフェイス間の継承関係を形成できますが、抽象クラスはクラス継承を通じてのみ継承関係を実装できます。
13.29 本当か嘘か?
a.インターフェイスは個別のバイトコード ファイルにコンパイルされます。
本物
b. インターフェイスには静的メソッドを含めることができます。
本物
c. インターフェイスは 1 つ以上のインターフェイスを拡張できます。
本物
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 ステートメントの代わりに 1 行のコードを使用して、リスト 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クラスを実装します。三角形の 3 つの辺、色、三角形が塗りつぶされているかどうかを示すブール値の入力をユーザーに求めるテスト プログラムを作成します。プログラムは、これらの辺とその色および塗りつぶされているかどうかに関する情報を使用して、ユーザーの入力に基づいて Triangle オブジェクトを作成する必要があります。プログラムは、領域、周囲長、色、および塗りつぶされているかどうかを示す true または false を表示する必要があります。
テスト.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();
}
}
幾何学オブジェクト.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 クラス内の 2 つの GeometricObject オブジェクトのうち大きい方を検索する静的 max メソッドを定義します。UML 図を描画し、この新しい GeometricObject クラスを実装します。max メソッドを使用して 2 つの円のうち大きい方と 2 つの四角形のうち大きい方を見つけるテスト プログラムを作成します。
テスト.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);
}
}
幾何学オブジェクト.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";
}
}
サークル.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类)
Circle クラスを継承し、Comparable インターフェイスを実装する ComparableCircle という名前のクラスを作成します。UML 図を描画し、compareTo メソッドを実装して、面積に基づいて 2 つの円を比較します。ComparableCircle オブジェクトの 2 つのインスタンスのうち大きい方を見つけるテスト プログラムを作成します。
テスト.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));
}
}
サークル.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 (色付け可能なインターフェース 色付け可能)
howToColor() という名前の void メソッドを使用して、Colorable という名前のインターフェイスを設計します。色付け可能なオブジェクトのすべてのクラスは、Colorable インターフェイスを実装する必要があります。Square というクラスを設計します。このクラスは CeometricObject クラスを継承し、Colorable インターフェイスを実装します。howToColor メソッドを実装して、「四辺すべてに色を付けます」(4 辺すべてに色を付ける) というメッセージを表示します。
Colorable、Square、CeometricObject を含む UML 図を描画します。5 つの GeometricObject オブジェクトの配列を作成するテスト プログラムを作成します。配列内の各オブジェクトについて、そのオブジェクトが色付け可能な場合、メソッド howToColor が呼び出されます。
テスト.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();
}
}
幾何学オブジェクト.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 クラスを書き換えます。
テスト.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 クラスを Comparable に変更)
コード リスト 13-2 の Circle クラスを書き換えます。このクラスは GeometricObject クラスを継承し、Comparable インタフェースを実装します。Object クラスの equals メソッドをオーバーライドします。2 つの Circle オブジェクトは、半径が同じであれば同一です。Circle、GeometricObject、Comparable を含む UML 図を描画します。
テスト.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));
}
}
サークル.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;
}
}
幾何学オブジェクト.java
package learning;
abstract class GeometricObject {
String color;
boolean filled;
GeometricObject() {
}
}
出力結果:
true
0
* 13.10 (Rectangle クラスを同等にする)
コード リスト 13-3 の Rectangle クラスを書き換えます。このクラスは GeometricObject クラスを継承し、Comparable インタフェースを実装します。Object クラスの equals メソッドをオーバーライドします。2 つの Rectangle オブジェクトは、同じ面積を持つ場合は同一です。Rectangle、GeometricObject、Comparable を含む UML 図を描画します
テスト.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));
}
}
幾何学オブジェクト.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)
GeometricObject クラスを継承し、Comparable インターフェイスと Cloneable インターフェイスを実装する Octagon というクラスを作成します。八角形の 8 つの辺の長さがすべて等しいと仮定します。その面積は次の式を使用して計算できます。
面積 = ( 2 + 4 / √ 2 ) × 辺の長さ × 辺の長さ 面積 = (2+4/√2) × 辺の長さ × 辺の長さエリア=( 2+4/√2 )×辺の長さ×辺の長さ
Octagon、GeometricObject、Comparable、Cloneable などの UML 図を描画します。一辺の長さが 5 の八角形オブジェクトを作成し、その面積と周囲長を表示するテスト プログラムを作成します。clone メソッドを使用して新しいオブジェクトを作成し、compareTo メソッドを使用して 2 つのオブジェクトを比較します。
テスト.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));
}
}
幾何学オブジェクト.java
package learning;
abstract class GeometricObject{
String color;
boolean filled;
GeometricObject() {
}
protected abstract double getPerimeter();
protected abstract double getArea();
}
オクタゴン.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)
4 つのオブジェクト (2 つの円と 2 つの長方形) の配列を作成し、sumArea メソッドを使用してそれらの合計面積を求めるテスト プログラムを作成します。
テスト.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;
}
}
幾何学オブジェクト.java
package learning;
abstract class GeometricObject{
String color;
boolean filled;
GeometricObject() {
}
protected abstract double getArea();
}
サークル.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 (コースクラスをコピー可能にします)
コード リスト 10-6 の Course クラスを書き直して、students フィールドのディープ コピーを実行する clone メソッドを追加します。
テスト.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()));
}
}
コース.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 クラスを書き直します。次のように 2 つの整数を含む配列を作成します。
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 クラスでの BigInteger の使用)
BigInteger を使用して分子と分母を表し、再設計して実装します
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メソッドを使用して文字列を整数に変換する必要があります。
テスト.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 (数学: 複合クラス)
複素数は、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 + bi ∣ = a 2 + b 2 |a + bi| = \sqrt{a^2 + b^2}∣ a+バイ∣=ある2+b2
(複素数は平面上の点として解釈でき、(a, b) 値は点の座標として使用されます。図に示すように、複素数の絶対値は点から原点までの距離です。図 13-10b)。
複素数を表す Complex という名前の複素数を設計し、複素数演算を完了する加算、減算、乗算、除算、および abs メソッドを設計し、複素数を表す文字列を返す toString メソッドをオーバーライドします。toString メソッドは文字列 a+bi を返します。b が 0 の場合は、a を返します。Complex クラスは Cloneable インターフェイスも実装する必要があります。
Complex(a, b)、Complex(a)、Complex() の 3 つの構築メソッドが提供されています。Complex() は番号 0 の Complex オブジェクトを作成し、Complex(a) は b が 0 の Complex オブジェクトを作成します。getRealPart() メソッドと getImaginaryPart() メソッドも、それぞれ複素数の実数部と虚数部を返すために提供されています。
ユーザーに 2 つの複素数の入力を求め、それらの加算、減算、乗算、除算の結果を表示するテスト プログラムを作成します。実行例を次に示します。
テスト.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());
}
}
複雑な.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 テスト クラスのみを以下に示します。
テスト.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 (小数から分数への変換)
ユーザーに 10 進数の入力を求め、その数値を分数として表示するプログラムを作成します。ヒント: 文字列の形式で 10 進数を読み取り、その整数部分と小数部分を文字列から抽出してから、プログラミング演習 13.15 で Biglnteger を使用して実装された Rational クラスを使用して、10 進数の有理数を取得します。以下に実行例をいくつか示します。
最後の3問は数学力を比較するもので、プログラミングロジックは難しくありませんが、数学は難しいです(笑)。。。
13.15 の Rational コードを直接コピーし、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 (数学: 2 つの変数で方程式を解く)
プログラミング演習 3.1 を書き直し、行列式が 0 より小さい場合は、プログラミング演習 13.17 の Complex クラスを使用して虚数根を取得します。以下に実行例をいくつか示します。
最後の3問は数学力を比較するもので、プログラミングロジックは難しくありませんが、数学は難しいです(笑)。。。
13.17 の Complex コードを直接コピーします。以下に 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=ax 2+bx+c) または頂点形式 (y=a(xh) 2+k) で表すことができます。ユーザーに標準形式で整数 a、b、c の入力を要求し、h と k の値を頂点形式で表示するプログラムを作成します。以下に実行例をいくつか示します。
最後の3問は数学力を比較するもので、プログラミングロジックは難しくありませんが、数学は難しいです(笑)。。。
13.15 の Rational コードを直接コピーし、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