时间限制:1.000S 空间限制:32MB
计算a+b,但输入方式有所改变。
第一行是一个整数N,表示后面会有N行a和b,通过空格隔开。
对于输入的每对a和b,你需要在相应的行输出a、b的和。
如第二对a和b,对应的和也输出在第二行。
2
2 4
9 21
6
30
注意,测试数据不仅仅一组。也就是说,会持续输入N以及后面的a和b
本节课会学习到下面知识:
本题仍然是计算a+b,但是我们要通过输入一个整数n来控制输入a,b的次数。
⚠️ 题目 「提示」这里说明了,一组数据包括一个整数n和n行a和b, 但测试数据不仅仅一组,也就是说会有多个n的输入,直到人为终止。
既然是持续性的输入,说明在循环做输入n这个操作,那我们就需要使用到上一节中使用的while
连续输入n的代码如下:
// 多组数据,需要处理多次,使用while循环解决
while(sc.hasNext()) {
}
当输入n之后,表示后面会有N行a和b的输入,while代表了循环,那怎么控制循环次数呢?
我们只想让这个循环执行n次,然后自动退出循环,那应该对n进行怎样的操作呢?
现实中,我们想要把一件事情做n次,比如数n只羊,就是说第一只羊,第二只羊,第n只羊,这样数,那在编程中是同样的逻辑,只不过这次我们使用的是for循环
for语句的语法格式是:
for(初始化语句;条件判断;操作) {
// 代码块
}
初次接触,或许不是很好理解,让我们使用计数的例子来类比,假设我们从1数到100
根据上面的表述,我们可以将之转换为for语言的逻辑
for(初始化值为1;判断是否小于100;计数值+1) {
// 循环体代码
}
不过编程中初始化值一般都是从0开始,所以想要将循环体执行n次,Java的代码如下:
// int i = 0表示初始化值为0
// i < n用于判断,从0到n-1, 代码执行了n次,当i = n时退出循环
// i++表示i的值加1
for(int i = 0; i < n; i++) {
}
每次循环,都需要接收输入的a和b,并根据a和b计算出结果,这个代码和上一题的类似,就不再赘述,代码如下:
for(int i = 0; i < n; i++) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(a + b);
}
我们再来看一遍代码的执行流程:
int i = 0
, 循环开始时会执行一次初始化语句,这里表示初始化一个变量的值为0i < n
, 执行完初始化语句之后会进行条件判断,这里将i当前的值和n进行对比,想要让循环代码体执行n次,所需要计数的次数应该是0~n-1
, 当还没有数到n时,则执行循环体的代码,当数到n
时,n < n
显然是错误的,就退出了循环,循环终止。i < n
的条件为真,执行循环体,循环体的含义是输入a和b并输出两者的和i++
, 表示当前的i值不符合条件,让变量i的值加1,进行下一轮判断完整的代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
for(int i = 0; i < n; i++) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(a + b);
}
}
}
}
刚才我们使用了i++
表示计数值i的数值+1,实际上除了i++
,还有++i
,它们之间有什么区别呢?
++i
是前缀自增运算符,所谓前缀自增是指,它会先将变量增加 1,然后返回增加后的值,再将返回增加后的值参与别的运算比如下面的代码
int i = 99;
int result = ++i;
result
的值是99还是100呢?答案是100。
++i
会先从原来的99自增加到100,然后将增加后的值赋值给变量result
,所以result
的值就是100
i++
是后缀自增运算符,它会先返回变量的当前值,然后再将变量增加 1。int i = 99;
int result = i++;
这里result
的值就是99了,它会先将i原来的值赋值给result
, 所以result
的值就是99, 然后i再执行自增操作,i的值增加为100。
总结来说,就是++i
和i++
的执行顺序不一样,前者是先自增后运算,后者是先运算后自增, 如果你想在表达式中使用变量的递增后的值,你可以使用 ++i
。如果你想在表达式中使用变量的当前值,然后再将其递增,你可以使用 i++
。
除了自增之外,Java还提供递减的运算,i--
和--i
, 都是用来减少一个整数变量的值的操作, 它们之间的区别和递增一样,前自减会在修改变量的值后返回修改后的值,而后自减会在返回变量的当前值后再进行减少操作。
int i = 100;
int result = --i; // i 先减少为 99,然后赋值给 result
// 现在 i = 99, result = 99
int i = 100;
int result = i--; // 先赋值给 result,然后 i 减少为 99
// 现在 i = 99, result = 100
刚才我们使用了for循环来解决问题,既然是循环,那能不能仍使用while
循环来解决呢?
我们先来回顾上一节中学到的while基本语法:
while(条件) {
// 当条件为真时,就会一直执行
}
与上面的for循环对比就会发现,while只有条件判断,而没有初始化语句和操作语句,所以要在while结构之外进行书写。
所以按照for循环的逻辑,我们可以这样写代码:
while (scanner.hasNext()) {
int n = scanner.nextInt();
int i = 0;
while(i < n) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(a + b)
i++;
}
}
这样这个循环才算是完整的,但是这样做引入了无关变量i, 又加了一个循环控制语句,有没有简化的办法呢?
由于我们先定义了变量n, 在输入N的时候,已经为这个变量n赋值了,相当于循环的次数已经被初始化了,想要让代码执行n次,我们可以“倒着数”,从n数到1,每次让n-1, 当n为0的时候退出循环,这样代码也执行了n次。
⚠️ 注意:Java中不允许将数字当作布尔值使用。
使用while的代码如下:
while (n-- > 0) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(a + b)
}
想一想,为什么是n--
,而不是--n
呢?
如果是while(--n)
,则计算n的值,再进行条件判断,此时n的值已经是n-1
了,从n-1
到1, 循环只执行了n-1
次,而如果是while(n--)
可以控制这个循环只执行n次,当n为0的时候就终止了。
完整的代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
while (n-- > 0) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(a + b);
}
}
}
}
其实除了for循环
和while
循环之外,还有一种循环结构,叫做do while
, 它和 while
循环有些类似, 不过while
循环要更常用一些,它的使用方法如下:
do {
// 循环体(代码块)
} while (条件判断);
do while
循环会先去执行循环体内的代码块,然后再检查条件判断,如果条件判断为真,则继续执行下一次循环迭代;如果 条件判断为假,则终止循环,这意味着,无论条件的值如何,我们都至少执行一次循环。
int sum = 0;
int i = 11;
// 先去执行一次循环体中的代码,再去判断条件要不要执行
do {
sum = sum + i; // 将 sum + i的值赋值给sum
i++; // i的值自增
} while (i <= 10);
在这个例子中,虽然不满足于i < =10
的条件,但是仍然会执行一次循环体中的代码。
上一节中,我们讲到几种基本数据类型,但是基本数据类型本身不是对象,因此Java内部将这些基础数据类型用“类”的形式包装起来,形成“包装类”,这些包装类内部提供了很多的方法方便我们使用,并且可以执行与对象有关的操作,下面就是常见的基本数据类型和其对应的包装类。
Integer
: 包装int
类型。
Long
: 包装long
类型。
Short
: 包装short
类型。
Byte
: 包装byte
类型。
Double
: 包装double
类型。
Float
: 包装float
类型.
Character
: 包装char
类型。
Boolean
: 包装boolean
类型。
可以看到大多数包装类型只是将数据类型的首字母大写(类的首字母必须大写),可以简单理解包装类型就是在“基本数据”类型外面包装了一层,使其变成了对象,并在上面增加了一点功能而已。
而自动装箱和自动拆箱则是基本数据类型和对应的包装类型之间转换的一种应用,其实这个名称很形象,我们不是在“基本数据类型”上包装了一层形成“包装类”嘛,这个过程就叫自动装箱,而自动拆箱是把这个“包装”给拆掉,将包装类型对象转为对应的基本数据类型。
比如下面的示例:
int age = 10; // 基本数据类型10
Integer boxedAge = age; // Integer boxedAge声明了一个包装对象,直接将10赋予这个对象,系统会帮我们将 10 装箱后放入到 boxedAge
同样的:boxedAge
是一个Integer
对象,而age
是一个基本数据类型的int
类型,可以直接赋值,系统会我们自动拆箱。
Integer boxedAge = 10;
int age = boxedAge;
我们已经知道,byte、short、int、long
等几种数据类型都是整数类型,只不过能表示的范围不同,就好像都是瓶子,只不过容量的大小有差距,就像小容量的瓶子里的水可以倒入大容量的瓶子一样,当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型,这也被称之为自动类型转换。
byte b = 10;
short s = b; //byte可以转为short类型
int i = b; // byte可以转为 int类型
long l = b; // byte可以转为 long类型
不过大容量的瓶子却无法将全部的水都倒入小容量的瓶子,如果非要倒入,就必须使用到强制类型转换。强制类型转换需要使用强制类型转换运算符(圆括号中包含目标数据类型)来明确指定数据类型转换,不过这种做法可能会是的数据丢失精度。
int i = 100;
byte b = (byte)i; // 将 int类型的 i 转为 byte类型
System.out.println(b); //输出结果为100
我们这节学习到了三种循环结构,分别是for/while/do-while
,知道了for循环的执行流程以及对while循环有了更多认识,此外我们还学习到了++i
和i++
的区别,不必纠结于使用哪个,只需要知道两者之间的区别,在具体的场景下你会根据它们之间的区别来选择合适的进行使用的。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in); // 创建一个Scanner对象来读取输入
while (sc.hasNext()) { // 循环读取输入直到没有下一个输入
int n = sc.nextInt(); // 读取一个整数n
for (int i = 0; i < n; i++) { // 循环n次
int a = sc.nextInt(); // 读取一个整数a
int b = sc.nextInt(); // 读取一个整数b
System.out.println(a + b); // 输出a和b的和
}
}
}
}