天天看點

IO流——特殊操作類 & Properties集合特殊操作流

特殊操作流

1. 标準輸入流 & 标準輸出流

1.1 概述

  • System類中有兩個靜态的成員變量
    • public static final InputStream in:标準輸入流。通常該流對應于鍵盤輸入或由主機環境或使用者指定的 另一個輸入源
    • public static final PrintStream out:标準輸出流。通常該流對應于顯示輸出或由主機環境或使用者指定的 另一個輸出目标

1.2 示例

  • 标準輸入流
//自己實作鍵盤錄入資料過于麻煩,是以Java體統了一個類供我們使用
Scanner sc = new Scanner(System.in);
           
  • 标準輸出流
    //System.out的本質是一個位元組輸出流
    System.out.print(); //不換行
    System.out.println(); //換行
               

2. 位元組列印流 & 字元列印流

2.1 概述

  • PrintStream:位元組列印流
  • PrintWriter:字元列印流
  • 使用繼承父類的方法寫資料,檢視的時候會轉碼;使用自己的特有方法寫資料,檢視的資料原樣輸出
  • 可以改變輸出語句的目的地
    • public static void setOut(PrintStream out):重新配置設定“标準”輸出流

2.2 特點

  • 隻負責輸出資料,不負責讀取資料
  • 永遠不會抛出IOException
  • 有自己特有的方法

2.3 構造方法

2.3.1 位元組列印流

方法名 說明
PrintStream(String fileName) 使用指定的檔案名建立新的列印流

2.3.2 字元列印流

方法名 說明
PrintWriter(String fileName) 使用指定的檔案名建立一個新的PrintWriter,不會自動執行重新整理
PrintWriter(Writer out, boolean autoFlush) 建立一個新的PrintWriter out:字元輸出流 autoFlush: 一個布爾值,如果 為真,則println 、print 或format方法将重新整理輸出緩沖區

2.4 代碼示例

2.4.1 位元組列印流

import java.io.IOException;
import java.io.PrintStream;
//位元組列印流
public class Demo01 {
    public static void main(String[] args) throws IOException {
        PrintStream ps = new PrintStream("iodemo\\demo.txt");
        //位元組輸出流的方法 寫資料
        ps.write(97); //寫入的是a
        //位元組列印流 特有方法寫資料
        ps.print(97); // 寫的是多少 寫入的就是多少
        ps.println(98);//換行
        ps.print(99);
        //釋放資源
        ps.close();
    }
}
           

2.4.2 字元列印流

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo02 {
    public static void main(String[] args) throws IOException {
/*        //第一種構造方法
        PrintWriter pw = new PrintWriter("iodemo\\demo.txt");
        pw.write("hello");
        pw.write("\r\n");
        pw.flush();
        //println 自動換行
        pw.println("world");
        pw.flush();*/

        //第二種構造方法    加一個參數必須為true 這種構造方法能實作自動重新整理
        PrintWriter pw = new PrintWriter(new FileWriter("iodemo\\demo.txt"),true);
        pw.println("hello");
        /* 這一步相當于 第一種構造方法中
        pw.write("hello");
        pw.write("\r\n");
        pw.flush();
         */
        pw.println("world");
        pw.close();
    }
}
           

2.5 複制Java檔案(列印流改進版)

import java.io.*;
//複制Java檔案 (列印流改進)
/*
1.根據資料源建立字元緩沖輸入流對象
2.根據目的地建立字元列印流對象
3.讀寫資料,複制檔案
4.釋放資源
 */
public class Test01 {
    public static void main(String[] args) throws IOException {
        //由于列印流隻能寫資料 不能讀資料 是以讀資料還是使用 字元緩沖輸入流來進行
        BufferedReader br = new BufferedReader(new FileReader("iodemo\\src\\com\\wang\\test\\Test01.java"));
        //建立列印流對象
        PrintWriter pw = new PrintWriter(new FileWriter("iodemo\\Test.java"),true);
        //讀資料仍用 字元緩沖輸入流的方法
        String line;
        while ((line = br.readLine()) != null){
            //寫資料采用列印字元流
            pw.println(line);
            /*
            bw.write(String);
            bw.nerLine();
            bw.flush();
             */
        }
        //釋放資源
        br.close();
        pw.close();
    }
}
           

3. 對象序列化流 & 對象反序列化流

