本文纲要IO概述IO分类字节流 – 字节输出流快速入门字节流 – 注意事项字节流 – 一次写多个数据字节流 – 两个问题换行与追加写入字节流 –try-catch捕获异常字节流 – 字节输出流小结字节流 – 字节输入流基本学习字节流 – 读多个字节字节流 – 文件复制字节流 – 定义小数组拷贝字节流 – 小数组拷贝原理IO概述在Java中IOInput/Output用于实现程序与外部数据通常是文件的交互。早期的数据存储方式往往依赖变量、数组或集合这些数据保存在内存中程序结束后便会丢失无法实现永久化存储。引入IO后我们可以将数据写出到硬盘文件实现持久化也可以从文件中读取数据到内存中使用。IO流的两个核心操作写数据将内存中的数据写入文件Output。读数据将文件中的数据读入内存Input。以内存为参照物的数据流动方向输入 Input / 读输出 Output / 写硬盘/文件内存输入Input硬盘 → 内存由内存进行读操作。输出Output内存 → 硬盘由内存进行写操作。因此学习IO的重点就是掌握如何读和如何写。IO分类根据不同的维度Java的IO流可以分成以下几种类型分类依据类型说明流向输入流InputStream硬盘 → 内存读输出流OutputStream内存 → 硬盘写操作的数据类型字节流Byte Stream可以操作所有类型的文件文本、图片、音视频等字符流Character Stream只能操作纯文本文件纯文本文件的判断标准用Windows自带的记事本Notepad打开文件如果能读懂内容就是纯文本文件否则就是非纯文本文件必须使用字节流操作。例如.txt文件纯文本 → 可用字符流.doc.avi.mp3.jpg等非纯文本 → 必须用字节流注意Office文件Word、Excel虽然包含文字但内部含有格式、图片等二进制数据用记事本打开会显示乱码因此不是纯文本只能使用字节流。字节流 – 字节输出流快速入门写数据的三个步骤创建字节输出流对象关联目标文件。调用write方法写入数据。释放资源调用close方法。代码示例项目结构bytestream/ └── src/ └── com/ └── wb/ └── output/ ├── OutputDemo1.java ├── OutputDemo2.java ├── ... └── OutputDemo10.javaOutputDemo1.java 展示了最基本的写操作packagecom.wb.output;importjava.io.FileOutputStream;importjava.io.IOException;publicclassOutputDemo1{publicstaticvoidmain(String[]args)throwsIOException{//1.创建字节输出流对象 --- 告诉虚拟机我要往哪个文件中写数据FileOutputStreamfosnewFileOutputStream(D:\\a.txt);//也可以使用File对象//FileOutputStream fos new FileOutputStream(new File(D:\\a.txt));//2.写数据fos.write(97);//97对应ASCII码中的a//3.释放资源fos.close();}}运行后D:\a.txt被创建内容为字母a。字节流 – 注意事项OutputDemo2.java 演示了以下注意点packagecom.wb.output;importjava.io.FileOutputStream;importjava.io.IOException;publicclassOutputDemo2{publicstaticvoidmain(String[]args)throwsIOException{//1.创建字节输出流对象//注意如果文件不存在会自动创建// 如果文件存在会清空原有内容FileOutputStreamfosnewFileOutputStream(C:\\wb\\a.txt);//2.写数据传递一个整数实际写入的是该整数在码表中对应的字符fos.write(98);//b//3.释放资源告诉操作系统不再使用该文件fos.close();}}关键结论文件不存在 → 自动创建。文件已存在 → 内容会被清空除非开启追加模式。write(int)写入的是整数对应的字符ASCII/Unicode码表。close()必须调用否则文件可能被占用而无法删除可运行一个死循环测试不调用close时文件无法删除。字节流 – 一次写多个数据字节输出流提供了三种写数据的方式方法说明write(int b)一次写一个字节write(byte[] b)一次写一个字节数组全部write(byte[] b, int off, int len)写字节数组的一部分从off索引开始连续写len个字节OutputDemo3.java – 一次写多个单字节FileOutputStreamfosnewFileOutputStream(bytestream\\a.txt);fos.write(97);//afos.write(98);//bfos.write(99);//cfos.close();OutputDemo4.java – 一次写一个字节数组以及写部分数组FileOutputStreamfosnewFileOutputStream(bytestream\\a.txt);//一次写整个数组byte[]bys{97,98,99};fos.write(bys);//写入abc//一次写数组的一部分byte[]bys2{97,98,99,100,101,102,103};fos.write(bys2,1,2);//从索引1开始写2个字节即98(b)和99(c)fos.close();注意第三个参数len是写入的字节个数不是结束索引。字节流 – 两个问题换行与追加写入1 ) 如何实现换行不同操作系统的换行符Windows\r\nLinux\nMac\r将换行符转换成字节数组后写入即可fos.write(\r\n.getBytes());OutputDemo5.java 示例包含追加和换行FileOutputStreamfosnewFileOutputStream(bytestream\\a.txt,true);//追加模式fos.write(97);fos.write(\r\n.getBytes());fos.write(98);fos.write(\r\n.getBytes());fos.write(99);fos.write(\r\n.getBytes());fos.write(100);fos.write(\r\n.getBytes());fos.write(101);fos.write(\r\n.getBytes());fos.close();2 ) 如何实现追加写入构造方法FileOutputStream(String path, boolean append)append false默认清空原文件内容append true打开续写开关新内容追加到文件末尾使用true参数后每次运行都不会清空原有数据实现追加效果。字节流 – try-catch捕获异常之前的代码一直使用throws抛出异常但更规范的做法是使用try-catch-finally自行处理特别是保证close()一定被执行。标准异常处理结构FileOutputStreamfosnull;try{fosnewFileOutputStream(D:\\a.txt);fos.write(97);}catch(IOExceptione){e.printStackTrace();}finally{//finally中的代码一定会执行除非JVM退出if(fos!null){try{fos.close();}catch(IOExceptione){e.printStackTrace();}}}注意要点fos必须声明在try外部否则finally中无法访问。若new FileOutputStream或write出现异常fos可能仍为null调用close会引发空指针异常因此需做非空判断。close本身也可能抛出异常需要再次捕获。字节流 – 字节输出流小结步骤要点创建对象关联目标文件文件不存在则创建存在则清空除非appendtrue写数据可写单个字节、整个字节数组或数组的一部分换行需写\r\n的字节形式释放资源close()必须执行通常放在finally中并做非空判断字节流 – 字节输入流基本学习读取文件的三步骤创建FileInputStream对象关联源文件。调用read方法读取字节。释放资源。OutputDemo7.javaFileInputStreamfisnewFileInputStream(bytestream\\a.txt);intreadfis.read();//read方法一次读取一个字节返回该字节对应的ASCII码0~255//若想看到字符需强转为charSystem.out.println((char)read);// 输出a假设文件内容为afis.close();注意若文件不存在直接抛出FileNotFoundException。read()返回的是字节数据int类型到达文件末尾时返回-1。字节流 – 读多个字节文件中通常有多个字节需要循环读取直到返回-1。OutputDemo8.java标准循环读取FileInputStreamfisnewFileInputStream(bytestream\\a.txt);intb;while((bfis.read())!-1){System.out.print((char)b);}fis.close();错误示范死循环读取//错误无法停止且一直读到文件结束后会打印大量-1或乱码while(true){intifis.read();System.out.println(i);}正确做法中以read()返回值是否为-1作为循环结束条件。字节流 – 文件复制文件复制的本质从源文件数据源读取字节写入目标文件目的地。需要同时使用FileInputStream和FileOutputStream。OutputDemo9.javaFileInputStreamfisnewFileInputStream(C:\\wb\\a.avi);FileOutputStreamfosnewFileOutputStream(bytestream\\a.avi);intb;while((bfis.read())!-1){fos.write(b);//读一个字节立即写一个字节}fis.close();fos.close();该方式每次读写一个字节当文件较大时效率较低。字节流 – 定义小数组拷贝解决大文件拷贝效率问题一次性读写多个字节类似“用篮子一次买多个鸡蛋”。提高效率的方案使用read(byte[] b)一次读取多个字节存入数组返回值是本次读取的有效字节数。使用write(byte[] b, int off, int len)将数组中指定部分写出。OutputDemo10.javaFileInputStreamfisnewFileInputStream(C:\\wb\\a.avi);FileOutputStreamfosnewFileOutputStream(bytestream\\a.avi);byte[]bytesnewbyte[1024];//相当于“篮子”一次读取1024字节intlen;//本次读到的实际字节数while((lenfis.read(bytes))!-1){fos.write(bytes,0,len);//只写出实际读到的有效字节}fis.close();fos.close();为什么不用整个数组作为写入长度最后一次读取可能填不满数组文件剩余字节不足1024len记录的就是实际数据量若写成fos.write(bytes)会将数组末尾脏数据一并写入导致文件错误。字节流 – 小数组拷贝原理用示意图解释小数组拷贝过程。假设数组长度为2源文件内容为ABCDE五个字节。目的地bytes[2]数据源 (ABCDE)目的地bytes[2]数据源 (ABCDE)初始为空[A,B] , len2[C,D] (覆盖A,B) , len2[E,D] (只覆盖第一个位置) , len1第1次read读2字节(A,B)write(bytes,0,2) 写入 A,B第2次read读2字节(C,D)write(bytes,0,2) 写入 C,D第3次read只剩1字节(E)write(bytes,0,1) 只写入 Elen始终表示实际读到的字节数第3次只有1个字节所以写出时长度必须为1否则会把上一次残留的D也写入这正是为什么在write中必须指定0到len的范围。总结通过上述内容我们已经掌握了Java中最基础的字节流操作包括文件读写、异常处理、效率优化等核心技能。后续可进一步学习字符流、缓冲流等高级特性。