IO流__【對象的序列化】【管道流】【RandomAccessFile】【DataStream】【ByteArrayStream等】


對象的序列化(持久化)


就是爲了保存在內存中的各種對象的狀態,並且可以把保存的對象狀態再讀出來。
雖然你可以用你自己的各種各樣的方法來保存Object States,但是Java給我們提供了一種應該比自己更好的保存對象狀態的機制,那就是序列化。
簡單說:把對象轉換爲字節序列的過程稱爲對象的序列化
    把字節序列恢復爲對象的過程稱爲對象的反序列化

操作對象
ObjectInputStream與ObjectOutputStream(兩個類要一起使用)
被操作的對象需要實現Serializable (標記接口)以啓用其序列化功能
該接口沒有方法;沒有方法的接口通常稱爲標記接口
writeObject();、readObject();操作對象

示例:通過序列化操作一個Person類

import java.io.*;
class  ObjectStreamDemo
{
	public static void main(String[] args) throws Exception
	{
//		writeObj();
		readObj();
	}
	public static void readObj() throws Exception	//反序列化
	{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
		Person p = (Person)ois.readObject();//類型強轉,引用了另一個。java文件的Person類,所以會拋異常ClassNotFoundException
		System.out.println(p);
		ois.close();
	}
	public static void writeObj() throws IOException	//序列化
	{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
		oos.writeObject(new Person("xiaoqiang",22,"America"));
		oos.close();
	}
}
Person類

被操作的對象

import java.io.*;
class Person implements Serializable //標記接口
{
	public static final long serialVersionUID = 42L;//1,自定義UID
	private String name;//1,私有化的成員
	transient int age;//3,修飾符,不會被序列化
	static String country ="cn";//2,靜態成員
	Person(String name, int age, String country){
		this.name = name;
		this.age = age;
	}
	public String toString(){	//僞代碼,沒寫set,get
		return name+" : "+age+" : "+counrty;
	}
}

1,將Person的成員私有化後就不能再被序列化,說明序列號是根據成員獲取的
可以設置UID固定標識:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
設置後Person的成員即使被私有化也能被序列化
2,靜態的成員不能被私有化,例如靜態的成員country序列化修改後依然是cn
因爲序列化操作的是堆內存中的對象,而靜態成員隨着類加載,存在於棧中的方法區,所以執行不到
3,如果想讓非靜態成員不被序列化,就在該成員前面加上transient修飾

管道流

PipedInputStream和PipedOutputStream
一般流的輸入輸出沒有太大關聯,而管道流輸入輸出可以直接進行連接,必須相互連接後創建通信管道;通過結合多線程使用,單線程可能會死鎖。

示例:

import java.io.*;
class Read implements Runnable	//管道輸入流線程
{
	private PipedInputStream in;
	Read(PipedInputStream in){	//構造函數
		this.in = in;
	}
	public void run(){	//實現run方法
		try{
			byte[] buf = new byte[1024];//緩衝數組
			System.out.println("阻塞...數據讀取中...");
			int len = in.read(buf);		//字節輸入流讀取方法
			System.out.println("讀到數據,阻塞結束");
			String s = new String(buf,0,len);//轉成字符串
			System.out.println(s);
			in.close();
		}
		catch (IOException e){
			throw new RuntimeException("管道流read讀取失敗!!!");
		}
	}
}
class Write implements Runnable	//管道輸出流線程
{
	private PipedOutputStream out;
	Write(PipedOutputStream out){	//構造函數
		this.out = out;
	}
	public void run(){	//覆蓋run方法
		try{
			System.out.println("開始寫入...等待中。。。");
			Thread.sleep(3000);
			out.write("hello Piped !".getBytes());//字節讀取流write方法,需要將字符轉爲字節
			out.close();
		}
		catch (Exception e){
			throw new RuntimeException("管道流write輸出失敗");
		}
	}
}
class PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out=new PipedOutputStream(in);
//		in.connect(out);//connect()創建通信管道,也可直接將in傳入out構造參數列表
		new Thread(new Read(in)).start();
		new Thread(new Write(out)).start();
	}
}

運行結果:


由運行結果可以看出:管道流輸入輸出兩個線程在執行的過程中,輸入流線程如果沒有讀到輸出流的數據,就會一直處於阻塞狀態,直到輸出流write完畢輸入流纔開始read。

如果是單線程,極有可能會線程死鎖,所以要和多線程配合使用。

RandomAccessFile

隨機讀寫文件
該類不算是IO體系的子類,直接繼承自Object;
但RandomAccessFile是IO包中成員,具備文件讀寫功能,能夠讀寫是其內部封裝了字節輸入流和輸出流
能夠隨機是因爲在內部封裝了一個byte[] 數組,通過指針對數組的元素進行操作,同時可以通過getFilePointer獲取指針位置,通過seek改變指針位置。seek相當強大

