天天看點

《JavaSE-第十五章》之檔案(二)

前言

在你立足處深挖下去,就會有泉水湧出!别管蒙昧者們叫嚷:“下邊永遠是地獄!”

共勉:talk is cheap, show me the code

作者是爪哇島的新手,水準很有限,如果發現錯誤,一定要及時告知作者哦!感謝感謝!

File(二)

文章目錄

  • ​​File(二)​​
  • ​​轉換流​​
  • ​​列印流​​
  • ​​輸出語句重定向​​
  • ​​序列化​​
  • ​​對象序列化​​
  • ​​對象反序列化​​
  • ​​序列化ID​​
  • ​​序列化ID的作用​​
  • ​​commons-io​​

轉換流

由于檔案編碼與代碼檔案編碼不一緻,進而會導緻亂碼,為了解決上述問題,可以使用轉換流,在将資料輸入和輸出時指定流的編碼格式與檔案的編碼格式保持一緻。

字元輸入轉換流

字元輸入轉換流:InputStreamReader,可以把原始的位元組流按照指定編碼轉換成字元輸入流。

構造器 說明
public InputStreamReader(InputStream is ,String charset) 可以把原始的位元組流按照指定編碼轉換成字元輸入流,這樣字元流中的字元就不亂碼了

代碼示例

package com.kc.system.io;

import java.io.*;

public class InputStreamReaderDemo01 {
    //代碼UTF-8
    public static void main(String[] args) throws Exception {
        InputStream is = new FileInputStream("./bbb.txt");//GBK
        // 把原始位元組流轉換成字元輸入流
        // Reader isr = new InputStreamReader(is); // 預設以UTF-8的方式轉換成字元流。 還是會亂碼的  跟直接使用FileReader是一樣的
        Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK編碼轉換成字元輸入流  完美的解決了亂碼問題

        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
    }
}      

字元輸出轉換流

字元輸入轉換流:OutputStreamWriter,可以把位元組輸出流按照指定編碼轉換成字元輸出流。

構造器 說明
public OutputStreamWriter(OutputStream os,String charset) 可以把原始的位元組輸出流按照指定編碼轉換成字元輸出流(重點)

代碼示例

package com.kc.system.io;

public class OutputStreamWriterDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、定義一個位元組輸出流
        OutputStream os = new FileOutputStream("./bbb.txt");

        // 2、把原始的位元組輸出流轉換成字元輸出流
        // Writer osw = new OutputStreamWriter(os); // 以預設的UTF-8寫字元出去 跟直接寫FileWriter一樣
        Writer osw = new OutputStreamWriter(os , "GBK"); // 指定GBK的方式寫字元出去

        // 3、把低級的字元輸出流包裝成進階的緩沖字元輸出流。
        BufferedWriter bw = new BufferedWriter(osw);

        bw.write("今天天氣真好");
        bw.write("今天天氣真好");
        bw.write("今天天氣真好");

        bw.close();
    }
}      

列印流

列印流可以實作友善、高效的列印資料到檔案中去。列印流一般是指:PrintStream,PrintWriter兩個類。這兩個類分類繼承FilerOutStream,Writer。列印流可以看做是輸出流的更新版。可以實作列印什麼資料就是什麼資料,例如列印整數97寫出去就是97,列印boolean的true,寫出去就是true,列印Hello World,寫出去就是Hello World。

PrintStream構造器

構造器 說明
public PrintStream(OutputStream os) 列印流直接通向位元組輸出流管道
public PrintStream(File f) 列印流直接通向檔案對象
public PrintStream(String filepath) 列印流直接通向檔案路徑

方法

方法 說明
public void print(Xxx xx) 列印任意類型的資料出去

PrintWriter構造器

構造器 說明
public PrintWriter(OutputStream os) 列印流直接通向位元組輸出流管道
public PrintWriter (Writer w) 列印流直接通向字元輸出流管道
public PrintWriter (File f) 列印流直接通向檔案對象
public PrintWriter (String filepath) 列印流直接通向檔案路徑

方法

方法 說明
public void print(Xxx xx) 列印任意類型的資料出去

代碼示例

package com.kc.system.io;

import java.io.*;

/**
 * PrintWriter 繼承Writer
 * PrintStream 繼承自FilerOutStream
 */
public class PrintDemo {
    public static void main(String[] args) throws FileNotFoundException {
        //可追加資料
//        PrintStream ps= new PrintStream(new FileOutputStream("bbb.txt",true));
//        ps.println("Hello World");
//        ps.print("今天是10月25日");
//        ps.close();
        //PrintStream和PrintWriter的使用方式幾乎一樣
        PrintWriter pw= new PrintWriter("bbb.txt");
        pw.println("今天天氣真好");
        pw.println(12);
        pw.println(12.09);
        pw.println('a');
        pw.println(true);
        pw.println(97);
        pw.close();
    }
}      

輸出語句重定向

重定向:可以将輸出語句列印位置改為某個檔案

