05_IO流(2):处理流——第一遍扫盲式笔记整理

碎碎念

      内容基本是尚硅谷的视频笔记和ppt内容的整理。叽几的内容只有:蓝色字体的思考分析 + 手写的一些图示。怕涉及到侵权问题,就把链接放在开头高亮!!链接:https://pan.baidu.com/s/1fJIiEhG-NCfgWhn-eyRtZA 提取码:dqvy
      说实话到四点的时候真的想放弃了,还差最后一个处理流,想拖到第二天来着。后来想想算了,学完再睡心里比较爽。

一、处理流之一:缓冲流

1.缓冲流

      缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
      ① BufferedInputStream 和 ② BufferedOutputStream
      ③ BufferedReader 和 ④ BufferedWriter

2.作用

  • 作用: 提高了数据的读写速度
  • 原因: 提供了8192个字节(8Kb)的缓冲区
    在这里插入图片描述

3.非文本文件的复制

    @Test
    public void BufferedStreamTest() throws FileNotFoundException {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造文件
            File srcFile = new File("爱情与友情.jpg");
            File destFile = new File("爱情与友情3.jpg");
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream((srcFile));
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[10];
            int len;
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);

//          bos.flush();//刷新缓冲区

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }
    }

      分析:① 步骤: 总体步骤和节点流一样。造文件 → 造流 → 复制 → 资源关闭,然后再用try-catch-finally包起来。处理流是在节点流的“包装”下进行的,所以和节点流的区别只是在节点流的基础上“套”了一层流bis = new BufferedInputStream(fis); 和 bos = new BufferedOutputStream(fos);)。
      ② 刷新: bos.flush();//刷新缓冲区,有自动刷新,可以不用写
      ③ 关闭:在关闭流的时候,要先关闭外层的流,再关闭内层的流,而同级别的两个流的关闭顺序不分先后。注意:在关闭外层流的同时,内层流也会自动的进行关闭,所以关于内层流的关闭可以省略。

4.文本文件的复制

