【Java基礎】——IO流(下)


 一、對象序列化

1、概述

①對象序列化概念
程序運行時,會在內存中創建多個對象,如果希望永久保存這些對象,則可以將對象轉化爲字節數據寫入到硬盤,這個過程稱爲對象的序列化。
②對象序列化前提
當對象要序列化,必須保證該對象所屬類實現 Serializable 接口,以啓用其序列化功能。
其中Serializable類無任何方法,屬於 標記接口。
③UID:能序列化的對象所屬類編譯後都有一個UID,當對象屬性修改後再編譯對象(指類)會產生一個新序列號
(即UID,UID是根據類中成員算的),想要屬性修改後對象的序列號不變,加 static final long serialVersionUID = 42L;修飾。
注意三點:
  • 靜態是不能被序列化的,因爲靜態存在與方法區,對象存在於堆內存中。
  • 如果非靜態想被序列化可以加 transient 修飾。
  • 想被序列化的對象要實現 Serializable 接口。

2、ObjectInputStream 和 ObjectOutputStream

①ObjectOutputStream:將 Java 對象的基本數據類型和圖形寫入流中
  • 構造方法:ObjectOutputStream(OutputStream out) 
  • 方法:writeObject(Object obj)//將對象寫入流中,稱爲對象的序列化
②ObjectInputStream :對以前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化。
  • 構造方法:ObjectInputStream(InputStream in)
  • 方法:Object readObject()將對象反序列化

二、管道流(PipedInputStream 和 PipedOutputStream)

1、概述

管道流是一種特殊的流,必須先建立連接才能進行彼此間通信。通常,數據由某個線程從 PipedInputStream 對象讀取,並由其他線程將其寫入到相應的 PipedOutputStream 。
不建議對這兩個對象嘗試使用單個線程,因爲這樣可能死鎖線程。

2、連接方法

①void connect(PipedOutputStream src)//使此管道輸入流連接到管道輸出流 src。
②void connect(PipedInputStream snk)//將此管道輸出流連接到接收者。

代碼演示如下所示:
package com.huang.stream;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * @author huangxiang
 * @date 創建時間:2015年5月26日上午10:10:21
 * @version 1.0
 */
public class PipedStreamDemo {
	public static void main(String[] args) throws IOException {

		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();

	}
}

class Read implements Runnable {
	private PipedInputStream in;

	Read(PipedInputStream in) {
		this.in = in;
	}

	public void 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("管道讀取流失敗");
		}
	}
}

class Write implements Runnable {
	private PipedOutputStream out;

	Write(PipedOutputStream out) {
		this.out = out;
	}

	public void run() {
		try {
			System.out.println("開始寫入數據,等待6秒後。");
			Thread.sleep(6000);
			out.write("piped lai la".getBytes());
			out.close();
		} catch (Exception e) {
			throw new RuntimeException("管道輸出流失敗");
		}
	}
}

三、隨機訪問文件(RandomAccessFile)

1、概述

①該類直接繼承自Object類,不屬於流類,但是它是IO包中成員,具備讀寫文件數據的功能。 內部封裝了一個數組和記錄指針,而且可通過指針對數組的元素進行操作。
②具備讀寫數據功能原理:是其內部封裝了字節輸入和輸出流。

2、構造方法

①構造方法
  • RandomAccessFile(File file,String mode)
  • RandomAccessFile(String name, String mode)
②從構造方法中我們可以看出:
  • file:被訪問的文件
  • name:被訪問文件的路徑
  • mode:指定訪問文件的模式,其中只讀"r"不會創建文件,會去讀取一個已存在文件,如果文件不存在或執行寫入操作,則會出現異常。
  • "rw"表示讀寫文件,如果文件不存在,會自動創建。如果存在,不會覆蓋原文件

3、常用方法

①void seek(long pos)//設定讀寫指針的位置,與文件開頭(0處)相隔POS個字節數。
②int skipBytes(int n)//使讀寫指針從當前位置開始,跳過n個字節。
③void writeInt(int v)//按4個字節將 int 寫入該文件,先寫高字節。

代碼演示如下:
package com.huang.stream;

import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * @author huangxiang
 * @date 創建時間:2015年5月26日上午12:06:52
 * @version 1.0
 */