代碼示例

package com.kc.system.io;

import java.io.FileNotFoundException;
import java.io.PrintStream;

/**
 * PrintStream 繼承自FilerOutStream
 */
public class PrintDemo2 {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我為如來,又有何懼?");
        System.out.println("相遇皆是緣緣盡莫強求。");
        //重定向到bbb.txt檔案
        PrintStream ps = new PrintStream("bbb.txt");
        //改變語句的重定向
        System.setOut(ps);
        System.out.println("他甯願死,也不肯輸。");
        System.out.println("如來是什麼,如是道來。");
    }
}      

上述重定向前

《JavaSE-第十五章》之檔案(二)

重定向後

《JavaSE-第十五章》之檔案(二)

序列化

對象序列化

序列化:以堆記憶體為基準,将生活在這上面的對象,通過某種的方式,以二進制的形式儲存到硬碟上。

《JavaSE-第十五章》之檔案(二)

對象序列化API

使用對象位元組輸出流:ObjectOutStream

ObjectOutStream構造器

構造器 說明
public ObjectOutputStream(OutputStream out) 把低級位元組輸出流包裝成進階的對象位元組輸出流

ObjectOutStream方法

方法 說明
public final void writeObject(Object obj) 把對象寫出去到對象序列化流的檔案中去

對象反序列化

反序列化:以堆記憶體為基準,将存放在硬碟上的對象資料還原對象。

《JavaSE-第十五章》之檔案(二)

反序列化

使用對象位元組輸入流:ObjectInputStream

ObjectInputStream構造方法

構造器 說明
public ObjectInputStream(InputStream out) 把低級位元組輸如流包裝成進階的對象位元組輸入流

反序列化的方法

方法 說明
public Object readObject() 把存儲到磁盤檔案中去的對象資料恢複成記憶體中的對象傳回

序列化ID

通常在序列化的時候會給類加上一個字段serialVersionUID,這個字段就是序列化ID

private static final long serialVersionUID = -4985775903060349049L;      
序列化ID的作用

序列化ID是為了保證能夠成功反序列化的成功,在進行反序列化的過程中,JVM會将來自輸入流的當中的序列化ID和本地實體類的ID進行,若ID相同則說明儲存在硬碟上的對象資料是該類所執行個體化的對象,反之則不能反序列化成功。如果在類中沒有手動添加一個序列化ID,編譯時會預設配置設定一個序列化ID,這樣就會導緻一個問題,那就是如果我們再次向類中添加字段時,重新編譯就意味着重新配置設定一個序列化ID,假設沒有添加字段前序列化後的對象資料為版本一,添加字段後,再次反序列化,此時由于實體類和序列化對象的ID不一緻,就會導緻序列化失敗,為了解決上述問題可以一開始就手動添加一個類型為long的serialVersionUID,值保證不變,這種情況下,無論後序對類進行修改,編譯時,總是使用自己手動加的,就不會出現反序列化失敗了。

SerializeUtil

将上述序列化以及反序列化的方法封裝成一個工具類,以便後續重複使用。

package com.kc.system.io;

import java.io.*;

public class SerializeUtil {
    //序列化,将對象轉換成二進制
    public static void saveObject(Object object) throws IOException {
        ObjectOutputStream oos = null;
        OutputStream out = null;
        try{
            out = new FileOutputStream("D:/demo.txt");
            oos = new ObjectOutputStream(out);
            oos.writeObject(object);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            out.close();
            oos.close();
        }
    }
    //反序列化,還原成對象
    public static  Object readObject() throws IOException {
        ObjectInputStream ois = null;
        InputStream is = null;
        try {
            is = new FileInputStream("D:/demo.txt");
            ois = new ObjectInputStream(is);
            Object object = ois.readObject();
            return object;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally{
            is.close();
            ois.close();
        }
        return null;
    }
}      

Student

Studne類必須實作Serializable,Studnet才可以被序列化。Serializable一個空接口,可以将其視為可以序列化的标志。

package com.kc.system.io;

import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private String name;
    private int age;
    private String address;
    private String school;
}      

對Studnet序列化和反序列化

package com.kc.system.io;

import java.io.IOException;

public class SerializeUtilDemo {
    public static void main(String[] args) throws IOException {
        Student s = new Student();
        s.setName("葉秋涵");
        s.setAge(18);
        s.setAddress("北海");
        s.setSchool("哔哩哔哩");
        SerializeUtil.saveObject(s);
        System.out.println("序列化完成了");
        System.out.println("-----");
        Student student = (Student)SerializeUtil.readObject();
        System.out.println(student);
    }
}      

Student序列化結果

《JavaSE-第十五章》之檔案(二)

Studnent反序列化結果

《JavaSE-第十五章》之檔案(二)

向Student中添加weight字段

package com.kc.system.io;

import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private String name;
    private int age;
    private String address;
    private String school;
    private double weight;
}      

再次反序化

package com.kc.system.io;

import java.io.IOException;

