1. 程式人生 > 實用技巧 >Java自學第10期——File類與IO流(輸入輸出流、處理流、轉換流、緩衝流、Properties集合、列印流)

Java自學第10期——File類與IO流(輸入輸出流、處理流、轉換流、緩衝流、Properties集合、列印流)

1、IO簡介

IO(輸入輸出)通過java.io包下的類和介面來支援,包下包括輸入、輸出兩種IO流,每種輸入輸出流又可分為字元流和位元組流兩大類。

2、File類

File類是io包下與平臺無關的檔案和目錄,File能新建、刪除、重新命名檔案和目錄,不能訪問檔案本身,後者需要使用輸入輸入流。

2.1 構造方法

File類的構造方法:

File(File parent, String child) 引數:父路徑,子路徑
根據 parent 抽象路徑名和 child 路徑名字串建立一個新 File 例項。
File(String pathname)
通過將給定路徑名字串轉換為抽象路徑名來建立一個新 File 例項。
File(String parent, String child)
根據 parent 路徑名字串和 child 路徑名字串建立一個新 File 例項。
File(URI uri)
通過將給定的 file: URI 轉換為一個抽象路徑名來建立一個新的 File 例項。

2.2 靜態方法

File類靜態方法:

static String pathSeparator()
與系統有關的路徑分隔符,為了方便,它被表示為一個字串。
static char pathSeparatorChar()
與系統有關的路徑分隔符。
static String separator()
與系統有關的預設名稱分隔符,為了方便,它被表示為一個字串。
static char separatorChar()
與系統有關的預設名稱分隔符。

2.3 常用方法:

2.3.1 獲取相關

public String getAbsolutePath()
返回此File的絕對路徑名字串。
public String getPath

()
將此File轉換為路徑名字串。
public String getName()
返回由此File表示的檔案或目錄的名稱。
public long length()
返回由此File表示的檔案的長度。不能獲取資料夾大小,返回0。如果路徑不存在則返回0。

