Java笔记 —— IO(概念,方法,实例)

IO的相关概念

IO:是用来解决设备之间数据传输的问题
流(Stream)是一个抽象、动态的概念,是一连串连续动态的数据集合,是一组有顺序的,有起点和终点的字节集合。流就像是一个连接了文件和程序的水管中的水,是对数据传输的抽象。
Java对数据的操作是通过流的方式。用于操作流的对象都在IO包中

按照流向进行划分:

  1. 输入流 InputStream
  2. 输出流 OuputStream

按照数据类型划分:

  1. 字节流 InputStream / OutputStream
    字节流以byte(字节)为单位,进行输入或输出操作
    InputStream字节输入流:读取数据。抽象类,是所有输入流的父类。定义了所有输入流都具有的共同特征。
    OutputStream字节输出流:写出数据。抽象类,是所有输出流的父类。定义了所有输出流都具有的共同特征。
  2. 字符流 Reader / Writer
    字符流以字符为单位,根据ASCII码表映射字符
    Reader字符输入流:读取数据,抽象类
    Writer字符输出流:写出数据,抽象类
什么时候使用字节流,什么时候使用字符流?

只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
文本文件,视频图片等在计算机中的存储方式都是字节,因此可以用windows自带的记事本打开需要上传或者下载的数据文件,如果里面的内容可以看懂,那么就用字符流。看不懂就用字节流。

字节输入流FileInputStream

FileInputStream是InputStream的子类,可以从文件流中读取数据。
这是jdk文档中对FileInputStream的作用的描述
在这里插入图片描述
构造方法:
FileInputStream(File file)
FileInputStream(String name)

读取方式:
  1. int read()
    从该输入流读取一个字节的数据。
    读取输入流的下一个字节,并返回字节表示的int值(0~255)。一个字节是八位,二进制的11111111转为十进制是255
  2. int read(byte[] b)
    从该输入流读取最多 b.length个字节的数据为字节数组。

文件里面的内容是a,读取的结果是97,因为字节流的单位是字节byte,是一组二进制的数据,传输的实际内容是对应的ASCII码值。如果想查看具体是哪个字符,可以用char类型做强制转换

package review.IO;

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

public class Demo1 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        System.out.println(fis.read()); //97
        System.out.println((char)fis.read()); //b,已经读取过的字节a不会被反复读取,因此是b


        //像上面这样读取太慢,用while循环改进
        //当读到末尾时,fis.read()语句返回-1
        int n = 0;
        while((n=fis.read())!=-1){
            System.out.print((char)n); //cdefg
        }
        //关闭流
		fis.close();
    }
}

package review.IO;

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

public class Demo2 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        //数组长度用1024,这样比较合适,不会太长浪费资源,也不会太短
        byte[] bytes = new byte[1024];
        int len;
        //用len接收fis.read(byte[] b)实际读取的字节长度
        //读到文件末尾时返回-1
        //(len =fis.read(bytes)注意这里用括号括住
        //这里不会读取一个字节就打印s,因为是一次性读取一个字节数组
        while((len =fis.read(bytes))!=-1){
            //这里的长度是从0到len,按实际读取的字节数将数组转为String类型
            String s = new String(bytes,0,len);
            System.out.println(s); //结果为abcdefg
        }
        fis.close();
    }
}

字节输出流 FileOutputStream

FileOutputStream是OutputStream的具体实现类
构造方法:
FileOutputStream(File file)
FileOutputStream(String name)

写入方式
  1. void write(int b) 将指定的字节写入此文件输出流。
  2. void write(byte[] b) 将 b.length个字节从指定的字节数组写入此文件输出流。
  3. write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
package review.IO;

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

public class Demo3 {
    public static void main(String[] args) throws IOException {
        //构造方法一,通常使用这种构造方式
        FileOutputStream fos = new FileOutputStream("b.txt");
        //构造方法二
        File f = new File("a.txt");
        FileOutputStream fos1 = new FileOutputStream(f);

        //write(int b)
        fos.write(97);
        fos.write(98);
        //写入换行符,否则写入的所有字符都在同一行
        fos.write("\r\n".getBytes());
        
        //getBytes()将字符串存入字符数组中
        //write(byte[] b)
        fos.write("hello java".getBytes());
        fos.write("\r\n".getBytes());

        byte[] bys = {97,98,99,100,101};
        //从数组的索引1位置读取三个字节写入文件
        fos.write(bys,1,3);

        fos.close();

    }
}

在这里插入图片描述

实现数据的追加

FileOutputStream(File file, boolean append)
FileOutputStream里面的append参数默认是false,即每次往文件中写入数据时,都会覆盖文件中原来的内容
将append参数改为true后,就会修改为往文件中原来的内容后面追加内容

 FileOutputStream fos = new FileOutputStream("b.txt",true);

将上面的代码里面,构造FileOutputStream对象的代码修改一下
结果为
在这里插入图片描述
在原来内容的后面追加,而不是覆盖原有的内容

字节缓冲输入流 BufferedInputStream

