Java IO流基础

IO流的概念:

先来看一下百度百科对于IO流的解释:

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。

换句话说,IO流就是以流的方式进行输入输出

IO流的分类:

  1. 根据数据流向划分:输入流和输出流

     输入流只能进行写操作
     输出流只能进行读操作
    复制代码
  2. 根据数据操作的数据单元划分:字节流和字符流

     字节流以字节为单位,字符流以字符为单位
     字节流能处理大部分类型的数据(图片,视频),字符流处理字符类型的数据
    复制代码
  3. 根据操作对象不同划分:节点流和处理流

     可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流
     节点流是可以直接连接数据源的流
     
     处理流是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能
     处理流是节点流使用装饰者模式增加更多的功能
    复制代码

IO流的四大基类

  • InputStream

InputStream 是所有的输入字节流的父类,它是一个抽象类。

主要包含三个方法:

//读取一个字节并以整数的形式返回(0~255),如果返回-1说明已到输入流的末尾。
int read();
//读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
int read(byte[] buffer);
//读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。
int read(byte[] buffer, int off, int len);
复制代码
  • OutputStream

OutputStream 是所有的输出字节流的父类,它是一个抽象类。

主要包含如下四个方法:

//向输出流中写入一个字节数据,该字节数据为参数b的低8位。
void write(int b) ;
//将一个字节类型的数组中的数据写入输出流。
void write(byte[] b);
//将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。
void write(byte[] b, int off, int len);
//将输出流中缓冲的数据全部写出到目的地。
void flush();
复制代码
  • Reader

Reader 是所有的输入字符流的父类,它是一个抽象类。

主要包含三个方法:

//读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read() ;
//读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。
int read(char[] cbuf) ;
//读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。
int read(char[] cbuf, int off, int len)
复制代码
  • Writer

Writer 是所有的输出字符流的父类,它是一个抽象类。

主要包含如下六个方法:

//向输出流中写入一个字符数据,该字节数据为参数b的低16位。
void write(int c);
//将一个字符类型的数组中的数据写入输出流,
void write(char[] cbuf)
//将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。
void write(char[] cbuf, int offset, int length);
//将一个字符串中的字符写入到输出流。
void write(String string);
//将一个字符串从offset开始的length个字符写入到输出流。
void write(String string, int offset, int length);
//将输出流中缓冲的数据全部写出到目的地。
void flush()
复制代码

Writer比OutputStream多出两个方法,主要是支持写入字符和字符串类型的数据

节点流(文件流)

FileReader是Reader的子类,也有read的三个方法

  1. 使用int read()方法
@Test
    public void FileReaderTest() throws IOException{

        //1.实例化File类的对象
        File file = new File("文件路径");
        //2.提供具体的流
        FileReader fr=new FileReader(file);
        //3.数据的读入
        int data=fr.read();
        while (data!=-1){
            System.out.print((char)data);
            data=fr.read();
        }
        //4.关闭流
        fr.close();
    }
复制代码
  1. 使用int read(char [] cbuf)方法
            //3.读入操作
            //read(char[] cbuf):返回每次读入cbuf数组的字符的个数
            char[] cbuf =new char[5];
            int len;
            while((len=fr.read(cbuf))!=-1){
                //下面不能使用 i<cbuf.length
                for(int i=0;i<len;i++){
                    System.out.print(cbuf[i]);
                }
            }
复制代码

当以上代码如果for循环中判断条件是:i<cbuf.length时,

分析:定义的cbuf数组每次存放5个字符,fr的读取操作就是相当于不断将数据读入到数组中,而这个我们遍历的时候每次都是打印出数组的所有内容(即5个字符)。

当文件内容是HelloWorld123时,输出的数据是HelloWorld123ld

原因就是最后读取的数据没有将ld覆盖

  1. 使用int read (char[] cbuf,int off,int len)方法
        int read (cbuf,0,len)
复制代码

使用FileReader和FileWriter实现文件复制

    @Test
    public void FileReaderFileWriteTest(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File对象
            File srcFile = new File("源文件地址");
            File destFile = new File("目标文件地址");
            //2.创建输入流输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);
            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中字符的个数
            while((len=fr.read(cbuf))!=-1){
                fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if(fr!=null){
                fr.close();}
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fw!=null){
                fw.close();}
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
复制代码

FileInputStream和FileOutputStream一般用来处理非文本文件,其他使用步骤与上述一致

缓冲流

作用:提高流读取,写入的速度

使用缓冲流实现复制

public void BufferedStreamTest(){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1.造文件
            File srcfile=new File("");
            File destfile=new File("");
            //2.造流
            FileInputStream fis = new FileInputStream(srcfile);
            FileOutputStream fos = new FileOutputStream(destfile);

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

        }

    }
复制代码

转换流

作用:提供了字节流和字符流之间的转换

InputStreamReader:将字节输入流转为字符输入流,继承自Reader

OutputStreamWriter: 将字符输出流转为字节输出流,继承自Writer

计算机中存储的数据都是二进制的数字,我们在电脑屏幕上看到的文字信息是将二进制转换之后显示的,两者之间存在编码与解码的过程,其互相转换必须遵循某种规则,即编码和解码都遵循同一种规则才能将文字信息正常显示,如果编码跟解码使用了不同的规则,就会出现乱码的情况。

  • 编码:字符、字符串–>字节
  • 解码:字节–>字符、字符串

image.png
InputStreamReader构造方法

  • InputStreamReader(InputStream in):创建一个默认字符集字符输入流
  • InputStreamReader(InputStream in, String charsetName):创建一个指定字符集的字符流

OutputStreamWriter构造方法

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
  • OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

转换流参考博客

对象流

ObjectInputStream
ObjectOutputStream

可以把Java中的对象写入到数据源中,也能把对象从数据源中还原出来

序列化:用ObjectOutputStream类保存基本数据类型或对象的机制

反序列化:用ObjectInputStream类读取基本数据类型或对象的机制

在传递和保存对象时.为了保证对象的完整性和可传递性:要保证对象是序列化的

实现序列化:

  1. 对象实现Serializable 接口
  2. 将对象写入文件
  3. 将文件读取出来转换为对象

问题:

  1. static 属性不能被序列化
  2. Transient 属性不会被序列化
  3. 序列化版本号serialVersionUID

JDK工具生成的serialVersionUID 是根据对象的属性信息生成的一个编号,这就意味着只要对象的属性有一点变动那么他的序列化版本号就会同步进行改变

序列化参考博客

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享