一、引言

        你是不是也在 Java 输入输出上栽过跟头?用nextInt()后接nextLine(),明明没输入却多出空行;想把浮点数格式化成金额,结果显示一长串小数;读取文件时要么报 “路径错误”,要么找不到文件…… 其实这些问题都不是 “玄学”,而是没摸透 Java IO 的底层逻辑 ——Scanner的方法特性、printf的格式规则、文件路径的转义要求,每一个细节都藏着 “正确用法”。今天这篇博客,就从控制台输入的 “换行符坑” 讲起,到格式化输出的定制技巧,再到文件读写的路径与异常处理,把 Java 输入输出的核心知识点和实战坑点全拆透,让你看完就能避开 90% 的 IO 错误,写交互和文件代码时再也不用反复查手册!

二、控制台输入 —— 用Scanner类读取用户输入

   Scanner是 Java 处理控制台输入的核心类,它提供了多种方法读取不同类型的数据(整数、字符串、浮点数等),但需注意方法间的配合问题。

1. Scanner核心输入方法:分类与实战示例

   Scanner通过封装输入流(如System.in,对应控制台输入)实现数据读取,常用方法可按 “读取数据类型” 分类,以下是完整整理及示例:

方法

功能描述

示例代码

输入示例

输出结果

String nextLine()

读取整行内容(包括空格,以Enter结束)

Scanner sc = new Scanner(System.in); String line = sc.nextLine(); System.out.println(line);

今天天气晴

今天天气晴

String next()

读取下一个 “单词”(以空格 /Enter为分隔符)

Scanner sc = new Scanner(System.in); String word = sc.next(); System.out.println(word);

Hello World

Hello

int nextInt()

读取并转换为整数

Scanner sc = new Scanner(System.in); int num = sc.nextInt(); System.out.println(num + 10);

123

133

double nextDouble()

读取并转换为浮点数

Scanner sc = new Scanner(System.in); double d = sc.nextDouble(); System.out.println(d * 2);

3.14

6.28

boolean hasNext()

检测输入流中是否还有 “单词”

Scanner sc = new Scanner(System.in); while (sc.hasNext()) { System.out.println(sc.next()); }

Java IO

Java
IO

boolean hasNextInt()

检测输入流中是否还有整数

Scanner sc = new Scanner(System.in); if (sc.hasNextInt()) { System.out.println(sc.nextInt() + 5); }

45

50

boolean hasNextDouble()

检测输入流中是否还有浮点数

Scanner sc = new Scanner(System.in); if (sc.hasNextDouble()) { System.out.println(sc.nextDouble() + 1.5); }

2.5

4.0

2. 实战坑点:nextInt()nextLine()的 “残留换行符” 问题

        当nextInt()nextLine()配合使用时,会因 “换行符残留” 导致nextLine()读取空内容,这是初学者最常踩的坑。问题复现:

import java.util.Scanner;
public class Demo1 {
    public static void main(String[] args) {
        Scanner a = new Scanner(System.in); // 创建Scanner对象,直接打印会输出哈希码
        int num = a.nextInt(); // 读取整数,残留换行符
        String str1 = a.nextLine(); // 读取到残留的换行符,返回空串
        String str2 = a.nextLine(); // 正常读取第二次输入
        System.out.println(num);
        System.out.println(str1);
        System.out.println(str2);
    }
}

  • 原因解析

    • nextInt()仅读取整数部分(如123),不会读取输入末尾的\nEnter键产生),导致\n残留在输入流中;

    • nextLine()会读取 “当前位置到下一个\n” 的内容,遇到残留的\n时,直接返回空串。

两种解决方案

方案 1:nextLine()读取整数(再手动转类型)将 “读取整数” 也通过nextLine()实现,避免换行符残留,步骤:

  1. nextLine()读取整行字符串(如"123");

  2. 通过Integer.parseInt(字符串)将其转为int类型。

import java.util.Scanner;
public class Demo1 {
    public static void main(String[] args) {
        Scanner a = new Scanner(System.in);
        int num = Integer.parseInt(a.nextLine()); // 读取整行转整数,无残留
        String str1 = a.nextLine(); // 正常读取
        String str2 = a.nextLine();
        System.out.println(num);
        System.out.println(str1);
        System.out.println(str2);
    }
}

方案 2:若坚持用nextInt()读取整数,可在其后额外调用一次a.nextLine(),专门读取残留的\n,步骤:

  1. nextInt()读取整数后,调用a.nextLine()(不接收结果,仅 “消费”\n);

  2. 后续nextLine()正常读取内容。

import java.util.Scanner;
public class Demo1 {
    public static void main(String[] args) {
        Scanner a = new Scanner(System.in);
        int num = a.nextInt(); // 读取整数
        a.nextLine(); // 关键:吃掉残留的\n
        String str1 = a.nextLine(); // 正常读取
        String str2 = a.nextLine();
        System.out.println(num);
        System.out.println(str1);
        System.out.println(str2);
    }
}

