JAVASE——2.IO流

JAVASE-IO

2.1 IO流概述和分类.

  • java io可以让我们用标准的读写操作来完成对不同设备的读写数据工作.

  • java将IO按照方向划分为输入与输出,参照点是我们写的程序.

  • 输入:用来读取数据的,是从外界到程序的方向,用于获取数据.

  • 输出:用来写出数据的,是从程序到外界的方向,用于发送数据.

java将IO比喻为"流",即:stream. 就像生活中的"电流",“水流"一样,它是以同一个方向顺序移动的过程.只不过这里流动的是字节(2进制数据).所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的"管道”,用于获取或发送数据到另一端.

在这里插入图片描述

Java定义了两个超类(抽象类):

  • java.io.InputStream:所有字节输入流的超类,其中定义了读取数据的方法.因此将来不管读取的是什么设备(连接该设备的流)都有这些读取的方法,因此我们可以用相同的方法读取不同设备中的数据
  • java.io.OutputStream:所有字节输出流的超类,其中定义了写出数据的方法.
java将流分为两类:节点流与处理流:
  • 节点流:也称为低级流.节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的.
  • 处理流:也称为高级流.处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.
实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在.
  • IO:输入/输出(Input/Output)

  • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输成为流,流的本质是数据传输。

  • IO流就是用来处理设备间数据传输问题的。

    ​ 常见的应用:文件复制;文件上传;文件下载

    IO流分类:

    • 按数据的方向:

      ​ 输入流:读数据

      ​ 输出流:写数据

    • 按数据类型分:

      ​ 字节流:字节输入流;字节输出流

      ​ 字符流:字符输入流;字符输出流

一般来说,我们说IO流的分类是按照数据类型来分的,那么这两种流都在什么情况下使用呐?

  • 如果数据通过windows自带的记事本软件打开,我们还可以读懂里面的内容就使用字符流,否则使用字节流。不知道何种类型就是用字节流。

2.2字节流写数据

字节流抽象基类

  • InputStream:这个抽象类是表示输入流的所有类的超类
  • OutputStream:这个抽象类是表示输出流的所有类的超类
  • 子类名特点:子类名称都是以其父类名作为子类名的后缀

使用字节输出流写数据的步骤:

  • 创建字节输出流对象(调用系统创建文件;创建字节输出流对象;让字节输出流对象指向文件)
  • 调用字节输出流对象的书写数据方法
  • 释放资源(关闭文件输出流并释放与此流相关联的任何系统资源)

文件流

文件流是一对低级流,用于读写文件数据的流.用于连接程序与文件(硬盘)的"管道".负责读写文件数据.

文件输出流:java.io.FileOutputStream

package io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * JAVA IO  Input&Output  输入和输出
 * java程序与外界交换数据是基于IO完成的,这里输入与输出一个负责读一个负责写
 * 输入:是从外界到我们写的程序的方向,是用来从外界获取信息的。因此是"读"操作
 * 输出:是从我们写的程序到外界的方向,是用来向外界发送信息的。因此是"写"操作
 *
 * java将IO比喻为"流",可以理解为是程序与外界连接的"管道",内部流动的是字节,并且
 * 字节是顺着同一侧方向顺序移动的。
 *
 * java.io.InputStream 输入流,这个类是所有字节输入流的超类,规定了所有字节输入
 * 流读取数据的相关方法。
 * java.io.OutputStream 输出流,这个类是所有字节输出流的超类,规定了所有字节输出
 * 流写出数据的相关方法。
 *
 * 实际应用中,我们连接不同的设备,java都专门提供了用于连接它的输入流与输出流,而
 * 这些负责实际连接设备的流称为节点流,也叫低级流。是真实负责读写数据的流。
 * 与之对应的还有高级流,高级流可以连接其他的流,目的是当数据流经它们时,对数据做某
 * 种加工处理,用来简化我们的操作。
 *
 *
 * 文件流
 * java.io.FileInputStream和FileOutputStream
 * 这是一对低级流,继承自InputStream和OutputStream。用于读写硬盘上文件的流
 *
 */