通過其構造函數可以看出:該類只能操作文件
而且還有操作文件的模式:只讀r,讀寫rw

如果模式爲r,不會創建文件,而是讀取一個已存在的文件,若該文件不存在會拋出異常
如果模式爲rw,操作的文件不存在,會自動創建,如果存在則不會覆蓋,而是直接在該文件上操作

示例:

import java.io.*;
class RandomAccessFileDemo
{
	public static void main(String[] args) throws IOException
	{
//		writeFile(1);
//		readFile(0);
		writeFile_2(1);
	}
	public static void readFile(int x) throws IOException	//隨機獲取
	{
		RandomAccessFile raf = new RandomAccessFile("raf.txt","r");//"r"只讀權限
//		raf.seek(8*x);	//調整對象中指針,一個人的信息爲8個字節,可以通過8的倍數來決定取哪個人的資料
		raf.skipBytes(8*x);//跳過指定的字節數,只能往後跳

		byte[] buf = new byte[4];
		raf.read(buf);			//一次讀取四個字節,因爲設置的名字是由2個字符(4個字節)組成
		String name = new String(buf);//將該字節數組轉換成字符串
		int age = raf.readInt();//直接一次讀取四個字節,並轉成int型

		sop("name: "+name);
		sop("age: "+age);
		raf.close();
	}
	public static void writeFile_2(int x)throws IOException		//隨機寫入
	{
		RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");
		raf.seek(8*x);	//調整指針,直接將信息插入到第五個信息欄,也可對指定位置信息修改(覆蓋原有信息)
		raf.write("王五".getBytes());
		raf.writeInt(110);
		raf.close();
	}
	public static void writeFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");//"rw"權限,讀寫
		raf.write("小強".getBytes());//將字符轉成字節
		raf.writeInt(98);	//write只寫int型的最低八位(一個字節),該類提供了writeInt()方法,寫入4個字節
		raf.write("小明".getBytes());
		raf.writeInt(99);
		raf.close();
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}

RandomAccessFile類具備強大的隨機讀寫功能,可以對數據進行分段錄入,如果引入多線程技術,可以大大提高效率,實際應用:P2P多線程下載

該類要做重點掌握!


一、專門操作基本數據類型的流對象
DataInputStream與DataOutputStream

import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
//		writeData();
//		readData();
//		writeUTFDemo();
		readUTFDemo();
	}
	public static void readUTFDemo()	throws IOException{
		DataInputStream dis = new DataInputStream(new FileInputStream("utfData.txt"));
		String s = dis.readUTF();
		sop(s);
		dis.close();
	}
	public static void writeUTFDemo() throws IOException{	//以 UTF-8 修改版格式寫入此 String 的基本數據。
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfData.txt"));
		dos.writeUTF("小強");
		dos.close();
	}
	public static void readData()	throws IOException{	//讀取基本數據類型
		DataInputStream dis = new DataInputStream(new FileInputStream("Data.txt"));
		int		n = dis.readInt();
		boolean b = dis.readBoolean();
		double  d = dis.readDouble();
		sop("n= "+n);
		sop("b= "+b);
		sop("d= "+d);
		dis.close();
	}
	public static void writeData()	throws IOException{	//寫入基本數據類型
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("Data.txt"));
		dos.writeInt(22);		//4個字節
		dos.writeBoolean(true);	//1個字節
		dos.writeDouble(3.1415926535);//8個字節
		dos.close();
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}

二、用於操作字節數組的流對象

用流的讀寫思想操作數組
ByteArrayInputStream:在構造時需要接受數據源,數據源是一個字節數組
ByteArrayOutputStream:在構造時不用定義數據目的,因爲該對象內部封裝了可變長度的字節數組,該數組就是數據目的地。
因爲這兩個流對象都操作數組,並沒有操作系統資源,所以不用close()關閉

流操作規律
源: 鍵盤System.in、硬盤FileStream、內存ArrayStream
目的: 控制檯System.our、硬盤FileStream、內存ArrayStream
writeTo(OutputStream out);
示例:

import java.io.*;
class  ByteArrayStream
{
	public static void main(String[] args) //操作的是數組,一般不用拋異常
	{
		ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFG".getBytes());//數據源
		ByteArrayOutputStream baos =new ByteArrayOutputStream();		//數據目的
		int by = 0;
		while ((by=bais.read()) !=-1){
			baos.write(by);
		}
		System.out.println(baos.size());		//返回緩衝區大小length長度
		System.out.println(baos.toString());	//將緩衝區字節
//		baos.writeTo(new FileOutputStream("a.txt")); //該方法調用了底層資源,會拋異常
	}

三、操作字符數組
CharArrayReader與CharArrayWrite
四、操作字符串
StringReader 與 StringWriter

這兩個類與ByteArrayStream類似



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章