Java進階篇-IO流-動力節點

IO流介紹

IO流,什麼是IO?

I : Input
O : Output
通過IO可以完成硬盤文件的讀和寫。

IO流的分類

有多種分類方式:
	一種方式是按照流的方向進行分類:
		以內存作爲參照物,
			往內存中去,叫做輸入(Input)。或者叫做讀(Read)。
			從內存中出來,叫做輸出(Output)。或者叫做寫(Write)。

	另一種方式是按照讀取數據方式不同進行分類:
		有的流是按照字節的方式讀取數據,一次讀取1個字節byte,等同於一次讀取8個二進制位。
		這種流是萬能的,什麼類型的文件都可以讀取。包括:文本文件,圖片,聲音文件,視頻文件等....
			假設文件file1.txt,採用字節流的話是這樣讀的:
				a中國bc張三fe
				第一次讀:一個字節,正好讀到'a'
				第二次讀:一個字節,正好讀到'中'字符的一半。
				第三次讀:一個字節,正好讀到'中'字符的另外一半。

		有的流是按照字符的方式讀取數據的,一次讀取一個字符,這種流是爲了方便讀取
		普通文本文件而存在的,這種流不能讀取:圖片、聲音、視頻等文件。只能讀取純
		文本文件,連word文件都無法讀取。
			假設文件file1.txt,採用字符流的話是這樣讀的:
				a中國bc張三fe
				第一次讀:'a'字符('a'字符在windows系統中佔用1個字節。)
				第二次讀:'中'字符('中'字符在windows系統中佔用2個字節。)

綜上所述:流的分類
	輸入流、輸出流
	字節流、字符流

IO流需要掌握的知識點

Java中的IO流都已經寫好了,我們程序員不需要關心,我們最主要還是掌握:
	在java中已經提供了哪些流,每個流的特點是什麼,每個流對象上的常用方法有哪些?
java中所有的流都是在:java.io.*;下。
java中主要還是研究:
	怎麼new流對象。
	調用流對象的哪個方法是讀,哪個方法是寫。

java IO流這塊有四大家族:

四大家族的首領:
	java.io.InputStream  字節輸入流
	java.io.OutputStream 字節輸出流

	java.io.Reader		字符輸入流
	java.io.Writer		字符輸出流

	四大家族的首領都是抽象類。(abstract class)

	所有的流都實現了:
		java.io.Closeable接口,都是可關閉的,都有close()方法。
		流畢竟是一個管道,這個是內存和硬盤之間的通道,用完之後一定要關閉,
		不然會耗費(佔用)很多資源。養成好習慣,用完流一定要關閉。

	所有的輸出流都實現了:
		java.io.Flushable接口,都是可刷新的,都有flush()方法。
		養成一個好習慣,輸出流在最終輸出之後,一定要記得flush()
		刷新一下。這個刷新表示將通道/管道當中剩餘未輸出的數據
		強行輸出完(清空管道!)刷新的作用就是清空管道。
		注意:如果沒有flush()可能會導致丟失數據。


注意:在java中只要“類名”以Stream結尾的都是字節流。以“Reader/Writer”結尾的都是字符流。

java.io包下需要掌握的流有16個:

文件專屬:
	java.io.FileInputStream(掌握)
	java.io.FileOutputStream(掌握)
	java.io.FileReader
	java.io.FileWriter

轉換流:(將字節流轉換成字符流)
	java.io.InputStreamReader
	java.io.OutputStreamWriter

緩衝流專屬:
	java.io.BufferedReader
	java.io.BufferedWriter
	java.io.BufferedInputStream
	java.io.BufferedOutputStream

數據流專屬:
	java.io.DataInputStream
	java.io.DataOutputStream

標準輸出流:
	java.io.PrintWriter
	java.io.PrintStream(掌握)

對象專屬流:
	java.io.ObjectInputStream(掌握)
	java.io.ObjectOutputStream(掌握)

FileInputStream(字節輸入流)

