缓冲流和对象流
目录
本节任务
缓冲流
对象流
标准输入输出流
教学目标
掌握缓冲流的用法
掌握序列化和反序列化
掌握标准输入输出流的用法
教学内容
一、缓冲流
主要为了提高读写效率,增强基础流的功能
- 实例化File对象
- 实例化缓冲流对象,需要基础流作为参数
1. 缓冲输入流
(1)BufferedInputStream
// TODO 字符缓冲输入流BufferedReader
// 1.实例化一个BufferedReader
// 默认缓冲区大小:8192,当标记中指定的记录的字符数小于缓冲数组剩余长度时,不会发生异常
// 当缓冲数组足够大时,该标记一直会保留到当前缓冲数组装满
Reader reader = new BufferedReader(new FileReader(new File("f:/test.txt")),5);
// 2.BufferedReader中支持mark和reset
String result = "";
// 3.使用mark进行标记
result += (char)reader.read();
result += (char)reader.read();
result += (char)reader.read();
result += (char)reader.read();
// mark当中参数的作用:再读取多少个字符之后,标记点失效
reader.mark(3);
// 4.使用reset方法返回标记的位置
result += (char)reader.read();
result += (char)reader.read();
result += (char)reader.read();
// 失效:limit -> 要求记录的字符数 -> 失效条件:读取的字符总数会超出缓冲区大小
// 当需要记录的字符刚好超出了当前缓冲区,会在下一个缓冲区中继续存放,直到下一个缓冲区存满,标记失效
reader.reset();
result += (char)reader.read();
result += (char)reader.read();
result += (char)reader.read();
System.out.println(result);
reader.close();
- mark(int marklimit):标记位置
- reset():如果可能,回到标记位置
// TODO 缓冲流-标记和重置练习
// 在文本文件中指定一对特殊字符(能够被唯一识别的)
// 当读取到第一个特殊字符时,标记当前位置
// 当读取到第二个特殊字符时,重置,继续读取完所有文件内容
Reader reader = new BufferedReader(new FileReader(new File("f:/test.txt")));
int i = 0;
int count = 1;
String result = "";
while ((i = reader.read()) != -1) {
if ('!' == (char)i && count == 1) {
reader.mark(30);
count ++;
}else if('!' == (char)i && count == 2) {
reader.reset();
count ++;
}
result += (char)i;
}
System.out.println(result);
reader.close();
(2)BufferedReader
- readLine():直接读取一行
// TODO 字符缓冲流 - 每次读取一行 - 读取文件中所有内容
// 1.初始化缓冲流
BufferedReader bufferedReader = new BufferedReader(new FileReader(new File("E:/test.txt")));
String line = "";
// 2.读取文件内容
while ((line = bufferedReader.readLine()) != null) {
// 读取的内容本身不包括换行符
System.out.println(line);
}
// 3.关闭流
bufferedReader.close();
-
小练习
// TODO 从文件中读取所有数据 - 单词计数 // 1.数据采集 - 数据来源:文本文件 // (1)指定文件路径 // 使用File类指定数据文件路径 File dataFile = new File("E:/data.txt"); // 使用dataFile初始化一个输入流 Reader fileReader = new FileReader(dataFile); // 使用fileReader初始化一个缓冲流 BufferedReader bufferedReader = new BufferedReader(fileReader); // (2)使用缓冲流一次读取一行 // 定义一个集合,存储读取到的数据 List<String> lines = new ArrayList<>(); // 定义字符串变量,接收每次读取的一行数据 String temp = ""; // (3)将获取到的数据存放到集合当中 while ((temp = bufferedReader.readLine()) != null) { lines.add(temp); } // (4)关闭流 -> 关闭顺序与打开顺序相反 bufferedReader.close(); fileReader.close(); // 2.数据清洗 - 去除重复、缺失、偏差较大的数据 // 3.数据分析 - 统计计数 Map<String, Integer> result = new HashMap<>(); // (1)从集合中获取到每一行数据 for (String line : lines) { // (2)根据相应的分隔符取出每一个单词作为待统计数据 String[] words = line.split(" "); for (String word : words) { // (3)当某一个单词第一次出现时,记为1 // (4)当某一个单词再次出现时,将出现的次数累加 // 第一次出现该单词 if (!result.containsKey(word)) { result.put(word, 1); }else { // 取出当前单词已经出现的次数 int count = result.get(word); result.put(word, count + 1); } } } // (5)重复以上步骤,直到遍历结束 // 4.结果打印 for (Map.Entry<String, Integer> string : result.entrySet()) { System.out.println(string); }
2. 缓冲输出流
(1)BufferedOutputStream
(2)BufferedWriter
- newLine():写入一个行分隔符
二、对象流
如果流中流动的数据是对象则可称为对象流
- 序列化:将一个对象写入到本地文件中
- 反序列化:将一个本地文本文件中的对象读取出来
- 一个对象流只能操作一个对象
- 当需要同时序列化多个对象时,可以使用集合
// TODO 对象流
// 输出流 -> 序列化过程 : 将当前对象当中包含的信息(状态) -> 保存至文件当中
// 1-1.初始化对象输出流 -> 需要的参数 : 通用的输出流 -> FileOutputStream -> File -> 路径
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("E:/shop.txt")));
// 输入流 -> 反序列化过程 : 将文件当中存放的对象的信息 -> 返回一个对象(数据)
// 1-2.初始化对象输入流 -> 需要的参数 : 通用的输入流 -> FileInputStream -> File -> 路径
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("E:/shop.txt")));
// 2.获得一个自定义类的实例
Shop shop = new Shop("百货商厦");
// 3-1.序列化对象 - 自定义类实现序列化接口 - ID根据需要进行生成
outputStream.writeObject(shop);
// 3-2.反序列化 - 判断类型 - 强制转换接收
Object object = inputStream.readObject();
// 如果反序列化得到的对象是相应的实例
if (object instanceof Shop) {
// 检查是否成功反序列化
Shop newShop = (Shop)object;
System.out.println(newShop);
}
// 4.强制刷新
outputStream.flush();
// 5.关闭流
inputStream.close();
outputStream.close();
//Product类中有引用数据类型变量Shop
// TODO 对象流应用 - 对象的深度克隆
// 输出流 -> 序列化过程 : 将当前对象当中包含的信息(状态) -> 保存至文件当中
// 1-1.初始化对象输出流 -> 需要的参数 : 通用的输出流 -> FileOutputStream -> File -> 路径
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("E:/product.txt")));
// 输入流 -> 反序列化过程 : 将文件当中存放的对象的信息 -> 返回一个对象(数据)
// 1-2.初始化对象输入流 -> 需要的参数 : 通用的输入流 -> FileInputStream -> File -> 路径
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("E:/product.txt")));
// 2.获得一个自定义类的实例
Product oldProduct = new Product("商品1", 200, new Shop("商店1"));
// 3-1.序列化对象 - 自定义类实现序列化接口 - ID根据需要进行生成
outputStream.writeObject(oldProduct);
// 3-2.反序列化 - 判断类型 - 强制转换接收
Object object = inputStream.readObject();
// 如果反序列化得到的对象是相应的实例
if (object instanceof Product) {
// 检查是否成功反序列化
Product newProduct = (Product) object;
System.out.println(oldProduct);
System.out.println(newProduct);
oldProduct.setName("商品11");
newProduct.setName("商品2");
newProduct.getShop().setName("商店2");
System.out.println(oldProduct);
System.out.println(newProduct);
}
// 4.强制刷新
outputStream.flush();
// 5.关闭流
inputStream.close();
outputStream.close();
public class Product implements Serializable {
public Product() {
}
public Product(String name, Integer price) {
this.name = name;
this.price = price;
}
public Product(String name, Integer price, Shop shop) {
this.name = name;
this.price = price;
this.shop = shop;
}
private String name;
// 封装类型比较时注意缓存机制以及手动拆箱
private Integer price;
// 成员变量中出现自定义类型,实例是一个引用类型
private Shop shop;
public Shop getShop() {
return shop;
}
public void setShop(Shop shop) {
this.shop = shop;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Product [name=" + name + ", price=" + price + ", shop=" + shop + "]";
}
public class Shop implements Serializable {
private static final long serialVersionUID = -9099382180185766414L;
public Shop() {
}
public Shop(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Shop [name=" + name + "]";
}
}
1. 序列化
- ObjectOutputStream
- writeObject(Object obj):将对象写入流中
2. 反序列化
- ObjectInputStream
- readObject():从流中读取对象
// TODO 对象流应用 - 对象的深度克隆
// 输出流 -> 序列化过程 : 将当前对象当中包含的信息(状态) -> 保存至文件当中
// 1-1.初始化对象输出流 -> 需要的参数 : 通用的输出流 -> FileOutputStream -> File -> 路径
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("E:/product.txt")));
// 输入流 -> 反序列化过程 : 将文件当中存放的对象的信息 -> 返回一个对象(数据)
// 1-2.初始化对象输入流 -> 需要的参数 : 通用的输入流 -> FileInputStream -> File -> 路径
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("E:/product.txt")));
// 2.获得一个自定义类的实例
Product oldProduct = new Product("商品1", 200, new Shop("商店1"));
// 3-1.序列化对象 - 自定义类实现序列化接口 - ID根据需要进行生成
outputStream.writeObject(oldProduct);
// 3-2.反序列化 - 判断类型 - 强制转换接收
Object object = inputStream.readObject();
// 如果反序列化得到的对象是相应的实例
if (object instanceof Product) {
// 检查是否成功反序列化
Product newProduct = (Product) object;
System.out.println(oldProduct);
System.out.println(newProduct);
oldProduct.setName("商品11");
newProduct.setName("商品2");
newProduct.getShop().setName("商店2");
System.out.println(oldProduct);
System.out.println(newProduct);
}
// 4.强制刷新
outputStream.flush();
// 5.关闭流
inputStream.close();
outputStream.close();
三、标准输入输出流
标准输入输出流是System.in和System.out,默认情况下代表键盘和显示器
1. 输出流
- PrintStream
- setOut(PrintStream out):重新分配标准输出流
// TODO 标准的输出流
PrintStream out = System.out;
System.out.println("123");
OutputStream outputStream = new FileOutputStream("E:/SystemTest.txt");
// 自定义输出路径 默认屏幕上
System.setOut(new PrintStream(outputStream));
System.out.println("456");
System.setOut(out);
System.out.println(456);
2. 输入流
- setIn(InputStream in):重新分配标准输入流
// TODO 标准的输入流
InputStream in = System.in;
InputStream inputStream = new FileInputStream(new File("E:/SystemTest.txt"));
System.setIn(inputStream);
Scanner scanner = new Scanner(System.in);
// 使用Scanner实现文件内容读取
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
System.setIn(in);
scanner = new Scanner(System.in);
scanner.nextLine();
scanner.close();
- 小练习
// TODO 标准流练习
// 从键盘录入一组数据(字符串)
PrintStream out = System.out;
// (1)使用默认的标准输入流初始化一个Scanner对象
Scanner scanner = new Scanner(System.in);
// (2)定义一个集合用于存放接收到的字符串
List<String> list = new ArrayList<>();
int n = 4;
System.out.println("请输入四条数据:");
for(int i = 0;i < n;i ++) {
list.add(scanner.nextLine());
}
// 使用标准输出流写入到文件中
// (1)定义一个文件输出流 -> 指定到某一个文件
OutputStream outputStream = new FileOutputStream(new File("E:/SystemTest.txt"));
// (2)改变标准输出流的指向 -> 文件输出流
System.setOut(new PrintStream(outputStream));
// (3)遍历集合,使用println()方法输出集合元素
for (String line : list) {
System.out.println(line);
}
// 利用标准输入流实现使用Scanner读取文件中的内容,打印输出
// (1)定义一个文件输入流 -> 指定到数据记录文件
InputStream inputStream = new FileInputStream(new File("E:/SystemTest.txt"));
// (2)改变标准输入流的指向 -> 文件输入流
System.setIn(inputStream);
// (3)使用改变后的标准输入流初始化一个新的Scanner对象
scanner = new Scanner(System.in);
// (4)重置标准输出流 -> 显示器
System.setOut(out);
// (5)使用Scanner对象的nextLine()方法读取数据
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
scanner.close();