JavaIO流04
4.常用的類03
4.4節點流和處理流02
4.4.5物件處理流-ObjectInputStream和ObjectOutputStream
1.序列化和反序列化
例子1:
看一個需求
- 將
int num= 100這個int 型別的資料保存到檔案中,注意不是100 數字,而是int 100,并且能夠從檔案中直接恢復int 100 - 將
Dog dog = new Dog("小黃",3)這個Dog物件保存到檔案中,并且能夠從檔案恢復,
上面的要求,就是能夠將 基本資料型別 或者 物件 進行 序列化 和 反序列化操作
- 序列化和反序列化
- 序列化就是在保存資料時,保存資料的值和資料型別
- 反序列化就是在恢復資料時,恢復資料的值和資料型別
- 需要讓某個物件支持序列化機制,則必須讓其類是可序列化的,為了讓這個類是可序列化,該類必須實作以下兩個介面之一:
Serializable //這是一個標記介面,沒有方法(推薦)Externalizable //該介面有方法需要實作,因此推薦實作上面的Serializable介面
2. 物件處理流基本介紹與應用
- 功能:提供了對基本型別或物件型別的序列化和反序列化的方法
- ObjectOutputStream提供序列化功能
- ObjectInputStream 提供反序列化功能
例子1:序列化應用
使用ObjectOutputStream序列化 基本資料型別 和一個 Dog物件(name,age),并保存到data.dat檔案中,
package li.io.outputstream_;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//演示ObjectOutputStream的使用,完成資料的序列化
public class ObjectOutputStream_ {
public static void main(String[] args) throws Exception {
//序列化后,保存的檔案格式不是純文本格式,而是按照它自己的格式來保存
String filePath = "d:\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//存盤
//序列化資料到檔案d:\data.dat
oos.writeInt(100);//100在底層實作了自動裝箱,int ->Integer (Integer實作了Serializable介面)
oos.writeBoolean(true);//boolean ->Boolean (Boolean實作了Serializable介面)
oos.writeChar('a');// char ->Char (實作了Serializable介面)
oos.writeDouble(9.5);//double ->Double (實作了Serializable介面)
oos.writeUTF("你好火星");//String(實作了Serializable介面)
//保存一個Dog物件
oos.writeObject(new Dog("旺財", 10));
//關閉外層流
oos.close();
System.out.println("資料保存完畢(序列化形式)");
}
}
//如果需要序列化某個物件,必須實作介面Serializable或者 Externalizable介面
class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
}