java.io.FileInputStream:
    1、文件字節輸入流,萬能的,任何類型的文件都可以採用這個流來讀。
    2、字節的方式,完成輸入的操作,完成讀的操作(硬盤---> 內存)
代碼演示(常用方法):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
int read(byte[] b)
    一次最多讀取 b.length 個字節。
    減少硬盤和內存的交互,提高程序的執行效率。
    往byte[]數組當中讀。
 */
public class FileInputStreamTest03 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 相對路徑的話呢?相對路徑一定是從當前所在的位置作爲起點開始找!
            // IDEA默認的當前路徑是哪裏?工程Project的根就是IDEA的默認當前路徑。
            //fis = new FileInputStream("tempfile3");
            //fis = new FileInputStream("chapter23/tempfile2");
            //fis = new FileInputStream("chapter23/src/tempfile3");
            fis = new FileInputStream("chapter23/src/com/bjpowernode/java/io/tempfile4");

            // 開始讀,採用byte數組,一次讀取多個字節。最多讀取“數組.length”個字節。
            byte[] bytes = new byte[4]; // 準備一個4個長度的byte數組,一次最多讀取4個字節。
            // 這個方法的返回值是:讀取到的字節數量。(不是字節本身)
            int readCount = fis.read(bytes);
            System.out.println(readCount); // 第一次讀到了4個字節。
            // 將字節數組全部轉換成字符串
            //System.out.println(new String(bytes)); // abcd
            // 不應該全部都轉換,應該是讀取了多少個字節,轉換多少個。
            System.out.println(new String(bytes,0, readCount));

            readCount = fis.read(bytes); // 第二次只能讀取到2個字節。
            System.out.println(readCount); // 2
            // 將字節數組全部轉換成字符串
            //System.out.println(new String(bytes)); // efcd
            // 不應該全部都轉換,應該是讀取了多少個字節,轉換多少個。
            System.out.println(new String(bytes,0, readCount));

            readCount = fis.read(bytes); // 1個字節都沒有讀取到返回-1
            System.out.println(readCount); // -1

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

