首页 / Java 入门教程 / 控制台输入输出

Java 入门教程

控制台输入输出

本教程共 100 篇 · 第 17 篇 · 更新于 2026-08-05 · 约 17 分钟阅读

JavaJava 入门教程控制台Scanner输入输出System.out

本节目标:学会在控制台打印内容,并用 Scanner 从键盘读入用户输入,躲开 nextnextLine 混淆、以及读数字抛异常的坑。

为什么要专门学输入输出

程序不是自言自语的黑盒。你得让它把结果说出来,也得让它听懂你说什么。控制台就是初学阶段最省事的「嘴」和「耳朵」——不用装数据库、不用起服务器,一个类就能跑。

更实际的理由:调试。代码跑歪了,最快的定位手段就是在关键位置打一行值出来看看。这招老掉牙,但至今没人能离开它。

先说输出

输出你已经用了无数次:System.out.println("...")。它的意思是把内容打到控制台,并换行

System.out.println("你好");
System.out.println("世界");

两行会分别占一行。如果不想换行,用 print

System.out.print("你好");
System.out.print("世界");   // 输出:你好世界(同一行)

要一次拼出多个值,用 + 连接:System.out.println("总和=" + sum);。字符串和别的类型相加时,会自动把对方也转成字符串再拼。

还有个 printf,能按格式占位输出,像模板填空:

String name = "小明";
int age = 18;
System.out.printf("我叫%s,今年%d岁%n", name, age);
// 输出:我叫小明,今年18岁

%s 填字符串,%d 填整数,%n 是换行。格式化输出在后面讲字符串格式化时会专门展开,这里先知道有这么个用法。

Note

想在 print 里手动换行,可以在字符串里写 \nSystem.out.print("第一行\n第二行"); 会打出两行。println 本质上就是「打印内容 + 自动补一个换行」。所以 print("hi\n")println("hi") 效果一样。

三种输出方法怎么选

方法是否换行参数形式什么时候用
println(x)自动换行一个值绝大多数情况,打一行看一行
print(x)不换行一个值想在同一行拼接输出,比如循环打星号
printf(fmt, ...)看格式串里有没有 %n格式串 + 多个值要对齐、要控小数位、要一次填多个值

一条经验:能用 println 说清楚的,就别上 printf。等到要打表格、要保留两位小数,printf 才真正省事。

System.out 到底是什么

这一长串看着像咒语,拆开其实很直白:System 是 Java 自带的一个类,out 是它的一个静态字段,类型是「打印流」。println 就是 out 这个对象上的方法。

初学不用深究,只要记住「想往控制台输出,就写 System.out.println(...)」这个固定套路。等你学了 static 和对象,回头看这行会豁然开朗。

错误信息走 System.err

除了 System.out,还有个 System.err,专门打错误信息。

System.out.println("这是正常输出");
System.err.println("这是错误输出");

两者都往控制台打,但它们是两条独立的流。在 IDE 里 System.err 通常显示成红色;在命令行里可以分别重定向到不同文件:

java Demo > out.txt 2> err.txt

正常结果进 out.txt,错误信息进 err.txt,互不干扰。这就是分两条流的价值。

Tip

两条流的刷新时机不一样,混着打时输出顺序可能和你写的顺序对不上。调试时统一用 System.out,别一会 out 一会 err,省得自己被顺序绕晕。

读取键盘输入:Scanner

要在控制台读用户输入,最常用的是 java.util.Scanner 类。先创建它,绑定到「标准输入」(键盘):

import java.util.Scanner;

public class InputDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("请输入姓名:");
        String name = sc.next();
        System.out.println("你输入的是:" + name);

        sc.close();   // 用完关闭
    }
}

逐行看这段:

  • import java.util.Scanner; 告诉编译器去哪找 Scanner 这个类。
  • new Scanner(System.in) 造一个扫描器,System.in 就代表键盘。这句固定这么写。
  • System.out.print("请输入姓名:")print 不换行,光标停在冒号后面,看起来像在等你输入。
  • sc.next() 阻塞在这儿,直到你敲了内容并按回车。
  • sc.close() 收尾,释放资源。

漏掉 import 这一行,编译器立刻翻脸:

InputDemo.java:5: error: cannot find symbol
        Scanner sc = new Scanner(System.in);
        ^
  symbol:   class Scanner
  location: class InputDemo

cannot find symbolsymbol: class Scanner,意思就是「我不认识 Scanner」。九成是漏了导入,剩下一成是名字拼错了大小写。

Note

IDE 通常会自动帮你补上导入。但用记事本 + 命令行练手时没人替你补,看到这条报错先回头检查文件头几行。

next 和 nextLine 的区别

读取字符串有两个方法,行为不一样,这是头号坑。

next()遇到空格或回车就停,只拿空格前的内容。它读不到带空格的字符串。

nextLine()遇到回车才停,回车前所有内容(包括空格)都拿走。

Scanner sc = new Scanner(System.in);