3.1 概述

  • 對象序列化:就是将對象儲存到磁盤中,或者在網絡中傳輸對象
  • 這種機制就是使用一個位元組序清單示一個對象,該位元組序列包含:對象的類型、對象的資料和對象中存儲的屬性等資訊位元組序列寫到檔案之後,相當于檔案中持久儲存了一個對象的資訊
  • 反之,該位元組序列還可以從檔案中讀取回來,重構對象,對它進行 反序列化
  • 要實作序列化和反序列化就要使用對象序列化流和對象反序列化流
    • ObjectOutputStream:對象序列化流
    • ObjectInputStream:對象反序列化流

3.2 對象序列化流

3.2.1 概述

  • 将Java對象的原始資料類型和圖形寫入OutputStream。 可以使用ObjectInputStream讀取(重構)對象。可以通過使用流的檔案來實作對象的持久存儲。 如果流是網絡套接字流,則可以在另一個主機上或另一個程序中重構對象

3.2.2 構造方法 & 方法

構造方法 說明
ObjectOutputStream(OutputStream out) 建立一個寫入指定的OutputStream的 ObjectOutputStream
方法名 說明
void writeObject(Object obj) 将指定的對象寫入ObjectOutputStream

3.2.3 代碼示例

  • 測試類
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo03 {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("iodemo\\demo.txt"));

        Student03 s1 = new Student03("張三", 18);

        oos.writeObject(s1);

        oos.close();
    }
    //NotSerializableException 抛出一個執行個體需要一個Serializable接口,序列化運作時或執行個體的類可能會抛出此異常
}
/* 寫入結果
�� sr com.wang.specialdemo.Student03������� I ageL namet Ljava/lang/String;xp   t 張三
*/
           
  • 學生類
import java.io.Serializable;

//如何解決NotSerializableException異常
//Serializable接口 僅僅是一個辨別接口 沒有方法需要重寫
public class Student03 implements Serializable {
    private String name;
    private int age;

    public Student03() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
           

3.2.4 注意事項

  • 一個對象要想被序列化,該對象所屬的類必須必須實作Serializable 接口
  • Serializable是一個标記接口,實作該接口,不需要重寫任何方法

3.3 對象反序列化流

3.3.1 前提

  • ObjectInputStream反序列化前 先使用ObjectOutputStream編寫的原始資料和對象

3.3.2 構造方法 & 方法

構造方法 說明
ObjectInputStream(InputStream in) 建立從指定的InputStream讀取的ObjectInputStream
方法名 說明
Object readObject() 從ObjectInputStream讀取一個對象

3.3.3 代碼示例

  • 測試類
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
//對象反序列化流
public class Demo04 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("iodemo\\demo.txt"));
        Object obj = ois.readObject();
        //向下轉型
        Student03 s = (Student03) obj;
        System.out.println(s.getName() + "," + s.getAge());
        //釋放資源
        ois.close();
    }
}
           
  • 學生類
import java.io.Serializable;

//如何解決NotSerializableException異常
//Serializable接口 僅僅是一個辨別接口 沒有方法需要重寫
public class Student03 implements Serializable {
    private String name;
    private int age;

    public Student03() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
           

3.4 三個問題

3.4.1 serialVersionUID & transient的應用

  • 用對象序列化流序列化了一個對象後,假如我們修改了對象所屬的類檔案,讀取資料會不會出問題呢?
    • 會出現問題,抛出InvalidClassException異常
  • 如果出現了問題,應該如何解決?
    • 重新序列化
    • 給對象所屬的類加一個 serialVersionUID
  • 如何一個對象中的某個成員變量的值不想被系列化,又該如何實作?
    • 給該成員變量加transient關鍵字修飾,該關鍵字标記的成員變量不參與序列化過程
    • 不參與序列化過程的成員變量,輸出預設值

4. Properties 集合

4.1 概述

