IO流 (字节流、字符流)

目录

一、IO流概述

1.1 IO流概述

1.2 FileInputStream读取一个字节

1.3 FileInputStream读取多个字节

1.4 FileInputStream读取全部字节

 1.5 FileOutputStream写字节

1.6 字节流复制文件

二、IO流资源释放

2.1 JDK7以前的资源释放

2.2 JDK7以后的资源释放

三、字符流

3.1 FileReader类

 3.2 FileWriter类

3.3 FileWriter写的注意事项

四、缓冲流

4.1 缓冲字节流

 4.2 字符缓冲流

 4.3 缓冲流性能分析

五、转换流

5.1 InputStreamReader类

5.2 OutputStreamWriter类

六、打印流

6.1 打印流基本使用

6.2 重定向输出语句

七、数据流

 7.1 DataOutputStream类

7.2 DataInputStream类

八、序列化流

 8.1 ObjectOutputStraem类

8.2 ObjectInputStream类

九、补充知识:IO框架


一、IO流概述

1.1 IO流概述

IO流的作用:就是可以对文件或者网络中的数据进行读、写的操作。如下图所示

  • 把数据从磁盘、网络中读取到程序中来,用到的是输入流。
  • 把程序中的数据写入磁盘、网络中,用到的是输出流。
  • 简单记:输入流(读数据)、输出流(写数据)

 IO流在Java中有很多种,不同的流来干不同的事情。Java把各种流用不同的类来表示,这些流的继承体系如下图所示:

IO流分为两大派系:
    1.字节流:字节流又分为字节输入流、字节输出流
    2.字符流:字符流由分为字符输入流、字符输出流

 

1.2 FileInputStream读取一个字节

需要用到的方法如下图所示:有构造方法、成员方法

 使用FileInputStream读取文件中的字节数据,步骤如下

第一步:创建FileInputStream文件字节输入流管道,与源文件接通。
第二步:调用read()方法开始读取文件的字节数据。
第三步:调用close()方法释放资源

代码如下:

/**
 * 目标:掌握文件字节输入流,每次读取一个字节。
 */
public class FileInputStreamTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建文件字节输入流管道,与源文件接通。
        InputStream is = new FileInputStream(("file-io-app\\src\\itheima01.txt"));

        // 2、开始读取文件的字节数据。
        // public int read():每次读取一个字节返回,如果没有数据了,返回-1.
        int b; // 用于记住读取的字节。
        while ((b = is.read()) != -1){
            System.out.print((char) b);
        }
        
        //3、流使用完毕之后,必须关闭!释放系统资源!
        is.close();
    }
}

 这里需要注意一个问题:由于一个中文在UTF-8编码方案中是占3个字节,采用一次读取一个字节的方式,读一个字节就相当于读了1/3个汉字,此时将这个字节转换为字符,是会有乱码的。

1.3 FileInputStream读取多个字节

学习了FileInputStream调用read()方法,可以一次读取一个字节。但是这种读取方式效率太太太太慢了。 为了提高效率,我们可以使用另一个read(byte[] bytes)的重载方法,可以一次读取多个字节,至于一次读多少个字节,就在于你传递的数组有多大。

使用FileInputStream一次读取多个字节的步骤如下

第一步:创建FileInputStream文件字节输入流管道,与源文件接通。
第二步:调用read(byte[] bytes)方法开始读取文件的字节数据。
第三步:调用close()方法释放资源

 代码如下:

/**
 * 目标:掌握使用FileInputStream每次读取多个字节。
 */
public class FileInputStreamTest2 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输入流对象代表字节输入流管道与源文件接通。
        InputStream is = new FileInputStream("file-io-app\\src\\itheima02.txt");

        // 2、开始读取文件中的字节数据:每次读取多个字节。
        //  public int read(byte b[]) throws IOException
        //  每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1.

        // 3、使用循环改造。
        byte[] buffer = new byte[3];
        int len; // 记住每次读取了多少个字节。  abc 66
        while ((len = is.read(buffer)) != -1){
            // 注意:读取多少,倒出多少。
            String rs = new String(buffer, 0 , len);
            System.out.print(rs);
        }
        // 性能得到了明显的提升!!
        // 这种方案也不能避免读取汉字输出乱码的问题!!

        is.close(); // 关闭流
    }
}

 需要我们注意的是:read(byte[] bytes)它的返回值,表示当前这一次读取的字节个数。