代碼演示(完整程序):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamTest04 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("chapter23/src/tempfile3");
            // 準備一個byte數組
            byte[] bytes = new byte[4];
            /*while(true){
                int readCount = fis.read(bytes);
                if(readCount == -1){
                    break;
                }
                // 把byte數組轉換成字符串,讀到多少個轉換多少個。
                System.out.print(new String(bytes, 0, readCount));
            }*/

            int readCount = 0;
            while((readCount = fis.read(bytes)) != -1) {
                System.out.print(new String(bytes, 0, readCount));
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
代碼演示(available和skip方法):
FileInputStream類的其它常用方法:
    int available():返回流當中剩餘的沒有讀到的字節數量
    long skip(long n):跳過幾個字節不讀。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamTest05 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("tempfile");
            System.out.println("總字節數量:" + fis.available());
            // 讀1個字節
            //int readByte = fis.read();
            // 還剩下可以讀的字節數量是:5
            //System.out.println("剩下多少個字節沒有讀:" + fis.available());
            // 這個方法有什麼用?
            //byte[] bytes = new byte[fis.available()]; // 這種方式不太適合太大的文件,因爲byte[]數組不能太大。
            // 不需要循環了。
            // 直接讀一次就行了。
            //int readCount = fis.read(bytes); // 6
            //System.out.println(new String(bytes)); // abcdef

            // skip跳過幾個字節不讀取,這個方法也可能以後會用!
            fis.skip(3);
            System.out.println(fis.read()); //100

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileOutputStream(字節輸出流)

 * 文件字節輸出流,負責寫。
 * 從內存到硬盤。
 * 寫完後需要刷新:變量名.flush();
代碼演示(文件新建):
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamTest01 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // myfile文件不存在的時候會自動新建!
            // 這種方式謹慎使用,這種方式會先將原文件清空,然後重新寫入。
            //fos = new FileOutputStream("myfile");
            //fos = new FileOutputStream("chapter23/src/tempfile3");

            // 以追加的方式在文件末尾寫入。不會清空原文件內容。
            fos = new FileOutputStream("chapter23/src/tempfile3", true);
            // 開始寫。
            byte[] bytes = {97, 98, 99, 100};
            // 將byte數組全部寫出!
            fos.write(bytes); // abcd
            // 將byte數組的一部分寫出!
            fos.write(bytes, 0, 2); // 再寫出ab

            // 字符串
            String s = "我是一箇中國人,我驕傲!!!";
            // 將字符串轉換成byte數組。
            byte[] bs = s.getBytes();
            // 寫
            fos.write(bs);

            // 寫完之後,最後一定要刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileInputStream和FileOutputStream完成文件拷貝

使用FileInputStream + FileOutputStream完成文件的拷貝。
拷貝的過程應該是一邊讀,一邊寫。
使用以上的字節流拷貝文件的時候,文件類型隨意,萬能的。什麼樣的文件都能拷貝。
代碼演示(文件拷貝):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Copy01 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 創建一個輸入流對象
            fis = new FileInputStream("D:\\course\\02-JavaSE\\video\\chapter01\\動力節點-JavaSE-杜聚賓-001-文件擴展名的顯示.avi");
            // 創建一個輸出流對象
            fos = new FileOutputStream("C:\\動力節點-JavaSE-杜聚賓-001-文件擴展名的顯示.avi");

            // 最核心的:一邊讀,一邊寫
            byte[] bytes = new byte[1024 * 1024]; // 1MB(一次最多拷貝1MB。)
            int readCount = 0;
            while((readCount = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, readCount);
            }

            // 刷新,輸出流最後要刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 分開try,不要一起try。
            // 一起try的時候,其中一個出現異常,可能會影響到另一個流的關閉。
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileRead(字符輸入流):

FileReader:
    文件字符輸入流,只能讀取普通文本。
    讀取文本內容時,比較方便,快捷。
代碼演示:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader reader = null;
        try {
            // 創建文件字符輸入流
            reader = new FileReader("tempfile");

            //準備一個char數組
            char[] chars = new char[4];
            // 往char數組中讀
            reader.read(chars); // 按照字符的方式讀取:第一次e,第二次f,第三次 風....
            for(char c : chars) {
                System.out.println(c);
            }

            /*// 開始讀
            char[] chars = new char[4]; // 一次讀取4個字符
            int readCount = 0;
            while((readCount = reader.read(chars)) != -1) {
                System.out.print(new String(chars,0,readCount));
            }*/
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileWrite(字符輸入流)

FileReader:
    文件字符輸入流,只能讀取普通文本。
    讀取文本內容時,比較方便,快捷。
代碼演示:
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        FileWriter out = null;
        try {
            // 創建文件字符輸出流對象
            //out = new FileWriter("file");
            out = new FileWriter("file", true);

            // 開始寫。
            char[] chars = {'我','是','中','國','人'};
            out.write(chars);
            out.write(chars, 2, 3);

            out.write("我是一名java軟件工程師!");
            // 寫出一個換行符。
            out.write("\n");
            out.write("hello world!");

            // 刷新
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileRead + FileWrite 拷貝文本

使用FileReader FileWriter進行拷貝的話,只能拷貝“普通文本”文件。
代碼演示(文本拷貝):
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Copy02 {
    public static void main(String[] args) {
        FileReader in = null;
        FileWriter out = null;
        try {
            // 讀
            in = new FileReader("chapter23/src/com/bjpowernode/java/io/Copy02.java");
            // 寫
            out = new FileWriter("Copy02.java");

            // 一邊讀一邊寫:
            char[] chars = new char[1024 * 512]; // 1MB
            int readCount = 0;
            while((readCount = in.read(chars)) != -1){
                out.write(chars, 0, readCount);
            }

            // 刷新
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

BufferedReader,BufferedWrite(帶有緩衝區的字符輸入流):

BufferedReader:
    帶有緩衝區的字符輸入流。
    使用這個流的時候不需要自定義char數組,或者說不需要自定義byte數組。自帶緩衝。
代碼演示:
import java.io.BufferedReader;
import java.io.FileReader;

public class BufferedReaderTest01 {
    public static void main(String[] args) throws Exception{

        FileReader reader = new FileReader("Copy02.java");
        // 當一個流的構造方法中需要一個流的時候,這個被傳進來的流叫做:節點流。
        // 外部負責包裝的這個流,叫做:包裝流,還有一個名字叫做:處理流。
        // 像當前這個程序來說:FileReader就是一個節點流。BufferedReader就是包裝流/處理流。
        BufferedReader br = new BufferedReader(reader);

        // 讀一行
        /*String firstLine = br.readLine();
        System.out.println(firstLine);

        String secondLine = br.readLine();
        System.out.println(secondLine);

        String line3 = br.readLine();
        System.out.println(line3);*/

        // br.readLine()方法讀取一個文本行,但不帶換行符。
        String s = null;
        while((s = br.readLine()) != null){
            System.out.print(s);
        }

        // 關閉流
        // 對於包裝流來說,只需要關閉最外層流就行,裏面的節點流會自動關閉。(可以看源代碼。)
        br.close();
    }
}

InputStreamReader,OutputStreamWrite(轉換流):

代碼演示(讀取Read):
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/*
    轉換流:InputStreamReader
 */
public class BufferedReaderTest02 {
    public static void main(String[] args) throws Exception{

        /*// 字節流
        FileInputStream in = new FileInputStream("Copy02.java");

        // 通過轉換流轉換(InputStreamReader將字節流轉換成字符流。)
        // in是節點流。reader是包裝流。
        InputStreamReader reader = new InputStreamReader(in);

        // 這個構造方法只能傳一個字符流。不能傳字節流。
        // reader是節點流。br是包裝流。
        BufferedReader br = new BufferedReader(reader);*/

        // 合併
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Copy02.java")));

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

        // 關閉最外層
        br.close();
    }
}

代碼演示(寫入Write):
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;

/*
BufferedWriter:帶有緩衝的字符輸出流。
OutputStreamWriter:轉換流
 */
public class BufferedWriterTest {
    public static void main(String[] args) throws Exception{
        // 帶有緩衝區的字符輸出流
        //BufferedWriter out = new BufferedWriter(new FileWriter("copy"));

        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("copy", true)));
        // 開始寫。
        out.write("hello world!");
        out.write("\n");
        out.write("hello kitty!");
        // 刷新
        out.flush();
        // 關閉最外層
        out.close();
    }
}

DataOutputStream(數據字節輸出流)

java.io.DataOutputStream:數據專屬的流。
這個流可以將數據連同數據的類型一併寫入文件。
注意:這個文件不是普通文本文檔。(這個文件使用記事本打不開。)
代碼演示:
import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class DataOutputStreamTest {
    public static void main(String[] args) throws Exception{
        // 創建數據專屬的字節輸出流
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        // 寫數據
        byte b = 100;
        short s = 200;
        int i = 300;
        long l = 400L;
        float f = 3.0F;
        double d = 3.14;
        boolean sex = false;
        char c = 'a';
        // 寫
        dos.writeByte(b); // 把數據以及數據的類型一併寫入到文件當中。
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(sex);
        dos.writeChar(c);

        // 刷新
        dos.flush();
        // 關閉最外層
        dos.close();
    }
}

DataInputStream(數據字節輸入流)

DataOutputStream寫的文件,只能使用DataInputStream去讀。並且讀的時候你需要提前知道寫入的順序。
讀的順序需要和寫的順序一致。纔可以正常取出數據。

代碼演示():
import java.io.DataInputStream;
import java.io.FileInputStream;

public class DataInputStreamTest01 {
    public static void main(String[] args) throws Exception{
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));
        // 開始讀
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        boolean sex = dis.readBoolean();
        char c = dis.readChar();

        System.out.println(b);
        System.out.println(s);
        System.out.println(i + 1000);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(sex);
        System.out.println(c);

        dis.close();
    }
}

PrintStream(標準的字節輸出流)

默認輸出到控制檯。
代碼演示
import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest {
    public static void main(String[] args) throws Exception{

        // 聯合起來寫
        System.out.println("hello world!");

        // 分開寫
        PrintStream ps = System.out;
        ps.println("hello zhangsan");
        ps.println("hello lisi");
        ps.println("hello wangwu");

        // 標準輸出流不需要手動close()關閉。
        // 可以改變標準輸出流的輸出方向嗎? 可以
        /*
        // 這些是之前System類使用過的方法和屬性。
        System.gc();
        System.currentTimeMillis();
        PrintStream ps2 = System.out;
        System.exit(0);
        System.arraycopy(....);
         */

        // 標準輸出流不再指向控制檯,指向“log”文件。
        PrintStream printStream = new PrintStream(new FileOutputStream("log"));
        // 修改輸出方向,將輸出方向修改到"log"文件。
        System.setOut(printStream);
        // 再輸出
        System.out.println("hello world");
        System.out.println("hello kitty");
        System.out.println("hello zhangsan");

    }
}
class LogTest {
    public static void main(String[] args) {
        //測試工具類是否好用
        Logger.log("調用了System類的gc()方法,建議啓動垃圾回收");
        Logger.log("調用了UserService的doSome()方法");
        Logger.log("用戶嘗試進行登錄,驗證失敗");
        Logger.log("我非常喜歡這個記錄日誌的工具哦!");
    }
}

代碼演示(日誌工具):

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger {
    /*
    記錄日誌的方法。
     */
    public static void log(String msg) {
        try {
            // 指向一個日誌文件
            PrintStream out = new PrintStream(new FileOutputStream("log.txt", true));
            // 改變輸出方向
            System.setOut(out);
            // 日期當前時間
            Date nowTime = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
            String strTime = sdf.format(nowTime);

            System.out.println(strTime + ": " + msg);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

序列化(ObjectOutputStream,ObjectInputStream)

1、java.io.NotSerializableException:
    Student對象不支持序列化!!!!

2、參與序列化和反序列化的對象,必須實現Serializable接口。

3、注意:通過源代碼發現,Serializable接口只是一個標誌接口:
    public interface Serializable {
    }
    這個接口當中什麼代碼都沒有。
    那麼它起到一個什麼作用呢?
        起到標識的作用,標誌的作用,java虛擬機看到這個類實現了這個接口,可能會對這個類進行特殊待遇。
        Serializable這個標誌接口是給java虛擬機參考的,java虛擬機看到這個接口之後,會爲該類自動生成
        一個序列化版本號。

4、序列化版本號有什麼用呢?
    java.io.InvalidClassException:
        com.bjpowernode.java.bean.Student;
        local class incompatible:
            stream classdesc serialVersionUID = -684255398724514298(十年後),
            local class serialVersionUID = -3463447116624555755(十年前)

java語言中是採用什麼機制來區分類的?
    第一:首先通過類名進行比對,如果類名不一樣,肯定不是同一個類。
    第二:如果類名一樣,再怎麼進行類的區別?靠序列化版本號進行區分。

小鵬編寫了一個類:com.bjpowernode.java.bean.Student implements Serializable
胡浪編寫了一個類:com.bjpowernode.java.bean.Student implements Serializable
不同的人編寫了同一個類,但“這兩個類確實不是同一個類”。這個時候序列化版本就起上作用了。
對於java虛擬機來說,java虛擬機是可以區分開這兩個類的,因爲這兩個類都實現了Serializable接口,
都有默認的序列化版本號,他們的序列化版本號不一樣。所以區分開了。(這是自動生成序列化版本號的好處)

請思考?
    這種自動生成序列化版本號有什麼缺陷?
        這種自動生成的序列化版本號缺點是:一旦代碼確定之後,不能進行後續的修改,
        因爲只要修改,必然會重新編譯,此時會生成全新的序列化版本號,這個時候java
        虛擬機會認爲這是一個全新的類。(這樣就不好了!)

最終結論:
    凡是一個類實現了Serializable接口,建議給該類提供一個固定不變的序列化版本號。
    這樣,以後這個類即使代碼修改了,但是版本號不變,java虛擬機會認爲是同一個類。
代碼演示(ObjectOutputStream,ObjectInputStream):
import com.bjpowernode.java.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws Exception{
        // 創建java對象
        Student s = new Student(1111, "zhangsan");
        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));

        // 序列化對象
        oos.writeObject(s);

        // 刷新
        oos.flush();
        // 關閉
        oos.close();
    }
}

Student類

import java.io.Serializable;

public class Student implements Serializable {

    // IDEA工具自動生成序列化版本號。
    //private static final long serialVersionUID = -7998917368642754840L;

    // Java虛擬機看到Serializable接口之後,會自動生成一個序列化版本號。
    // 這裏沒有手動寫出來,java虛擬機會默認提供這個序列化版本號。
    // 建議將序列化版本號手動的寫出來。不建議自動生成
    private static final long serialVersionUID = 1L; // java虛擬機識別一個類的時候先通過類名,如果類名一致,再通過序列化版本號。

    private int no;
    //private String name;

    // 過了很久,Student這個類源代碼改動了。
    // 源代碼改動之後,需要重新編譯,編譯之後生成了全新的字節碼文件。
    // 並且class文件再次運行的時候,java虛擬機生成的序列化版本號也會發生相應的改變。
    private int age;
    private String email;
    private String address;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        //this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    /*public String getName() {
        return name;
    }*/

    /*public void setName(String name) {
        this.name = name;
    }*/

    /*@Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }*/

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", age=" + age +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

反序列化


import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class ObjectInputStreamTest01 {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
        // 開始反序列化,讀
        Object obj = ois.readObject();
        // 反序列化回來是一個學生對象,所以會調用學生對象的toString方法。
        System.out.println(obj);
        ois.close();
    }
}

代碼演示(序列化集合):
import com.bjpowernode.java.bean.User;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/*
一次序列化多個對象呢?
    可以,可以將對象放到集合當中,序列化集合。
提示:
    參與序列化的ArrayList集合以及集合中的元素User都需要實現 java.io.Serializable接口。
 */
public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws Exception{
        List<User> userList = new ArrayList<>();
        userList.add(new User(1,"zhangsan"));
        userList.add(new User(2, "lisi"));
        userList.add(new User(3, "wangwu"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));

        // 序列化一個集合,這個集合對象中放了很多其他對象。
        oos.writeObject(userList);

        oos.flush();
        oos.close();
    }
}

