Java IO 实战学习指南:从基础到进阶(附代码实操)

Java IO(输入/输出)是Java开发的核心基础,负责程序与外部设备(文件、网络、键盘等)的数据交互。很多初学者觉得IO类繁多、流概念抽象,其实掌握“先理解模型,再分场景实操”的思路就能快速上手。本文从核心概念到实战案例,带你一步步学会Java IO编程。

一、先搞懂3个核心前提(避免踩坑)

在写代码前,必须明确IO的核心逻辑,否则容易陷入“流用错、资源漏”的坑:

  1. IO的核心是“流”:数据像水流一样从源头(输入流)流向目的地(输出流),不可回退(除非用缓冲流);
  2. 按数据类型分2大类
    • 字节流(InputStream/OutputStream):处理所有数据(图片、视频、文本等),最小单位是字节(1byte=8bit);
    • 字符流(Reader/Writer):仅处理文本数据(.txt、.java等),最小单位是字符(依赖编码,如UTF-8中1个中文=3字节);
  3. 资源必须关闭:IO流是“稀缺资源”,不用时必须关闭,否则会导致文件占用、内存泄露(推荐用Java 7+的try-with-resources自动关闭)。

二、入门实操:从最常用的“文件IO”开始

文件操作是IO最基础的场景,先掌握字节流和字符流的文件读写,再扩展到其他场景。

场景1:字节流读写文件(万能场景,推荐优先掌握)

适合读写任意文件(文本、图片、视频等),核心类:FileInputStream(读)、FileOutputStream(写)。

案例1:读取文件内容(字节流)

需求:读取本地test.txt文件的内容,打印到控制台。

import java.io.FileInputStream;
import java.io.IOException;

public class FileByteReadDemo {
    public static void main(String[] args) {
        // 关键:用try-with-resources声明流,自动关闭(无需手动close())
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            byte[] buffer = new byte[1024]; // 缓冲区(一次读1024字节,提升效率)
            int len; // 记录每次实际读取的字节数

            // 循环读取:read()返回-1表示读取完毕
            while ((len = fis.read(buffer)) != -1) {
                // 字节数组转字符串(注意编码一致,避免乱码)
                String content = new String(buffer, 0, len, "UTF-8");
                System.out.print(content);
            }
        } catch (IOException e) {
            // 捕获IO异常(文件不存在、权限不足等)
            e.printStackTrace();
        }
    }
}

代码说明

  • byte[] buffer = new byte[1024]:缓冲区是IO高效读写的关键,避免每次只读1字节(频繁IO操作耗时);
  • new String(buffer, 0, len, "UTF-8"):必须指定编码,否则默认使用系统编码(Windows是GBK,Linux是UTF-8,容易乱码);
  • try-with-resources:流对象在try括号内声明,代码执行完自动关闭,无需手动写fis.close()
案例2:写入文件内容(字节流)

需求:将字符串“Java IO 实战”写入output.txt,若文件不存在则创建,存在则覆盖。

import java.io.FileOutputStream;
import java.io.IOException;