System.out.print("next 方式:");
if (sc.hasNext()) {
    String s1 = sc.next();
    System.out.println("得到:" + s1);   // 输入 "hello world" 只拿到 hello
}

System.out.print("nextLine 方式:");
if (sc.hasNextLine()) {
    String s2 = sc.nextLine();
    System.out.println("得到:" + s2);   // 输入 "hello world" 拿到整句
}
sc.close();

想读一整句话(可能带空格),用 nextLine();只想读一个单词,用 next()

吞行问题的完整现场

「吞行」是这一章最值得记住的坑。先看它长什么样:

import java.util.Scanner;

public class SwallowDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("年龄:");
        int age = sc.nextInt();        // 只吃走数字,回车留在缓冲区

        System.out.print("住址:");
        String addr = sc.nextLine();   // 一上来就撞到那个回车,直接返回空串

        System.out.println("年龄=" + age + ",住址=[" + addr + "]");
        sc.close();
    }
}

输入 20 回车,程序连问都不问就结束了,打出 年龄=20,住址=[]

原因不神秘:你敲的其实是三个字符 20、换行。nextInt() 只取走 20,那个换行还躺在缓冲区里。轮到 nextLine() 时,它一看第一个字符就是换行,判定「这行到头了」,于是返回空字符串走人。

next() 后面接 nextLine() 也是同一个病根。

两种解法

解法一:中间多吃一次回车。

int age = sc.nextInt();
sc.nextLine();                 // 专门用来吃掉残留的回车,返回值丢弃
String addr = sc.nextLine();   // 这下能正常读到了

解法二:全程只用 nextLine,需要数字就自己转。

System.out.print("年龄:");
int age = Integer.parseInt(sc.nextLine().trim());   // 整行读进来再转成 int

System.out.print("住址:");
String addr = sc.nextLine();

解法二更稳,因为它从头到尾只有一种读取方式,不存在「谁吃了谁没吃」的问题。trim() 顺手去掉两端空格,防止用户手滑多打一个空格。

Warning

别在同一段代码里混用 nextInt()/next()nextLine()。混用是绝大多数「读不到内容」「程序不等我输入就跑完了」的根源。要么全程 nextLine(),要么每次读完非整行内容后补一次 sc.nextLine()

读取数字

读整数、小数同样用 Scanner,但有个稳妥写法:先判断、再读取。

Scanner sc = new Scanner(System.in);

System.out.print("请输入一个整数:");
if (sc.hasNextInt()) {          // 先判断是不是整数
    int n = sc.nextInt();        // 再读取
    System.out.println("你输入了:" + n);
} else {
    System.out.println("那不是整数");
}
sc.close();

hasNextInt() 先确认输入真的是整数,才用 nextInt() 取。类型对应:hasNextDouble()nextDouble(),依此类推。

不先判断会怎样?用户敲个 abc,程序当场崩:

Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:964)
	at java.base/java.util.Scanner.next(Scanner.java:1619)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2284)
	at InputDemo.main(InputDemo.java:8)

InputMismatchException 直译就是「输入类型对不上」。看到它,去检查 nextXxx() 那一行要的类型和用户实际敲的东西是否匹配。

还有个近亲:输入已经读完了还继续读,会抛 NoSuchElementException。命令行里按 Ctrl+D(Windows 是 Ctrl+Z 再回车)表示输入结束,之后再调 nextLine() 就是这个报错。

读多个数字求平均

把上面拼起来,写个实用的小例子:连续读入若干数字,输入非数字就结束,最后报总和与平均值。

import java.util.Scanner;

public class AverageDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        double sum = 0;
        int count = 0;

        System.out.println("请输入数字,输入非数字结束:");
        while (sc.hasNextDouble()) {
            double x = sc.nextDouble();
            sum += x;
            count++;
        }

        if (count > 0) {
            System.out.println(count + " 个数的和是 " + sum);
            System.out.println("平均值是 " + (sum / count));
        }
        sc.close();
    }
}

while (sc.hasNextDouble()) 会一直读,直到你输入一个不是数字的东西(比如敲个字母回车)才停下来。

Scanner 常用方法速查

方法读到什么配套判断方法
next()一个「单词」,空格或回车就停hasNext()
nextLine()一整行,回车才停,结果不含回车hasNextLine()
nextInt()一个 int 整数hasNextInt()
nextLong()一个 long 整数hasNextLong()
nextDouble()一个 double 小数hasNextDouble()
nextBoolean()true 或 falsehasNextBoolean()

规律很整齐:nextXxx() 负责读,hasNextXxx() 负责先探路。养成「探一下再读」的习惯,程序就不会被一次手滑输入干掉。

用完记得关闭

Scanner 用完后调 sc.close() 关掉,释放资源。这是个好习惯。前面每段示例结尾都写了 sc.close()

Warning

关掉包裹 System.in 的 Scanner,等于把标准输入一起关了。之后再 new 一个新 Scanner 也读不到东西,直接抛 NoSuchElementException。所以一个程序里通常只建一个 Scanner,在 main 的最后统一关闭,别在每个方法里各建各关。