public class FOSDemo {
    public static void main(String[] args) throws IOException {

        //向当前目录下的demo.dat文件中写入数据
        /*
            FileOutputStream提供的常用构造器
            FileOutputStream(String path)
            FileOutputStream(File file)
         */
        //文件流创建时,如果该文件不存在会自动将其创建(前提是该文件所在目录必须存在!)
        FileOutputStream fos = new FileOutputStream("./demo.dat");
        /*
            void write(int d)
            向文件中写入1个字节,写入的内容是给定的int值对应的2进制的"低八位"

            int值 1:                         vvvvvvvv
            二进制:00000000 00000000 00000000 00000001
            demo.dat文件内容:
            00000000

         */
        fos.write(1);
        /*
                                       vvvvvvvv
            00000000 00000000 00000000 00000010

            demo.dat文件内容
            00000001 00000010
         */
        fos.write(2);



        fos.close();
        System.out.println("执行完了!");


    }
}
package exercise.File;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo01 {

    public static void main(String[] args) throws IOException {
        /**
         * FileOutputStream:文件输出流用于将数据写入File
         * FileOutputStream(String name):创建文件输出流以指定的名称写入文件
         */
        FileOutputStream fos = new FileOutputStream("fos.txt");
        /**
         * 1.调用系统功能创建了文件
         * 2.创建了字节输出流对象
         * 3.让字节输出流对象指向创建好的文件
         */
        //void write(int b):将指定的字节写入此文件输出流
        fos.write(97);
        //fos.write(57);
        //fos.write(55);
        /**
         * 所有IO相关的最后都要释放资源
         * void close():关闭文件输出流并释放与此流相关联的任何系统资源。
         */
        fos.close();
    }
}

2.3字节流写数据的3中方式

方法名说明
void write(int b)将指定的字节写入此文件输出流,一次写一个字节数据
void write(byte[] b)将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b, int off, int len)将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据
package exercise.File;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        //FileOutStream(String name):创建文件输出流以指定的名称写入文件
        FileOutputStream fos1 = new FileOutputStream("fos1.txt");
        FileOutputStream fos2 = new FileOutputStream("fos2.txt");
        FileOutputStream fos3 = new FileOutputStream("fos3.txt");
        //new File (name)
        /*FileOutputStream fos = new FileOutputStream(new File("fos.txt"));
        FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件
        File file = new File("fos.txt");

         */
       // FileOutputStream fos2 = new FileOutputStream(new File("fos2.txt"))
        //1.void write(int b),将指定的字节写入此文件输出流
        fos1.write(97);
        fos1.write(98);
        fos1.write(99);
        fos1.write(100);//abcd
        //2.void write(byte[] b),将b.length字节从指定的字节数组写入此文件输出流
        byte[] bytes = {97,98,99,100};
        fos2.write(bytes);//abcd
        byte[] bytes1 = "efgh".getBytes();
        fos2.write(bytes1);//abcdefgh
        //3.void write(byte[] b,int off,int len),将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流
        fos3.write(bytes,0,bytes.length);//abcd
        //释放资源
        fos1.close();
        fos2.close();
        fos3.close();


    }
}

2.4字节流写数据的两个小问题

  • 字节流写数据如何实现换行呐?

    windows:\r\n
    linux:\n
    mac:\r

  • 字节流写数据如何实现追加写入呐?

public FileOutputStream(String name,boolean append) throws FileOutputStream创建文件输出流以指定的名称写入文件,如果第二个 参数为true,则字节将写入文件的末尾而不是开头

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo03 {
    public static void main(String[] args) throws IOException {
        /**
         * - 字节流写数据如何实现换行呐?
         *windows:\r\n
         * linux:\n
         * mac:\r
         * - 字节流写数据如何实现追加写入呐?
         * public FileOutputStream(String name,boolean append)
         * throws FileOutputStream创建文件输出流以指定的名称写入文件,如果第二个
         * 参数为true,则字节将写入文件的末尾而不是开头
         */
        //创建字节输出流对象
        //FileOutputStream fos = new FileOutputStream("fos.txt");
        FileOutputStream fos = new FileOutputStream("fos.txt",true);
        //写数据
        for (int i = 0; i < 10; i++) {
            fos.write("hello".getBytes());
    //hellohellohellohellohellohellohellohellohellohello
            fos.write("\n".getBytes());//换行输出,但用记事本打开仍没有换行
            fos.write("\r\n".getBytes());//可行
        }
        //释放资源
        fos.close();
    }
}

2.5字节流写数据加异常处理

