IO流之特殊操作流

1. 标准输入流

System 类中有两个静态的成员变量:
① public static final InputStream in:标准输入流,通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源;
② public static final PrintStream out:标准输出流,通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标。


package com.zxe;

import java.io.*;

public class Test {
    
    
    public static void main(String[] args) throws IOException {
    
    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入一个字符串:");
        String s = br.readLine();
        System.out.println("你输入的字符串是:" + s);
        System.out.println("请输入一个整数:");
        int i = Integer.parseInt(br.readLine());
        System.out.println("你输入的整数是:" + i);
    }
}

在这里插入图片描述

以上是自己实现键盘录入数据,但写起来太麻烦了,Java 就提供了一个类来实现键盘录入:

Scanner sc = new Scanner(System.in);

2. 标准输出流


PrintStream ps = System.out;
ps.print("123");
ps.println("hhhh");
System.out.print("不换行");
System.out.println("换行");

在这里插入图片描述

3. 打印流

打印流分为:字节打印流 PrintStream 和字符打印流 PrintWriter。

打印流的特点:
① 只负责输出数据,不负责读取数据;
② 有自己的特有方法。

3.1 字节打印流


PrintStream ps = new PrintStream("idea_test\\myOtherStream.txt");
ps.print("23");
ps.println("中国");
ps.println("你好");
ps.write(97);
ps.close();

在这里插入图片描述
字节打印流使用继承父类的方法 write 写数据时,查看的时候会转码,而使用自己的特有方法 print 和 println 写数据时,查看的数据原样输出!

3.2 字符打印流

在这里插入图片描述


PrintWriter pw = new PrintWriter(new FileWriter("idea_test\\myOtherStream.txt"), true);
pw.println("你好");
pw.println("世界");
pw.close();

打印流只负责写数据,不负责读数据!

3.3 复制文件


package com.zxe;

import java.io.*;

public class Test {
    
    
    public static void main(String[] args) throws IOException {
    
    
        BufferedReader br = new BufferedReader(new FileReader("idea_test\\myOtherStream.txt"));
        PrintWriter pw = new PrintWriter(new FileWriter("idea_test\\copy.java"), true);
        String line;
        while ((line = br.readLine()) != null) {
    
    
            pw.println(line);
        }
        br.close();
        pw.close();
    }
}

在这里插入图片描述
写入时使用字符打印流可自动刷新缓存,同时可使用其特有功能 println 进行换行,相比缓冲输出流减少了代码量!

4. 对象序列化

对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象。
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。

要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
① 对象序列化流:ObjectOutputStream;
② 对象反序列化流:ObjectInputStream。

4.1 对象序列化流

将 Java 对象的原始数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过使用流的文件来实现对象的持久存储。如果流式网络套接字流,则可以在另一个主机上或另一个进程中重构对象。

序列化对象的方法:writeObject(Object obj),将指定的对象写入 ObjectOutputStream。


ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("idea_test\\myOtherStream.txt"));
Student s = new Student("刘德华", 60, 99);
oos.writeObject(s);
oos.close();
        

在这里插入图片描述

在这里插入图片描述

类的序列化由实现 java.io.Serializable 接口的类启用,不实现此接口的类将不会使任何状态序列化或反序列化,该接口是一个标记接口,我们只需要实现接口即可,无需重写方法!

4.2 对象反序列化流

以上的序列化流可以帮助我们将学生对象写入到文件中,但是打开文件后发现这些内容我们是看不懂的,这时候就用到我们的对象反序列化流来将文件内容读取出来。

反序列化对象的方法:
Object readObject():从 ObjectInputStream 中读取一个对象。


ObjectInputStream ois = new ObjectInputStream(new FileInputStream("idea_test\\myOtherStream.txt"));
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + ", " + s.getAge() + ", " + s.getGrade());
ois.close();

在这里插入图片描述

4.3 serialVersionUID&transient

这里给出两个问题:

① 用对象序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出现问题呢?如果出现了问题,该如何解决呢?

显然是会出问题的,因为当序列化运行时检测到类的串行版本与从流中读取的类描述符的类型不匹配时,就会抛出异常。
也就是说,每一个学生类都会有一个特定的 serialVersionUID,当该类发生改变的时候,serialVersionUID也会随着改变,当写入和读取的 ID不一致时,必然会抛出异常。
解决办法就是给对象所属的类加一个值:private static final long serialVersionUID = 42L。

在这里插入图片描述

