字符输出流,缓冲流和序列化

字符输出流,缓冲流和序列化

1. IO流

1.1 文件操作字符输出流
FileWriter文件操作输出字符流

Constructor 构造方法
	FileWriter(File file);
		根据File类对象创建对应文件的文件操作输出字符流
	FileWriter(String pathName);
		根据String类型文件路径创建对应文件的文件操作输出字符流
	FileWriter(File file, boolean append);
		根据File类对象创建对应文件的文件操作输出字符流,并要求为追加写
	FileWriter(String pathName, boolean append);
		根据String类型文件路径创建对应文件的文件操作输出字符流,并要求为追加写
	
	如果创建FileWrite对象时,这里文件不存在,路径合法,这里会创建对应的操作文件。如果路径不合法,抛出异常 FileNotFoundException 
	
Method 成员方法	
	void write(int ch);
		写入一个char类型数据到文件中
	void write(char[] arr);
		写入一个char类型数组到文件中
	void write(char[] arr, int offset, int length);	
		写入一个char类型数组到文件中,要求从offset下标位置开始读取数组数据,长度为
		length
	void write(String str);
		写入一个字符串到文件中
	void write(String str, int offset, int lenght);
		写入一个字符串到文件中,要求从指定字符串offset下标位置开始,长度为length
	如果写入数据操作过程中,发生问题,这里会有一个IOException
1.2 使用演示
package com.qfedu.b_io;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 文件操作字符输出流
 */