finally:在异常处理时提供finally块来执行所有清楚操作。比如说IO流中的释放资源。

特点:被finally控制的语句一定会执行,除非JVM退出。

try{

​ 可能出现异常的代码;

}catch(异常类名 变量名){

​ 异常的处理代码;

}finally{

​ 执行所有清楚操作;

}

2.6字节流读数据(一次读一个字节数据)

需求:把文件fos.txt中的内容读取出来在控制台输出

FileInputStream:从文件系统中的文件获取输入字节。

  • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名。

使用字节输入流都数据的步骤:

1.创建字节输入流对象

2.调用字节输入流对象的读数据方法

3.释放资源

package exercise01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStream01 {
    public static void main(String[] args) throws IOException {
        //创建字节输入流对象
        FileInputStream fis  = new FileInputStream("fos1.txt");
        //调用字节输入流对象的读数据方法,int read():从该输入流读取一个字节的数据
       /** int by = fis.read();
        System.out.println(by);
        System.out.println((char)(by));
        //再多读取两次
        by = fis.read();
        System.out.println(by);
        by = fis.read();
        System.out.println(by);//如果达到文件末尾会输出:-1
*/
       /**
       int by = fis.read();
       while(by != -1){
           System.out.print((char)(by));
           by = fis.read();
       }
        */
       int by;
       while((by = fis.read()) != -1){
           System.out.println((char)by);
       }
        //释放资源
        fis.close();

    }

}

2.7字节流复制文本文件

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件的复制
 */
public class CopyDemo {
    public static void main(String[] args) throws IOException {
        //创建文件输入流读取原文件
        FileInputStream fis = new FileInputStream("image.jpg");
        //创建文件输出流写入复制文件
        FileOutputStream fos = new FileOutputStream("image_cp.jpg");

        int d;//保存每次读取到的字节
        /*
            原文件数据:
            11000011 10101010 00001111 11001100 00110011 ...
                     ^^^^^^^^
            d = fis.read();
            d:00000000 00000000 00000000 10101010
            fos.write(d);
            复制文件的数据:
            11000011 10101010
         */
        long start = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间)
        while((d = fis.read()) != -1) {
            fos.write(d);
        }

        long end = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间)
        System.out.println("复制完毕!耗时:"+(end-start)+"ms");
        fis.close();
        fos.close();
    }
}

需求:把E:\File\斗罗大陆.txt复制到模块目录下的“斗罗大陆.txt”.

分析:

1.复制文本文件,其实就把文本文件的内容从一个文件中读取出来(读数据),然后写入到另一个文件中(目的地);

2.数据源:E:\File\斗罗大陆.txt-----读数据----InputStream----FileInputStream.

3.目的地:\斗罗大陆.txt-----写数据—OutputStream----FileOutputFile.

思路:

1.根据数据源创建字节输入流对象;

2.根据目的地创建字节输出流对象;

3.读写数据,复制文本文件(一次读取一个字节,一次写入一个字节);

4.释放资源。

package exercise01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyTxtDemo {
    public static void main(String[] args) throws IOException {
        /**
         * 1.根据数据源创建字节输入流对象;
         *
         * 2.根据目的地创建字节输出流对象;
         *
         * 3.读写数据,复制文本文件(一次读取一个字节,一次写入一个字节);
         *
         * 4.释放资源。
         */
        //1.根据数据源创建字节输入流对象;
        FileInputStream fis = new FileInputStream("E:\\File\\斗罗大陆.txt");
        //2.根据目的地创建字节输出流对象;
        FileOutputStream fos = new FileOutputStream("斗罗大陆.txt");
        //3.读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
        int by;
        while((by = fis.read())!= -1){
            fos.write(by);

        }
        //4.释放资源。
        fis.close();
        fos.close();

    }
}

2.8字节流读数据(一次读一个字节组数据)

需求:把文件fos.txt中的内容读取出来在控制台输出

1.创建字节输入流对象

2.调用字节输入流对象的读数据方法

3.释放资源

package exercise01;

import com.sun.org.apache.xpath.internal.operations.String;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 需求:把文件fos.txt中的内容读取出来在控制台输出
 *
 * 1.创建字节输入流对象
 *
 * 2.调用字节输入流对象的读数据方法
 *
 * 3.释放资源
 */
