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部分的基礎知識算是學完了。這裡做了總結,方便以後來複習。。。。