打印用户输入的整数并计算各位数字之和,是Java入门阶段非常经典的一道练习题。题目本身不难,但它涉及输入处理、循环控制、取余运算、边界情况等多个知识点,正好可以用来检验基础是否扎实。很多初学者写出来的程序在正数上运行正常,一旦输入负数或者特别大的数字就出问题。本文从读取输入开始,一步步拆解正确写法,并分析几个高频错误。

一、用Scanner正确读取用户输入的整数
读取控制台输入最常用的方式是java.util.Scanner。Scanner提供了nextInt()方法直接读取一个整数,省去了手动转换的麻烦。一个基础的读取框架如下:
import java.util.Scanner;
public class DigitSum {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = scanner.nextInt();
System.out.println("你输入的整数是:" + num);
scanner.close();
}
}这段代码有两个细节值得注意。第一,Scanner用完之后调用close()关闭是一个好习惯,虽然对System.in来说不关闭也不算错误,但在其他IO场景下养成这个习惯很有必要。第二,nextInt()遇到非数字输入会抛出InputMismatchException,如果想让程序更健壮,可以配合hasNextInt()做校验:
Scanner scanner = new Scanner(System.in);
int num;
while (true) {
System.out.print("请输入一个整数:");
if (scanner.hasNextInt()) {
num = scanner.nextInt();
break;
} else {
System.out.println("输入的不是整数,请重新输入!");
scanner.next(); // 丢弃非法输入,这一步不能少
}
}注意scanner.next()那一行,很多初学者会漏掉。如果非法输入没有被丢弃,下一次循环hasNextInt()仍然读到同一个错误内容,程序就陷入死循环了。另外要提醒一点:如果在nextInt()之后紧接着用nextLine()读取整行文本,会发现读到的永远是空字符串,这是因为nextInt不会消耗行尾的换行符,此时需要额外调用一次nextLine()把换行符吃掉。
二、逐位拆分并求和的核心算法
拆分各位数字的核心思路是:对一个整数不断对10取余得到最后一位,再除以10去掉这一位,循环往复直到数字变成0。以1234为例:1234对10取余得到4,除以10变成123;123取余得到3,除以10变成12;以此类推,最终把4、3、2、1全部累加起来得到10。
这里存在一个经典陷阱:循环条件写num > 0还是num != 0。如果输入保证是非负数,两者等价;但如果用户输入了负数,num > 0会导致循环一次都不执行,直接输出0,而先取绝对值再循环则能正确处理。完整的实现代码如下:
import java.util.Scanner;
public class DigitSum {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = scanner.nextInt();
System.out.println("你输入的整数是:" + num);
// 处理负数:先取绝对值再逐位拆分
int temp = Math.abs(num);
int sum = 0;
if (temp == 0) {
sum = 0; // 输入为0时,各位之和就是0
}
while (temp > 0) {
sum += temp % 10; // 取出最后一位
temp /= 10; // 去掉最后一位
}
System.out.println("各位数字之和为:" + sum);
scanner.close();
}
}这段代码特意处理了输入为0的情况。虽然对0来说while循环不执行、sum本来就是0,结果碰巧也正确,但显式写出来能让逻辑更清晰,也方便后续扩展成打印每一位数字的需求。还有一点需要小心:Math.abs()对Integer.MIN_VALUE会返回负数本身,因为int的范围是-2147483648到2147483647,绝对值2147483648超出了int的上限。如果担心这种极端输入,可以先把temp声明为long类型:long temp = Math.abs((long) num);,一行代码就规避了溢出。
三、while与do-while的选择及扩展写法
上面的实现用的是while循环,也就是先判断后执行。有些教材会用do-while来写这道题:先执行一次拆分操作,再判断是否继续。两种写法在正数场景下结果一致,但存在一个本质区别:当输入为0时,do-while会执行一次循环体,恰好也能算出正确结果0;而当temp已经是负数(没做绝对值处理时),do-while至少执行一次反而会算出错误结果。因此在没有做输入合法性校验的前提下,while配合绝对值处理是更稳妥的选择。
除了循环,还可以用纯数学的方式递归求解,代码更简短:
public static int digitSum(long n) {
if (n < 0) {
n = -n;
}
if (n < 10) {
return (int) n;
}
return (int) (n % 10 + digitSum(n / 10));
}递归写法的好处是结构清晰,缺点是每一位数字会多一层方法调用,数字位数很多时栈开销不可忽视,不过int最多十位,这点开销完全可以忽略。如果追求极致简洁,还可以把数字转成字符串后逐字符累加:
String s = String.valueOf(Math.abs(num));
int sum = 0;
for (int i = 0; i < s.length(); i++) {
sum += s.charAt(i) - '0'; // 字符减去'0'得到对应数字
}这种写法的关键在于s.charAt(i) - '0'这步转换。字符'5'的ASCII码是53,字符'0'是48,相减正好得到数字5。三种方案各有适用场景:取余除法的循环写法最通用也最高效,适合面试和日常使用;字符串写法直观易懂,适合教学演示;递归写法则适合用来练习递归思维。掌握这道题的核心逻辑后,很多变种题比如统计位数、反转数字、判断回文数,都可以用同样的拆分思路轻松解决。