1.4 FileInputStream读取全部字节

 

前面我们到的读取方式,不管是一次读取一个字节,还是一次读取多个字节,都有可能有乱码。那么接下来我们介绍一种,不出现乱码的读取方式。

我们可以一次性读取文件中的全部字节,然后把全部字节转换为一个字符串,就不会有乱码了

 

 

// 1、一次性读取完文件的全部字节到一个字节数组中去。
// 创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");

// 2、准备一个字节数组,大小与文件的大小正好一样大。
File f = new File("file-io-app\\src\\itheima03.txt");
long size = f.length();
byte[] buffer = new byte[(int) size];

int len = is.read(buffer);
System.out.println(new String(buffer));

//3、关闭流
is.close(); 

// 1、一次性读取完文件的全部字节到一个字节数组中去。
// 创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");

//2、调用方法读取所有字节,返回一个存储所有字节的字节数组。
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));

//3、关闭流
is.close(); 

最后,还是要注意一个问题:一次读取所有字节虽然可以解决乱码问题,但是文件不能过大,如果文件过大,可能导致内存溢出。

 1.5 FileOutputStream写字节

往文件中写数据需要用到OutputStream下面的一个子类FileOutputStream。写输入的流程如下图所示

 使用FileOutputStream往文件中写数据的步骤如下:

第一步:创建FileOutputStream文件字节输出流管道,与目标文件接通。
第二步:调用wirte()方法往文件中写数据
第三步:调用close()方法释放资源

 代码如下:

/**
 * 目标:掌握文件字节输出流FileOutputStream的使用。
 */
public class FileOutputStreamTest4 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输出流管道与目标文件接通。
        // 覆盖管道:覆盖之前的数据
//        OutputStream os =
//                new FileOutputStream("file-io-app/src/itheima04out.txt");

        // 追加数据的管道
        OutputStream os =
                new FileOutputStream("file-io-app/src/itheima04out.txt", true);

        // 2、开始写字节数据出去了
        os.write(97); // 97就是一个字节,代表a
        os.write('b'); // 'b'也是一个字节
        // os.write('磊'); // [ooo] 默认只能写出去一个字节

        byte[] bytes = "我爱你中国abc".getBytes();
        os.write(bytes);

        os.write(bytes, 0, 15);

        // 换行符
        os.write("\r\n".getBytes());

        os.close(); // 关闭流
    }
}

1.6 字节流复制文件

比如:我们要复制一张图片,从磁盘D:/resource/meinv.png的一个位置,复制到C:/data/meinv.png位置。

复制文件的思路如下图所示:

1.需要创建一个FileInputStream流与源文件接通,创建FileOutputStream与目标文件接通
2.然后创建一个数组,使用FileInputStream每次读取一个字节数组的数据,存如数组中
3.然后再使用FileOutputStream把字节数组中的有效元素,写入到目标文件中

 代码如下:

/**
 * 目标:使用字节流完成对文件的复制操作。
 */
public class CopyTest5 {
    public static void main(String[] args) throws Exception {
        // 需求:复制照片。
        // 1、创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("D:/resource/meinv.png");
        // 2、创建一个字节输出流管道与目标文件接通。
        OutputStream os = new FileOutputStream("C:/data/meinv.png");

        System.out.println(10 / 0);
        // 3、创建一个字节数组,负责转移字节数据。
        byte[] buffer = new byte[1024]; // 1KB.
        // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
        int len; // 记住每次读取了多少个字节。
        while ((len = is.read(buffer)) != -1){
            os.write(buffer, 0, len);
        }

        os.close();
        is.close();
        System.out.println("复制完成!!");
    }
}

二、IO流资源释放

2.1 JDK7以前的资源释放

在JDK7版本以前,我们可以使用try...catch...finally语句来处理。格式如下

try{
    //有可能产生异常的代码
}catch(异常类 e){
    //处理异常的代码
}finally{
    //释放资源的代码
    //finally里面的代码有一个特点,不管异常是否发生,finally里面的代码都会执行。
}

2.2 JDK7以后的资源释放

格式如下:

try(资源对象1; 资源对象2;){
    使用资源的代码
}catch(异常类 e){
    处理异常的代码
}

//注意:注意到没有,这里没有释放资源的代码。它会自动是否资源

三、字符流

3.1 FileReader类

 FileReader读取文件的步骤如下:

