JAVA 文件与IO 学习总结(二)------流

一.前言

1.IO流概述

IO流分为输入输出流。
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在量设备间的传输称为流。
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观地进行数据操作

2.IO流的分类

根据处理数据类型的不同分为:字节流和字符流
根据数据流向不同分为:输入流和输出流

3.理解输入输出流两个概念

在java api中,可以从中读入一个字节序列的对象称为输入流,而可以向其中写入一个字节序列的对象称为输入流。

可以这样理解,输入流表示程序从一个源读取数据,输出流表示程序向一个目标写数据。
这两者可以认为是相对于程序来说的,程序向文件写入东西,是输出流,所以用OutputStream;程序从文件读取东西,是输入流,用InputStream。

4.IO流图示

这里写图片描述

如图所示,java的io体系其实不难,so,我们可以分几步来学习

①首先是各种字节输入流的超类InputStream && 字节输出流的超类OutputStream.
②接着是各种字符输入流的超类Reader && 字符输出流的超类Writer.
③然后是转换流InputStreamReader && OutputStreamWriter,这两个类用于字符字节流之间的转换。

二.字节流

1.字节输入输出流

①输出流
Outputstream类
public abstract class OutputStream implements Closeable, Flushable
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到Inputstream类的某个接收器中
向文件中输出,一般用他的子类FileoutputStream。
②输入流
Inputstream类
public abstract class InputStream implements Closeable
此抽象类表示输入字节流的所有类的超类。
从文件系统中的某个文件获得输入字节,一般用他的子类FileInputStream。

2.Outputstream类使用

/**
     * 简单字节输出流 || 写
     */
    private static void out() {
        try {
            //1.输出流对象,第二个位置为true,表示追加,不写则表示覆盖
            OutputStream os = new FileOutputStream(new File("C:\\Users\\sisheng\\Desktop\\out.txt"), true);
            //2.要输出的内容
            String message = "hello world\r\n"; //windows下,换行符号
            //3.输出到文件,写入文件
            os.write(message.getBytes());
            //4.一定要关闭流
            os.close();
            System.out.println("write success");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.Inputstream类使用

/**
     * 简单字节输入流 || 读
     */
    private static void in() {
        try {
            //1.输入流对象
            InputStream in = new FileInputStream(new File("C:\\Users\\sisheng\\Desktop\\out.txt"));
            //2.声明一个字节数组,相当于去运书的小车
            byte[] bytes = new byte[1024];   //1兆,这里注意是基本类型byte,不是包装类型Byte
            //3.声明一个长度变量作为输入流的长度,当长度为-1,说明读到底了
            int length = -1;
            //4.构造字符串存储读到的字节
            StringBuilder sb = new StringBuilder();
            java.lang.String s = new java.lang.String(bytes);
            //5.不断读取字节
            while (-1 != (length = in.read(bytes))) {
                sb.append(new java.lang.String(bytes,0,length)); 
            }
            System.out.println(sb);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意:
①变量length的作用:我们看到,in.read(bytes)返回的是输入流读取到的字节数目,当读取到尽头的时候返回-1,说明读取完毕。
②length的第二个作用:sb.append(new java.lang.String(bytes,0,length));,为什么将字节数组转成字符串时要指定长度呢。假设代码改成 byte[] bytes = new byte[10];
那么读入“学习是很快乐的”,那么第一次循环读到“学习是很快”10个字节,第二次循环自然读剩下的“乐的”这4个字节,但是由于是字节数组存储,第二次读完后bytes的前4个字节确实变了,但后6个字节保持不变,那么转换过来的字符串就是“乐的是很快”。
③字节流是没有缓存机制的,执行完write(),数据就会直接写入内存,但字符流有

4.字节输入流源码分析

①OutputStream.write()

public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

②上面的write方法

public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

可以看到,写入是按照一个一个字节来的

5.字节输出流源码分析

①Inputstream.read()

public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

②上面的read()

public int read(byte b[], int off, int len) throws IOException {
       ...
        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

③继续看上面的read()

public abstract int read() throws IOException;

这个就看不到源码了,老师说是底层用c或c++实现的。
所以,读取操作也是一个一个读的。

三.字符流

1.字符流使用:Writer and Reader

/**
     * 输入流 || 读
     */
    private static void in() {
        try {
            Reader r = new FileReader(new File("C:\\Users\\sisheng\\Desktop\\out.txt"));
            char[] chars = new char[1];
            int len = -1;
            StringBuilder sb = new StringBuilder();
            while (-1 != (len=(r.read(chars)))) {    //这里读也是一样的,自带缓存
                sb.append(new String(chars,0,len));
            }
            System.out.println(sb);
            r.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 输出流 || 写
     */
    private static void out() {
        try {
            Writer w = new FileWriter(new File("C:\\Users\\sisheng\\Desktop\\out.txt"), true);
            String message = ",追梦赤子心";
            w.write(message);   //这句话执行后就会将字符串缓存在内存,并没有真正写入文件里。Writer源码里有缓存大小变量为1024
            w.close();          //close之后会将缓存的数据全部写入文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.缓存机制

Writer的源码中有个变量指定了默认的缓存大小1024:private static final int WRITE_BUFFER_SIZE = 1024;也就是1兆,在执行完write方法后并没与直接写入文件。执行close()方法后会是缓存的数据全部写入文件。字节流则没有这种机制。

四.字符流与字节流的区别

在所有的流操作中,字节永远是最基础的。任何基于字节的操作都是正确的。无论你是文本文件还是二进制的文件。如果确认流里面只有可打印的字符,包括英文的和各个国家的文字,也包括中文,那么而已考虑用字符流。由于编码不同,多字节的字符可能占用多个字节。比如GBK的汉字就占用两个字节。而UTF8的汉字就占用三个字节。所以,字符流是根据指定的编码,将1个或者多个字节转化为java中的unicode字符,然后再进行操作。字符操作一般使用Writer,Reader等,字节操作一般都是Inputstream,OutputStream以及各种包装类,不如BufferedInputStream和BufferedOutputstream等。

总结:如果你确认要处理的流是可打印的字符,那么用字符流看上去会简单一些。如果不确认,用字节流总是没错的。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值