public class SerializeUtilDemo {
    public static void main(String[] args) throws IOException {
        Student student = (Student)SerializeUtil.readObject();
        System.out.println(student);
    }
}      

控制台異常

java.io.InvalidClassException: com.kc.system.io.Student; local class incompatible: stream classdesc serialVersionUID = -2853823296530063815, local class serialVersionUID = 6312346039733332667//流中的ID與本地的類中ID不一緻      

解決異常

向類中添加serialVersionUID

private static final long serialVersionUID = -4985775903060349049L;      

再次序列化覆寫上一個版本的對象資料,然後再反序列化

示例代碼

package com.kc.system.io;

import java.io.IOException;

public class SerializeUtilDemo {
    public static void main(String[] args) throws IOException {
        Student s = new Student();
        s.setName("葉秋涵");
        s.setAge(18);
        s.setAddress("北海");
        s.setSchool("哔哩哔哩");
        SerializeUtil.saveObject(s);
        System.out.println("序列化完成了");
        System.out.println("-----");
        Student student = (Student)SerializeUtil.readObject();
        System.out.println(student);
    }
}
//運作結果
//序列化完成了
//-----
//Student(name=葉秋涵, age=18, address=北海, school=哔哩哔哩, weight=0.0)      

再向Studnet中添加一個gender字段,在執行上述的代碼就不會出異常了

《JavaSE-第十五章》之檔案(二)

properties

Properties是一個存儲鍵值對資料的檔案對象,該檔案字尾為.properties。

構造器 說明
void load(Reader reader) 從輸入字元流讀取屬性清單(鍵和元素對)
void store(Writer writer, String comments) 将此屬性清單(鍵和元素對)寫入此 Properties表中,以适合使用 load(Reader)方法的格式寫入輸出字元流
void load(InputStream inStream) 從輸入位元組流讀取屬性清單(鍵和元素對)
void store(OutputStream out, String comments) 将此屬性清單(鍵和元素對)寫入此 Properties表中,以适合于使用 load(InputStream)方法的格式寫入輸出位元組流
public Object setProperty(String key, String value) 儲存鍵值對
public String getProperty(String key) 使用此屬性清單中指定的鍵搜尋屬性值 (get)
public Set stringPropertyNames() 所有鍵的名稱的集合 (keySet())

首先建立一個user.properties,再向user.properties檔案中添加是資料

package com.kc.system.io;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesDemo1 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("admin","123456");
        properties.setProperty("name","葉秋涵");
        properties.setProperty("pwassword","123456");
        System.out.println(properties);
        properties.store(new FileWriter("./user.properties"),"使用者資訊");//該檔案必須是存在的
    }
}      

從user.properties擷取資料

package com.kc.system.io;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

public class PropertiesDemo2 {
    public static void main(String[] args) throws IOException {
        //讀取properties檔案中的鍵值對資訊
        Properties properties = new Properties();
        System.out.println(properties);
        //加載屬性檔案中的鍵值對資料到屬性對象properties中去
        properties.load(new FileReader("./user.properties"));
        System.out.println(properties);
        String name = properties.getProperty("name");
        System.out.println(name);
        String admin = properties.getProperty("admin");
        System.out.println(admin);
        String pwassword = properties.getProperty("pwassword");
        System.out.println(pwassword);
        //擷取配置檔案中所有的鍵
        Set<String> str = properties.stringPropertyNames();
        for (String s : str) {
            System.out.print(s+" ");
        }
    }
}      

commons-io

commons-io是apache開源基金組織提供的一組有關IO操作的類庫,就是對常見檔案操作的封裝,如檔案的複制,删除,移動等。可以大的的提高效率。commons-io工具包提供了很多有關io操作的類。有兩個主要的類FileUtils, IOUtils。

方法

方法 說明
String readFileToString(File file, String encoding) 讀取檔案中的資料, 傳回字元串
void copyFile(File srcFile, File destFile) 複制檔案
void copyDirectoryToDirectory(File srcDir, File destDir) 複制檔案夾

maven依賴

package com.kc.system.io;


import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtilsDemo {
    public static void main(String[] args) throws IOException {
        //完成檔案的複制
        IOUtils.copy(new FileInputStream("./bbb.txt"), new FileOutputStream("./bbb2.txt"));
        //完成檔案複制到某個檔案夾下
        FileUtils.copyFileToDirectory(new File("D:/picture/1.jpg"),new File("D:/tools"));
        //完成檔案夾複制到某個檔案夾下
        FileUtils.copyDirectoryToDirectory(new File("D:/picture"),new File("D:/tools"));
        //讀取檔案中的資料
        String str = FileUtils.readFileToString(new File("./bbb.txt"),"UTF-8");
        System.out.println(str);
        //複制檔案
        FileUtils.copyFile(new File("./bbb.txt"),new File("./bbb3.txt"));
        //删除檔案夾
        FileUtils.deleteDirectory(new File("D:/demo"));
    }  
}