第一步:创建FileReader对象与要读取的源文件接通
第二步:调用read()方法读取文件中的字符
第三步:调用close()方法关闭流

 

 需要用到的方法:先通过构造器创建对象,再通过read方法读取数据(注意:两个read方法的返回值,含义不一样

 

/**
 * 目标:掌握文件字符输入流。
 */
public class FileReaderTest1 {
    public static void main(String[] args)  {
        try (
                // 1、创建一个文件字符输入流管道与源文件接通
                Reader fr = new FileReader("io-app2\\src\\itheima01.txt");
                ){
            // 2、一个字符一个字符的读(性能较差)
//            int c; // 记住每次读取的字符编号。
//            while ((c = fr.read()) != -1){
//                System.out.print((char) c);
//            }
            // 每次读取一个字符的形式,性能肯定是比较差的。

            // 3、每次读取多个字符。(性能是比较不错的!)
            char[] buffer = new char[3];
            int len; // 记住每次读取了多少个字符。
            while ((len = fr.read(buffer)) != -1){
                // 读取多少倒出多少
                System.out.print(new String(buffer, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 3.2 FileWriter类

 FileWriter往文件中写字符数据的步骤如下:

第一步:创建FileWirter对象与要读取的目标文件接通
第二步:调用write(字符数据/字符数组/字符串)方法读取文件中的字符
第三步:调用close()方法关闭流

 需要用到的方法如下:构造器是用来创建FileWriter对象的,有了对象才能调用write方法写数据到文件。

 接下来,用代码演示一下:

/**
 * 目标:掌握文件字符输出流:写字符数据出去
 */
public class FileWriterTest2 {
    public static void main(String[] args) {
        try (
                // 0、创建一个文件字符输出流管道与目标文件接通。
                // 覆盖管道
                // Writer fw = new FileWriter("io-app2/src/itheima02out.txt");
                // 追加数据的管道
                Writer fw = new FileWriter("io-app2/src/itheima02out.txt", true);
                ){
            // 1、public void write(int c):写一个字符出去
            fw.write('a');
            fw.write(97);
            //fw.write('磊'); // 写一个字符出去
            fw.write("\r\n"); // 换行

            // 2、public void write(String c)写一个字符串出去
            fw.write("我爱你中国abc");
            fw.write("\r\n");

            // 3、public void write(String c ,int pos ,int len):写字符串的一部分出去
            fw.write("我爱你中国abc", 0, 5);
            fw.write("\r\n");

            // 4、public void write(char[] buffer):写一个字符数组出去
            char[] buffer = {'黑', '马', 'a', 'b', 'c'};
            fw.write(buffer);
            fw.write("\r\n");

            // 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
            fw.write(buffer, 0, 2);
            fw.write("\r\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3 FileWriter写的注意事项

**FileWriter写完数据之后,必须刷新或者关闭,写出去的数据才能生效。**

四、缓冲流

 **缓冲流的作用**:可以对原始流进行包装,提高原始流读写数据的性能。

4.1 缓冲字节流

我们先来学习字节缓冲流是如何提高读写数据的性能的,原理如下图所示。是因为在缓冲流的底层自己封装了一个长度为8KB(8129byte)的字节数组,但是缓冲流不能单独使用,它需要依赖于原始流。

  • 读数据时:它先用原始字节输入流一次性读取8KB的数据存入缓冲流内部的数组中(ps: 先一次多囤点货),再从8KB的字节数组中读取一个字节或者多个字节(把消耗屯的货)。

 写数据时: 它是先把数据写到缓冲流内部的8BK的数组中(ps: 先攒一车货),等数组存满了,再通过原始的字节输出流,一次性写到目标文件中去(把囤好的货,一次性运走)。

在创建缓冲字节流对象时,需要封装一个原始流对象进来。构造方法如下

如果我们用缓冲流复制文件,代码写法如下:

public class BufferedInputStreamTest1 {
    public static void main(String[] args) {
        try (
                InputStream is = new FileInputStream("io-app2/src/itheima01.txt");
                // 1、定义一个字节缓冲输入流包装原始的字节输入流
                InputStream bis = new BufferedInputStream(is);

                OutputStream os = new FileOutputStream("io-app2/src/itheima01_bak.txt");
                // 2、定义一个字节缓冲输出流包装原始的字节输出流
                OutputStream bos = new BufferedOutputStream(os);
        ){

            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 4.2 字符缓冲流

 BufferedReader读数据时:它先原始字符输入流一次性读取8KB的数据存入缓冲流内部的数组中(ps: 先一次多囤点货),再从8KB的字符数组中读取一个字符或者多个字符(把消耗屯的货)。

创建BufferedReader对象需要用到BufferedReader的构造方法,内部需要封装一个原始的字符输入流,我们可以传入FileReader.

而且BufferedReader还要特有的方法,一次可以读取文本文件中的一行

 使用BufferedReader读取数据的代码如下

public class BufferedReaderTest2 {
    public static void main(String[] args)  {
        try (
                Reader fr = new FileReader("io-app2\\src\\itheima04.txt");
                // 创建一个字符缓冲输入流包装原始的字符输入流
                BufferedReader br = new BufferedReader(fr);
        ){
//            char[] buffer = new char[3];
//            int len;
//            while ((len = br.read(buffer)) != -1){
//                System.out.print(new String(buffer, 0, len));
//            }
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());

            String line; // 记住每次读取的一行数据
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 **BufferedWriter写数据时:** 它是先把数据写到字符缓冲流内部的8BK的数组中(ps: 先攒一车货),等数组存满了,再通过原始的字符输出流,一次性写到目标文件中去(把囤好的货,一次性运走)。如下图所示

 创建BufferedWriter对象时需要用到BufferedWriter的构造方法,而且内部需要封装一个原始的字符输出流,我们这里可以传递FileWriter。

 

 接下来,用代码演示一下,使用BufferedWriter往文件中写入字符数据。

public class BufferedWriterTest3 {
    public static void main(String[] args) {
        try (
                Writer fw = new FileWriter("io-app2/src/itheima05out.txt", true);
                // 创建一个字符缓冲输出流管道包装原始的字符输出流
                BufferedWriter bw = new BufferedWriter(fw);
        ){

            bw.write('a');
            bw.write(97);
            bw.write('磊');
            bw.newLine();

            bw.write("我爱你中国abc");
            bw.newLine();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 4.3 缓冲流性能分析

我们说缓冲流内部多了一个数组,可以提高原始流的读写性能。讲到这一定有同学有这么一个疑问,它和我们使用原始流,自己加一个8BK数组不是一样的吗? 缓冲流就一定能提高性能吗?先告诉同学们答案,**缓冲流不一定能提高性能**。

> 下面我们用一个比较大文件(889MB)复制,做性能测试,分别使用下面四种方式来完成文件复制,并记录文件复制的时间。

① 使用低级流一个字节一个字节的复制

② 使用低级流按照字节数组的形式复制

③ 使用缓冲流一个字节一个字节的复制

④ 使用缓冲流按照字节数组的形式复制

低级流一个字节复制: 慢得简直让人无法忍受
低级流按照字节数组复制(数组长度1024): 12.117s
缓冲流一个字节复制: 11.058s
缓冲流按照字节数组复制(数组长度1024): 2.163s
【注意:这里的测试只能做一个参考,和电脑性能也有直接关系】

 经过上面的测试,我们可以得出一个结论:**默认情况下,采用一次复制1024个字节,缓冲流完胜。**

但是,缓冲流就一定性能高吗?我们采用一次复制8192个字节试试

低级流按照字节数组复制(数组长度8192): 2.535s
缓冲流按照字节数组复制(数组长度8192): 2.088s

经过上面的测试,我们可以得出一个结论:一次读取8192个字节时,低级流和缓冲流性能相当。相差的那几毫秒可以忽略不计。

继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*32个字节数据试试

低级流按照字节数组复制(数组长度8192): 1.128s
缓冲流按照字节数组复制(数组长度8192): 1.133s

经过上面的测试,我们可以得出一个结论:数组越大性能越高,低级流和缓冲流性能相当。相差的那几秒可以忽略不计。

继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*6个字节数据试试

低级流按照字节数组复制(数组长度8192): 1.039s
缓冲流按照字节数组复制(数组长度8192): 1.151s

此时你会发现,当数组大到一定程度,性能已经提高了多少了,甚至缓冲流的性能还没有低级流高。

最终总结一下:缓冲流的性能不一定比低级流高,其实低级流自己加一个数组,性能其实是不差。只不过缓冲流帮你加了一个相对而言大小比较合理的数组 。

五、转换流

5.1 InputStreamReader类

InputStreamReader类,你看这个类名就比较有意思,前面是InputStream表示字节输入流,后面是Reader表示字符输入流,合在一起意思就是表示可以把InputStream转换为Reader,最终InputStreamReader其实也是Reader的子类,所以也算是字符输入流。

InputStreamReader也是不能单独使用的,它内部需要封装一个InputStream的子类对象,再指定一个编码表,如果不指定编码表,默认会按照UTF-8形式进行转换。

需求:我们可以先准备一个GBK格式的文件,然后使用下面的代码进行读取,看是是否有乱码。

public class InputStreamReaderTest2 {
    public static void main(String[] args) {
        try (
                // 1、得到文件的原始字节流(GBK的字节流形式)
                InputStream is = new FileInputStream("io-app2/src/itheima06.txt");
                // 2、把原始的字节输入流按照指定的字符集编码转换成字符输入流
                Reader isr = new InputStreamReader(is, "GBK");
                // 3、把字符输入流包装成缓冲字符输入流
                BufferedReader br = new BufferedReader(isr);
                ){
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行完之后,你会发现没有乱码。

5.2 OutputStreamWriter类

我们先学习OutputStreamWriter类,你看这个类名也比较有意思,前面是OutputStream表示字节输出流,后面是Writer表示字符输出流,合在一起意思就是表示可以把OutputStream转换为Writer,最终OutputStreamWriter其实也是Writer的子类,所以也算是字符输出流。

OutputStreamReader也是不能单独使用的,它内部需要封装一个OutputStream的子类对象,再指定一个编码表,如果不指定编码表,默认会按照UTF-8形式进行转换。

需求:我们可以先准备一个GBK格式的文件,使用下面代码往文件中写字符数据。

public class OutputStreamWriterTest3 {
    public static void main(String[] args) {
        // 指定写出去的字符编码。
        try (
                // 1、创建一个文件字节输出流
                OutputStream os = new FileOutputStream("io-app2/src/itheima07out.txt");
                // 2、把原始的字节输出流,按照指定的字符集编码转换成字符输出转换流。
                Writer osw = new OutputStreamWriter(os, "GBK");
                // 3、把字符输出流包装成缓冲字符输出流
                BufferedWriter bw = new BufferedWriter(osw);
                ){
            bw.write("我是中国人abc");
            bw.write("我爱你中国123");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

六、打印流

6.1 打印流基本使用

打印流,这里所说的打印其实就是写数据的意思,它和普通的write方法写数据还不太一样,一般会使用打印流特有的方法叫`print(数据)`或者`println(数据)`,它打印啥就输出啥。

打印流有两个,一个是字节打印流PrintStream,一个是字符打印流PrintWriter,如下图所示

 

 **PrintStream和PrintWriter的用法是一样的,所以这里就一块演示了。**

public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/itheima08.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/itheima08.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/itheima08.txt", true));
                ){
                ps.print(97);	//文件中显示的就是:97
                ps.print('a'); //文件中显示的就是:a
                ps.println("我爱你中国abc");	//文件中显示的就是:我爱你中国abc
                ps.println(true);//文件中显示的就是:true
                ps.println(99.5);//文件中显示的就是99.5

                ps.write(97); //文件中显示a,发现和前面println方法的区别了吗?

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6.2 重定向输出语句

而且System还提供了一个方法,可以修改底层的打印流,这样我们就可以重定向打印语句的输出目的地了。我们玩一下, 直接上代码。

public class PrintTest2 {
    public static void main(String[] args) {
        System.out.println("老骥伏枥");
        System.out.println("志在千里");

        try ( PrintStream ps = new PrintStream("io-app2/src/itheima09.txt"); ){
            // 把系统默认的打印流对象改成自己设置的打印流
            System.setOut(ps);

            System.out.println("烈士暮年");	
            System.out.println("壮心不已");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此时打印语句,将往文件中打印数据,而不在控制台。

七、数据流

 7.1 DataOutputStream类

DataOutputStream类,它也是一种包装流,创建DataOutputStream对象时,底层需要依赖于一个原始的OutputStream流对象。然后调用它的wirteXxx方法,写的是特定类型的数据。

 代码如下:往文件中写整数、小数、布尔类型数据、字符串数据

public class DataOutputStreamTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个数据输出流包装低级的字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("io-app2/src/itheima10out.txt"));
                ){
            dos.writeInt(97);
            dos.writeDouble(99.5);
            dos.writeBoolean(true);
            dos.writeUTF("黑马程序员666!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

7.2 DataInputStream类

DataOutputStream后,再学习DataIntputStream类,它也是一种包装流,创建DataInputStream对象时,底层需要依赖于一个原始的InputStream流对象。然后调用它的readXxx()方法就可以读取特定类型的数据。

 代码如下:读取文件中特定类型的数据(整数、小数、字符串等)

public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try (
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("io-app2/src/itheima10out.txt"));
                ){
            int i = dis.readInt();
            System.out.println(i);

            double d = dis.readDouble();
            System.out.println(d);

            boolean b = dis.readBoolean();
            System.out.println(b);

            String rs = dis.readUTF();
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

八、序列化流

 序列化:意思就是把对象写到文件或者网络中去。(简单记:写对象)
反序列化:意思就是把对象从文件或者网络中读取出来。(简单记:读对象)

 8.1 ObjectOutputStraem类

接下来,先学习ObjectOutputStream流,它也是一个包装流,不能单独使用,需要结合原始的字节输出流使用。

代码如下:将一个User对象写到文件中去

- 第一步:先准备一个User类,必须让其实现Serializable接口。

// 注意:对象如果需要序列化,必须实现序列化接口。
public class User implements Serializable {
    private String loginName;
    private String userName;
    private int age;
    // transient 这个成员变量将不参与序列化。
    private transient String passWord;

    public User() {
    }

    public User(String loginName, String userName, int age, String passWord) {
        this.loginName = loginName;
        this.userName = userName;
        this.age = age;
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "User{" +
                "loginName='" + loginName + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}

第二步:再创建ObjectOutputStream流对象,调用writeObject方法对象到文件。

public class Test1ObjectOutputStream {
    public static void main(String[] args) {
        try (
                // 2、创建一个对象字节输出流包装原始的字节 输出流。
                ObjectOutputStream oos =
                        new ObjectOutputStream(new FileOutputStream("io-app2/src/itheima11out.txt"));
                ){
            // 1、创建一个Java对象。
            User u = new User("admin", "张三", 32, "666888xyz");

            // 3、序列化对象到文件中去
            oos.writeObject(u);
            System.out.println("序列化对象成功!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

**注意:写到文件中的对象,是不能用记事本打开看的。因为对象本身就不是文本数据,打开是乱码**

 怎样才能读懂文件中的对象是什么呢?这里必须用反序列化,自己写代码读。

8.2 ObjectInputStream类

接下来,学习ObjectInputStream流,它也是一个包装流,不能单独使用,需要结合原始的字节输入流使用。

接着前面的案例,文件中已经有一个Student对象,现在要使用ObjectInputStream读取出来。称之为反序列化。

public class Test2ObjectInputStream {
    public static void main(String[] args) {
        try (
            // 1、创建一个对象字节输入流管道,包装 低级的字节输入流与源文件接通
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("io-app2/src/itheima11out.txt"));
        ){
            User u = (User) ois.readObject();
            System.out.println(u);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

九、补充知识:IO框架

由apache开源基金组织提供了一组有关IO流小框架,可以提高IO流的开发效率。

这个框架的名字叫commons-io:其本质是别人写好的一些字节码文件(class文件),打包成了一个jar包。我们只需要把jar包引入到我们的项目中,就可以直接用了。

介绍一个jar包中提供的工具类叫FileUtils,它的部分功能如下,很方便,你一看名字就知道怎么用了。

在写代码之前,先需要引入jar包,具体步骤如下

1.在模块的目录下,新建一个lib文件夹
2.把jar包复制粘贴到lib文件夹下
3.选择lib下的jar包,右键点击Add As Library,然后就可以用了。

 代码如下:

public class CommonsIOTest1 {
    public static void main(String[] args) throws Exception {
        //1.复制文件
        FileUtils.copyFile(new File("io-app2\\src\\itheima01.txt"), new File("io-app2/src/a.txt"));
        
        //2.复制文件夹
        FileUtils.copyDirectory(new File("D:\\resource\\私人珍藏"), new File("D:\\resource\\私人珍藏3"));
        
        //3.删除文件夹
        FileUtils.deleteDirectory(new File("D:\\resource\\私人珍藏3"));

        // Java提供的原生的一行代码搞定很多事情
         Files.copy(Path.of("io-app2\\src\\itheima01.txt"), Path.of("io-app2\\src\\b.txt"));
        System.out.println(Files.readString(Path.of("io-app2\\src\\itheima01.txt")));
    }
}

  • 12
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值