//靜態方法直接通過類名呼叫
    public static void main(String[] args) {
        System.out.println(File.pathSeparator);//列印 ;
        System.out.println(File.pathSeparatorChar);//列印 ;

        System.out.println(File.separator);//列印 \
        System.out.println(File.separatorChar);//列印 \

        File obj1 = new File("D:\\test.txt");//以檔案結尾的路徑
        System.out.println("檔案絕對路徑:" + obj1.getAbsolutePath());
        System.out.println("檔案構造路徑:" + obj1.getPath());
        System.out.println("檔名:" + obj1.getName());
        System.out.println("檔案長度:" + obj1.length());

        File obj2 = new File("D\\TEST");//以資料夾結尾的路徑
        System.out.println("目錄絕對路徑:" + obj2.getAbsolutePath());
        System.out.println("目錄構造路徑:" + obj2.getPath());
        System.out.println("目錄名:" + obj1.getName());
        System.out.println("目錄長度:" + obj2.length());
        /*
        檔案絕對路徑:D:\test.txt
        檔案構造路徑:D:\test.txt
        檔名:test.txt
        檔案長度:3位元組

        目錄絕對路徑:D:\Coding\demo01\D\TEST
        目錄構造路徑:D\TEST
        目錄名:test.txt
        目錄長度:0
         */

2.3.2 判斷相關

boolean exists()
測試此抽象路徑名錶示的檔案或目錄是否存在。
boolean isFile()
測試此抽象路徑名錶示的是否是檔案,而不是目錄。
boolean isDirectory()
測試此抽象路徑名錶示的檔案是否是一個目錄。
boolean isAbsolute()
測試此抽象路徑名是否為絕對路徑名。
注意:路徑必須存在,否則isFile()和isDirectory()都返回false。

2.3.3 操作相關

boolean createNewFile()
當次File物件對應的檔案不存在時,新建一個該File物件所指定的新檔案,建立成功返回true,反之false。
boolean delete()
刪除File物件對應的檔案或路徑。
boolean mkdir()
建立此抽象路徑名指定的目錄。
boolean mkdirs()
建立此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。

2.3.4 遍歷目錄:

String[] list()
返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中的檔案和目錄。
String[] list(FilenameFilter filter)
返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。
File[] listFiles()
返回一個抽象路徑名(File)陣列,這些路徑名錶示此抽象路徑名錶示的目錄中的檔案。
File[] listFiles(FileFilter filter)
返回抽象路徑名(File)陣列,這些路徑名錶示此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。
File[] listFiles(FilenameFilter filter)
返回抽象路徑名(File)陣列,這些路徑名錶示此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。

注意:如果list()和listFiles()遍歷的目錄不存在,或者構造方法給的路徑不是目錄,
都會丟擲空指標異常NullPointerEcxeption;該遍歷非能獲取到隱藏的檔案或目錄

路徑分隔符:Windows下為分號; Linux下為冒號:
預設名稱分隔符:Windows下為反斜槓\,Linux下為正斜槓/

public static void main(String[] args) {
        File f1 = new File("D:\\TEST");
        System.out.println(f1.exists());
        System.out.println(f1.isFile());
        System.out.println(f1.isDirectory());
        System.out.println(f1.isAbsolute());

        File f2 = new File("E:\\FireFox");
        String[] str1 = f2.list();//list()返回字串陣列
        //增強for遍歷字串陣列
        for (String str:str1) {
            System.out.println(str);
        }
        //返回的是File型別的陣列
        File[] file = f2.listFiles();
        //遍歷目錄
        for (File f : file){
            System.out.println(f);
        }
    }

2.3.5 絕對路徑和相對路徑:

絕對路徑:從碟符開始的路徑
相對路徑:相對於當前專案的根目錄的路徑
注意:路徑不區分大小寫;

2.4 遞迴

遞迴:
在當前方法呼叫自己

分類:
直接遞迴,間接遞迴(a呼叫b,b呼叫a)

要求:
1、必須要有條件限定結束方法,不認棧記憶體溢位
2、遞迴次數不能太多,不然也會記憶體溢位
3、構造方法不允許遞迴,建立的物件太多

使用前提:
呼叫方法時,方法的主體不變,但每次呼叫的引數在不斷改變
如果僅僅是求1到n的和,建議用for迴圈,遞迴會導致頻繁的建立呼叫銷燬方法,效率低。

public static void main(String[] args) {
        //遞迴累加求和
        int min = 1;
        int max = 4;
        int sum = a(min,max);
        System.out.println(sum);

        //遞迴計算階乘n!
        int num = 4;
        int sum2 = b(num);
        System.out.println(sum2);
    }

    private static int a(int min,int max){
        if ( max==1){
            return 1;
        }else {
            return max + a(min, --max);
        }
    }

    private  static int b(int num){
        if (num == 1){
            return 1;
        }else {
            return num * b(--num);
        }
    }

遍歷多級資料夾,遇到檔案直接列印路徑,判斷為資料夾則遞迴遍歷這個資料夾,不斷重複,停止條件為沒有資料夾
檔案搜尋,根據結尾副檔名,使用String類的endswith方法

 public static void main(String[] args) {
        File f = new File("D:\\Coding\\demo01\\demo1\\src");
        //遍歷所有檔案
        a(f);
        System.out.println("______________");
        //檔案搜尋
        b(f);

    }

    private static void a(File f){
        //返回路徑裡的所有File檔案到數組裡
        File[] f1 =  f.listFiles();
        //遍歷
        for (File f2 : f1) {
            //是檔案就直接列印路徑
            if (f2.isFile()){
                System.out.println("檔名:"+f2.getAbsolutePath());
            }else {//是資料夾就對其進行遞迴,不斷重複,知道都是檔案
                System.out.println("目錄列表:"+f2.getAbsolutePath());
                a(f2);
            }
        }
    }

    private static void b(File f){
        File[] f1 = f.listFiles();
        for (File f2:f1) {
            if (f2.isFile()){
                //是檔案就判斷是不是.java字尾
                //endswith判斷後綴,因為是String類的方法 要把f2轉化(getname),toLowerCase是將JAVA這種大寫字尾統一降為小寫
                if (f2.getName().toLowerCase().endsWith(".java")){
                    System.out.println(f2);
                }
            }else {
                b(f2);
            }
        }

2.4.1 檔案過濾器:

File類的list方法可以接受FilenameFilter型別作引數,通過該引數可以進行篩選
FilenameFilter是介面,只有一個抽象方法accept,可以使用lambda表示式書寫
boolean accept(File dir, String name)
對指定File子目錄和檔案迭代,如果符合條件則返回true,則list方法列出該子目錄或者檔案

public static void main(String[] args) {
        File f = new File("D:\\Coding\\demo01\\demo1\\src\\Interface");
//        a(f);
        //如果是以.java結尾,或者是資料夾,則返回true。
        String[] nameList = f.list((dir,name) -> name.endsWith(".java") || new File(name).isDirectory());
        for(String name : nameList){
            System.out.println(name);
        }
    }
//    private static void a(File f){
//        File[] f1 = f.listFiles();
//        for (File f2 : f1){
//            if (f2.isDirectory()){
//                a(f2);
//            }
//            String[] f3 = f2.list((dir, name) -> name.toLowerCase().endsWith(".java"));
//            for(String f4:f3){
//                System.out.println(f4);
//            }
//        }
//    }

3、流Stream

Java傳統的流型別(類或抽象類)在java.io包中,Java把各種輸入輸出源抽象為流stream,流是從起源source到接收sink的資料

IO分為分為輸入流(從外部讀取到記憶體程式)和輸出流(從程式到外部),各自又分為字元流和位元組流(二者區別僅僅為操作的資料單元不同,字元16位,位元組8位),

輸入流兩個基類:InputStream(位元組流),Reader(字元流)
輸出流兩個基類:OutputStream(位元組流),Writer(字元流)

如果處理文字內容,建議使用字元流,如果處理二進位制內容,建議位元組流。

按照流的角色分類可以分為 節點流(對直接和指定檔案關聯)和 處理流,處理流是對節點流的一種封裝

IO資源不屬於記憶體,垃圾回收機制不回收,使用close方法手動關閉流,IO類都實現了AutoCloseable介面,都可通過trycatch關閉IO流

3.1 輸入流

3.1.1 位元組輸入流抽象基類:InputStream:

int read(): 從輸入流中讀取單個位元組,返回位元組資料
int read(byte[] b): 從輸入流中最多讀取b.length個位元組,並存儲在位元組陣列b中,返回讀取的位元組數
int read(byte[] b,int off,int len): 從輸入流中最多讀取b.length個位元組的資料,儲存在陣列b中,不是從陣列起點開始,而是從off位置開始,返回讀取的位元組數
void close()
關閉此輸入流並釋放與該流關聯的所有系統資源。

3.1.2 字元輸入流抽象基類:Reader:

int read(): 從輸入流中讀取單個位元組,返回位元組資料
int read(char[] b): 從輸入流中最多讀取b.length個位元組,並存儲在位元組陣列b中,返回讀取的位元組數
int read(char[] b,int off,int len): 從輸入流中最多讀取b.length個位元組的資料,儲存在陣列b中,不是從陣列起點開始,而是從off位置開始,返回讀取的位元組數
void close()
關閉此輸入流並釋放與該流關聯的所有系統資源。

二者幾乎一樣。

InputStream/Reader都是抽象類,不能直接建立例項,但他們有讀取檔案的輸入流:
FileInputStream和FileReader,
二者都是節點流

3.1.3 位元組輸入流:FileInputStream

是抽象類InputStream的實現類,可以拿來建立物件。FileInputStream繼承了InputStream的方法。
構造方法可以傳路徑:

FileInputStream(File file)
通過開啟一個到實際檔案的連線來建立一個 FileInputStream,該檔案通過檔案系統中的 File 物件 file 指定。

3.1.2 字元輸入流:FileReader

是抽象類Reader的實現類,可以拿來建立物件。FileReader繼承了Reader的方法。
構造方法可以傳路徑:
FileReader(File file)
在給定從中讀取資料的 File 的情況下建立一個新 FileReader。

public static void main(String[] args) throws IOException {
        //FileInputStream
        //建立位元組輸入流,讀取自身.name是路徑名
        FileInputStream fis = new FileInputStream("D:\\Coding\\demo01\\demo1\\src\\IO\\Demo06_IO.java");
        //建立長度為24的竹筒
        byte[] b = new byte[1024];
        //計數器
        int i = 0;
        //迴圈輸入,只要還有資料就一直執行
        while((i = fis.read(b)) >0){
            //取出資料,將陣列轉換成字串
            System.out.println(new String(b,0,i));
        }
        //列印原始碼
        //使用close方法關閉輸入流
        fis.close();

        //FileReader
        try (
                FileReader f = new FileReader("D:\\Coding\\demo01\\demo1\\src\\IO\\Demo06_IO.java");
        ){
            char[] f1 = new char[32];
            int f3 = 0;
            while ((f3 = f.read(f1)) > 0) {
                System.out.println(new String(f1, 0, f3));
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

3.2 輸出流

輸出流抽象基類:
抽象位元組輸出流OutputStream、抽象字元輸出流Writer

方法與輸入流基本相似,不過將read換成了write。

Writer還包換了額外方法:

void writer(String str): 將str字串裡的字元輸出到指定輸出流中
void write(String str,int off,int len): 將str字串裡從off開始,長為len的字元輸出到指定輸出流中

win下簡體中文字符集預設GBK,linux下預設UTF-8

3.2.1 位元組輸出流:FileOutputStream

構造方法:
FileOutputStream(File file)
建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。

成員方法
void write(byte[] b) :
將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。

void write(byte[] b, int off, int len)
將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。
void write(int b)
將指定位元組寫入此檔案輸出流。

3.2.2 字元輸出流:FileWriter

FileWriter(File file)
根據給定的 File 物件構造一個 FileWriter 物件。

public static void main(String[] args) {
        try(
                //建立位元組輸入流
                FileInputStream f0 = new FileInputStream("D:\\Coding\\demo01\\demo1\\src\\IO\\Demo05.java");
                //建立位元組輸出流
                FileOutputStream f1 = new FileOutputStream("D:\\Coding\\test.txt");
        ){
          byte[] by = new byte[8];
          int i = 0;
          while ( (i = f0.read(by) ) > 0){
              f1.write(by,0,i);
          }
        }catch (IOException e){
            e.printStackTrace();
        }

        //直接輸出字串內容
        try (
                FileWriter writer = new FileWriter("D:\\Coding\\test2.txt");
        ){
            //win平臺換行符\r\n,Linux平臺\n
            writer.write("ddd \r\n");
            writer.write("666 \r\n");
        }catch (IOException e){
            e.printStackTrace();
        }


    }

3.3 處理流

以上流程比較繁瑣,使用處理流(高階流,節點流又叫低階流)簡化:
處理流可以忽視底層裝置節點流的差異,並提供更方便的方法,除了方便程式設計師,本身執行效率也高

使用PrintStream包裝OutputStream(@1),再直接使用PrintLn方法列印

關閉流時,只需關閉最上層的處理流即可,系統自動關閉其包裝的節點流

除了用陣列作為節點外,字元流還可以用字串作為物理節點StringReader,見下兩層程式碼

public static void main(String[] args) {
        try(
                //建立位元組輸出流
                FileOutputStream f1 = new FileOutputStream("D:\\Coding\\test3.txt");
                //@1
                //包裝該節點流,節點流當做引數傳給構造器
                PrintStream ps = new PrintStream(f1);){
            //直接列印字串
            ps.println("處理流 \r\n");
            //也可以輸出物件
            ps.println(new Demo08());
        }catch (IOException e){
            e.printStackTrace();
        }

        String str = "fighting!\n";
        char[] ch = new char[32];
        int hasRead = 0;
        try(
                StringReader sr = new StringReader("D:\\Coding\\test2.txt");
                ) {
            while ((hasRead = sr.read(ch)) > 0){
                System.out.println(new String(ch,0,hasRead));
            }
        }catch (IOException e){
            e.printStackTrace();
        }

3.4 轉換流

轉換流:用於將位元組流轉換為字元流(不存在字元流轉位元組流,沒有必要)

InputStreamReader:將位元組輸入流轉換為字元輸入流
OutputStreamWriter:將位元組輸出流轉換為字元輸出流

程式碼示範 將鍵盤輸入System.in轉換為字元輸出流的過程,
由於InputStreamReader依然不是很方便,再包裝成BufferedReader,
利用BufferedReader的readLine方法一次讀取一行(以換行符為標誌,沒有則程式阻塞,按回車繼續。)
String readLine()
讀取一個文字行。

public static void main(String[] args) {
        try (
                //將System.in物件轉換為Reader物件
                InputStreamReader a = new InputStreamReader(System.in);
                //將普通的Reader包裝成BufferedReader
                BufferedReader b = new BufferedReader(a);

                ){
            String line = null;

            while ((line = b.readLine()) != null){
                if (line.equals("exit")){
                    System.exit(1);
                }
                System.out.println("輸出內容:" + line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

將GBK編碼的文字檔案,轉換為UTF-8編碼的文字檔案。
案例分析
1. 指定GBK編碼的轉換流,讀取文字檔案。
2. 使用UTF-8編碼的轉換流,寫出文字檔案。
案例實現

public static void main(String[] args) throws IOException {
        // 1.定義檔案路徑
        String srcFile = "D:\\Coding\\test.txt";
        String destFile = "D:\\Coding\\GBK.txt";
        // 2.建立流物件
        // 2.1 轉換輸入流,指定GBK編碼
        InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");
        // 2.2 轉換輸出流,預設utf8編碼
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));
        // 3.讀寫資料
        // 3.1 定義陣列
        char[] cbuf = new char[1024];
        // 3.2 定義長度
        int len;
        // 3.3 迴圈讀取
        while((len=isr.read(cbuf))!=-1){
        // 迴圈寫出
        osw.write(cbuf,0,len);
        }
        // 4.釋放資源
        osw.close();
        isr.close();
        }

3.5 緩衝流

緩衝流:又叫高效流,是四個基本流的增強,速度非常快。在建立流物件時,會建立一個內建的預設大小的緩衝區陣列,通過緩衝區讀寫,減少系統IO次數,從而提高讀寫的效率

位元組緩衝流:
BufferedInputStream (輸入流),
BufferedOutputStream(輸出流)
構造方法:
public BufferedInputStream(InputStream in) :建立一個新的緩衝輸入流。
public BufferedOutputStream(OutputStream out) :建立一個新的緩衝輸出流。

字元緩衝流:
BufferedReader(輸入流) ,
BufferedWriter(輸出流)
構造方法:
public BufferedReader(Reader in) :建立一個新的緩衝輸入流。
public BufferedWriter(Writer out) :建立一個新的緩衝輸出流。

複製檔案速度測試:

public static void main(String[] args) throws FileNotFoundException {
        //記錄開始時間
        long start = System.currentTimeMillis();
        //建立流物件
        try(
                BufferedInputStream a = new BufferedInputStream(new FileInputStream("E:\\素材\\嗶哩嗶哩\\67370009\\1\\67370009_1_0.mp4"));
                BufferedOutputStream b = new BufferedOutputStream(new FileOutputStream("E:\\素材\\嗶哩嗶哩\\67370009\\1.mp4"));
                ){
             int len = 0;
             byte[] byt = new byte[8*1024];
             while ((len = a.read(byt)) > 0){
                 b.write(byt,0,len);
             }
        }catch (IOException e){
                    e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }

3.6 與io有關的屬性集合Properties

java.util.Properties 繼承於Hashtable,來表示一個持久的屬性集。它使用鍵值結構儲存資料,每個鍵及其對應值都是一個字串。

該類也被許多Java類使用,比如獲取系統屬性時,System.getProperties 方法就是返回一個Properties 物件。

構造方法:
public Properties() :建立一個空的屬性列表。

基本方法:
public Object setProperty(String key, String value) :儲存一對屬性。
public String getProperty(String key) :使用此屬性列表中指定的鍵搜尋屬性值。
public Set stringPropertyNames() :所有鍵的名稱的集合。
void load(InputStream inStream) : 從輸入流中讀取屬性列表(鍵和元素對)。
void load(Reader reader) : 按簡單的面向行的格式從輸入字元流中讀取屬性列表(鍵和元素對)。
void store(OutputStream out, String comments) : 以適合使用 load(InputStream) 方法載入到 Properties 表中的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流。
void store(Writer writer, String comments) : 以適合使用 load(Reader) 方法的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出字元。

public static void main(String[] args) {
        Properties properties = new Properties();
        //新增鍵值對元素,鍵和值都預設的字串
        properties.setProperty("1","one");
        properties.setProperty("2","two");
        properties.setProperty("3","three");
        //列印屬性集物件
        System.out.println("屬性集:"+properties);
        //通過鍵,獲取屬性值
        System.out.println(properties.getProperty("1"));
        System.out.println(properties.getProperty("2"));
        System.out.println(properties.getProperty("3"));
        //遍歷屬性集,獲取包含所有鍵的集合
        Set<String> set = properties.stringPropertyNames();
        //列印鍵值對
        for (String str : set) {
            System.out.println(str + "對應" + properties.getProperty(str) );
        }

3.6.1 store輸出和load輸入


Properties集合的store方法,將集合中的臨時資料寫入到硬碟中儲存

1、位元組輸出流:
void store(OutputStream out, String comments):
以適合使用 load(InputStream) 方法載入到 Properties 表中的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流。

3、字元輸出流:
void store(Writer writer, String comments):
以適合使用 load(Reader) 方法的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出字元。

3、引數:
OutputStream out:位元組輸出流,不能寫中文
writer:字元輸出流,可以寫中文
comments:註釋,說明檔案用處。不能用中文,預設Unicode編碼,可用空的字串" "代替
——————————————————————————————————
Properties集合裡的load方法:
可以把鍵值對的檔案,讀取到集合裡使用

1、位元組輸入流:
void load(InputStream inStream)
從輸入流中讀取屬性列表(鍵和元素對)。

2、字元輸入流:
void load(Reader reader)
按簡單的面向行的格式從輸入字元流中讀取屬性列表(鍵和元素對)。

引數:
InputStream:位元組輸入流,不能讀取含有中文的鍵值對
Reader:能讀取中文鍵值對

使用步驟:
1.建立屬性集合物件
2.用load方法讀取含有鍵值對的檔案
3.遍歷屬性集合

注意
1.儲存鍵值對的檔案中,鍵與值預設的連線符號使用=或者空格
2.儲存鍵值對的檔案中,可以使用#進行註釋,被註釋的鍵值將不會被讀取
3.儲存鍵值對的檔案中,鍵與值預設都是字串,不用再加引號

public static void main(String[] args) throws IOException {
        //建立Properties屬性集合,並初始化資料
        Properties properties2 = new Properties();
        properties2.setProperty("1","one");
        properties2.setProperty("2","two");
        properties2.setProperty("3","three");
        //建立字元輸出流物件(位元組輸出流不能用中文)
        FileWriter fw = new FileWriter("D:\\Coding\\屬性集合.txt");
        //使用集合的store方法,將集合的臨時資料寫入硬碟.註釋不能寫中文
        properties2.store(fw,"hello");
        //關閉輸出流
        fw.close();


        //建立屬性集合物件
        Properties pp = new Properties();
        //使用load方法讀取檔案
        pp.load(new FileReader("D:\\Coding\\屬性集合.txt"));
        //將key值統一到集合中
        Set<String> set2 = pp.stringPropertyNames();
        for (String str2:set2) {
//          //通過key值訪問value
            String property = pp.getProperty(str2);
            System.out.println(property);

        }
    }

3.6.2 物件的序列化

把物件以流的方式,寫入到檔案中儲存,叫寫物件,或物件的序列
ObjectOutputStream:物件的序列化流

把檔案中儲存的物件,以流的方式讀取出來,叫物件的反序列化
ObjectInputStream:物件的反序列化流

一、ObjectOutputStream繼承了OutputStream,其構造方法的引數是傳入一個OutputStream

成員方法: void writeObject(Object obj)
將指定的物件寫入ObjectOutputStream

使用步驟:
1.建立ObjectOutputStream物件,構造方法傳遞位元組輸出流
2.使用ObjectOutputStream物件的writeObject把物件寫入檔案中
3.資源釋放

注意
序列化和反序列化時,會丟擲NotSerizable未序列化異常
類通過實現java.io.Serialzable介面以實現其序列化功能,未實現此介面的類無法使其序列化或者反序列化
Serializable介面叫標記型介面,接口裡沒有什麼方法。實現該介面時會給類新增一個標記
有這個標記才允許序列化或者反序列化,沒有則拋異常。

二、java.io.ObjectInputStream extends InputStream
作用:把檔案中儲存的物件,以流的方式讀取出來使用

構造方法:
傳入位元組輸入流InputStream

成員方法:
Object readObject():從ObjectInputStream讀取物件
該方法丟擲了ClassNotFoundException(class檔案找不到異常),說明不存在物件的class檔案

反序列化前提:
1.類必須實現Serilizable介面
2.必須存在對應的class檔案

使用步驟:
1、建立ObjectInputStream物件,構造方法傳入位元組輸入流
2、使用readObject方法讀取儲存物件的檔案
3.釋放資源
4.列印物件

public static void main(String[] args) throws IOException, ClassNotFoundException {
        //建立ObjectOutputStream
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Coding\\ObjectputStream.txt"));
        Demo00_person person = new Demo00_person("小明", 12);
        oos.writeObject(person);
        oos.close();

        //
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Coding\\ObjectputStream.txt"));
        Object o = ois.readObject();//聲明瞭ClassNotFound異常
        ois.close();
        System.out.println(o);

        Demo00_person obj1 = (Demo00_person)o;
        System.out.println(obj1.getName()+obj1.getAge());

    }

3.6.3 物件集合的序列化

transient:瞬態關鍵字
static:靜態關鍵字
記憶體先載入靜態關鍵字,被static修飾的成員變數不能被序列化,物件才能被序列化
被transient修飾的成員變數,不能被序列化,但又沒有static靜態的含義

序列化集合:
如果想儲存多個物件,可以把多個物件儲存在集合中, 對集合進行序列化和反序列化

步驟:
1、建立要儲存的多個物件
2、建立物件陣列ArrayList,將物件新增進陣列
3、寫序列化方法,建立序列化流,傳入位元組輸出流,指定輸出路徑,用writeObject方法將物件陣列寫入序列化流中,關閉流
//4、反序列化:建立反序列化流,傳入位元組輸入流,指定操作檔案路徑
//5、readObject從反序列化流中讀取檔案,強轉為ArrayList型別,增強for遍歷

public static void main(String[] args) throws IOException, ClassNotFoundException {
        Demo00_person person1 = new Demo00_person("人",12);
        Demo00_person person2 = new Demo00_person("鬼",1);
        Demo00_person person3 = new Demo00_person("仙",13);
        ArrayList<Demo00_person> arrayList = new ArrayList<>();
        arrayList.add(person1);
        arrayList.add(person2);
        arrayList.add(person3);
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Coding\\序列化集合.txt"));
        oos.writeObject(arrayList);
        oos.close();
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Coding\\序列化集合.txt"));
        ArrayList<Demo00_person> arrayList1 = (ArrayList<Demo00_person>)ois.readObject();
        for (Demo00_person obj2 : arrayList1) {
            System.out.println(obj2.getName()+obj2.getAge());
        }
    }

列印流

java.io.PrintStream:
列印流,繼承了OutputStream,為其他輸出流添加了功能,使它們能夠方便地列印各種資料值表示形式

1、只負責資料輸出,不負責資料讀取
2、與其他輸出流不同,PrintStream永遠不會丟擲IOException
3、特有方法:print()\println(),可以輸出任意型別的值

構造方法
PrintStream(File file):
建立具有指定檔案且不帶自動行重新整理的新列印流。
PrintStream(OutputStream out):
建立新的列印流。
PrintStream(String fileName):
建立具有指定檔名稱且不帶自動行重新整理的新列印流。

成員方法
void close():
關閉流。
void flush():
重新整理該流的緩衝。
void write(int b):
將指定的位元組寫入此流。
void write(byte[] b):
將b.length位元組從指定的位元組陣列寫入此輸入流。
void write(byte[] buf, int off, int len):
將 len 位元組從指定的初始偏移量為 off 的 byte 陣列寫入此流。

注意:
如果使用繼承自父類的write方法寫資料,檢視資料的時候會檢視編碼表 97->a
用自己特有的print|println方法寫資料,寫的資料原樣輸出97->97

改變輸出語句的目的地(預設控制檯輸出),使用System.setOut方法改變輸出語句的目的地改為引數中傳遞的列印流的目的地
靜態方法:
static void setOut(PrintStream out)
重新分配“標準”輸出流。

java.io.PrintStream:列印流,繼承了OutputStream,為其他輸出流添加了功能,使它們能夠方便地列印各種資料值表示形式

1、只負責資料輸出,不負責資料讀取
2、與其他輸出流不同,PrintStream永遠不會丟擲IOException
3、特有方法:print()\println(),可以輸出任意型別的值
構造方法:
PrintStream(File file)
          建立具有指定檔案且不帶自動行重新整理的新列印流。
PrintStream(OutputStream out)
          建立新的列印流。
PrintStream(String fileName)
          建立具有指定檔名稱且不帶自動行重新整理的新列印流。
成員方法:
void close()
          關閉流。
void flush()
          重新整理該流的緩衝。
void write(int b)
          將指定的位元組寫入此流。
void write(byte[] b)
           將b.length位元組從指定的位元組陣列寫入此輸入流。
void write(byte[] buf, int off, int len)
          將 len 位元組從指定的初始偏移量為 off 的 byte 陣列寫入此流。
注意:
如果使用繼承自父類的write方法寫資料,檢視資料的時候會檢視編碼表 97->a
用自己特有的print|println方法寫資料,寫的資料原樣輸出97->97

改變輸出語句的目的地(預設控制檯輸出),使用System.setOut方法改變輸出語句的目的地改為引數中傳遞的列印流的目的地
靜態方法:
static void setOut(PrintStream out)
          重新分配“標準”輸出流。