/*
 * RandomAccessFile 該類不是算是IO體系中子類。 而是直接繼承自Object。
 * 
 * 但是它是IO包中成員。因爲它具備讀和寫功能。 內部封裝了一個數組,而且通過指針對數組的元素進行操作。 
 * 可以通過getFilePointer獲取指針位置,同時可以通過seek改變指針的位置。
 * 
 * 其實完成讀寫的原理就是內部封裝了字節輸入流和輸出流。 通過構造函數可以看出,該類只能操作文件。 
 * 而且操作文件還有模式:只讀r,,讀寫rw等。
 * 
 * 如果模式爲只讀 r。不會創建文件。會去讀取一個已存在文件,如果該文件不存在,則會出現異常。 
 * 如果模式rw。操作的文件不存在,會自動創建。如果存則不會覆蓋。
 */
public class RandomAccessFileDemo {
	public static void main(String[] args) throws IOException {
		// writeFile_2();
		readFile();

		// System.out.println(Integer.toBinaryString(258));

	}

	public static void readFile() throws IOException {
		RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");

		// 調整對象中指針。
		// raf.seek(8*1);

		// 跳過指定的字節數
		raf.skipBytes(8);

		byte[] buf = new byte[4];

		raf.read(buf);

		String name = new String(buf);

		int age = raf.readInt();

		System.out.println("name=" + name);
		System.out.println("age=" + age);

		raf.close();

	}

	public static void writeFile_2() throws IOException {
		RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
		raf.seek(8 * 0);
		raf.write("週期".getBytes());
		raf.writeInt(103);

		raf.close();
	}

	public static void writeFile() throws IOException {
		RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");

		raf.write("李四".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);

		raf.close();
	}
}

四、基本數據操作流(DataInputStream 與 DataOutputStream)

1、概述

是兩個與平臺無關的數據操作流,不僅提供了讀寫各種基本數據類型的方法,還提供了readUTF()和writeUTF()方法。

2、方法

a、void writeUTF(String str):使用 UTF-8 修改版編碼將一個字符串寫入輸出流
b、String readUTF():讀取writeUTF方法寫入的字節   

代碼演示如下:
package com.huang.stream;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;

/*
 DataInputStream與DataOutputStream

 可以用於操作基本數據類型的數據的流對象。
 */
/**
 * @author huangxiang
 * @date 創建時間:2015年5月27日上午12:13:26
 * @version 1.0
 */
public class DataStreamDemo {
	public static void main(String[] args) throws IOException {
		// writeData();
		// readData();

		// writeUTFDemo();

		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
				"gbk.txt"), "gbk");

		osw.write("你好");
		osw.close();

		readUTFDemo();

	}

	public static void readUTFDemo() throws IOException {
		DataInputStream dis = new DataInputStream(
				new FileInputStream("utf.txt"));

		String s = dis.readUTF();

		System.out.println(s);
		dis.close();
	}

	public static void writeUTFDemo() throws IOException {
		DataOutputStream dos = new DataOutputStream(new FileOutputStream(
				"utfdate.txt"));

		dos.writeUTF("你好");

		dos.close();
	}

	public static void readData() throws IOException {
		DataInputStream dis = new DataInputStream(new FileInputStream(
				"data.txt"));

		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();

		System.out.println("num=" + num);
		System.out.println("b=" + b);
		System.out.println("d=" + d);

		dis.close();
	}

	@SuppressWarnings("null")
	public static void writeData() throws IOException {
		DataOutputStream dos = new DataOutputStream(new FileOutputStream(
				"data.txt"));

		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(9887.543);

		dos.close();

		ObjectOutputStream oos = null;
		oos.writeObject(new Object());

	}
}    

五、字節數組操作流(ByteArrayInputStream 和 ByteArrayOutputStream)

1、概述

此類源和目的都是內存,不會調用系統資源,故不需要關流,此類中的方法在關閉此流後仍可被調用,而不會產生任何 IOException.    

2、方法摘要

①ByteArrayInputStream:包含一個內部緩衝區,該緩衝區包含從流中讀取的字節
a、字段    
  • protected  byte[] buf 由該流的創建者提供的 byte 數組。
  • protected  int count  計數器,記錄緩衝區的字節數。
  • protected  int mark 流中當前的標記位置。
  • protected  int pos    要從輸入流緩衝區中讀取的下一個字符的索引。