User類

import java.io.Serializable;

public class User implements Serializable {
    private int no;
    // transient關鍵字表示遊離的,不參與序列化。
    private transient String name; // name不參與序列化操作!

    public User() {
    }

    public User(int no, String name) {
        this.no = no;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

反序列化集合

import com.bjpowernode.java.bean.User;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
        //Object obj = ois.readObject();
        //System.out.println(obj instanceof List);
        List<User> userList = (List<User>)ois.readObject();
        for(User user : userList){
            System.out.println(user);
        }
        ois.close();
    }
}

java.io.File類。

File
    1、File類和四大家族沒有關係,所以File類不能完成文件的讀和寫。
    2、File對象代表什麼?
        文件和目錄路徑名的抽象表示形式。
        C:\Drivers 這是一個File對象
        C:\Drivers\Lan\Realtek\Readme.txt 也是File對象。
        一個File對象有可能對應的是目錄,也可能是文件。
        File只是一個路徑名的抽象表示形式。
    3、需要掌握File類中常用的方法
代碼演示(File常用方法):
import java.io.File;

public class FileTest01 {
    public static void main(String[] args) throws Exception {
        // 創建一個File對象
        File f1 = new File("D:\\file");

        // 判斷是否存在!
        System.out.println(f1.exists());

        // 如果D:\file不存在,則以文件的形式創建出來
        /*if(!f1.exists()) {
            // 以文件形式新建
            f1.createNewFile();
        }*/

        // 如果D:\file不存在,則以目錄的形式創建出來
        /*if(!f1.exists()) {
            // 以目錄的形式新建。
            f1.mkdir();
        }*/

        // 可以創建多重目錄嗎?
        File f2 = new File("D:/a/b/c/d/e/f");
        /*if(!f2.exists()) {
            // 多重目錄的形式新建。
            f2.mkdirs();
        }*/

        File f3 = new File("D:\\course\\01-開課\\學習方法.txt");
        // 獲取文件的父路徑
        String parentPath = f3.getParent();
        System.out.println(parentPath); //D:\course\01-開課
        File parentFile = f3.getParentFile();
        System.out.println("獲取絕對路徑:" + parentFile.getAbsolutePath());

        File f4 = new File("copy");
        System.out.println("絕對路徑:" + f4.getAbsolutePath()); // C:\Users\Administrator\IdeaProjects\javase\copy

    }
}
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FileTest02 {
    public static void main(String[] args) {

        File f1 = new File("D:\\course\\01-開課\\開學典禮.ppt");
        // 獲取文件名
        System.out.println("文件名:" + f1.getName());

        // 判斷是否是一個目錄
        System.out.println(f1.isDirectory()); // false

        // 判斷是否是一個文件
        System.out.println(f1.isFile()); // true

        // 獲取文件最後一次修改時間
        long haoMiao = f1.lastModified(); // 這個毫秒是從1970年到現在的總毫秒數。
        // 將總毫秒數轉換成日期?????
        Date time = new Date(haoMiao);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime = sdf.format(time);
        System.out.println(strTime);

        // 獲取文件大小
        System.out.println(f1.length()); //216064字節。
    }
}
代碼演示(listFiles方法:獲取當前目錄所有子文件方法):
import java.io.File;

