第1关:整钱兑零
任务描述
本关任务:掌握增强赋值以及自增自减相关知识。编写一个能计算数组平均值和最大值的小程序,将大额的钱替换为等价的小货币单位。
相关知识
为了完成本关任务,你需要学习以下知识:
- 增强赋值;
- 自增与自减;
- 整钱兑零任务背景。
增强赋值
本课程之前的内容已经简要介绍了赋值操作(=
)以及基本的数值操作(+
、-
、*
、/
、%
)。这里要讲解的增强赋值正是将这两者结合起来使用。
增强赋值操作示例如下:
/** 代码清单 1 */
int a = 10;
int b = 5;
a += b; // a = 15
a -= b; // a = 10
a *= b; // a = 50
a /= b; // a = 10
a %= b; // a = 0
在代码清单 1
中,第 4 ~ 8 行的语句皆是对变量a
赋值,会导致a
被赋予新值,这便是“增强赋值”中的“赋值”,那么增强的含义是什么呢?
事实上,代码代码清单 1
中的代码等价于如下代码:
/** 代码清单 2 */
int a = 0;
int b = 1;
a = a + b; // 即 a += b
a = a - b; // 即 a -= b
a = a * b; // 即 a *= b
a = a / b; // 即 a /= b
a = a % b; // 即 a %= b
可见,增强赋值类似于一种简略写法。此外需要注意的是,增强赋值与赋值一样,既可以作为语句,也可以作为表达式。 示例如下:
/** 代码清单 3 */
int a = 0;
a += 2; // 这里的增强赋值是一个语句
System.out.println(a+=2); // 这里的增强赋值是一个表达式
输出:
4
自增与自减
自增操作符(++
)以及自减操作符 (--
)是对变量进行“加一”或者“减一”的操作。 示例如下:
/** 代码清单 4 */
int i = 3;
int j = 3;
// 自增
i++;
// 自减
j--;
System.out.println(i);
System.out.println(j);
输出:
4
2
其实i++
可以理解为是i=i+1
或i+=1
的简便写法,因为在编程的过程中,这样的操作会经常用到,尤其是在循环结构中,在课程后续课程的任务中你将经常用到自增与自减。
另外,++
和--
还可以放在变量的前方,写成++i
、--j
这样的形式,这种前置的写法在操作变量值上没有区别,但在作为表达式传值时会有“传新值”还是“传旧值”上的区别。
示例如下:
/** 代码清单 5 */
int i = 3;
int j = 3;
// 自增作为语句
i++; // 自增后 i 的值变为 4
++j; // 自增后 j 的值变为 4
// 自增作为表达式
int a = i++; // 自增后置写法,表达式传递旧值 4
int b = ++j; // 自增前置写法,表达式传递新值 5
System.out.println(a);
System.out.print(b);
输出:
4
5
代码清单 5
直观的展示了前置自增以及后置自增在作为表达式时的区别,前置自增传递的时自增后的“新值”(即第 9 行b=++j
,++j
从4
变5
后的5
),后置自增传递的是自增前的“旧值”(即第 8 行a=i++
,i++
从4
变5
前的4
)。同样的,前置自减与后置自减的区别也在于此。
自增与自减在 Java 编程中的使用频率会非常的高,请一定要弄清楚他们的特性,从而能更加准确高效的编写程序以及阅读程序。
整钱兑零任务背景
设有以下货币单位,从大到小依次为:
- dollar (1 美元,相当于 100 美分);
- quarter (25 美分);
- dime (10 美分);
- nickel (5 美分);
- penny (1 美分)。
当输入了一个以美元为单位的金额时,需要将其兑换成以上货币单位的零钱,例如,0.5 dollar 可以兑换为 2 quarter 或者 5 dime。在本任务中,需要优先兑换大单位货币。即 0.5 dollar 应该兑换为 2 quarter 而不是其他单位的货币,只有在无法兑换更多当前单位的货币时,才考虑兑换小一级单位的货币。这样能够保证兑换唯一性。例如: 11.56 dollar 可以进行如下兑换:
- 11 dollar;
- 2 quarter;
- 0 dime;
- 1 nickel;
- 1 penny。
这是本任务中唯一正确的兑换结果,其他结果都不正确。
编程要求
根据提示,在右侧编辑器 Begin-End 区域补充代码,实现整钱兑零功能。具体要求如下:
-
变量
money
是一个int
类型的数,个位十位代表小数点后的部分,例如1156
代表 11.56 美元。 -
输出兑换零钱的结果,样式详见测试说明的测试输出。
测试说明
平台会对你编写的代码进行测试:
测试输入:1156
; 预期输出:
11 dollar
2 quarter
0 dime
1 nickel
1 penny
提示:
- 输出最后一行
penny
后不换行 - 以下代码展示了一个输出示例:
/** 代码清单 6 */
int a = 1;
int b = 2;
System.out.println(a + " nickel");
System.out.print(b + " penny");
1 nickel
2 penny
代码如下:
package step1;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
int money = input.nextInt();
input.close();
//请在指定位置填写代码。
/********* Begin *********/
int dollar=money/100;
System.out.println(dollar+" dollar");
money%=100;
int quarter=money/25;
System.out.println(quarter+" quarter");
money%=25;
int dime=money/10;
System.out.println(dime+" dime");
money%=10;
int nickel=money/5;
System.out.println(nickel+" nickel");
money%=5;
int penny=money/1;
System.out.print(penny+" penny");
/********* End *********/
}
}
第2关:整钱兑零(改)
任务描述
本关任务:学习类型转换相关知识,并在第一关的整钱兑零任务基础上,完成修改输入要求的任务。
相关知识
为了完成本关任务,你需要了解并学习:
- 整钱兑零的输入问题;
- 数值类型转换;
- 类型转换带来的问题。
整钱兑零的输入问题
细心的你应该会发现,第一关的整钱兑零任务中,要求输入的是int
类型的整型数据,即用1156
代表实际的 11.56 美元。为何不用double
或者float
这样的浮点型数据作为输入呢,输入11.56
不是跟实际情况更相符吗?
如果你完成了第一关的任务,应该明白解决问题的关键操作就是“整数除”(/
)“取余”操作(%
),例如如下代码:
/** 代码清单 1 */
int money = 1156;
// 取出了 dollar 单位的部分
int dollar = money/100;
// 取出了不足 1 dollar 的剩余其他单位部分
money = money%100;
代码清单 1
提供了一种解决整钱兑零任务的常规思路,你可以尝试将代码清单 1
中的int
类型替换为double
类型,此时就会出现类型不对应导致的操作上的问题。例如如下代码:
/** 代码清单 2 */
double money = 11.56;
// 这是有语法错误的赋值语句,不能将 double 类型赋值给 int 类型
int dollar = money*100/100;
// 这是错误的表达式,不能对 double 类型使用 % 操作符。
money = money%1;
代码清单 2
中的错误代码,就是为什么第一关任务中使用整型作为输入的原因。 那么,这个任务就无法接受浮点类型作为输入了吗?
当然不是,Java 提供了强制类型转换,可以帮助我们解决这个问题,从而使得第一关的整钱对零任务可以接受浮点输入。
数值类型转换
其实,我们编写的程序可能会在不经意将发生数值类型转换,例如3 + 4.5
。当一个二元操作符(例如+
)的操作数中一个是浮点型(1.5
),一个是整型(3
),Java 会自动的将整型转化为浮点型。这样,3 + 4.5
就变成了3.0 + 4.5
,表达式产生的结果自然也就是一个浮点型。
总是可以将一个数值赋值给支持更大数值范围类型的变量。例如可以直接将 long
类型的值赋值给float
类型的变量。但反过来则不能直接进行,而必须进行显式的类型转换。这其实很好理解,要把一个“大范围”的值赋值给“小范围”的变量,这里可能会有“超范围”的风险,这种风险需要由程序员自己来承担,Java 本身不“背锅”。而显示的类型转换也就是强制类型转换,即明知道风险还是要进行的类型转换。
强制类型转换的语法要求是将“目标类型”放置于括号()
内,其后紧跟需要进行类型转换的变量名或值。
示例如下:
/** 代码清单 3 */
double a = 12.01;
// 将 float 类型的变量 a 中的值进行强制类型转换
int b = (int)a;
System.out.print(b);
输出:
12
当然强制类型转换的也可以是表达式,但需要注意执行顺序:
/** 代码清单 4 */
double a = 12.1;
// 先将变量 a 中值强制转换为 int 型,再 *10 。
int b = (int)a*10;
// 先将变量 a 中的值 * 10 ,再将结果强制转换为 int 型。
int c = (int)(a*10);
System.out.println(b);
System.out.print(c);
输出:
120
121
注意,强制类型转换不能转换变量的类型,以下代码是错误的:
/** 代码清单 5 */
double a = 12.1;
// 错误,以下语句不会改变变量 a 的类型。
(int)a;
事实上,代码清单 5
第 2 行的语句会报错,错误是期待boolean
类型,得到的却是int
类型。至于为什么会报这样的错误,你可以将它当作一个暂时的疑问,通过后续课程的学习,你将会解开这个疑问。
类型转换带来的问题
上一节提到,之所以强制类型转换必须要显式进行,是因为 Java 不承担这种类型转换可能会带来的“意外风险”。因此,在进行强制类型转换操作之前,我们一定要充分考虑这个操作是否会造成错误。
例如如下语句:
/** 代码清单 6 */
short a = (short) 65536;
System.out.print(a);
输出:
0
代码清单 6
很好的为我们展示了“超范围”的风险,显然数值65536
超出了short
类型的表示范围,因此得到了“出乎意料”的结果0
。这种情况是我们在编程中使用强制类型转换时需要充分考虑的到的,这样才能对这些“意外”进行合理的规避或是处理。
编程要求
根据提示,在右侧编辑器 Begin-End 区域补充代码,实现整钱兑零功能。具体要求如下:
- 变量
money
是一个double
类型的数,个位十位代表小数点后的部分,例如11.56
代表 11.56 美元,小数点后只有两位。 - 输出兑换零钱的结果,样式详见测试说明的测试输出。
测试说明
平台会对你编写的代码进行测试:
测试输入:11.56
; 预期输出:
11 dollar
2 quarter
0 dime
1 nickel
1 penny
提示:
- 输出最后一行
penny
后不换行 - 以下代码展示了一个输出示例:
/** 代码清单 6 */
int a = 1;
int b = 2;
System.out.println(a + " nickel");
System.out.print(b + " penny");
1 nickel
2 penny
代码如下:
package step2;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
double money = input.nextDouble();
input.close();
//请在指定位置填写代码。
/********* Begin *********/
int moneyTrans=(int)(money*100);
int dollar=moneyTrans/100;
System.out.println(dollar+" dollar");
moneyTrans%=100;
int quarter=moneyTrans/25;
System.out.println(quarter+" quarter");
moneyTrans%=25;
int dime=moneyTrans/10;
System.out.println(dime+" dime");
moneyTrans%=10;
int nickel=moneyTrans/5;
System.out.println(nickel+" nickel");
moneyTrans%=5;
int penny=moneyTrans/1;
System.out.println(penny+" penny");
/********* End *********/
}
}