Java - System.in、 System.out, PrintStream, Data Stream

By sunwc 2023-03-31 Java

System類

  • System.in:標準輸入,默認s從鍵盤輸入;其類別為InputStream
  • System.out:標準輸出,默認從console輸出;其類別為PrintStream,為OutputStream的子類

練習一 System.in(return InputStream 類) 讀取鍵盤輸入的兩種方式

從鍵盤輸入字串,要求將讀取到的整行字串轉成大寫輸出。然後繼續進行輸入操作,直至當輸入“e”或“exit”時,退出程式

public static void main(String[] args) {
    // 方法一 使用Scanner實現
//        Scanner scan = new Scanner(System.in);
//        while (true) {
//            System.out.println("請輸入一段文字:");
//            String input = scan.next().toLowerCase(Locale.ROOT);
//            if ("e".equals(input) || "exit".equals(input)) {
//                break;
//            }
//
//            String upper = input.toUpperCase(Locale.ROOT);
//            System.out.println(upper);
//        }

    // 方法二 使用 System.in 實現:System.in 回傳 InputStream
    // => 使用 InputStreamReader 將 InputStream(字節流) 轉 => BufferedReader(字符流)

    // 1. 將讀取鍵盤的輸入(byte[])轉成(char[])
    BufferedReader br = null;
    try {
        InputStreamReader isr = new InputStreamReader(System.in);

        br = new BufferedReader(isr);

        while (true) {
            System.out.println("請輸入一段文字:");

            String line = br.readLine();

            if ("e".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)) {
                System.out.println("程式結束");
                break;
            }

            // 轉大寫輸出
            System.out.println(line.toUpperCase(Locale.ROOT));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            // 關閉流
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

練習二 System.out(return PrintStream 類) 輸出至console改為輸出至文件

/**
     * 將System.out 默認輸出內容至console
     * 但我們可以透過System.setOut()的方式
     * 將內容輸出至指定文件(此指定文件可以不存在、但是此指定文件上層的目錄必須存在)
     */
    @Test
    public void printStreamTest() {

        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(new File("C:\\io\\testSystemSetOut.txt"));
            ps = new PrintStream(fos);
            System.setOut(ps);

            // 欲輸出的內容
            for (int i = 0; i < 255; i++) {

                System.out.print((char) i); // 輸出ascii碼
                if (i % 50 == 0) { // 每行50個字符就換行
                    System.out.println();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

Data Stream

  • 作用:為了將記憶體中的基本資料型態(byte/short/char/int/long/float/double/boolean)、String 類的資料輸出到指定文件,就可以使用 Data OutputStream;為了將指定文件內容輸入到程式中,就可以使用 Data InputStream

練習三 DataInputStream、DataOutputStream

  /**
     * 使用DataOutputStream將基本資料型態資料輸出到文件
     */
    @Test
    public void dataOutStreamTest() {

        DataOutputStream dos = null;
        try {
            dos = new DataOutputStream(new FileOutputStream("C:\\io\\testDataOutputStream.txt"));

            dos.writeUTF("sunwc");
            dos.flush(); // 刷新,將記憶體資料寫到文件
            dos.writeInt(124);
            dos.flush();
            dos.writeChar('c');
            dos.flush();
            dos.writeBoolean(false);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用DataInputStream將文件內容輸入到程式中
     */
    @Test
    public void dataInputStreamTest() {

        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new FileInputStream("C:\\io\\testDataOutputStream.txt"));

            // 按照寫入的順序輸出資料型態
            String name = dis.readUTF();
            int number = dis.readInt();
            char ch = dis.readChar();
            boolean b = dis.readBoolean();

            System.out.printf("%s, %d, %c, %b", name, number, ch, b);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

ObjectInputStream, ObjectOutputStream

  • 序列化:用 ObjectOutputStream 將基本資料型態或物件資料寫入文件
  • 反序列化:用 ObjectInputStream 讀取文件將內容轉成基本資料型態或物件資料

什麼是物件序列化?

是允許將記憶體中的Java物件轉換成二進制流保存在硬碟中;或通過網路將這種二進制流傳輸到另一個網路節點

什麼是反序列化?

當其他程式取得了這種二進制流,就可以恢復其至原來的Java物件

    @Test
    public void objectOutputStreamTest() {

        /**
         * 序列化過程:將記憶體中的Java物件以文件形式儲存到硬碟中或通過網路傳輸出去(Java物件 => 二進制流)
         * 使用ObjectOutputStream
         */
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("objectOutputStream.dat"));

            // serialized
            oos.writeObject(new String("將Java物件序列化"));
            oos.flush(); // 刷新並寫出
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    @Test
    public void objectInputStreamTest() {

        /**
         * 反序列化過程:將硬碟中文件二進制流轉成Java物件;或是將從網路上取得的二進制流轉換成Java物件(二進制流 => Java物件)
         */
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("objectOutputStream.dat"));

            Object o = ois.readObject();
            if (o instanceof String) {
                String deserialized = (String) o;
                System.out.println(deserialized);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

自定義物件序列化的規則

1.當前物件需要實現 interface Serializable,並保證其屬性也是可序列化的

2.當前類提供一個全局常量(static final) serialVersionUID

3.static、transient 關鍵字修飾的成員屬性無法序列化(即序列化過程的值無法保存)

/**
 * @author sunwc
 * @create 2023-04-01 下午 02:48
 */
public class CustomizedClassObjectStreamTest {

    @Test
    public void objectOutputStreamTest() {

        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("person.dat"));

            // Serialize
            oos.writeObject(new String("先序列化String物件"));
            oos.flush();

            // Serialize
            oos.writeObject(new Person("Lily", 27));
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {

                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void objectInputStreamTest() {

        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("person.dat"));

            // Deserialize
            String str = (String) ois.readObject();
            System.out.println(str);

            // Deserialize
            Person person = (Person) ois.readObject();

            System.out.println(person);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


class Person implements Serializable {

    public static final long serialVersionUID = 483742747387L;

    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}