public class FileTest03 {
    public static void main(String[] args) {
        // File[] listFiles()
        // 獲取當前目錄下所有的子文件。
        File f = new File("D:\\course\\01-開課");
        File[] files = f.listFiles();
        // foreach
        for(File file : files){
            //System.out.println(file.getAbsolutePath());
            System.out.println(file.getName());
        }
    }
}

IO流+Properties集合的聯合使用

IO+Properties的聯合應用。
非常好的一個設計理念:
    以後經常改變的數據,可以單獨寫到一個文件中,使用程序動態讀取。
    將來只需要修改這個文件的內容,java代碼不需要改動,不需要重新
    編譯,服務器也不需要重啓。就可以拿到動態的信息。

類似於以上機制的這種文件被稱爲配置文件。
並且當配置文件中的內容格式是:
    key1=value
    key2=value
的時候,我們把這種配置文件叫做屬性配置文件。

java規範中有要求:屬性配置文件建議以.properties結尾,但這不是必須的。
這種以.properties結尾的文件在java中被稱爲:屬性配置文件。
其中Properties是專門存放屬性配置文件內容的一個類。
代碼演示:
import java.io.FileReader;
import java.util.Properties;

public class IoPropertiesTest01 {
    public static void main(String[] args) throws Exception{
        /*
        Properties是一個Map集合,key和value都是String類型。
        想將userinfo文件中的數據加載到Properties對象當中。
         */
        // 新建一個輸入流對象
        FileReader reader = new FileReader("chapter23/userinfo.properties");

        // 新建一個Map集合
        Properties pro = new Properties();

        // 調用Properties對象的load方法將文件中的數據加載到Map集合中。
        pro.load(reader); // 文件中的數據順着管道加載到Map集合中,其中等號=左邊做key,右邊做value

        // 通過key來獲取value呢?
        String username = pro.getProperty("username");
        System.out.println(username);

        String password = pro.getProperty("password");
        System.out.println(password);

        String data = pro.getProperty("data");
        System.out.println(data);

        String usernamex = pro.getProperty("usernamex");
        System.out.println(usernamex);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章