public class FileInputStream02 {
    public static void main(String[] args) throws IOException {
        //1.创建字节输入流对象
        FileInputStream fis = new FileInputStream("fos.txt");
       //2.调用字节输入流对象的读数据方法
        //int read(byte[] b):从该输入流读取最多b.length个字节的数据到下一个字节数组
        byte[] bys = new byte[1024];
        int len;
        while((len = fis.read())!=-1){
            System.out.println(new String(bys,0,len));

        }

        fis.close();


    }
}

2.9块读写的文件复制操作

int read(byte[] data)一次性从文件读取给定的字节数组总长度的字节量,并存入到该数组中。返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。

块写操作void write(byte[] data)一次性将给定的字节数组所有字节写入到文件中。

void write(byte[] data,int offset,int len)一次性将给定的字节数组从下标offset除开始的连续len个字节写入文件。

package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 通过提高每次读写的数据量,减少实际读写的次数,可以提高读写效率。
 * 单字节读写是一种随机读写形式。而一组一组字节的读写是块读写形式。
 */
public class CopyDemo2 {
    public static void main(String[] args) throws IOException {
        //使用块读写形式完成文件复制
        //创建文件输入流读取文件
        FileInputStream fis = new FileInputStream("1.mp4");
        //创建文件输出流写入文件
        FileOutputStream fos = new FileOutputStream("10.mp4");
        /*
            流提供了块读写的方法
            int read(byte[] data)
            一次性从文件读中读取给定的字节数组总长度的字节量,并存入到该数组中。
            返回值为实际读取到的字节量。若返回值为-1则表示读取到了末尾。
            文件数据
            11001100 11110000 10101010 00001111 00110011
            ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
            int d;
            byte[] data = new byte[3];
            [00000000 00000000 00000000]
            第一次调用
            d = fis.read(data);
            [11001100 11110000 10101010]
            d = 3 本次读取到了3个字节

            文件数据
            11001100 11110000 10101010 00001111 00110011
                                       ^^^^^^^^ ^^^^^^^^
            第二次调用
            d = fis.read(data);//仅读取了最后两个字节
            [00001111 00110011 10101010]//前两个字节为本次读取的内容
             ^^^^^^^^ ^^^^^^^^
            d = 2 本次读取到了2个字节

            文件数据
            11001100 11110000 10101010 00001111 00110011 文件末尾!
                                                         ^^^^^^^^
            第三次调用
            d = fis.read(data);//一个字节都没有读取到
            [00001111 00110011 10101010]数组没变化
            d = -1 文件末尾

            块写操作
            void write(byte[] data)
            一次性将给定的字节数组所有字节写入到文件中

            void write(byte[] data,int offset,int len)
            一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
         */
        int len;
        byte[] data = new byte[1024*10];
        while((len = fis.read(data))!=-1){
            fos.write(data,0,len);
            
        }
        System.out.println("写出完毕");
        fis.close();
        fos.close();
    }
}

2.10 写文本数据

String提供方法:byte[] getBytes(String charsetName),将当前字符串转换为一组字节。

参数为字符集的名字,常用的是UTF-8。其中中文3字节表示1个,英文1字节表示一个。

package io;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

/**
 * 向文件中写入文本数据
 */
public class WriteStringDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("demo.txt");
        String str = "super idol的笑容都没你的甜";
        /*
            支持中文的常见字符集有:
            GBK:国标编码。英文每个字符占一个字节,中文每个字符占两个字节
            UTF-8:内部是unicode编码,在这个基础上少了部分2进制信息作为长度描述
                    英文每个字符占1字节
                    中文每个字符占3个字节
            String提供了将字符串转换为一组字节的方法:
            byte[] getBytes(String charsetName)
            参数为字符集的名字,名字不区分大小写,但是拼写错误会引发异常:
            UnsupportedEncodingException
             不支持       字符集   异常
         */
        byte[] data = str.getBytes(StandardCharsets.UTF_8);
        fos.write(data);
        fos.write("正午的阳光".getBytes(StandardCharsets.UTF_8));
        System.out.println("写入完毕");

    }

}

2.11文件输出流——追加模式

  • FileOutputStream(String path,boolean append)
  • FileOutPutStream(File file,boolean append)