例子2:反序列化應用
使用ObjectInputStream讀取data.dat并且反序列化恢復資料
Dog:
package li.io.outputstream_;
import java.io.Serializable;
//如果需要序列化某個物件,必須實作介面Serializable或者 Externalizable介面
public class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
}
ObjectInputStream_:
package li.io.inputstream_;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import li.io.outputstream_.Dog;
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//指定反序列化的檔案
String filePath = "d:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//讀取
//1.讀取(反序列化)的順序要和你保存資料(序列化)的順序一致,否則會出現例外
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
//dog的編譯型別是Object ,dou的運行型別是 Dog
Object dog = ois.readObject();
System.out.println("運行型別=" + dog.getClass());//Dog
System.out.println("dog資訊=" + dog);//底層 Object->Dog
//一些重要細節:
//1. 如果我們希望呼叫Dog的方法,需要向下轉型
//2. 需要我們將Dog類的定義拷貝到可以參考的位置然后匯入類(將Dog類創建為公共類,重新序列化,再反序列化)
Dog dog1 = (Dog) dog;
System.out.println(dog1.getName());
//關閉外層流
ois.close();
}
}
3.物件處理流使用細節
- 讀寫順序要一致
- 要求實作序列化或者反序列化的物件的類,需要實作介面
Serializable或者Externalizable - 序列化的類中建議添加
SerialVersionUID,是為了提高版本的兼容性
在完成序列化操作后,如果對序列化物件進行了修改,比如增加某個欄位,那么我們再進行反序列化就會拋出InvalidClassException例外,這種情況叫不兼容問題,
解決的方法是:在物件中手動添加一個 serialVersionUID 欄位,用來宣告一個序列化版本號,之后再怎么添加屬性也能進行反序列化,凡是實作Serializable介面的類都應該有一個表示序列化版本識別符號的靜態變數,
-
序列化物件時,默認將里面的所有屬性都進行序列化,但除了
static或transient修飾的成員 -
序列化物件時,要求里面屬性的型別也需要實作序列化介面
例如:在(實作了Serializable介面的)Dog類中創建一個Master型別的屬性master,而Master類沒有實作Serializable介面,所以在序列化時,Dog類中的master屬性無法序列化,也不能反序列化
- 序列化具有可繼承性,也就是說如果某類已經實作序列化,則它的所有子類也已經默認實作了序列化
4.4.6標準輸入輸出流
不懂這些,你敢說自己知道Java標準輸入輸出流? - 簡書 (jianshu.com)
介紹:
| 型別 | 默認設備 | |
|---|---|---|
| System.in 標準輸入 | InputStream | 鍵盤 |
| System.out標準輸出 | PrintStream | 顯示幕 |
例子1:
package li.io.standard;
public class InputAndOutput {
public static void main(String[] args) {
// System 類 的 public final static InputStream in = null;
// System.in 編譯型別 InputStream
// System.in 運行型別 BufferedInputStream
// 表示的是標準輸入--用來讀取鍵盤錄入的資料
System.out.println(System.in.getClass());//class java.io.BufferedInputStream-位元組處理輸入流
// 1. System.out public final static PrintStream out = null;
// 2.編譯型別 PrintStream
// 3.運行型別 PrintStream
// 4.表示標準輸出顯示幕--將資料輸出到命令列
System.out.println(System.out.getClass());//class java.io.PrintStream - 位元組輸出流
}
}
應用案例1:
傳統方法: System.out.println(" ");是使用out物件將資料輸出到顯示幕
應用案例2:
傳統方法,Scanner是從標準輸入 鍵盤接收資料
// 給Scanner掃描器傳入的就是BufferedInputStream,
// 表示的是標準輸入--用來讀取鍵盤錄入的資料
// 因此 scanner會到鍵盤去 獲取輸入的資料
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入內容:");
//會去鍵盤去得到輸入流
String next = scanner.next();
System.out.println(next);
scanner.close();
4.4.7轉換流-InputStreamReader和OutputStreamWriter
1.檔案亂碼問題
先來看一個例子:
在d:\盤的根目錄下有一個a.txt檔案,內容如下:
現在想把該檔案讀取到程式中:
package li.io.transformation;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
//一個中文亂碼問題
public class CodeQuestion {
public static void main(String[] args) throws IOException {
//讀取a.txt檔案到程式中
//思路:
// 1.創建一個字符輸入流 這里用 BufferedRead[處理流]
// 2.使用 BufferedRead 物件讀取 a.txt
// 3.在默認情況下,讀取檔案是按照 UTF-8 編碼
String filePath = "d:\\a.txt";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String s = br.readLine();
System.out.println("讀取到的內容=" + s);
br.close();
}
}
在a.txt檔案編碼為uft-8的時候,輸出:
在a.txt檔案編碼為ANSI的時候,輸出:
出現亂碼的根本問題是:沒有指定檔案的編碼方式,
2.基本介紹與應用
轉換流也是一種處理流,它提供了位元組流和字符流之間的轉換,
在Java IO流中提供了兩個轉換流:InputStreamReader 和 OutputStreamWriter,這兩個類都屬于字符流,
轉換流的原理是:字符流 = 位元組流 + 編碼表
我們需要明確的是字符編碼和字符集是兩個不同層面的概念,
- encoding是charset encoding的簡寫,即字符集編碼,簡稱編碼,
- charset是character set的簡寫,即字符集,
編碼是依賴于字符集的,一個字符集可以有多個編碼實作,就像代碼中的介面實作依賴于介面一樣,
在轉換流中選擇正確的編碼非常的重要,因為指定了編碼,它所對應的字符集自然就指定了,否則很容易出現亂碼,所以編碼才是我們最終要關心的,
轉換流的特點:其是字符流和位元組流之間的橋梁,
? 可對讀取到的位元組資料經過指定編碼轉換成字符
? 可對讀取到的字符資料經過指定編碼轉換成位元組
那么何時使用轉換流?
? 當位元組和字符之間有轉換動作時
? 流操作的資料需要編碼或解碼時
Java IO流詳解(六)----轉換流(位元組流和字符流之間的轉換) - 唐浩榮 - 博客園 (cnblogs.com)
-
InputStreamReader:
如下圖,InputStreamReader有一個重要的構造器
InputStreamReader(InputStream, Charset):也就是說,我們可以通過這個方法,將傳入的位元組流轉為一個字符流并指定處理的編碼方式
-
OutputStreamWriter:
如下圖,OutputStreamWriter也有一個重要的構造器:
OutputStreamWriter(OutputStream, Charset):即我們也可以通過這個方法,將寫出的位元組流轉為一個字符流并指定處理的編碼方式
- 當處理純文本資料時,若使用字符流效率更高,并且可以有效解決中文問題,所以建議將位元組流轉換成字符流
- 可以在使用時指定編碼格式,比如:uft-8,gbk,gb2312,ISO8859-1等
應用案例1:
編程將位元組流FileInputStream轉換成(包裝成)字符流InputStreamReader,對檔案進行讀取(按照UTFF-8格式),進而再包裝成 BufferedReader
package li.io.transformation;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
//演示使用 InputStreamReader 轉換流解決中文亂碼問題
//將位元組流轉 FileInputStream 換成字符流 InputStreamReader,并指定編碼 gbk/utf-8
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
// 1.將 FileInputStream流(位元組流),轉成了 InputStreamReader流(字符流)
// 2.指定了gbk編碼
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
// 3. 把 InputStreamReader 傳入 BufferedReader
BufferedReader br = new BufferedReader(isr);
// 4. 讀取
String s = br.readLine();
System.out.println("讀取內容=" + s);
// 5.關閉外層流
br.close();
}
}
應用案例2:
編程將位元組流FileOutputStream 包裝(轉換)成字符流OutputStreamWriter,對檔案進行寫入(按照GBK格式,可以指定其他,比如UTF-8)
package li.io.transformation;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
/**
* 演示使用 OutputStreamWriter
* 將位元組流轉 FileOutputStream 換成字符流 OutputStreamWriter
* 指定處理的編碼 gbk/utf-8/utf8
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
String charSet = "GBK";
//創建物件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
//寫入
osw.write("我只是一只小貓咪,我不懂你在說什么");
//關閉外層流
osw.close();
System.out.println("按照 " + charSet + " 保存檔案成功~");
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/506534.html
標籤:其他
上一篇:一個遞回函式的分析