在我们之前用InputStream读取文件中的数据的时候就发现,用一个字节数组取读取,比每次单个读取一个个的字节要高效快速很多。这种思想被java用到了字节缓冲流里面,缓冲流的存在就是先将数据读取到缓冲流(内存中),然后一次性从内存中读取多个字符,从而提高读取的效率。

这里实际上用到了装饰者模式,详细内容可以看这篇博客
Java IO流之FilterInputStream和FilterOutputStream分析

BufferedInputStream本质上是通过一个内部缓冲区数组实现的。当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

构造方法:

  1. BufferedInputStream(InputStream in)
  2. BufferedInputStream(InputStream in, int size) 指定缓冲区大小size

这里的InputStream in参数,在创建对象时实际传入的是InputStream的具体实现类

package review.IO;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Demo4 {
    public static void main(String[] args)throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        int len = 0;
        byte[] bytes = new byte[1024];
        while((len=bis.read(bytes))!=-1){
            String s = new String(bytes,0,len);
            System.out.println(s); //读取结果为abcdefg
        }
        bis.close();
    }
}

字节缓冲输出流 BufferedOutputStream

flush()的作用:刷新缓冲区,将缓冲区的内容写入到文件中

flush()与close()的区别:
1、close()方法调用是关闭流对象,但是呢,会先刷新一次缓冲区,关闭之后,就不能继续使用该流对象了
2、flush()方法调用后,刷新一次缓冲区,但是流对象可以继续使用

使用缓冲输出流往文件里面写入数据的时候,flush和close至少有一个才能将数据写入文件,否则数据将一直存在缓冲区,直到缓冲区满,然后自动写入

package review.IO;

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

public class Demo5 {
    public static void main(String[] args) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
        bos.write("你好,hello".getBytes());
        //flush将缓冲区里面的内容直接输出,而不用等待缓冲区满
        bos.flush();
        bos.close();
    }
}

转换流

当文件内容中有中文的时候,如果我们只读取一个字节,然后输出这个字节,就会发现输出的结果是乱码
在这里插入图片描述

package review.IO;

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

public class Demo6 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        int b = 0;
        while((b=fis.read())!=-1){
            System.out.print((char)b);
        }

    }
}

读取的结果为
在这里插入图片描述
是一串乱码,这是因为中文占用的字节数不止一个,多个字节才能组成一个中文汉字。如果单独拆出一个字节,就会发现输出的就是乱码

正因为这样,字节流操作中文很不方便,所以需要用到字符流
字符流是建立在字节流之上的,它能够提供字符层次的编码和解码
转换流为我们建立了字节流到字符流的桥梁,其本身是一种处理流,提供了字节流和字符流之间的转换。
Java中提供了两个转换流:InputStreamReader 和 OutputStreamWriter,这两个类都属于字符流。
InputStreamReader将字节输入流转为字符输入流,继承自Reader。
OutputStreamWriter是将字符输出流转为字节输出流,继承自Writer。

在计算机中是以二进制的形式存储的数据,我们实际看到的文字信息都是讲二进制数据转换之后显示而来的。这两者之间存在着一种编码与解码的关系。
编码:类似与加密的过程,把能看懂的字符或字符串转成看不懂的字节。 String – byte[]
解码:类似于解密的过程,把看不懂的字节转成看得懂的字符或字符串。 byte[] – String

字符流其实可以理解为转换流。字符流=字节流+编码表。

package review.IO;

import java.io.IOException;
import java.util.Arrays;

public class Demo7 {
    public static void main(String[] args) throws IOException {
        String s = "你好世界";

        //byte[] getBytes(Charset charset)
        //使用给定的charset将该String编码为字节序列,将结果存储到新的字节数组中。
        //String -- byte[]
        byte[] bytes =s.getBytes("UTF-8");
        System.out.println(Arrays.toString(bytes));

        //String(byte[] bytes, Charset charset)
        //构造一个新的String由指定用指定的字节的数组解码charset
        //byte[] -- String
        String ss = new String(bytes,"UTF-8");
        System.out.println(ss);
    }
}

结果为
在这里插入图片描述
括号里面的UTF-8可以换成Unicode或者GBK等都可以,但是要注意编码和解码用的charset编码表要一样

字符输出流 OutputStreamWriter(字符转换流)

OutputStreamWriter写数据方法
public void write(int c)
public void write(char[] cbuf)
public void write(char[] cbuf,int off,int len)
public void write(String str)
public void write(String str,int off,int len)
注意:从字符流开始,一次性读取一个字符数组,而不是一个字节数组。用char[] 数组

package review.IO;

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

public class Demo8 {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));
        //write(int c)
        osw.write(97); //a

        //write(char[] cbuf) 写入一个字符数组
        char[] chars = {'a','b','c','d','e'};
        osw.write(chars); //abcde

        //write(char[] cbuf,int off,int len) 将字符数组中的一部分写到文件中
        char[] chars1 = {'a','b','c','d','e'};
        //从下标1开始读取两个字符
        osw.write(chars1,1,2); //bc

        //write(String str) 将字符串写入到文件中
        osw.write("hello"); //hello

        //write(String str,int off,int len) 将字符串的一部分写入到文件中
        String s2 = "hadoop";
        osw.write("hadoop",1,2); //ad

        osw.flush();
        osw.close();
    }
}