三、格式化输出 —— 用printfString.format定制输出格式

        默认的System.out.print()输出格式粗糙(如浮点数显示过多小数位),而printf方法(沿用 C 语言风格)可通过 “格式说明符” 定制输出,满足金额、日期等场景的格式化需求。

1. printf核心语法:格式说明符 + 参数

   printf的语法为:System.out.printf("格式字符串", 参数1, 参数2, ...),其中 “格式字符串” 包含普通文本和 “%开头的格式说明符”(如%d表示整数,%s表示字符串)。

(1)常用转换符(指定数据类型)

转换符

数据类型

示例

输出结果

d

十进制整数

System.out.printf("%d", 159);

159

s

字符串

System.out.printf("Hello %s", "Java");

Hello Java

c

字符

System.out.printf("%c", 'H');

H

b

布尔值

System.out.printf("%b", 3 > 5);

false

f

定点浮点数

System.out.printf("%.2f", 10000.0/3.0);

3333.33

e

指数浮点数

System.out.printf("%.2e", 15.9);

1.59e+01

x

十六进制整数

System.out.printf("%x", 159);

9f

o

八进制整数

System.out.printf("%o", 159);

237

%

百分号(转义)

System.out.printf("%.2f%%", 99.5);

99.50%

h

散列码

System.out.printf("%h", "Java");

42628b2

tx/Tx

日期时间(T 强制大写)

System.out.printf("%tc", new Date());

Mon Feb 09 18:05:19 PST 2015

n

平台相关行分隔符

System.out.printf("a%nb", "Java");

a(换行)b

(2)常用标志(定制格式细节)

标志

目的

示例

输出结果

+

显示正负号

System.out.printf("%+8.2f", 10000.0/3.0);

+3333.33

正数前加空格

System.out.printf("% 8.2f", 10000.0/3.0);

3333.33(前有空格)

0

数字前补 0

System.out.printf("%08.2f", 10000.0/3.0);

003333.33

-

左对齐

System.out.printf("%-8.2f", 10000.0/3.0);

3333.33 (后有空格)