当第二个参数传入true时,文件流为追加模式,即:指定的文件若存在,则原有数据保留,新写入的数据会被顺序的追加到文件中。

package io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 文件流的追加模式
 */
public class FileAppendDemo {
    public static void main(String[] args) throws IOException {
        /*
            FileOutputStream默认创建方式为覆盖模式,即:如果连接的文件存在,则会将该
            文件原有数据全部删除。然后将通过当前流写出的内容保存到文件中。

            重载的构造方法允许我们在传入一个Boolean型参数,如果这个值为true,则文件流为追加模式,
            即:若连接文件时该文件存在,原有数据全部保留,通过当前流写出的数据会顺序追加到文件中。

         */
        FileOutputStream fos = new FileOutputStream("demo.txt",true);
        fos.write("化学".getBytes(StandardCharsets.UTF_8));
        fos.write("数学".getBytes(StandardCharsets.UTF_8));
        fos.write("语文".getBytes(StandardCharsets.UTF_8));
        System.out.println("写入完毕");
        fos.close();


    }
}

2.12 读取文本数据

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 从文件中读取文本数据
 */
public class ReadStringDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("demo.txt");
        byte[] data = new byte[1024];
        int len = fis.read(data);
        System.out.println("读取到了"+len+"字节");//读取到了79字节
        /*
            String 提供了将字符数组转换为字符串的构造方法:
            String(byte[]data,String charsetName)将给定的字节数组
            中所有字节按照指定的字符集转换为字符串

            String(byte[]data,int offset,int len,String charsetName)
            将给定的字节数组从下标offset出开始的连续len个字节按照指定的字符集转换为字符串
         */
        String line = new String(data ,0,len,"UTF-8");
        System.out.println(line);
        System.out.println(line.length());//33

        fis.close();
    }
}

2.13高级流

连接示意图

在这里插入图片描述

缓冲流

java.io.BufferedOutputStream和java.io.BufferedInputStream

缓冲流是一对高级流,作用是提高读写数据的效率。

缓冲流内部有一个字节数组,默认长度为8K。缓冲流读写数据时一定是将数据的读写方式转换为块读写来保证读写效率。

使用缓冲流完成文件复制操作

在这里插入图片描述

package io;

import java.io.*;

/**
 * java将流分为节点流与处理流两类。
 * 节点流:也称为低级流,是真实连接程序与另一端的“管道”,负责实际读写数据的流。
 *        读写一定是建立在节点流的基础上进行的。
 * 处理流:也称高级流,不能独立存在,必须连接在其他流上,目的是当数据经过当前流时对其
 *        进行某种加工处理,简化我们对数据的同等操作。
 * 实际开发中,我们经常会串联一组高级流最终连接到低级流上,在读写操作时以流水线式的加工完成复杂的IO操作。这个过程也称为流的连接。
 *
 * 缓冲流,是一对高级流,作用是加快读写效率。
 * BufferedInputStream 和 BufferedOutputStream
 *
 */
public class CopyDemo3 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("1.mp4");
        BufferedInputStream bis = new BufferedInputStream(fis);
        FileOutputStream fos = new FileOutputStream("2.mp4");
        BufferedOutputStream bos = new BufferedOutputStream(fos);


        int d;
        long start = System.currentTimeMillis();
        while((d = bis.read())!=-1){//使用缓冲流读取字节数据
            fos.write(d);//使用缓冲流写出字节
        }
        long end = System.currentTimeMillis();
        System.out.println("写出完毕的时间:"+(end-start));
        bos.close();//关闭流时只需要关闭高级流即可,它会自动关闭它连接的流
        bis.close();


    }
}
缓冲输出流写出数据时的缓冲区问题

通过缓冲流写出的数据会被临时存入缓冲流内部的字节数组,直到数组存满数据才会真实写出一次。

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 缓冲输出流写出数据的时效性问题(缓冲区问题)
 */
public class BOSDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bos.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        String line = "你好,java。";
        byte[] bytes = line.getBytes(StandardCharsets.UTF_8);
        bos.write(bytes);
        /*
            void flush()
            将缓冲流的缓冲区以缓存的数据一次性写出。
            注:频繁调用该方法会提高写出的次数,降低写出效率。但是可以换来写出数据的即时性。
            
         */
        bos.flush();
        System.out.println("写出完毕");
        /*
            缓冲流的close操作中会自动调用一次flush,确保所有缓存的数据会被写出
         */
        bos.close();

    }
}