字符输入流 InputStreamReader
  1. InputStreamReader(InputStream in)
    读取数据,根据默认的编码将字节流转换为字符流
    创建一个使用默认字符集的InputStreamReader。
  2. InputStreamReader(InputStream in, String charsetName)
    读取数据,根据指定的编码将字节流转换为字符流
    创建一个使用指定字符集的InputStreamReader。

读取数据的方法:

  1. public int read():一次只读一个字符
  2. public int read(char[] cbuf):一次读取一个字符数组
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class Demo9 {
    public static void main(String[] args) throws IOException {
        //InputStream in = new FileInputStream("a.txt)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

        //一次只读取一个字符
        int ch = 0;
        while((ch=isr.read())!=-1){
            System.out.println((char)ch);
        }
        isr.close();
    }
}

结果为
在这里插入图片描述
可以发现控制台这里输出的是中文字符,而不再是int类型的数字

public class Demo9 {
    public static void main(String[] args) throws IOException {
        //InputStream in = new FileInputStream("a.txt)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

        //一次读取多个字符,也就是读取一个字符数组
        char[] chars = new char[1024];
        int len = 0;
        while((len=isr.read(chars))!=-1){
            System.out.println(new String(chars,0,len));
        }

        isr.close();

    }
}

在这里插入图片描述

字符转换流的简便写法

字符流 = 字节流 + 编码表
OutputStreamWriter = FileOutputStream + 编码表(Unicode)
InputStreamReader = FileInputStream + 编码表(Unicode)

字符输出流 OutputStreamWriter 简便写法 FileWriter
字符输入流 InputStreamReader 简便写法 FileReader

package review.IO;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt");
        FileWriter fw = new FileWriter("b.txt");

        int len = 0;
        char[] chars = new char[1024];
        while((len=fr.read(chars))!=-1){
            fw.write(chars,0,len);
            fw.flush();
        }
        
        fw.close();
        fr.close();
    }
}

结果成功将a.txt里面的内容写入到b.txt里面

注意:当程序中有多个流需要关闭的时候,按照从下到上的顺序关闭。
比如这里先打开的fr,后打开的fw。关闭的时候则先关fw后关fr

字符缓冲输入流 BufferedReader

字符流为了高效读写,也提供了相对应的字符缓冲流
BufferedWriter:字符缓冲输出流
BufferedReader:字符缓冲输入流

BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。

package review.IO;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Demo10 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        //一、一次读取一个字符
        int ch = 0;
        while((ch=br.read())!=-1){
            System.out.println((char)ch);
        }

        //二、一次读取一个字符数组
        int len = 0;
        char[] chars = new char[1024];
        while((len=br.read(chars))!=-1){
            System.out.println(new String(chars,0,len));
        }
        br.close();
    }
}

结果分别是(注意两段代码要分开运行,否则第二段代码读取内容为空,这里是为了直观所以放在了一起)
在这里插入图片描述
在这里插入图片描述

字符缓冲输出流 BufferedWriter

BufferedWriter:字符缓冲输出流
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。
默认值足够大,可用于大多数用途。

创建方式:

  1. BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流
  2. BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
package review.IO;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Demo12 {
    public static void main(String[] args) throws IOException {
        /*
            BufferedWriter bw = new BufferedWriter(
                       new OutputStreamWriter(new FileOutputStream("b.txt)));
            这是原来的写法,实在复杂,需要创建一个字节输出流FileOutputStream
            然后用OutputStreamWriter将字节输出流转为字符输出流
            再将字符输出流添加上缓冲区的功能变成BufferedWriter
         */

        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

        bw.write(97); //a
        bw.write(98); //b

        bw.write("hello"); //hello
        bw.write("hadoop",1,2); //ad

        char[] chars = {'a','b','c','d'};
        bw.write(chars); //abcd
        bw.write(chars,1,2); //bc

        bw.flush();
        bw.close();
    }
}

特殊方法

字符缓冲流的特殊方法:
BufferedWriter —— public void newLine()写一行行分隔符,用于换行
BufferedReader —— public String readLine()读一行文字,到换行符终止,然后转而读取下一行

package review.IO;

import java.io.*;

public class Demo13 {
    public static void main(String[] args) throws IOException {
		write();
        read();
    }

    public static void read() throws IOException{
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        String line = null;
        //(line=br.readLine())每次读取文件的一行给line,然后执行while循环体中的打印语句
        //所以每次打印一行内容
        while((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }

    public static void write() throws IOException{
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
        for(int i=0;i<10;i++){
            bw.write("学习大数据的第 "+i+" 天");
            //newLine()每写完一句就换到下一行
            bw.newLine();
            bw.flush();
        }
    }
}

运行write()方法的结果
在这里插入图片描述
使用read()方法的结果
在这里插入图片描述

序列化和反序列化

这篇博客字数已经太多了,为了方便查阅,我把这一部分内容又单独写了一篇博客
Java笔记 IO —— 序列化和反序列化(local class incompatible异常解决)

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一纸春秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值