(

负数用括号包裹

System.out.printf("%(8.2f", -10000.0/3.0);

(3333.33)

,

添加分组分隔符(千分位)

System.out.printf("%,8.2f", 10000.0/3.0);

3,333.33

#

浮点数必含小数点

System.out.printf("%#8.0f", 10000.0/3.0);

3333.

#

十六进制加0x/ 八进制加0

System.out.printf("%#x", 159);

0x9f

$

指定参数索引

System.out.printf("%1$d %1$x", 159);

159 9f

<

复用前一个参数

System.out.printf("%d %<x", 159);

159 9f

(3)日期时间格式化(%t系列转换符)

        虽推荐用java.time包处理日期,但遗留代码中仍可能用到printf的日期格式化,通过 “%t+ 子转换符” 实现,文档中表 3-7 列出核心子转换符,整理常用场景:

转换符

日期时间类型

示例

输出结果

%tc

完整日期时间

System.out.printf("%tc", new Date());

Mon Feb 09 18:05:19 PST 2015

%tF

ISO 8601 日期(年 - 月 - 日)

System.out.printf("%tF", new Date());

2015-02-09

%tD

美国格式日期(月 / 日 / 年)

System.out.printf("%tD", new Date());

02/09/2015

%tT

24 小时时间(时:分: 秒)

System.out.printf("%tT", new Date());

18:05:19

%tR

24 小时时间(时:分)

System.out.printf("%tR", new Date());

18:05

%tr

12 小时时间(时:分: 秒 上午 / 下午)

System.out.printf("%tr", new Date());

06:05:19 pm

%tY

4 位年份

System.out.printf("%tY", new Date());

2015

%tB

月的完整拼写

System.out.printf("%tB", new Date());

February

%tA

星期的完整拼写

System.out.printf("%tA", new Date());

Monday

%td

两位数字的日

System.out.printf("%td", new Date());

09

2. String.format:创建格式化字符串(不直接输出)

        若需将格式化结果保存为字符串(而非直接打印),可使用String.format方法,语法与printf一致,返回值为格式化后的字符串。

String name = "张三";
int age = 25;
String message = String.format("Hello, %s. Next year, you'll be %d", name, age + 1);
System.out.println(message); // 输出:Hello, 张三. Next year, you'll be 26

四、文件输入输出 —— 读取文件内容与处理路径

        除控制台交互外,Java 还支持读写文件,核心是用Scanner读取文件(输入)、PrintWriter写入文件(输出),需注意路径处理和 IO 异常。

1. 文件输入:用Scanner读取文件内容

        读取文件需先构造 “指向文件的Scanner对象”,依赖java.nio.file.Paths获取文件路径,步骤如下:

import java.util.Scanner;
import java.nio.file.Paths;
import java.io.IOException; // 处理IO异常

public class Demo2 {
    public static void main(String[] args) {
        try {
            // 构造文件路径(绝对路径:D盘桌面的myfile.txt,注意反斜杠转义)
            Scanner in = new Scanner(Paths.get("D:\\桌面\\myfile.txt"), "UTF-8");
            
            // 逐行读取文件内容并打印
            while (in.hasNextLine()) { 
                System.out.println(in.nextLine()); 
            }
  • IO 异常处理(完整逻辑)

        读取文件时可能出现 “文件不存在”“权限不足” 等异常,Java 要求显式处理,除了在main方法声明throws IOException,更规范的方式是用try-catch捕获异常,确保程序遇到错误时能友好提示:

import java.util.Scanner;
import java.nio.file.Paths;
import java.io.IOException;

public class Demo2 {
    public static void main(String[] args) {
        try {
            // 1. 构造文件路径(修正路径:去掉末尾多余反斜杠,避免路径解析错误)
            Scanner in = new Scanner(Paths.get("D:\\桌面\\myfile.txt"), "UTF-8");
            
            // 2. 逐行读取文件内容(循环判断是否有下一行,避免读取越界)
            while (in.hasNextLine()) {
                System.out.println(in.nextLine()); // 打印文件每行内容
            }
            
            // 3. 关闭输入流(释放文件资源,避免资源泄漏)
            in.close();
        } catch (IOException e) {
            // 捕获IO异常,打印错误信息(便于定位问题)
            e.printStackTrace();
            System.out.println("文件读取出错:" + e.getMessage());
        }
    }
}

运行结果:若myfile.txt中内容为 “今天天气很好,我的心情也很好。”,控制台会准确输出该内容;若文件不存在,会打印 “文件读取出错:D:\ 桌面 \myfile.txt (系统找不到指定的文件。)”。

  • 文件路径定位技巧

        获取当前工作目录:用System.getProperty("user.dir")打印 Java 虚拟机启动路径,确认相对路径的基准目录,示例:

String currentDir = System.getProperty("user.dir");
System.out.println("当前工作目录:" + currentDir); // 输出如“D:\JavaProjects\Demo”

        绝对路径规范:Windows 系统路径需用\\转义(如D:\\桌面\\myfile.txt),Linux/Mac 系统用/(如/home/user/myfile.txt),避免因路径分隔符错误导致文件找不到。

        命令行重定向:若用命令行启动程序,可通过java 类名 < 输入文件 > 输出文件简化文件读写,无需处理IOException,示例:

java Demo2 < D:\桌面\myfile.txt > D:\桌面\output.txt

2. 文件输出:用PrintWriter写入文件内容

        除了读取文件,Java 还支持用PrintWriter向文件写入内容,语法与System.out类似,可直接调用print()/println()方法。

import java.io.PrintWriter;
import java.io.IOException;

public class FileWriteDemo {
    public static void main(String[] args) {
        try {
            // 1. 创建PrintWriter对象,指定写入的文件路径(若文件不存在则自动创建)
            PrintWriter out = new PrintWriter("D:\\桌面\\output.txt");
            
            // 2. 向文件写入内容(支持字符串、整数、浮点数等类型)
            out.println("今天学习了Java文件输出");
            out.print("整数:");
            out.println(123);
            out.print("浮点数:");
            out.printf("%.2f", 3.1415); // 支持格式化写入
            
            // 3. 关闭输出流(必须关闭,否则内容可能未写入文件)
            out.close();
            System.out.println("内容写入完成!");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件写入出错:" + e.getMessage());
        }
    }
}
  • 关键注意点

    • PrintWriter创建时,若文件已存在,会覆盖原有内容;

    • 必须调用close()方法或flush()方法,否则缓冲区内容不会写入文件(close()会自动调用flush())。

3. 文件读写核心总结

操作类型

核心类 / 方法

关键注意点

文件输入

Scanner + Paths.get()

路径需转义,处理 IO 异常,读取后关闭流

文件输出

PrintWriter

覆盖原有内容,关闭流确保写入,支持格式化

路径定位

System.getProperty("user.dir")

确认相对路径基准,绝对路径区分操作系统

异常处理

try-catch/throws IOException

捕获文件不存在、权限不足等异常,便于调试

结语

        从Scanner的 “残留换行符” 解决方案,到printf用转换符和标志定制输出格式,再到文件读写的路径处理与异常捕获,我们终于把 Java 输入输出的 “实战逻辑” 理清楚了。其实 IO 操作看似琐碎,却藏着程序与外界交互的核心逻辑 —— 控制台输入是 “听用户说话”,格式化输出是 “把话说清楚”,文件读写是 “和硬盘打交道”。下次写代码时,当你用nextLine()读取整数、用%,.2f格式化金额、用Paths.get()定位文件,你会发现这些操作都有章可循。把这篇指南收起来,下次遇到 IO 问题翻一翻,让每一次输入输出都稳稳当当。如果还想深挖更多 Java 实战技巧,关注我,后续继续拆解开发中那些 “一看就懂、一用就会” 的核心知识!

更多推荐