对象流

java.io.ObjectOutputStream和ObjectInputStream

对象流时一对高级流,在流的连接中的作用是对对象的序列化与反序列化。

对象序列化:将一个java对象按照其结构转换为一组字节的过程。

对象反序列化:将一组字节还原为java对象(前提是这组字节是一个序列化得到的字节)

对象序列化的流连接操作原理图:

在这里插入图片描述

序列化:

package io;

import java.io.Serializable;
import java.util.Arrays;

/**
 * 使用当前类测试对象流的序列化和反序列化操作
 * 对象序列化:将一个java对象按照其结构转换为一组字节的过程
 */
public class Person implements Serializable {
    /*
    当一个类实现了可序列化接口时,最好显示的定义下面的属性:serialVersionUID
    即:序列化版本号
    当对象输出流在进行对象序列化时,会查看是否有显示的定义版本号,如果有则会根据当前类的结构
    计算版本号(当前类(这里是Person)的结构只要没有发生过变化,那么无论任何序列化版本号
    始终不会变化,只要改变过,那么序列化版本号一定会改变)并将该版本号保存在序列化后的字节中
     重点:
     当使用对象输入流反序列化时,对象输入流会检查要反序列化的对象与其他对应的类(比如我们
     反序列化一个之前的person对象)的版本号是否一致,若不一致,则会抛出异常:
     java.io.InvalidClassException
     比如OOSDemo序列化一个Person对象并写入文件person.obj后。我们在Person上添加一个新的
     属性salary,此时Person类发生了变化,那么再使用OISDemo反序列化person.obj文件中之前序列化的
     对象就会发生异常InvalidClassException(因为Person类改变了结构,版本号不一致了)。

     如果后期修改了类的结构,又希望原来的对象还可以进行反序列化,则需要显示的定义出来序列化版本号,这样一来
     ,当一个对象序列化后,当前类结构改变了,只要版本号不变,那么之前的对象可以进行反序列化。
     此时对象输入流会采取兼容方式,即:能还原的属性都会进行还原,没有的属性则采用默认值
     */
    static final long serialVersionUID = 1L;
    private String name;//姓名
    private int age;//年龄
    private String gender;//性别
    private String[] otherInfo;//其他信息
   //private int salary;//工资
    /*
    当一个属性被关键字transient修饰后,那么当进行对象序列化时,该属性的值会被忽略不必要的属性可以达到
    对象“瘦身”的目的,提高程序响应速度,减少资源开销。
    当然,反序列化时,该属性只会采用默认值。
    transient:短暂的,转瞬即逝
     */

    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;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String[] getOtherInfo() {
        return otherInfo;
    }

    public void setOtherInfo(String[] otherInfo) {
        this.otherInfo = otherInfo;
    }

    public Person(String name, int age, String gender, String[] otherInfo) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.otherInfo = otherInfo;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", otherInfo=" + Arrays.toString(otherInfo) +
                '}';
    }

}




package io;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/**
 * 对象流
 * java.io.ObjectOutputStream和ObjectInputStream
 * 对象流是一对高级流,在流的连接中完成对象与字节的转换,即:对象序列化与反序列化操作
 * 有了它们我们可以轻松读写任何java对象
 */
public class OOSDemo {
    public static void main(String[] args) throws IOException {
        String name = "苍老师";
        int age = 28;
        String gender = "男";
        String [] otherInfo = {"是一名演员","来自霓虹","爱好书法"};
        //重写toString方法,否则输出对象名为类名@地址
        //未重写:io.Person@232204a1  类名@地址
        Person p = new Person(name,age,gender,otherInfo);
        System.out.println(p);//输出p.toString()返回值
        //Person{name='苍老师', age=28, gender='男', otherInfo=[是一名演员, 来自霓虹, 爱好书法]}
        //将Person对象写入文件Person.obj中
        FileOutputStream fos = new FileOutputStream("./Person.obj");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        /*
            对象输出流提供的独有方法:writeObject(Object obj)
            该方法会进行对象序列化,并将序列化后的字节通过其连接的流写出

            序列化时要求该对象必须实现可序列化接口,否则会抛出异常
            java.io.NotSerializableException
         */
        oos.writeObject(p);
        System.out.println("写出完毕");
        oos.close();
    }
}