里面的 ID 值是可以随意修改的,写 serialVersionUID 的目的就是保证读入和取出的描述符的类型是相匹配的!

③ 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

解决办法就是用 transient 关键字来修饰不想被序列化的成员变量。

在这里插入图片描述

5. Properties 作为 Map 集合

5.1 使用

Properties 概述:
① 是一个 Map 体系的集合类;
② Properties 可以保存到流中或从流中加载。


package com.zxe;

import java.io.*;
import java.util.Properties;
import java.util.Set;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Properties prop = new Properties();
        prop.put(1, "曹操");
        prop.put(2, "刘备");
        prop.put(3, "孙策");
        Set<Object> keys = prop.keySet();
        for (Object key : keys) {
    
    
            Object value = prop.get(key);
            System.out.println(key + ", " + value);
        }
    }
}

在这里插入图片描述

Properties 集合类不能加泛型,尖括号去掉,集合的默认类型为 Object 类型!

5.2 特有方法

在这里插入图片描述


Properties prop = new Properties();
prop.setProperty("001", "猪八戒");
System.out.println(prop.getProperty("001"));
Set<String> names = prop.stringPropertyNames();
for (String key : names) {
    
    
    String value = prop.getProperty(key);
    System.out.println(key + ", " + value);
}

因为 Properties 的底层是 hashtable,所以是乱序的!

6. Properties 和 IO 流结合

在这里插入图片描述


package com.zxe;

import java.io.*;
import java.util.Properties;
import java.util.Set;

public class Test {
    
    
    public static void main(String[] args) throws IOException {
    
    
        //1.把集合中的数据保存到文件
        myStore();
        //2.把文件中的数据加载到集合
        myLoad();
    }

    private static void myStore() throws IOException {
    
    
        Properties prop = new Properties();
        prop.setProperty("001", "孙尚香");
        prop.setProperty("002", "妲己");
        prop.setProperty("003", "东方曜");
        prop.setProperty("004", "马可波罗");
        FileWriter fw = new FileWriter("idea_test\\myOtherStream.txt");
        prop.store(fw, null);
        fw.close();
    }

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

在这里插入图片描述

在这里插入图片描述

Properties 和 IO 流结合,利用 Properties 的 store 方法和 load 方法,实现数据的保存和加载!

7. 猜数字小游戏

需求:请写一个程序实现猜数字小游戏,只能玩 3 次,如果还想玩,提示:游戏试玩已结束,还想玩请充值。

思路:
① 从 game.txt 文件中读取数据到 Properties 集合,用 load() 方法实现,文件中有一个数据值 count = 0;
② 通过 Properties 集合获取到玩游戏的次数;
③ 判断次数是否到 3 次了,如果到了,给出提示,游戏试玩已结束,还想玩请充值;
④ 如果不到 3 次,玩游戏,count 加 1,重新写回文件,用 Properties 的 store() 方法实现。

//GuessNumber.java

package com.zxe;

import java.util.Random;
import java.util.Scanner;

public class GuessNumber {
    
    
    public static void start() {
    
    
        Random r = new Random();
        int num = r.nextInt(100) + 1;
        Scanner sc = new Scanner(System.in);
        int x;
        while(true) {
    
    
            System.out.println("请输入你要猜的数字:");
            x = sc.nextInt();
            if (x > num) {
    
    
                System.out.println("你猜的数据大了!");
            } else  if (x < num) {
    
    
                System.out.println("你猜的数据小了!");
            } else {
    
    
                System.out.println("恭喜你猜中了!");
                break;
            }
        }
    }
}

//Test.java

package com.zxe;

import java.io.*;
import java.util.Properties;

public class Test {
    
    
    public static void main(String[] args) throws IOException {
    
    
        int count = myLoad();
        if (count >= 3) {
    
    
            System.out.println("游戏试玩已结束,想玩请充值!");
        } else {
    
    
            GuessNumber.start();
            count++;
            myStore(count);
        }
    }

    private static void myStore(int count) throws IOException {
    
    
        Properties prop = new Properties();
        String number = String.valueOf(count);
        prop.setProperty("count", number);
        BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\game.txt"));
        prop.store(bw, null);
        bw.close();
    }

    private static int myLoad() throws IOException {
    
    
        Properties prop = new Properties();
        BufferedReader br = new BufferedReader(new FileReader("idea_test\\game.txt"));
        prop.load(br);
        String count = prop.getProperty("count");
        int number = Integer.parseInt(count);
        br.close();
        return number;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_52861684/article/details/129635542