public class Demo1 {
	public static void main(String[] args) {
		FileWriter fileWriter = null;
		
		try {
			fileWriter = new FileWriter(new File("D:/aaa/5.txt"), true);
			
			char[] charArray = "现在美国全国缺少口罩2.7亿".toCharArray();
			
			fileWriter.write(charArray);
			fileWriter.write("韩国目前疫情情况不容乐观");
			fileWriter.write("\r\n");
			fileWriter.write(charArray, 0, 5);
			fileWriter.write("韩国目前疫情情况不容乐观", 0, 5);
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}

	private static void writeTest1() {
		FileWriter fileWriter = null;
		try {
			fileWriter = new FileWriter(new File("D:/aaa/4.txt"), true);
			
			fileWriter.write('武');
			fileWriter.write('汉');
			fileWriter.write('加');
			fileWriter.write('油');
			fileWriter.write(',');
			fileWriter.write('中');
			fileWriter.write('国');
			fileWriter.write('加');
			fileWriter.write('油');
			fileWriter.write(',');
			fileWriter.write('世');
			fileWriter.write('界');
			fileWriter.write('加');
			fileWriter.write('油');
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

1.3 字符流文件拷贝
package com.qfedu.b_io;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 使用文件操作字符流量拷贝非文本文件问题
 * 【要求】
 * 		禁止使用字符流操作非文本文件,记事本打开乱码文件都不可以
 */
public class Demo2 {
	public static void main(String[] args) {
		FileReader fileReader = null;
		FileWriter fileWriter = null;
		
		
		try {
			fileReader = new FileReader(new File("D:/aaa/logo桌面.jpg"));
			fileWriter = new FileWriter(new File("D:/aaa/temp.jpg"));
			
			char[] buf = new char[1024 * 4];
			int length = -1;
			
			while ((length = fileReader.read(buf)) != -1) {
				fileWriter.write(buf, 0, length);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if (fileReader != null) {
				try {
					fileReader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

2. 缓冲流

2.1 缓冲流有什么作用
使用缓冲数组以后,整体的读取,写入效率提升很大!!!
降低了CPU通过内存访问硬盘的次数。提高效率,降低磁盘损耗。

字节输入缓冲
	BufferedInputStream
字节输出缓冲
	BufferedOutputStream
字符输入缓冲
	BufferedReader
字符输出缓冲
	BufferedWrite

【重点】
	所有的缓冲流都没有任何的读取,写入文件能力,这里都需要对应的输入流和输出流来提供对应的能力。
	在创建缓冲流流对象时,需要传入对应的输入流对象和输出流对象。
	底层就是提供了一个默认大小的缓冲数组,用于提高效率
2.2 字节缓冲流
输入
	BufferedInputStream(InputStream in);	
		这里需要的对象是一个字节输入流基类对象。同时也可也传入InputStream子类对象
输出
	BufferedOutputStream(OutputStream out);
		这里需要的对象是一个字节输出流基类对象。同时也可也传入OutputStream子类对象

以上传入的InputStream和OutputStream都是用于提供对应文件的读写能力。
2.2.1 字节输入流缓冲效率问题
1. 在BufferedInputStream底层中有一个默认容量为8KB的byte类型缓冲数组。
2. fill方法是一个操作核心
	a. 从硬盘中读取数据,读取的数据容量和缓冲数组容量一致。
	b. 所有的read方法,都是从缓冲数组中读取数据
	c. 每一次读取数据之前,都会检查缓冲区内是否有数据,如果没有,fill方法执行,填充数据。

3. 利用缓冲,fill方法,可以极大的降低CPU通过内存访问硬盘的次数。同时程序操作的数据是在内存中进行交互的。
2.2.2 字节输出流缓冲效率问题
1. 在BufferedOutputStream类对象,默认有一个8KB的byte类型缓冲数组
2. 数据写入文件时并不是直接保存到文件中,而是保存在内存8KB字节缓冲数组中
3. 如果8KB空间填满,会直接flush缓冲区,数据保存到硬盘中,同时清空整个缓冲区。
4. 在BufferedOutputStream关闭时,首先会调用flush方法,保存数据到文件,清空缓冲区,并且规划缓冲区占用内存,同时关闭缓冲流使用的字节输出流。
2.2.3 缓冲流拷贝和非缓冲拷贝时间效率区别
package com.qfedu.c_buffered;

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

public class Demo3 {
	public static void main(String[] args) {
		long start = System.currentTimeMillis();

		copy();
		
		long end = System.currentTimeMillis();
		// 总耗时
		System.out.println("Time:" + (end - start));
	}
	
	// 1716 ms
	public static void useBuffered() {
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		
		try {
			bis = new BufferedInputStream(new FileInputStream(new File("D:/aaa/2.txt")));
			bos = new BufferedOutputStream(new FileOutputStream(new File("D:/aaa/buffered.txt")));
			
			int content = -1;
			
			while ((content = bis.read()) != -1) {
				bos.write(content);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
 	}
	
    // 531000
	public static void copy() {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		try {
			fis = new FileInputStream("D:/aaa/2.txt");
			fos = new FileOutputStream("D:/aaa/copy.txt");
			
			int content = -1;
			
			while ((content = fis.read()) != -1) {
				fos.write(content);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
2.3 字符缓冲流
BufferedReader
	字符缓冲输入流
	BufferedReader(Reader reader);
BufferedWriter
	字符缓冲输出流
	BufferedWriter(Writer writer);
2.3.1 字符缓冲流效率问题
1. 字符缓冲输入流,底层有一个8192个元素的缓冲字符数组,而且使用fill方法从硬盘中读取数据填充缓冲数组

2. 字符缓冲输出流,底层有一个8192个元素的缓冲字符数组,使用flush方法将缓冲数组中的内容写入到硬盘当中。

3. 使用缓冲数组之后,程序在运行的大部分时间内都是内存和内存直接的数据交互过程。内存直接的操作效率是比较高的。并且降低了CPU通过内存操作硬盘的次数

4. 关闭字符缓冲流,都会首先释放对应的缓冲数组空间,并且关闭创建对应的字符输入流和字符输出流。

5. 
字符缓冲输入流中
	String readLine(); 读取一行数据
字符缓冲输出流中
	void newLine(); 换行

3. 项目中使用IO流和文件保存数据

Student类内需要保存的核心数据
	学生的信息
	count 学生ID计数器

文件操作这两个方法,我们做出一个工具类
	保存
		在程序退出之前保存学生数据
		存储于StudentManager对象中
		StudentManager中存储数据使用的结构是ArrayList
		从ArrayList中读取每一个学生数据,保存到文件中,最后保存count
		文件:
			[6,骚磊,16,男,99,95,97,291,0]
			[7,骚磊,16,男,99,95,97,291,0]
			[8,骚磊,16,男,99,95,97,291,0]
			[9,骚磊,16,男,99,95,97,291,0]
			count:20

	读取
		在程序启动之前读取数据
		判断 [
		读取一行内容,解析一行内容,创建一个Student对象,保存到StudentManager中
		[6,骚磊,16,男,99,95,97,291,0] 
			subString ==> 6,骚磊,16,男,99,95,97,291,0
			split(",") ==> String[] array = {"6",
				"骚磊","16","男","99","95","97","291","0"}
			解析
				String ==> 目标数据类型 int char 
		
		判断 c
			split(":") ==> String[] arr = {"count" , "20"};
			解析20
package com.system.student.qfedu.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import com.system.student.qfedu.entity.Student;
import com.system.student.qfedu.manager.StudentManager;

public class DataUtils {
	/**
	 * 从文件中读取数据到StudentManager对象中
	 * 
	 * @param studentManager 学生管理类对象
	 */
	public static void readData(StudentManager studentManager) {
		/*
		[11,千锋学生6,156,男,39,95,97,231,0]
		[12,千锋学生7,166,男,119,95,97,311,0]
		[13,千锋学生8,126,男,90,95,97,282,0]
		[14,千锋学生9,360,男,60,95,97,252,0]
		count:15
		 */
		BufferedReader br = null;
		
		try {
			br = new BufferedReader(new FileReader(new File("./data/students.txt")));
			
			String data = null;
			
			while ((data = br.readLine()) != null) {
				if ('[' == data.charAt(0)) {
					// 学生数据
					String[] split = data.substring(1, data.length() - 1).split(",");
					
					// 解析数组中每一个元素对应数据
					int id = Integer.parseInt(split[0]);
					String name = split[1];
					int age = Integer.parseInt(split[2]);
					char gender = split[3].charAt(0);
					int mathScore = Integer.parseInt(split[4]);
					int chnScore = Integer.parseInt(split[5]);
					int engScore = Integer.parseInt(split[6]);
					int totalScore = Integer.parseInt(split[7]);
					int rank = Integer.parseInt(split[8]);
					
					// 创建Student类对象,存储到StudentManager中
					Student stu = new Student(id, name, age, gender, mathScore, chnScore, engScore, totalScore, rank);
					studentManager.add(stu);
					
				} else if ('c' == data.charAt(0)) {
					// count数据 count:20
					Student.setCount(Integer.parseInt(data.split(":")[1]));
				}
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 从StudentManager中保存数据到文件
	 * 
	 * @param studentManager 学生管理类对象
	 */
	public static void saveData(StudentManager studentManager) {
		BufferedWriter bw = null;
		
		try {
			// 选择写入文件的方式是删除写!!!
			bw = new BufferedWriter(new FileWriter(new File("./data/students.txt")));
			
			// 获取StudentManager对象中保存的所有学生类对象数据
			Student[] allStudents = studentManager.getAllStudents();
			for (int i = 0; i < allStudents.length; i++) {
				// 写入学生数据
				bw.write(allStudents[i].getData());
				bw.newLine();
			}
			
			bw.write("count:" + Student.getCount());
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

4. 序列化

4.1 序列化概述
Java中提供了一种序列化操作的方式,用一个字节序列化来表示一个对象,该字节序列化中保存了【对象的属性】,【对象的类型】和【对象的数据】。把字节序列化保存到文件中,就可以做到持久化保存数据内容。

从文件中读取字节序列化数据,可以直接得到对应的对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zmOglTR-1582803391975)(img/序列化和反序列化.png)]

4.2 ObjectOutputStream类
将对象数据序列化,保存到文件中

构造方法 Constructor
	ObjectOutputStream(OutputStream out);
		输出字节流对象作为当前方法的参数
package com.qfedu.d_objectxxx;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo1 {
	public static void main(String[] args) {
		
		ObjectOutputStream objectOutputStream = null;
		
		try {
			objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/aaa/person.txt"));
			
			// 序列化对象,并且写入到文件中
			objectOutputStream.writeObject(new Person(1, "骚磊", 16));
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (objectOutputStream != null) {
				try {
					objectOutputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
4.3 ObjectInputStream类
从文件中读取被序列化之后的字节数据,提供反序列化操作,得到一个对象。

构造方法 Constructor
	ObjectInputStream(InputStream in);
		需要提供一个字节输入流对象来进行操作
【序列化注意事项】
1. 如果一个类需要进行序列化操作,必须遵从。java.io.Serializable。不遵从无法进行序列化操作

2. 序列化之后从文件中读取序列化内容,转换成对应的对象,
	ClassNotFoundException 对应类没有找到。
		对应的类型没有导包,不存在...
	InvalidClassException 类型不一样
		序列化之后的每一个类都会有一个serialVersionUID,该编号在使用过程中,序列化
		和反序列化必须一致
	
3. transient 修饰的成员变量不能被序列化 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章