目录
一、I/O Stream 概述
I/O Stream(输入/输出流)是计算机编程中用于处理输入和输出的抽象概念。它代表了一个数据源(如文件、网络、键盘等)或一个数据目标(如显示器、文件、网络等)。流的概念使得程序员能够以统一的方式处理不同类型的数据源和目标,而无需关心底层的具体实现细节。
二、流的基本特性
-
抽象性:
- 流是对数据源和数据目标的抽象。无论数据是来自磁盘文件、网络接口还是键盘输入,程序员都可以通过流来读取数据。同样,无论数据是输出到显示器、打印机还是网络,程序员都可以通过流来写入数据。
-
数据格式多样性:
- 流支持多种数据格式,包括字节数据、各种基本数据类型的数据、本地字符数据和对象数据等。
-
设备无关性:
- 流的概念使得程序能够与各种外部设备(如显示器、键盘、鼠标、打印机、扫描仪、网络接口等)进行交互,而不需要关心设备的具体细节。
三、流的分类
-
字节流(Byte Stream):
- 字节流用于处理8位数据(即字节)的输入输出。Java中所有字节流类的父类是
InputStream或OutputStream。常用的字节流类有FileInputStream和FileOutputStream。其他字节流类的使用方式与这两个类相似,但实例化时稍有差别。 - 字节流是最基础的输入输出形式,适用于各种格式的数据。在程序中应尽量使用高阶流(如对象流),以简化开发工作。任何流用完后都应关闭,以避免资源浪费。由于打开文件或其他I/O操作可能失败(如磁盘损坏),相关操作应放在异常处理块中。
-
从一个文件读数据,输出到另一个文件
-
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class t2 { public static void main(String[] args) { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("output.txt"); out = new FileOutputStream("outputOutput.txt"); int c; while ((c = in.read())!= -1) { out.write(c); } } catch (IOException e) { System.err.println("文件复制过程中出现错误: " + e.getMessage()); } finally { if (in!= null) { try { in.close(); } catch (IOException e) { System.err.println("关闭输入流时出现错误: " + e.getMessage()); } } if (out!= null) { try { out.close(); } catch (IOException e) { System.err.println("关闭输出流时出现错误: " + e.getMessage()); } } } } }
-
- 读取一个二进制文件(如图片文件)
-
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class t3 { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("java.png"); FileOutputStream fos = new FileOutputStream("output.jpg")) { int byteRead; while ((byteRead = fis.read())!= -1) { fos.write(byteRead); } } catch (IOException e) { e.printStackTrace(); } } }
-
- 字节流用于处理8位数据(即字节)的输入输出。Java中所有字节流类的父类是
-
字符流(Character Stream):
- 字符流以字符为单位处理数据,适用于处理文本数据。字符流会根据本地字符编码(如UTF-8、UTF-16等)自动进行字符转换。
- Java使用Unicode编码存储字符,并通过字符流(如FileReader和FileWriter)自动在Unicode与本地字符编码之间转换。这些字符流基于字节流(如FileInputStream和FileOutputStream),增加了对各种字符编码的支持,并支持逐字符或逐行输入输出。
- 当处理文本文件时,字符流可以方便地按字符或者按行来读取和写入内容。
-
import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class t4 { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("output.txt")); PrintWriter pw = new PrintWriter(new FileWriter("output2.txt"))) { String line; while ((line = br.readLine())!= null) { pw.println(line); } } catch (IOException e) { e.printStackTrace(); } } }
-
-
缓冲流(Buffered Stream):
- 缓冲流在底层流的基础上增加了缓冲区,可以提高I/O操作的效率。
- 在没有缓冲流时,如果要从文件读取数据,每次读取操作都需要直接与磁盘等设备进行交互。磁盘的读写速度相对较慢,频繁的直接读写会消耗大量时间。缓冲流通过在内存中建立缓冲区来提高数据输入输出的效率。当设备流(如文件流)与缓冲流结合使用时,缓冲区可以暂存数据,减少直接从设备读取或写入的次数,从而提高整体效率。例如,当设备输入流将数据填满缓冲区后,会通知缓冲流进行数据处理;而缓冲区被清空后,又会通知设备输入流继续填充数据。输出流的操作类似
- 2个字节缓冲流类: BufferedInputStream、BufferedOutputStream; 2个字符缓冲流类: BufferedReader、BufferedWriter; 还有1个常用的支持可自动刷新的缓冲流类: PrintWriter; 所谓自动刷新指的是不用等缓冲区满就可以从缓冲区输入输出数据。
-
import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class t5 { public static void main(String[] args) { try (BufferedReader in = new BufferedReader(new FileReader("output.txt")); PrintWriter out = new PrintWriter(new FileWriter("output3.txt"))) { String c; while ((c = in.readLine())!= null) { out.println(c); } } catch (IOException e) { e.printStackTrace(); } } }
-
扫描与格式化:
- 为了方便用户输入输出数据,Java 提供了数据的格式化输入输出功能。扫描类(如
Scanner)帮助程序员将用户输入的格式化数据转换为标记序列,这些标记最终对应于程序员感兴趣的各种类型的数据。格式化类(如PrintWriter、PrintStream)则负责将这些不同类型的数据包装为喜欢的格式。 -
import java.io.*; import java.util.Scanner; public class t6 { public static void main(String[] args) { Scanner s = null; PrintWriter out = null; File file = new File("output.txt"); if (file.exists()) { try { s = new Scanner(new BufferedReader(new InputStreamReader(new FileInputStream("output.txt"), "UTF-8"))); out = new PrintWriter(new FileWriter("output4.txt")); while ((s.hasNext())) { out.println(s.next()); } } catch (IOException e) { e.printStackTrace(); } finally { if (s!= null) { s.close(); } if (out!= null) { out.close(); } } } else { System.out.println("output.txt文件不存在"); } } }Scanner类主要关注输入的解析,与输入流紧密相关。import java.io.*; import java.util.*; public class t7 { public static void main(String[] args) { Scanner s = null; PrintWriter out = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader("f.txt"))); s.useLocale(Locale.US); out = new PrintWriter(new FileWriter("g.txt")); while ((s.hasNext())) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } out.println(sum); } catch (IOException e) { e.printStackTrace(); } finally { if (s!= null) { s.close(); } if (out!= null) { out.close(); } } } }从名为
f.txt的文件中读取数据,将其中的双精度浮点数累加起来,然后把累加结果写入到名为g.txt的文件中
-
Scanner类能够灵活、高效地处理各种输入数据,广泛应用于数据读取和解析的场景。
- 为了方便用户输入输出数据,Java 提供了数据的格式化输入输出功能。扫描类(如
-
数据流
-
数据流支持8种基本数据类型和字符串对象的二进制输入输出;所有数据流实现DataInput或DataOutput接口;最常用的数据流类有DataInputStream和DataOutputStream;数据流基于字节流实例化。
-
import java.io.*; import java.util.*; public class DataIO { public static void main(String[] args) throws IOException { Scanner scanner = null; DataOutputStream dataOutputStream = null; DataInputStream dataInputStream = null; String employeeName; int employeeSalary, year, month, day; String employeeGender; Date hireday; try { scanner = new Scanner(new BufferedReader(new FileReader("h.txt"))); dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("i.dat"))); while ((scanner.hasNext())) { String line = scanner.nextLine(); String[] tokens = line.split("\\|"); employeeName = tokens[0]; employeeSalary = Integer.parseInt(tokens[1]); employeeGender = tokens[2]; year = Integer.parseInt(tokens[3]); month = Integer.parseInt(tokens[4]); day = Integer.parseInt(tokens[5]); dataOutputStream.writeUTF(employeeName); dataOutputStream.writeInt(employeeSalary); dataOutputStream.writeUTF(employeeGender); dataOutputStream.writeInt(year); dataOutputStream.writeInt(month); dataOutputStream.writeInt(day); } dataOutputStream.close(); dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream("i.dat"))); try { while (true) { employeeName = dataInputStream.readUTF(); employeeSalary = dataInputStream.readInt(); employeeGender = dataInputStream.readUTF(); year = dataInputStream.readInt(); month = dataInputStream.readInt(); day = dataInputStream.readInt(); // 修改后的格式化输出语句 System.out.format("%-15s%-8d%4s%6d-%2d-%2d\n", employeeName, employeeSalary, employeeGender, year, month, day); } } catch (EOFException e) { } } finally { if (scanner!= null) { scanner.close(); } if (dataInputStream!= null) { dataInputStream.close(); } if (dataOutputStream!= null) { dataOutputStream.close(); } } } }
-
当程序运行时,它会先从
h.txt读取数据,将这些数据转换格式后写入i.dat文件,然后再从i.dat文件读取数据并在控制台输出。 -
在
DataIO.java程序中,i.dat文件是通过DataOutputStream写入数据的。DataOutputStream将数据以二进制形式存储,当使用记事本打开时,记事本期望的是文本数据(如UTF - 8、ASCII等编码的文本),而不是这种二进制数据格式,所以会显示乱码。 -

-
-
对象流
- 对象流支持对象的输入和输出,大多数标准类实现Serializable接口。它包括ObjectInputStream和ObjectOutputStream两个类,能够处理对象及其引用的所有属性,并确保相同对象只存储一次备份。对象流可以同时处理对象和基本数据类型。
-
import java.io.*; import java.math.BigDecimal; import java.util.Calendar; public class ObjectStreamsDemo { static final String dataFile = "obj.dat"; static final BigDecimal[] prices = { new BigDecimal("19.89"), new BigDecimal("199.99"), new BigDecimal("29"), new BigDecimal("9.89"), new BigDecimal("219.8"), new BigDecimal("39.4") }; static final int[] units = {12, 10, 14, 30, 48, 24}; static final String[] names = { "手机壳", "电饭煲", "小冰墩墩玩偶", "指甲剪", "电动牙刷", "乒乓球拍" }; public static void main(String[] args) throws IOException, ClassNotFoundException { // 将数据写入文件 ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream( new FileOutputStream(dataFile))); out.writeObject(Calendar.getInstance()); for (int i = 0; i < prices.length; i++) { out.writeObject(prices[i]); out.writeInt(units[i]); out.writeUTF(names[i]); } } finally { if (out!= null) { out.close(); } } // 从文件中读取数据并计算总值 ObjectInputStream in = null; try { in = new ObjectInputStream(new BufferedInputStream( new FileInputStream(dataFile))); Calendar date = (Calendar) in.readObject(); System.out.format("日期: %tA, %<tB %<te%n", date); BigDecimal total = new BigDecimal(0); BigDecimal price; int unit; String name; try { while (true) { price = (BigDecimal) in.readObject(); unit = in.readInt(); name = in.readUTF(); System.out.format("你订购了 %d 件 单价 %7.2f 元 的 %s%n", unit, price, name); total = total.add(price.multiply(new BigDecimal(unit))); } } catch (EOFException e) { System.out.format("商品总值:%7.2f 元%n", total); } } finally { if (in!= null) { in.close(); } } } } 

- 它首先将一些数据(包括一个
Calendar实例、BigDecimal类型的价格、int类型的数量和String类型的商品名称)写入到一个名为obj.dat的二进制文件中。然后再从这个文件中读取数据,计算商品总值并打印出相关信息。

1873

被折叠的 条评论
为什么被折叠?