public class FileByteWriteDemo {
    public static void main(String[] args) {
        String content = "Java IO 实战";
        // 追加内容:在构造方法加true(默认false=覆盖)
        try (FileOutputStream fos = new FileOutputStream("output.txt", true)) {
            // 字符串转字节数组(指定UTF-8编码)
            byte[] data = content.getBytes("UTF-8");
            fos.write(data); // 写入字节数组
            fos.flush(); // 强制刷新缓冲区(确保数据立即写入文件)
            System.out.println("写入成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • FileOutputStream("output.txt", true):第二个参数true表示“追加模式”,不会覆盖原有内容;
  • fos.flush():字节流缓冲区满了才会自动写入,flush()强制清空缓冲区,避免数据残留(字符流同理)。

场景2:字符流读写文本文件(专门处理文本,避免乱码)

字符流是字节流的“包装”,自动处理编码转换,适合读写文本文件(.txt、.md等),核心类:FileReader(读)、FileWriter(写)。

案例3:字符流读取文本文件

需求:读取test.txt(UTF-8编码)的文本内容,按行打印。

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class FileCharReadDemo {
    public static void main(String[] args) {
        // 包装流:BufferedReader(缓冲字符流,支持按行读取)
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line; // 记录每行内容
            // readLine():读取一行文本,返回null表示结束
            while ((line = br.readLine()) != null) {
                System.out.println("行内容:" + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • BufferedReader是“缓冲包装流”,基于FileReader增强,提供readLine()按行读取,效率比直接用FileReader高;
  • FileReader默认使用系统编码,若文件是UTF-8编码且系统编码不是UTF-8(如Windows),会乱码!解决方案:用InputStreamReader指定编码:
    // 解决乱码:FileInputStream + InputStreamReader(手动指定编码)
    BufferedReader br = new BufferedReader(
        new InputStreamReader(new FileInputStream("test.txt"), "UTF-8")
    );
    
案例4:字符流写入文本文件

需求:将多行文本写入note.txt,按行分隔。

import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;

public class FileCharWriteDemo {
    public static void main(String[] args) {
        // BufferedWriter:缓冲字符流,支持newLine()换行
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("note.txt"))) {
            bw.write("第一行:Java IO 字符流");
            bw.newLine(); // 跨平台换行(Windows是\r\n,Linux是\n,自动适配)
            bw.write("第二行:BufferedWriter 实战");
            bw.flush(); // 刷新缓冲区
            System.out.println("写入完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • bw.newLine():比手动写\n更通用,自动适配操作系统的换行符;
  • 若需指定编码,用OutputStreamWriter包装FileOutputStream
    BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(new FileOutputStream("note.txt"), "UTF-8")
    );
    

三、进阶实操:IO流的“包装思想”(核心技巧)

Java IO的设计核心是“装饰器模式”——通过“基础流+包装流”的组合,实现功能增强。比如:

  • 基础流:FileInputStream(读文件字节)、FileOutputStream(写文件字节);
  • 包装流:
    • 缓冲流:BufferedInputStream/BufferedOutputStream(提升读写效率);
    • 转换流:InputStreamReader/OutputStreamWriter(字节流转字符流,指定编码);
    • 数据流:DataInputStream/DataOutputStream(读写基本数据类型,如int、double)。

场景3:用数据流读写基本数据类型

需求:将int、double类型的数据写入文件,再读取出来(适合存储配置、数值数据)。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamDemo {
    public static void main(String[] args) {
        // 写入基本数据类型
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"))) {
            dos.writeInt(100); // 写int类型
            dos.writeDouble(3.14159); // 写double类型
            dos.writeUTF("Java IO 数据流"); // 写UTF-8字符串
            System.out.println("数据写入成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 读取基本数据类型(注意:读取顺序必须和写入顺序一致!)
        try (DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) {
            int num = dis.readInt();
            double pi = dis.readDouble();
            String str = dis.readUTF();
            System.out.println("读取结果:num=" + num + ", pi=" + pi + ", str=" + str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • 数据流的核心优势:能直接读写intdouble等基本类型,无需手动转换;
  • 关键注意:读取顺序必须和写入顺序完全一致(如先写int再写double,读取时也必须先读int再读double),否则会报EOFException或数据错乱。

场景4:文件复制(IO流综合实战)

需求:复制一张图片(或视频、文档),从source.jpgtarget.jpg(字节流万能复制,适合任意文件)。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyDemo {
    public static void main(String[] args) {
        // 源文件路径和目标文件路径
        String sourcePath = "source.jpg";
        String targetPath = "target.jpg";
        long startTime = System.currentTimeMillis(); // 记录开始时间

        // 用缓冲流包装基础流,提升复制效率
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {

            byte[] buffer = new byte[4096]; // 4KB缓冲区(常用最优大小)
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len); // 写入实际读取的字节数
            }
            bos.flush(); // 确保最后一批数据写入

            long endTime = System.currentTimeMillis();
            System.out.println("复制完成!耗时:" + (endTime - startTime) + "ms");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • 复制文件必须用字节流(字符流会损坏非文本文件,如图片、视频);
  • byte[] buffer = new byte[4096]:缓冲区大小建议设为2^n(如1024、4096、8192),平衡内存占用和效率;
  • 缓冲流(BufferedInputStream/BufferedOutputStream)能减少磁盘IO次数,比直接用基础流快10倍以上。

四、Java 8+ 新特性:NIO.2(Files工具类,简化IO操作)

Java 7引入的NIO.2(java.nio.file包)提供了更简洁的IO API,Files工具类封装了常用操作(读文件、写文件、复制、删除等),无需手动处理流,代码更简洁。

场景5:用Files工具类快速读写文本文件

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.io.IOException;

public class FilesDemo {
    public static void main(String[] args) {
        String filePath = "test.txt";

        try {
            // 1. 读取所有行(自动按行分割,返回List<String>)
            List<String> lines = Files.readAllLines(Paths.get(filePath), java.nio.charset.StandardCharsets.UTF_8);
            System.out.println("文件内容:");
            for (String line : lines) {
                System.out.println(line);
            }

            // 2. 写入文件(覆盖原有内容)
            String content = "Files工具类实战:简化IO操作";
            Files.write(Paths.get("newFile.txt"), content.getBytes(StandardCharsets.UTF_8));

            // 3. 复制文件(一行代码搞定)
            Files.copy(Paths.get("source.jpg"), Paths.get("target_nio.jpg"));

            System.out.println("操作完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • Files.readAllLines():一次性读取所有行,适合小文件(大文件不推荐,会占用大量内存);
  • Files.write():自动创建文件(父目录不存在会抛异常),支持字节数组、集合等参数;
  • 优势:代码简洁,无需手动管理流,自动处理资源关闭,是Java 8+推荐的简洁IO方式。

五、常见错误与避坑指南(新手必看)

  1. 乱码问题

    • 原因:字节流读文本时编码不匹配,或字符流未指定编码;
    • 解决方案:始终用InputStreamReader/OutputStreamWriter指定UTF-8编码,或用Files工具类显式指定编码。
  2. 资源泄露

    • 原因:忘记关闭流,导致文件被占用、内存泄露;
    • 解决方案:强制使用try-with-resources声明流(自动关闭),避免手动close()(容易遗漏)。
  3. 文件路径错误

    • 原因:相对路径写错(如test.txt实际在src/main/resources下,而非项目根目录);
    • 解决方案:用绝对路径测试(如C:/test.txt),或通过ClassLoader获取资源路径:
      // 获取resources目录下的文件
      String path = FileCopyDemo.class.getClassLoader().getResource("test.txt").getPath();
      
  4. 缓冲区未刷新

    • 原因:写入流后未调用flush(),缓冲区数据未写入文件;
    • 解决方案:写入完成后调用flush(),或依赖try-with-resources自动刷新。

六、学习路线建议(从易到难)

  1. 第一步:掌握字节流基础(FileInputStream/FileOutputStream),实现文件读写、复制;
  2. 第二步:学习字符流(FileReader/FileWriter)+ 缓冲流(BufferedReader/BufferedWriter),处理文本文件;
  3. 第三步:理解包装思想,学习转换流、数据流,掌握“基础流+包装流”的组合用法;
  4. 第四步:学习NIO.2(Files工具类),简化日常IO操作;
  5. 第五步:进阶学习NIO(通道、缓冲区),应对高并发IO场景(如网络编程)。

总结

Java IO学习的核心是“先理解流的模型,再分场景实操”。新手建议从文件读写、复制等简单场景入手,先能用起来,再深入理解“装饰器模式”“缓冲区原理”等底层逻辑。记住:IO编程的关键是“选对流、关对资源、避乱码”,多动手写案例(如复制文件、读写配置),很快就能熟练掌握。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