b、構造函數
ByteArrayInputStream(byte[] buf)//創建一個 ByteArrayInputStream,使用 buf 作爲其緩衝區數組。此緩衝區和字段的定義的緩衝區不同,該緩衝區表示要接受的數據源,而字段的緩衝區是流從源中讀取的數據。
②ByteArrayOutputStream:其數據被寫入一個 byte 數組。緩衝區會隨着數據的不斷寫入而自動增長。可使用 toByteArray() 和 toString() 獲取數據。
a、字段    
  • protected  byte[] buf 存儲數據的緩衝區。。
  • protected  int count  緩衝區中的有效字節數。
b、構造函數
ByteArrayOutputStream()  創建一個新的 byte 數組輸出流。其構造函數不許接收目的,原因是其在創建對象時就創建一個byte性數組的緩衝區,即數據目的。
小結:其實字符操作流和字符串操作流同字節數組操作流原理一樣。

代碼實例如下所示:
package com.huang.stream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author huangxiang
 * @date 創建時間:2015年6月26日上午12:22:13
 * @version 1.0
 */
/*
 * 用於操作字節數組的流對象。
 * 
 * ByteArrayInputStream :在構造的時候,需要接收數據源,。而且數據源是一個字節數組。
 * 
 * ByteArrayOutputStream: 在構造的時候,不用定義數據目的,因爲該對象中已經內部封裝了可變長度的字
 * 節數組。這就是數據目的地。
 * 
 * 因爲這兩個流對象都操作的數組,並沒有使用系統資源。 所以,不用進行close關閉。
 * 
 * 在流操作規律講解時:
 * 
 * 源設備, 鍵盤 System.in,硬盤 FileStream,內存 ArrayStream。 目的設備: 控制檯
 * System.out,硬盤FileStream,內存 ArrayStream。
 * 
 * 用流的讀寫思想來操作數據。
 */

public class ByteArrayStream {
	public static void main(String[] args) throws IOException {
		// 數據源。
		ByteArrayInputStream bis = new ByteArrayInputStream(
				"ABCDEFD".getBytes());

		// 數據目的
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		int by = 0;

		while ((by = bis.read()) != -1) {
			bos.write(by);
		}

		System.out.println(bos.size());
		System.out.println(bos.toString());

		bos.writeTo(new FileOutputStream("a.txt"));

	}
}

六、字符編碼

1、字符碼錶概述

①含義:是一種可以方便計算機識別的特定字符集,它是將每一個字符和一個唯一的數字對應而形成的一張表。
②常見字符碼錶
  • ASCII:美國標準信息交換碼。用一個字節的7位二進制數表示。
  • ISO8859-1:拉丁碼錶,兼容ASCII,歐洲碼錶用一個字節的8位表示。
  • GB2312:中文編碼表,兼容ASCII,英文佔一個字節,中文佔2和字節(2個字節都爲負數)。
  • GBK:中文編碼表升級,融合了更多的中文文字符號。用兩個字節來表示(第一個字節爲負數)。
  • Unicode:國際標準碼,融合了多種文字。所有文字都用2個字節來表示,Java語言使用的就是unicode
  • UTF-8:Unicode的可變長編碼,英文佔1字節,中文佔3。

2、字符編碼和解碼

①編碼:把字符串變成計算機識別的字節序列。 byte[] getBytes();  byte[] getBytes(String charsetName);
②解碼:把字節數組變成字符串。 new String(byte[]); new String(byte[],String charsetName); 當中文用gbk編碼,再用ISO8859-1解碼會出現亂碼,因爲ISO8859-1不識別中文,可以繼續用ISO8859-1編碼再用gbk解碼解決。

代碼實例如下:
/*
編碼:字符串變成字節數組。


解碼:字節數組變成字符串。

String-->byte[];  str.getBytes(charsetName);

byte[] -->String: new String(byte[],charsetName);

*/
import java.util.*;
class  EncodeDemo
{
	public static void main(String[] args)throws Exception 
	{
		String s = "哈哈";

		byte[] b1 = s.getBytes("GBK");

		System.out.println(Arrays.toString(b1));
		String s1 = new String(b1,"utf-8");
		System.out.println("s1="+s1);

		//對s1進行iso8859-1編碼。
		byte[] b2 = s1.getBytes("utf-8");
		System.out.println(Arrays.toString(b2));

		String s2 = new String(b2,"gbk");

		System.out.println("s2="+s2);

	}
}
發佈了30 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章