1. Java输入输出基础入门指南
作为一名从零开始学习Java的新手,掌握输入输出操作是打开编程世界大门的第一把钥匙。Java的I/O系统看似简单,但其中蕴含着许多初学者容易忽略的细节和技巧。让我们从最基础的System.out.println()开始,逐步构建完整的输入输出知识体系。
1.1 控制台输出基础
Java中最简单的输出方式是使用System.out对象,这是每个Java程序员最先接触的I/O操作:
java复制System.out.println("Hello, World!"); // 输出并换行
System.out.print("不换行输出"); // 输出但不换行
System.out.printf("格式化输出:%d", 100); // 格式化输出
注意:println()方法在输出内容后会追加一个换行符,而print()不会。这在需要连续输出多个内容时特别重要。
格式化输出是实际开发中非常实用的功能,常见的格式说明符包括:
- %d:整数
- %f:浮点数
- %s:字符串
- %n:平台相关的换行符
java复制System.out.printf("姓名:%s,年龄:%d,身高:%.2f米%n", "张三", 25, 1.75);
1.2 控制台输入详解
Java中获取用户输入有几种不同方式,每种适合不同的场景:
1.2.1 Scanner类基础用法
Scanner是Java 5引入的实用工具类,适合大多数控制台输入场景:
java复制import java.util.Scanner;
public class InputDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入您的姓名:");
String name = scanner.nextLine(); // 读取整行
System.out.print("请输入您的年龄:");
int age = scanner.nextInt(); // 读取整数
System.out.print("请输入您的身高(米):");
double height = scanner.nextDouble(); // 读取浮点数
System.out.printf("您好,%s!您今年%d岁,身高%.2f米。%n", name, age, height);
scanner.close(); // 重要:关闭Scanner释放资源
}
}
实操心得:nextInt()和nextDouble()等方法不会消耗行尾的换行符,如果后面跟着nextLine(),会直接读取空行。解决方法是在读取数值后额外调用一次nextLine()消耗换行符。
1.2.2 处理输入验证
健壮的程序应该验证用户输入:
java复制Scanner scanner = new Scanner(System.in);
int age = 0;
boolean validInput = false;
while (!validInput) {
System.out.print("请输入年龄(18-99):");
if (scanner.hasNextInt()) {
age = scanner.nextInt();
if (age >= 18 && age <= 99) {
validInput = true;
} else {
System.out.println("年龄必须在18-99之间!");
}
} else {
System.out.println("请输入有效的整数!");
scanner.next(); // 消耗无效输入
}
}
1.2.3 BufferedReader替代方案
对于需要高性能输入的场景,可以使用BufferedReader:
java复制import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入您的职业:");
String job = reader.readLine();
System.out.println("您的职业是:" + job);
}
}
1.3 文件输入输出操作
1.3.1 文件写入基础
Java提供了多种文件写入方式,这里介绍最常用的FileWriter:
java复制import java.io.FileWriter;
import java.io.IOException;
public class FileWriteDemo {
public static void main(String[] args) {
try (FileWriter writer = new FileWriter("output.txt")) {
writer.write("这是第一行内容\n");
writer.append("这是追加的第二行内容\n");
System.out.println("文件写入成功!");
} catch (IOException e) {
System.err.println("文件写入失败:" + e.getMessage());
}
}
}
注意事项:使用try-with-resources语句可以自动关闭资源,避免内存泄漏。这是Java 7引入的特性,比传统的try-catch-finally更简洁安全。
1.3.2 文件读取实践
使用BufferedReader读取文件是高效的方式:
java复制import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReadDemo {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("文件读取失败:" + e.getMessage());
}
}
}
1.3.3 二进制文件处理
对于非文本文件,需要使用字节流:
java复制import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BinaryFileDemo {
public static void main(String[] args) {
try (FileInputStream in = new FileInputStream("source.jpg");
FileOutputStream out = new FileOutputStream("copy.jpg")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("文件复制完成!");
} catch (IOException e) {
System.err.println("文件操作失败:" + e.getMessage());
}
}
}
1.4 常见问题与解决方案
1.4.1 输入不匹配异常处理
java复制Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数:");
try {
int number = scanner.nextInt();
System.out.println("您输入的是:" + number);
} catch (java.util.InputMismatchException e) {
System.out.println("错误:请输入有效的整数!");
scanner.next(); // 消耗无效输入
}
1.4.2 文件路径问题
文件路径是常见的错误来源,建议:
- 使用相对路径时,注意当前工作目录
- 跨平台路径使用File.separator或Paths.get()
- 检查文件是否存在和可读写权限
java复制import java.nio.file.Paths;
import java.nio.file.Files;
String filePath = Paths.get("data", "input.txt").toString();
if (Files.exists(Paths.get(filePath))) {
// 文件存在,进行读取操作
} else {
System.err.println("文件不存在:" + filePath);
}
1.4.3 资源泄漏预防
确保所有I/O资源都被正确关闭:
- 优先使用try-with-resources
- 在finally块中手动关闭资源
- 避免多次关闭同一资源
java复制// 不推荐的方式(可能泄漏资源)
FileWriter writer = null;
try {
writer = new FileWriter("output.txt");
writer.write("内容");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 推荐的方式(自动资源管理)
try (FileWriter writer = new FileWriter("output.txt")) {
writer.write("内容");
} catch (IOException e) {
e.printStackTrace();
}
1.5 高级I/O操作技巧
1.5.1 使用NIO进行文件操作
Java NIO (New I/O) 提供了更高效的文件操作方式:
java复制import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
// 读取所有行到List
List<String> lines = Files.readAllLines(Paths.get("input.txt"));
// 写入内容到文件
String content = "新的内容";
Files.write(Paths.get("output.txt"), content.getBytes(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
1.5.2 对象序列化
Java对象序列化允许将对象保存到文件或通过网络传输:
java复制import java.io.*;
class Person implements Serializable {
private String name;
private int age;
// 构造方法、getter和setter省略
}
// 序列化对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("person.dat"))) {
Person person = new Person("张三", 25);
oos.writeObject(person);
}
// 从文件反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("person.dat"))) {
Person person = (Person) ois.readObject();
System.out.println(person.getName() + ", " + person.getAge());
}
1.5.3 使用Files类简化操作
Java 7引入的Files类提供了许多便捷方法:
java复制import java.nio.file.*;
// 复制文件
Files.copy(Paths.get("source.txt"), Paths.get("dest.txt"),
StandardCopyOption.REPLACE_EXISTING);
// 移动/重命名文件
Files.move(Paths.get("old.txt"), Paths.get("new.txt"));
// 创建临时文件
Path tempFile = Files.createTempFile("prefix", ".suffix");
// 读取小文件全部内容
byte[] allBytes = Files.readAllBytes(Paths.get("smallfile.bin"));
1.6 性能优化建议
1.6.1 缓冲区的使用
对于频繁的I/O操作,使用缓冲可以显著提高性能:
java复制// 缓冲写入示例
try (BufferedWriter writer = new BufferedWriter(
new FileWriter("buffered.txt"))) {
for (int i = 0; i < 10000; i++) {
writer.write("第" + i + "行\n");
}
}
// 缓冲读取示例
try (BufferedReader reader = new BufferedReader(
new FileReader("buffered.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
// 处理每一行
}
}
1.6.2 大文件处理策略
处理大文件时,避免一次性读取全部内容:
java复制// 逐行处理大文件
try (BufferedReader reader = new BufferedReader(
new FileReader("largefile.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
// 处理单行内容
}
}
// 分块读取二进制大文件
try (InputStream in = new BufferedInputStream(
new FileInputStream("largefile.bin"))) {
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
// 处理读取的数据块
}
}
1.6.3 并行流处理文件
Java 8的流API可以简化文件处理:
java复制import java.nio.file.*;
import java.util.stream.*;
// 使用流处理文件行
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
lines.filter(line -> line.contains("error"))
.forEach(System.out::println);
}
// 并行处理大文件
try (Stream<String> lines = Files.lines(Paths.get("largefile.txt"))
.parallel()) {
long count = lines.filter(line -> !line.isEmpty())
.count();
System.out.println("非空行数:" + count);
}
1.7 实战案例:简单的日记本应用
结合前面所学,我们实现一个简单的控制台日记本:
java复制import java.io.*;
import java.time.*;
import java.time.format.*;
import java.util.*;
public class SimpleDiary {
private static final String DIARY_FILE = "diary.txt";
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\n1. 写日记");
System.out.println("2. 查看日记");
System.out.println("3. 退出");
System.out.print("请选择操作:");
int choice;
try {
choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
} catch (InputMismatchException e) {
System.out.println("请输入有效数字!");
scanner.next(); // 消耗无效输入
continue;
}
switch (choice) {
case 1:
writeDiary(scanner);
break;
case 2:
readDiary();
break;
case 3:
System.out.println("再见!");
scanner.close();
return;
default:
System.out.println("无效选择!");
}
}
}
private static void writeDiary(Scanner scanner) {
System.out.print("请输入今天的日记内容:");
String content = scanner.nextLine();
String timestamp = LocalDateTime.now().format(DATE_FORMATTER);
String entry = String.format("[%s]%n%s%n%n", timestamp, content);
try (FileWriter writer = new FileWriter(DIARY_FILE, true);
BufferedWriter bw = new BufferedWriter(writer)) {
bw.write(entry);
System.out.println("日记保存成功!");
} catch (IOException e) {
System.err.println("保存日记失败:" + e.getMessage());
}
}
private static void readDiary() {
System.out.println("\n=== 我的日记 ===");
try (BufferedReader reader = new BufferedReader(new FileReader(DIARY_FILE))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.out.println("还没有写过日记呢!");
} catch (IOException e) {
System.err.println("读取日记失败:" + e.getMessage());
}
}
}
这个案例综合运用了控制台输入输出、文件读写、日期处理和异常处理等知识点,是很好的入门练习项目。