对象反序列化:

package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * 使用对象输入流进行对象的反序列化
 */
public class OISDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream("Person.obj");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //抛出异常的操作:alt+enter(回车)
        Person p  = (Person) (ois.readObject());
        System.out.println(p);
        ois.close();
    }
}

字符流

  • java将流按照读写单位划分为字节流和字符流。
  • java.io.InputStream和java.io.OutputStream是所有字节流的超类
  • 而java.io.Reader和Writer是所有字符流的超类,他们和字节流的超类是平级关系。
  • Reader和Writer是两个抽象类,里面规定所有字符流都必须具备的读写字符的相关方法。
  • 字符流最小读写单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换工作由字符流完成。
转换流

java.io.InputStreamReader和OutputStreamWriter

他们是字符流非常常用的一对实现类同时也是一对高级流,实际开发中我们不直接操作他们,但是他们在流连接中是非常重要的一环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9Ehs2jH-1644756927773)(E:\达内\图片\转换输出流写数据.png)]

使用转换输出流写文本文件

package io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

/**
 * 字符流
 * 字符流的最小读取单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换
 * 工作由字符流完成。
 * 转换流
 * java.io.InputStreamReader和OutputStreamWriter
 */
public class OSWDemo {
    public static void main(String[] args) throws IOException {
        //向文件osw.txt中写入文字
        FileOutputStream fos = new FileOutputStream("osw.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("我可以接受你的所有,所有小脾气。");
        osw.write("我可以带你去吃很多,很多好东西。");
        System.out.println("写出完毕");
        osw.close();

    }
}

使用转换输入流读取文本文件

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 转换字符输入流
 * 可以将读取的字节按照指定的字符集转换为字符
 */
public class ISRDemo {
    public static void main(String[] args) throws IOException {
        //将osw.txt文件中的所有文字读取回来
        FileInputStream fis = new FileInputStream("osw.txt");
        InputStreamReader isr = new InputStreamReader(fis);
        /*
            字符流读一个字符的read方法定义:
            int read()
            读取一个字符。返回的int值实际上表示的是一个char(低16位有效),如果返回的int
            值表示的是-1则说明EOF
         */
        //循环将文件所有字符读取回来
        int d;
        while((d = isr.read())!=-1){
            System.out.print((char)d);

        }
        isr.close();
    }
}

转换流的意义:

实际开发中我们还有功能更好用的字符高级流,但是其他的字符高级流都有一个共同点:不能直接连接在字节流上。而实际操作设备的流都是低级流。同时也都是字节流。因此不能直接在流连接中串联起来。转换流是一对可以连接在字节流上的字符流。其他的高级字符流可以连接在转换流上,在流连接中起到”转换器“的作用(负责字符与字节的实际转换)。

缓冲字符流

缓冲字符输出流:java.io.PrintWriter

java.io.BufferedWriter和BufferedReader

缓冲字符流内部也有一个缓冲区,读写文本数据以块读写形式加快效率,并且并且缓冲流有一个特别的功能:可以按行读写文本数据.

java.io.PrintWriter具有自动行刷新的缓冲字符输出流,实际开发中更常用.它内部总是会自动连接BufferedWriter作为块写加速使用.

在这里插入图片描述

package io;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

/**
 * 转换字符流
 * 缓冲字符流是一对高级流,在流连接中的作用是提高块读写文本数据的效率,并且可以按行读写字符串。
 * java.io.BufferedReader和java.io.BufferedWriter
 *
 * 实际开发中缓冲字符输出流我们更常用于PrintWriter,具有自动行刷新功能的
 * 缓冲字符输出流,其内部总是连接BufferedWriter作为缓冲加速使用
 *
 */
public class PWDemo {
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        /*
            PrintWriter提供了对文件操作的构造方法:
            PrintWriter(String path)
            PrintWriter(File file)
             以上两种构造器都支持一个重载,允许再传入一个String类型的参数来指定字符集。
            需要注意字符集名字的拼写,大小写均可,但是拼写错误会抛出异常:
            UnsupportedEncodingException
            Unsupported:不支持
            Encoding:编码
         */
        //向文件中写入字符串
        PrintWriter pw = new PrintWriter("pw.txt","UTF-8");
        pw.println("我看过沙漠下暴雨");
        pw.println("看过大海亲吻鲨鱼");
        pw.println("看过黄昏追逐黎明");
        pw.println("没看过你");
        System.out.println("写出完毕");
        pw.close();
    }
}