@Test
    public void testBufferedReaderBufferedWriter(){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //创建文件和相应的流
            br = new BufferedReader(new FileReader(new File("dbcp.txt")));
            bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

            //读写操作
            //方式一:使用char[]数组————char(char[] cbuf)
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf)) != -1){
//                bw.write(cbuf,0,len);
//    //            bw.flush();
//            }

            //方式二:使用String————readLine()一次读一行
            String data;
            //为什么不等于null,就是
            while((data = br.readLine()) != null){
                //方法一:
//                bw.write(data + "\n");//data中不包含换行符
                //方法二:
                bw.write(data);//data中不包含换行符
                bw.newLine();//提供换行的操作

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(bw != null){

                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

分析:

  • ① 步骤: 总体步骤和上面非文本文件的复制一样。但是在这个程序中,在造文件和造流两步合为一体(匿名)。
  • ② 读写操作: 文本文件的“char”对应着非文本文件的“byte”。但文本文件还有别的读取方法,也就是String。本来两方在循环的时候用数组char(char[] cbuf) 和 byte(byte[] cbuf),文本文件还可以用readLine(一次读取一行,是String类型的)。
    • 源码中对于一行结束的判定: 要么返回当前这一行的数据不包含换行符<因为没有换行符,所以不会自动换行,要手动添加>,或者是到文件末尾返回null 所以:
      • 1.循环的判定条件为 —— (data = br.readLine()) != null
      • 2.要手动添加换行符
        • 第一种方式:加换行符 —— + “\n”
        • 第二种方式:提供换行的方法 —— bw.newLine();

5.练习

5.1对文件的加密解密

① 程序:为了观看整洁,省去了对程序的异常处理,具体处理看之前的代码。

--------------------加密--------------------
//1.创建流,流里面放需要加密的文件和目标文件
FileInputStream fis = new FileInputStream("爱情与友情.jpg");
FileOutputStream fos = new FileOutputStream("爱情与友情secret.jpg");

//2.读取数据,一边遍历读取,一边对数据进行运算从而实现加密
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
    //字节数组进行修改
    //错误的
    //            for(byte b : buffer){
    //                b = (byte) (b ^ 5);
    //            }
    //正确的
    for (int i = 0; i < len; i++) {
        buffer[i] = (byte) (buffer[i] ^ 5);
    }
    fos.write(buffer, 0, len);
}

//3.关闭资源
fos.close();
fis.close();

--------------------解密--------------------
//因为加密时用了亦或运算,所以在解密时,只需输入输出倒换一下即可
FileInputStream fis = new FileInputStream("爱情与友情secret.jpg");
FileOutputStream fos = new FileOutputStream("爱情与友情4.jpg");
//以下步骤不变,省略

② 思路

  • 加密——在循环遍历读取文件时,可以进行一些操作,比如:亦或运算,此时数据都被移位,无法正确复制,但大小是一样的。
  • 解密——解密就是将加密过的文件还原,方法如下(针对加密的亦或运算来说):
    • 因为加密时:是将原文件进行运算(此处用了亦或运算)得到目标文件。
    • 所以解密时:只要在创建输入流和输出流文件倒换一下即可,其余步骤不变。

③ 注意点

  • 不能使用加强for循环。因为这种循环,不会改变原有的数据,会将改动的数据赋给新的数组上。
  • 要有byte类型的强转。因为buffer是byte型的,在和int做运算时,会自动转化成int型。而输入输出的数据应该为byte型,所以要强转。

5.2 获取每个字符出现的次数

① 程序:知识点都讲过,自己要看懂它。

public class WordCount {
    @Test
    public void testWordCount() {
        FileReader fr = null;
        BufferedWriter bw = null;
        try {
            //1.创建Map集合
            Map<Character, Integer> map = new HashMap<Character, Integer>();

            //2.遍历每一个字符,每一个字符出现的次数放到map中
            fr = new FileReader("dbcp.txt");
            int c = 0;
            while ((c = fr.read()) != -1) {
                //int 还原 char
                char ch = (char) c;
                // 判断char是否在map中第一次出现
                if (map.get(ch) == null) {
                    map.put(ch, 1);
                } else {
                    map.put(ch, map.get(ch) + 1);
                }
            }

            //3.把map中数据存在文件count.txt
            //3.1 创建Writer
            bw = new BufferedWriter(new FileWriter("wordcount.txt"));

            //3.2 遍历map,再写入数据
            Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
            for (Map.Entry<Character, Integer> entry : entrySet) {
                switch (entry.getKey()) {
                    case ' ':
                        bw.write("空格=" + entry.getValue());
                        break;
                    case '\t'://\t表示tab 键字符
                        bw.write("tab键=" + entry.getValue());
                        break;
                    case '\r'://
                        bw.write("回车=" + entry.getValue());
                        break;
                    case '\n'://
                        bw.write("换行=" + entry.getValue());
                        break;
                    default:
                        bw.write(entry.getKey() + "=" + entry.getValue());
                        break;
                }
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关流
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

② 总结: 遍历文本的每一个字符;字符及出现的次数保存在Map中将Map中数据写入文件
③ 说明: 如果使用单元测试,文件相对路径为当前module。如果使用main()测试,文件相对路径为当前工程

6.说明

  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
  • 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流
  • 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
  • flush()方法的使用:手动将buffer中内容写入文件
  • 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出

二、处理流之二:转换流

1.概念

① 转换流: 提供了在字节流和字符流之间的转换,属于字符流
      InputStreamReader: 将一个字节的输入流转换为字符的输入流(将InputStream 转换为Reader)
      OutputStreamWriter: 将一个字符的输出流转换为字节的输出流( 将Writer 转换为OutputStream)
② 功能: 字节流中的数据都是字符时,转成字符流操作更高效,很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
      解码: 字节、字节数组 —>字符数组、字符串
      编码: 字符数组、字符串 —> 字节、字节数组

2.InputStreamReader

  • 实现将字节的输入流按指定字符集转换为字符的输入流。

  • 需要和InputStream“套接”。

  • 构造器

    • public InputStreamReader(InputStream in)←使用默认的字符集
    • public InputSreamReader(InputStream in,String charsetName)
      如: Reader isr = new InputStreamReader(System.in,”gbk”);←这里的gbk就是制定的字符集。
  • InputStreamReader的使用

	//此时还是应该用try-catch-finally,用throws纯属因为整洁
    public void test1() throws IOException {
    	//
        FileInputStream fis = new FileInputStream("dbcp.txt");
		//InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
        //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
            String str = new String(cbuf,0,len);
            System.out.print(str);
        }
        isr.close();
    }

3.OutputStreamWriter

  • 实现将字符的输出流按指定字符集转换为字节的输出流。
  • 需要和OutputStream“套接”。
  • 构造器
    • public OutputStreamWriter(OutputStream out)
    • public OutputSreamWriter(OutputStream out,String charsetName)
  • 综合使用InputStreamReader和OutputStreamWriter
	//此时还是应该用try-catch-finally,用throws纯属因为整洁
	@Test
    public void test2() throws Exception {
        //1.造文件、造流
        File file1 = new File("dbcp.txt");
        File file2 = new File("dbcp_gbk.txt");

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

        //2.读写过程
        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
            osw.write(cbuf,0,len);
        }

        //3.关闭资源
        isr.close();
        osw.close();
    }

补充:字符编码

1.由来

      计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。

2.常见编码表

① ASCII: 美国标准信息交换码。用一个字节的7位可以表示。
② ISO8859-1: 拉丁码表。欧洲码表用一个字节的8位表示。
③ GB2312: 中国的中文编码表。最多两个字节编码所有字符
④ GBK: 中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码。
⑤ Unicode: 国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
⑥ UTF-8: 变长的编码方式,可用1-4个字节来表示一个字符。修正后,一个字符可用6个字节表示
注意:
      GBK等双字节编码方式,用最高位是1或0表示两个字节和一个字节。
      在Unicode 出现之前,所有的字符集都是和具体编码方案绑定在一起的(即字符集≈ 编码方式),都是直接将字符和最终字节流绑定死了。

3.Unicode的问题及解决

  • Unicode不完美,这里就有三个问题,一个是,我们已经知道,英文字母只用一个字节表示就够了,第二个问题是如何才能区别Unicode和ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个,如果和GBK等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。Unicode在很长一段时间内无法推广,直到互联网的出现。
  • 面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
  • Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。 推荐的Unicode编码是UTF-8和UTF-16。

4.实现方式

在这里插入图片描述

三、处理流之三:标准输入、输出流(了解)

1.概念

  • System.in:标准的输入流,默认从键盘输入
    System.out:标准的输出流,默认从控制台输出
    默认输入设备是:键盘,输出设备是:显示器
  • System.in的类型是InputStream
    System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
  • 重定向:通过System类的setIn,setOut方法对默认设备进行改变。
    • public static void setIn(InputStream in)
    • public static void setOut(PrintStream out)

2.练习

2.1 键盘输入,读字符停止

① 题目: 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
② 方法:
      方法一:使用Scanner实现,调用next()返回一个字符串
      方法二:使用System.in实现。System.in —> 转换流 —> BufferedReader的readLine()
③ 程序:
      注意:IDEA不支持在单元测试下的键盘输入

    public static void main(String[] args) {
        BufferedReader br = null;
        try {
        	//1.创建转换流、缓冲流
            InputStreamReader isr = new InputStreamReader(System.in);
            br = new BufferedReader(isr);

            while (true) {
                System.out.println("请输入字符串:");
                String data = br.readLine();
                if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
                    System.out.println("程序结束");
                    break;
                }
                String upperCase = data.toUpperCase();
                System.out.println(upperCase);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2.2 造一个类似Scanner类

① 题目: Create a program named MyInput.java: Contain the methods for reading int,double, float, boolean, short, byte and String values from the keyboard.
② 错误方法: 不能直接将Scanner直接包成一个类这没有意义。程序如下,不要这么写。

class MyInput{
	Scanner s = new Scanner(System.in);
	public String readString(){
		return s.next();
	}.
}

③ 正确方法: 应该要使用流来处理

import java.io.*;
public class MyInput {
    // Read a string from the keyboard
    public static String readString() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // Declare and initialize the string
        String string = "";

        // Get the string from the keyboard
        try {
            string = br.readLine();

        } catch (IOException ex) {
            System.out.println(ex);
        }

        // Return the string obtained from the keyboard
        return string;
    }

    // Read an int value from the keyboard
    public static int readInt() {
        return Integer.parseInt(readString());
    }

    // Read a double value from the keyboard
    public static double readDouble() {
        return Double.parseDouble(readString());
    }

    // Read a byte value from the keyboard
    public static double readByte() {
        return Byte.parseByte(readString());
    }

    // Read a short value from the keyboard
    public static double readShort() {
        return Short.parseShort(readString());
    }

    // Read a long value from the keyboard
    public static double readLong() {
        return Long.parseLong(readString());
    }

    // Read a float value from the keyboard
    public static double readFloat() {
        return Float.parseFloat(readString());
    }
}

四、处理流之四:打印流(了解)

1.概念

  • 实现将 基本数据类型 的数据格式转化为 字符串 输出
  • 打印流:PrintStreamPrintWriter
    • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    • PrintStream和PrintWriter的输出不会抛出IOException异常
    • PrintStream和PrintWriter有自动flush功能
    • PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
    • System.out返回的是PrintStream的实例

2.练习

    public void test2() {
        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
            // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区),这里的true是会不会自动刷新缓冲区
            ps = new PrintStream(fos, true);
            
            if (ps != null) {// 把标准输出流(控制台输出)改成文件
                System.setOut(ps);
            }
            for (int i = 0; i <= 255; i++) { // 输出ASCII字符
                System.out.print((char) i);
                if (i % 50 == 0) { // 每50个数据一行
                    System.out.println(); // 换行
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

五、处理流之五:数据流(了解)

1.概念

  • ① 数据流有两个类: (用于读取和写出基本数据类型、String类的数据)
          DataInputStream 和 DataOutputStream
          分别“套接”在 InputStream 和 OutputStream子类的流上
  • ② 作用: 用于读取或写出基本数据类型的变量或字符串
  • ③ DataInputStream中的方法:
    • boolean readBoolean() byte readByte()
      char readChar()     float readFloat()
      double readDouble()  short readShort()
      long readLong()    int readInt()
      String readUTF()   void readFully(byte[] b)
  • ④ DataOutputStream中的方法:
    • 将上述的方法的read改为相应的write即可。

2.练习

2.1 写到文件

① 写入: 将内存中的字符串、基本数据类型的变量写出到文件中。
② 注意: 处理异常的话,仍然应该使用try-catch-finally。

	@Test
    public void test3() throws IOException {
        //1.创建流
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
        //2.写
        dos.writeUTF("刘建辰");
        dos.flush();//刷新操作,将内存中的数据写入文件
        dos.writeInt(23);
        dos.flush();
        dos.writeBoolean(true);
        dos.flush();
        //3.关闭资源
        dos.close();
    }

2.2 读取文件

① 读取: 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。

② 注意点: 读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!

    @Test
    public void test4() throws IOException {
        //1.创建流
        DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
        //2.读取
        String name = dis.readUTF();
        int age = dis.readInt();
        boolean isMale = dis.readBoolean();

        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("isMale = " + isMale);

        //3.关闭资源
        dis.close();
    }

六、处理流之六:对象流

1.概念

  • ObjectInputStream 和 OjbectOutputSteam
    用于存储和读取基本数据类型数据对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  • 序列化过程: 将内存中的java对象保存到磁盘中或通过网络传输出去。使用ObjectOutputStream实现
  • 反序列化: 将磁盘文件中的对象还原为内存中的一个java对象。使用ObjectInputStream来实现
  • ObjectOutputStream 和 ObjectInputStream不能序列化static和transient修饰的成员变量

2.对象的序列化

2.1概念

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  • 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是JavaEE 平台的基础
  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常
    • Serializable
    • Externalizable

2.2 serialVersionUID的理解

  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
    • private static final long serialVersionUID;
    • serialVersionUID用来表明类的不同版本间的兼容性。 简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
    • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
  • 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

3.序列化

  • ① 步骤: 若某个类实现了 Serializable 接口,该类的对象就是可序列化的
    • 创建一个 ObjectOutputStream
    • 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
    • 注意写出一次,操作flush()一次
  • ② 程序:
@Test
    public void testObjectOutputStream(){
        ObjectOutputStream oos = null;

        try {
            //1.创建流
            oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
            //2.写入
            oos.writeObject(new String("我爱北京天安门"));
            oos.flush();//刷新操作

            oos.writeObject(new Person("王铭",23));
            oos.flush();

            oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
            oos.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(oos != null){
                //3.关闭流
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

4.反序列化

  • ① 步骤:
    • 创建一个 ObjectInputStream
    • 调用 readObject() 方法读取流中的对象
  • ② 程序:
	public void testObjectInputStream(){
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("object.dat"));

            Object obj = ois.readObject();
            String str = (String) obj;

            Person p = (Person) ois.readObject();
            Person p1 = (Person) ois.readObject();

            System.out.println(str);
            System.out.println(p);
            System.out.println(p1);

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

5.自定义类实现序列化反序列化

5.1 要求

Person需要满足如下的要求,方可序列化

  • ① 需要实现接口:Serializable
  • ② 当前类提供一个全局常量:serialVersionUID(必须要自己给一个long类型 值)
  • ③ 除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)

5.2 程序

  • 自定义Person类如下所示
  • 具体的实现写在上面的程序中
//① 需要实现接口:Serializable
public class Person implements Serializable{
	//② 当前类提供一个全局常量:serialVersionUID
    public static final long serialVersionUID = 475463534532L;

    private String name;
    private int age;
    private int id;
    //③ 除了当前Person类需要实现Serializable接口之外,
	//还必须保证其内部所有属性也必须是可序列化的。
	//所以Accunt类也得是可序列化的
    private Account acct;

	//get set方法省略

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                ", acct=" + acct +
                '}';
    }

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

    public Person() {

    }
}

class Account implements Serializable{
    public static final long serialVersionUID = 4754534532L;
    private double balance;

    @Override
    public String toString() {
        return "Account{" +
                "balance=" + balance +
                '}';
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public Account(double balance) {
        this.balance = balance;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值