1. 程式人生 > >java IO 位元組流、字元流操作總結三之字元流、序列化和反序列化

java IO 位元組流、字元流操作總結三之字元流、序列化和反序列化

這一篇我講介紹一下字元流。什麼是字元流,就是若干個位元組組成一個字元。(為什麼說是若干個,不能確定嗎)這裡就來說說原因和編碼問題。

首先說說字元編碼的問題,比較常用的編碼有gbk,utf-8等。

a、.編碼問題(看前面所描述的)。

1、gbk 編碼中文佔用2個位元組,英文佔用1個位元組。

2、utf-8編碼中文佔用3個位元組,英文佔用1個位元組。

b、認識文字與文字檔案

Java是雙位元組編碼,utf-16be編碼。即char佔用2個位元組。注意:當你的位元組序列是某種編碼時,這個時候想把位元組序列變成字串,也需要用這種編碼方式。否則會出現亂碼。文字檔案就是位元組序列,可以是任意編碼的位元組序列。但是通常我們在中文的機器上直接建立檔案,那麼該檔案只認識ANSI編碼。

文字檔案是文字(char)序列按照某種編碼方案(utf-8,utf-16be,gbk)序列化為byte的儲存。

c、字元流分為輸入流(writer)和輸出流(reader)。操作的是文字、文字檔案字元的處理,一次處理一個字元。但是我們要知道字元的底層還是基本的位元組序列(位元組傳輸才是檔案最終的傳輸)。

這裡我們來上一張字元流的家族譜。

字元流的基本實現:

InputStreamReader:完成byte流解析為char流,按照編碼解析

InputStreamWriter:完成char流到byte流的解析,按照編碼處理

 

d、FileReader&FileWriter(檔案的輸入輸出字元流)

可以直接寫檔名的路徑。與InputStreamReader相比壞處:無法指定讀取和寫出的編碼,容易出現亂碼。

FileReader fr=new FileReader(“Demo\\im.txt”);//輸出流
FileWriter fw=new FileWriter(“Demo\\im2.txt”);輸入流

e、位元組流的過濾器

BufferedReader:readLine();一次讀取一行,但不能識別換行。。

BufferedWriter/PrintWriter;一次寫一行,PrintWriter可以自動換行

如:一次寫入一行

BufferedWriter                                      PrintWriter

bw.wrire(line)                                       pw.println(line);//自動換行,不加ln不換行

bw.newLine();//單獨換行                    pw.flush();//重新整理資料

bw.flush();//重新整理資料

程式碼如下:

package com.ll.iofile;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

/**
 * 這個例子是用來測試位元組流的過濾器
 * BufferedReader和BufferedWriter/PrintWriter類的使用
 * @author LULEI
 *
 */
public class testBufferedReaderAndBufferedWriter {

	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		//例項化一個BufferedReader的物件
		BufferedReader br=new BufferedReader(new InputStreamReader(
				new FileInputStream("C:\\Users\\Administrator\\Desktop\\搜狗.txt")));
		//例項化一個BufferedWriter的物件
		BufferedWriter wr=new BufferedWriter(
				new OutputStreamWriter(
						new FileOutputStream("Demo\\immoc1.txt")));
		//例項化一個PrintWriter的物件
		PrintWriter pw=new PrintWriter(
				new OutputStreamWriter(
						new FileOutputStream("Demo\\imooc2.txt")));
		
		String line=new String();
		while((line=br.readLine())!=null){
			/*測試BufferedWriter的write方法
			wr.write(line);//這樣直接輸入的只會在一行顯示
			wr.newLine();//這句話起到讀取一行就換行的作用
			wr.flush();
			*/
		//System.out.println(line);//每讀取一行位元組,直接輸出
			//測試PrintWriter的方法
			pw.println(line);//輸出一行位元組的內容,且加ln自動換行
			pw.flush();
		}
		wr.close();
		br.close();
		pw.close();
	}

}

這裡我就先介紹幾個比較常用的字元流。接下來介紹一下序列化和反序列化的知識。

f、什麼是物件的序列化與反序列化?

1、物件的序列化是指將object物件轉換成byte序列,反之將byte序列轉換為object物件稱之為反序列化。

2、序列化流(objetcOutputStream),是過濾流。方法:writeObject()

      反序列化流(objectInputStream),方法:readObject()的使用需要進行強制型別的轉換。

3、序列化介面(serializable)

物件必須實現序列化接口才能進行序列化,否則將會出現異常。這個介面沒有任何的方法,只是一個標準。

程式碼如下:

package com.ll.iofile;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 這個類是用於測試物件的序列化和反序列化
 * 呼叫ObjectOutputSrteam 和ObjectInputStream類的使用
 * @author LULEI
 *
 */
public class testObjectSerializableDemo {

	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		//1、實現物件的序列化流的物件
		String file="Demo/object.txt";
		ObjectOutputStream oos=new ObjectOutputStream(
				new FileOutputStream(file));
		//例項化一個MobilePhone物件
		MobilePhone mp=new MobilePhone(4, 5.5f, "紅米Note");
		//開始寫物件
		oos.writeObject(mp);
		//重新整理緩衝區資料
		oos.flush();
		oos.close();
		
		//2實現物件的反序列化流物件
		ObjectInputStream ois=new ObjectInputStream(
				new FileInputStream(file));
		//必須進行強制型別轉換
		MobilePhone mps=(MobilePhone)ois.readObject();
		System.out.println(mps);
		ois.close();
	}

}