流链接中使用PW

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * 在流连接中使用PW
 */
public class PWDemo2 {
    public static void main(String[] args) throws FileNotFoundException {
        //文件字节输出流(是一个字节流),向文件中写入字节数据
        FileOutputStream fos = new FileOutputStream("pw2.txt",true);
        //转换输出流(是一个高级流,且是一个字符流)1.衔接字节流2.将字符转换位字节
        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
        //缓冲输出流(是一个高级流,且是一个字符流)。块读写数据加速
        BufferedWriter bw = new BufferedWriter(osw);
        //具有自动行刷新的缓冲字符流
        PrintWriter pw = new PrintWriter(bw);

        //完成简易记事本。控制台输入的每行字符串都执行写入文件。单独输入exit是推出。
        Scanner scanner = new Scanner(System.in);
        while(true){
            String line = scanner.nextLine();
            if("exit".equalsIgnoreCase(line)){
                break;

            }
            pw.println(line);

        }
        pw.close();



    }
}

PrintWriter的自动行刷新功能

如果实例化PW时第一个参数传入的是一个流,则此时可以在传入一个boolean型的参数,此值位true时就打开了自动行刷新,每当我们应PW的println方法写出一行字符串后就会自动flush.

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * 在流流连接中使用PW
 */
public class PWDemo3 {
    public static void main(String[] args) throws FileNotFoundException {
        //文件字节输出流(低级流),向文件中写入数据
        FileOutputStream fos = new FileOutputStream("pw3.txt",true);
        //转换输出流(一个高级流,且是一个字符流)。1.衔接字符与字节流2.将字符流转换位字节流
        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
//        OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
        //缓冲字符流(高级流)。块写文本数据加速。
        BufferedWriter bw = new BufferedWriter(osw);
        //加上true具有了自动行刷新功能
        PrintWriter pw = new PrintWriter(bw,true);
        //完成简易记事本
        Scanner scanner = new Scanner(System.in);
        while(true){
            String line = scanner.nextLine();
            if("exit".equalsIgnoreCase(line)){
                break;
            }
            pw.println();

        }
        pw.close();



    }
}

缓冲字符输入流:java.io.BufferedReader

是一个高级字符流,特点是块读文本数据,并且可以按行读取字符串。

package io;

import java.io.*;

/**
 * 使用java.io.BufferedReader按行读取数据
 */
public class BRDemo {
    public static void main(String[] args) throws IOException {
        //将当前源程序读取出来并输出到控制台上
        FileInputStream fis = new FileInputStream("pw2.txt");
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        String line;
        /*
            BufferedReader提供了一个读取一行字符串的方法:
            String readLine()
            该方法会返回一行字符串,返回的字符串不含有最后的换行符。
            当某一行是空行时(该行内容只有一个换行符),则返回值为空字符串。
            如果读取到了末尾,则返回值为null.
         */
        while((line = br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fE8RQtl5-1644756927774)(E:\达内\图片\IO总结.png)]
加上true具有了自动行刷新功能
PrintWriter pw = new PrintWriter(bw,true);
//完成简易记事本
Scanner scanner = new Scanner(System.in);
while(true){
String line = scanner.nextLine();
if(“exit”.equalsIgnoreCase(line)){
break;
}
pw.println();

    }
    pw.close();



}

}


 **缓冲字符输入流:java.io.BufferedReader**

是一个高级字符流,特点是块读文本数据,并且可以按行读取字符串。

```java
package io;

import java.io.*;

/**
 * 使用java.io.BufferedReader按行读取数据
 */
public class BRDemo {
    public static void main(String[] args) throws IOException {
        //将当前源程序读取出来并输出到控制台上
        FileInputStream fis = new FileInputStream("pw2.txt");
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        String line;
        /*
            BufferedReader提供了一个读取一行字符串的方法:
            String readLine()
            该方法会返回一行字符串,返回的字符串不含有最后的换行符。
            当某一行是空行时(该行内容只有一个换行符),则返回值为空字符串。
            如果读取到了末尾,则返回值为null.
         */
        while((line = br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值