Tip

在正式项目里,更现代的做法是用 try-with-resources 让 Scanner 自动关闭,写到第 67 章讲异常处理时你会见到。初学阶段手动 close() 就够了。

printf 的占位符速查

printf 里那些 %x 叫格式符,常用的几个:

格式符作用例子
%s字符串printf("%s", "hi")
%d十进制整数printf("%d", 10)
%f小数printf("%f", 3.14)
%n换行跨平台通用
%c单个字符printf("%c", 'A')

%f 还能控小数位,比如 %.2f 保留两位小数:printf("%.2f", 3.14159) 输出 3.14。格式化输出在字符串章节会更系统地讲。

一次读入一行里的多个整数

有时用户会在一行里敲好几个数,用空格隔开。这时可以配合 hasNextInt 循环读:

Scanner sc = new Scanner(System.in);
System.out.println("请输入若干整数,用空格分开:");
int sum = 0;
while (sc.hasNextInt()) {
    sum += sc.nextInt();
}
System.out.println("总和:" + sum);
sc.close();

只要还在同一行、且敲的是整数,就一直累加;敲个字母或回车结束,就报总和。这比「每行一个数」更贴近真实交互。

中文输入乱码怎么办

如果你在控制台输入中文,读出来变成乱码,多半是编码不一致。控制台按系统编码传字符,而你的源文件是 UTF-8,两边对不上就会乱。

创建 Scanner 时显式指定字符集:

import java.nio.charset.StandardCharsets;
import java.util.Scanner;

Scanner sc = new Scanner(System.in, StandardCharsets.UTF_8);

Scanner(InputStream, Charset) 这个构造方法是 Java 10 加的,用常量而不是字符串,拼错了编译期就报错,比较踏实。

老代码里常见另一种写法:

// 历史兼容,不推荐:字符集用字符串传,拼错要到运行时才炸
Scanner sc = new Scanner(System.in, "UTF-8");

功能一样,但字符串写错(比如打成 "UTF8 " 多个空格)编译器不管,运行时才抛 IllegalArgumentException。新代码统一用 StandardCharsets 常量。

Note

Windows 命令行默认可能是 GBK。除了改代码,也可以在命令行先执行 chcp 65001 把控制台切到 UTF-8。两边口径一致,乱码自然就没了。

Java 25 LTS 的新写法:IO.println 与 IO.readln

Java 25 LTS 正式引入了 java.lang.IO 类(JEP 512),专门简化控制台交互。它在 java.lang 包下,不用写 import 就能直接用:

public class HelloIO {
    public static void main(String[] args) {
        String name = IO.readln("请输入姓名:");   // 打印提示并读一行
        IO.println("你好," + name);
    }
}

对比一下老套路,能省掉不少仪式感的代码:

需求传统写法Java 25 写法
打印一行System.out.println(x)IO.println(x)
打印不换行System.out.print(x)IO.print(x)
读一行new Scanner(System.in).nextLine()IO.readln()
带提示读一行print 提示再 nextLine()IO.readln("提示:")

IO.readln() 永远返回字符串,要数字得自己转,和前面说的「全程 nextLine」策略是一个思路。

Note

IO 只提供最基础的读写,功能远不如 Scanner 丰富(没有 hasNextInt 这类探路方法)。它的定位是让初学者和写小脚本的人少敲几个字。本教程后续示例仍以 System.outScanner 为主,因为它们在 Java 8 到 25 各版本上都能跑。

小结

输出用 System.out.println(换行)、print(不换行)、printf(格式化占位)。错误信息可以走 System.err,方便单独重定向。

读键盘用 Scanner,先 new Scanner(System.in)。读字符串 next() 遇空格停、nextLine() 遇回车停,两者混用会踩吞行坑。

读数字先 hasNextXxx() 判断再 nextXxx() 读取,避免 InputMismatchException。用完 sc.close() 关闭,且整个程序只关一次。

常见疑问

问:print 里写 \nprintf 里写 %n 有区别吗?

有。\n 固定是换行符 LF,%n 会按当前操作系统给出对应的行分隔符(Windows 上是 CRLF)。只在控制台看,两者没差别;要把内容写进文件给别的程序读,用 %n 更稳妥。

问:为什么我明明输入了内容,程序却好像没反应?

先确认 Scanner 是不是在等一个它要不到的东西。比如你调了 nextInt(),却输入了 12ab,Scanner 会一直卡着等合法整数。另一种情况是漏了按回车——Scanner 是按行缓冲的,不敲回车它收不到任何字符。

问:一定要用 Scanner 吗?有别的读输入方式吗?

有。BufferedReaderInputStreamReader 读大量数据更快,Java 25 还多了 IO.readln()。但 Scanner 胜在会自动切分单词、自动转类型、还带 hasNextXxx 探路,学习阶段最顺手。等到第 87 章讲字符流时会见到 BufferedReader 的写法。