package com.ll.iofile;

import java.io.Serializable;

/**
 * 這個類是用於參與測試物件的序列化和反序列化
 * ObjectOutputSrteam 和ObjectInputStream類的使用
 * @author LULEI
 *
 */
//這裡必須要繼承serializable介面
public class MobilePhone implements Serializable {
	private int cpu;
	
	private float screen;
	
	private String name;
	public MobilePhone(){
		
	}
	
	public MobilePhone(int cpu, float screen, String name) {
		super();
		this.cpu = cpu;
		this.screen = screen;
		this.name = name;
	}

	public int getCpu() {
		return cpu;
	}
	public void setCpu(int cpu) {
		this.cpu = cpu;
	}
	public float getScreen() {
		return screen;
	}
	public void setScreen(float screen) {
		this.screen = screen;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "MobilePhone [cpu=" + cpu + ", screen=" + screen + ", name="
				+ name + "]";
	}
	
}

g、 transient關鍵字

transient關鍵字修飾的屬性預設是不能序列化的,但是可以使用writeObject()自己完成這個元素的序列化。ArrayList就是用了此方法進行了優化的操作。ArrayList最核心的容器Object[] elementData使用了transient修飾,但是在writeObject自己實現對elementData陣列的序列化。只能序列化陣列中真實存在的元素,對於陣列中的空的元素時不能進行序列化的。

如:

private void writeObject(java.io.ObjectOutputStream s) throws
java.io.IOException{
 }
private void readObject(java.io.ObjectOutputStream s) throws
java.io.IOException,classNotFoundException{
 } 

程式碼如下:

package com.ll.iofile;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 這個類是用於測試物件的序列化和反序列化
 * 呼叫ObjectOutputSrteam 和ObjectInputStream類的使用
 * 1、首先例項化ObjectOutputSrteam 和ObjectInputStream類的物件oos和ois
 * 2、例項化將要序列化的物件qq
 * 3、物件oos呼叫writeObject()方法來對物件qq進行序列化流的輸出操作。
 * 4、物件ois呼叫readObject()方法來進行反序列化的輸入操作。同時要使用強制型別轉換來轉化
 * 賦值給相應的例項化物件x。
 * 4、最後直接輸出x的內容
 * @author LULEI
 *
 */
public class testTransient {

	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		//1、例項化首先例項化ObjectOutputSrteam 和ObjectInputStream類的物件
		String file=new String("Demo\\testTransient");
		ObjectOutputStream oos=new ObjectOutputStream(
				new FileOutputStream(file));
		ObjectInputStream ois=new ObjectInputStream(
				new FileInputStream(file));
		//2、例項化將要被序列化的物件MobilePhone
		MobilePhone2 mp=new MobilePhone2(4, 4.0f, "華為");
		//3、oos物件呼叫writeObject()方法來進行序列化
		oos.writeObject(mp);
		oos.flush();//重新整理緩衝區
		oos.close();//關閉檔案流
		//4、ois物件呼叫readObject方法來進行反序列化的操作,同時賦值給相應型別的物件
		try {
			MobilePhone2 mps=(MobilePhone2)ois.readObject();
			System.out.println(mps);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ois.close();
	}

}




package com.ll.iofile;

import java.io.Serializable;

/**
 * 這個類是用於參與測試物件的序列化和反序列化
 * ObjectOutputSrteam 和ObjectInputStream類的使用
 * 同時檢測了transient關鍵字的使用
 * @author LULEI
 *
 */
//這裡必須要繼承serializable介面
public class MobilePhone2 implements Serializable {
	private int cpu;
	//使用transient關鍵字修飾,這時screen的值不會被序列化。這時
	//如果想進行序列化,只能夠自己人為的進行序列化
	private transient float screen;
	
	private String name;
	public MobilePhone2(){
		 
	}
	
	public MobilePhone2(int cpu, float screen, String name) {
		super();
		this.cpu = cpu;
		this.screen = screen;
		this.name = name;
	}
	/*這裡是自己認為的完成序列化的操作
	//人工的進行序列化
	private void writeObject(java.io.ObjectOutputStream s)
	        throws java.io.IOException{
			s.defaultWriteObject();//則是執行預設的序列化操作
			s.writeFloat(screen);//自己人工的進行screen序列化操作
	}
	//人工的進行反序列化的操作
	 private void readObject(java.io.ObjectInputStream s)
		        throws java.io.IOException, ClassNotFoundException {
		 s.defaultReadObject();//執行預設的反序列化的操作
		 this.screen=s.readFloat();//自己完成screen的反序列化的操作
	 }
	 */
	public int getCpu() {
		return cpu;
	}
	public void setCpu(int cpu) {
		this.cpu = cpu;
	}
	public float getScreen() {
		return screen;
	}
	public void setScreen(float screen) {
		this.screen = screen;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "MobilePhone [cpu=" + cpu + ", screen=" + screen + ", name="
				+ name + "]";
	}
	
}

h、序列化過程中子父類建構函式的問題

1>父類實現了serializable介面,子類繼承父類就可以序列化了。

2>但子類在進行反序列化的時候,父類 實現了序列化介面,則不會遞迴呼叫其建構函式。

3>父類未實現了serializable介面,子類自己實現了介面,子類可以自行實現可序列化。

4>子類在反序列化時,父類沒有實現序列化的介面,則會遞迴呼叫其建構函式。

  
到這裡,我的Java IO部分的基礎知識算是學完了。這裡做了總結,方便以後來複習。。。。