  • 是一個Map體系的集合類
  • Properties可以儲存到流中或從流中加載
  • 屬性清單中的每個鍵及其對應的值都是一個字元串

4.2 作為Map集合使用<代碼示例>

import java.util.Properties;
import java.util.Set;
// Properties作為Map集合的使用
public class Test02 {
    public static void main(String[] args) {
        //建立集合對象 Properties 木有泛型 是一個Map體系的集合類
        Properties prop = new Properties();
        //存儲元素 key value
        prop.put("11111","張三");
        prop.put("22222","李四");
        prop.put("33333","王五");
        //周遊集合 輸出集合元素
        Set<Object> keySet = prop.keySet();
        for (Object key : keySet) {
            Object value = prop.get(key);
            System.out.println(key + "," + value);
        }
    }
}
           

4.3 特有方法

方法名 說明
Object setProperty(String key, String value) 設定集合的鍵和值,都是String類型,底層調用 Hashtable方法 put
String getProperty(String key) 使用此屬性清單中指定的鍵搜尋屬性
Set stringPropertyNames() 從該屬性清單中傳回一個不可修改的鍵集,其中鍵及其對應的值是字元串

4.3.1 代碼示例

import java.util.Properties;
import java.util.Set;

public class Demo05 {
    public static void main(String[] args) {
        Properties prop = new Properties();
        //Object setProperty(String key, String value) 底層調用的是put方法
        prop.setProperty("11111","張三");
        prop.setProperty("22222","李四");
        prop.setProperty("33333","王五");
        System.out.println(prop); //{33333=王五, 11111=張三, 22222=李四}
        //String getProperty(String key) 鍵找值
        System.out.println(prop.getProperty("11111")); //張三
        //Set<String> stringPropertyNames() 輸出鍵集
        Set<String> names = prop.stringPropertyNames();
        for (String key : names) {
            String value = prop.getProperty(key);
            System.out.println(key + "," + value);
        }
    }
           

4.4 結合IO流的方法

4.4.1 方法

方法名 說明
void load(InputStream inStream) 從輸入位元組流讀取屬性清單(鍵和元素對)
void load(Reader reader) 從輸入字元流讀取屬性清單(鍵和元素對)
void store(OutputStream out, String comments) 将此屬性清單(鍵和元素對)寫入此 Properties表中,以适合于使用 load(InputStream)方法的格式寫入輸出位元組流
void store(Writer writer, String comments) 将此屬性清單(鍵和元素對)寫入此 Properties表中,以适合使用 load(Reader)方法的格式寫入輸出字元流

4.4.2 代碼示例

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

public class Demo06 {
    public static void main(String[] args) throws IOException{
        myStore();

        myLoad(); //{33333=王五, 11111=張三, 22222=李四}

    }

    private static void myLoad() throws IOException{
        Properties prop = new Properties();
        FileReader fr = new FileReader("iodemo\\demo.txt");
        prop.load(fr);
        fr.close();
        System.out.println(prop);
    }

    private static void myStore() throws IOException {
        Properties prop = new Properties();

        prop.setProperty("11111","張三");
        prop.setProperty("22222","李四");
        prop.setProperty("33333","王五");

        FileWriter fw = new FileWriter("iodemo\\demo.txt");
        //store();方法 集合元素 寫入檔案
        prop.store(fw,null);
        fw.close();
    }
           

4.5 案例

4.5.1 遊戲次數

  • 遊戲類
import java.util.Random;
import java.util.Scanner;
//猜數字遊戲
public class GuessGame {
    public static void game(){
        Random r = new Random();

        int i = r.nextInt(100) + 1;

        while (true){
            Scanner sc = new Scanner(System.in);
            System.out.println("請輸入你猜的數字");
            int i1 = sc.nextInt();

            if (i > i1){
                System.out.println("小了");

            }else if (i < i1){
                System.out.println("大了");
            }else{
                System.out.println("恭喜你猜對了,正确答案為:" + i);
                break;
            }
        }
    }
}
           
  • 測試類
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
//遊戲次數
/*
1.寫一個遊戲類,裡面有一個猜數字的小遊戲
2.寫一個測試類
3.從檔案中讀取資料到Properties集合,用load()方法實作
	檔案已經存在:game.txt
	裡面有一個資料值:count=0
4.通過Properties集合擷取到玩遊戲的次數
5.判斷次數是否到到2次了
	如果到了,給出提示:遊戲試玩已結束,想玩請充值(www.itcast.cn)
	如果不到3次:
		次數+1,重新寫回檔案,用Properties的store()方法實作
		玩遊戲
*/
public class Test {
    public static void main(String[] args) throws IOException {
        Properties pt = new Properties();
        FileReader fr = new FileReader("iodemo\\demo.txt");
        //load();方法 将檔案内容存儲到Properties集合中
        pt.load(fr);
        //釋放資源
        fr.close();
        //getProperty();方法 鍵找值 
        String count = pt.getProperty("count");
        //值是數字  将String轉成Int類型
        int number = Integer.parseInt(count);
        if (number>=2){
            System.out.println("你的免費遊戲次數不足,請充值648繼續暢玩");
        }else {
            GuessGame.game();
            number++;
            //将元素存儲到集合
            pt.setProperty("count",String.valueOf(number));
            FileWriter fw = new FileWriter("iodemo\\demo.txt");
            //store();方法 将集合中元素寫入檔案
            pt.store(fw,null);
            fw.close();
        }
    }
}