字符輸出流,緩衝流和序列化

字符輸出流,緩衝流和序列化

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 修飾的成員變量不能被序列化 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章