一、對象序列化
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(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 要從輸入流緩衝區中讀取的下一個字符的索引。
ByteArrayInputStream(byte[] buf)//創建一個 ByteArrayInputStream,使用 buf 作爲其緩衝區數組。此緩衝區和字段的定義的緩衝區不同,該緩衝區表示要接受的數據源,而字段的緩衝區是流從源中讀取的數據。
②ByteArrayOutputStream:其數據被寫入一個 byte 數組。緩衝區會隨着數據的不斷寫入而自動增長。可使用 toByteArray() 和 toString() 獲取數據。
a、字段
- protected byte[] buf 存儲數據的緩衝區。。
- protected int count 緩衝區中的有效字節數。
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);
}
}