Java基础之IO流

0.引言

  • 如果是操作二进制文件那我们就使用字节流,如果操作的是文本文件那我们就使用字符流。
  • 尽可能的多使用处理流,这会使我们的代码更加灵活,复用性更好。

0.1 IO流的概念

在java中,将不同的输入输出源(键盘\文件\网络通信连接 etc.)抽象为 *"流"(stream)* . 通过流的形式,使得允许java使用相同的方式访问不同的输入/输出源.
  • stream是从(起源)source到接收的(sink)的有序数据.
  • 所有传统的流型在 java.io 包下.用于实现输入输出功能.

0.2 分类

img

按操作对象分类结构图

字符流字节流

注: 在java中,中文通过Unicode编码。一个字符两个字节。

  • 字符流: 以字节为单位,每次读入或读出都是16为数据,可以读字符类数据,

  • 字节流: 以字节为单位,每次读入或读出都是8为数据,可以读任何类型数据,
    输入流输出流

  • 输入流: 从文件读入到内存。只能进行读操作。

  • 输出流: 从内存读出到文件 。只能进行写操作。

    节点流处理流

  • 节点流 : 直接与数据源相连,读入或读出

  • 处理流 : 与节点流一块使用,在节点流的基础上再套一层。套在节点流上的就是处理流。

1. 处理流 & 节点流

表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例.

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
特殊流DataInputStreamDataOutputStream

节点流

节点流执行图示

文件(节点流):

  • FileInputStream 字节输入流
  • FileOutputStream 字节输出流
  • FileReader 字符输入流
  • FileWriter 字符输出流

管道 (节点流):

  • PipedInputStream
  • PipedOutStream
  • PipedReader
  • PipedWriter
  • PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。

字节/字符数组流 ()节点流

  • ByteArrayInputStream
  • ByteArrayOutputStream
  • CharArrayReader
  • CharArrayWriter

处理流

处理流执行的图示

Buffered 缓冲流

带缓冲区的处理流,缓冲区的目的是避免每次和硬盘交互,降低 I/O 的次数,通过闪存提高数据访问的效率

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedReader
  • BufferedWriter

过滤流

在数据进行读写时进行过滤

  • FilterReader
  • FilterWriter

**转化流 **(Converting between Bytes and Characters )

  • InputStreamReader: 字节转化成字符
  • OutputStreamWriter: 字节转化成字符

**基本数据类型流 ** (DataConversion)

按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数))

因为平时若是我们输出一个8个字节的long类型或4个字节的float类型,那怎么办呢?可以一个字节一个字节输出,也可以把转换成字符串输出,但是这样转换费时间,若是直接输出该多好啊,因此这个数据流就解决了我们输出数据类型的困难。数据流可以直接输出float类型或long类型,提高了数据读写的效率。

  • DataInputStream
  • DataOutputStream

计数流 Counting

在读入数据时对行计数

  • LineNumberReader
  • LineNumberInputStream

预读流 (Peeking Ahead)

通过缓存机制,进行预读

  • PushbackReader
  • PushbackInputStream

打印流

包含方便的打印方法

  • PrintStream
  • PrintWriter

对象流 (Object Serialization)

把封装的对象直接输出,而不是一个个再转换成字符串然后输出

  • ObjectInputStream, 对象反序列化
  • ObjectOutputStream, 对象序列化

合并流

SequenceInputStream:可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取

why 处理流?

直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

2. 缓冲流

2.1 缓冲区

A buffer is a temporary holding area for data while it’s waiting to be transferred to another location. It is usually located in the RAM. The concept of the buffer was developed in order to prevent data congestion from an incoming to an outgoing port of transfer.

There are common uses for the buffer that help improve a device’s overall performance.

来源于此

数据从内存要写入磁盘中时,数据会被先写入到磁盘缓冲区,磁盘缓冲区满了再把数据写入磁盘。

磁盘缓冲区是为了平滑不同I/O设备的速度差。

来源于此

2.1.1 mark() & reset()

通俗的讲

mark(int readlimit) 就像书签,在缓冲流对应的buffer做一个标记,再readlimit的长度之前让系统保持该标记的有效(在图书馆看一本书,夹了一个书签,并且打算读完readlimit页后,从书签出再来看一遍)。读过这么多字符之后,系统可以使mark不再有效,而你不能觉得奇怪或怪罪它。这跟buffer有关,如果你需要很长的距离,那么系统就必须分配很大的buffer来保持你的mark。

Created with Raphaël 2.2.0 new a BufferedReader reader.mark(50) int a=reader.read() int b=reader.read() 省略了处理代码 reader.reset() reader.read() // 与a相同 int b=reader.read() // 与b相同

Demo

import org.junit.Test;

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

public class IoTest {

    public static void main(String[] args){

    }

    @Test
    public void markTest02(){
        try {
            // 初始化一个字节数组,内有10个字节的数据
            byte[] bytes={1,2,3,4,5,6,7,8,9,10};
            // 用一个ByteArrayInputStream来读取这个字节数组
            ByteArrayInputStream in=new ByteArrayInputStream(bytes);
            // 将ByteArrayInputStream包含在一个BufferedInputStream,并初始化缓冲区大小为2。
            /**
             * 调用mark(int readlimit)方法时,如果readlimit大于BufferedInputStream类缓冲区的大小,缓冲区会被扩大,那mark后最多就可以读readlimit字节。
             */
            BufferedInputStream bis=new BufferedInputStream(in,2);
            // 读取字节1
            System.out.print(bis.read()+",");
            // 在字节2处做标记,同时设置readlimit参数为1
            // 根据JAVA文档mark以后最多只能读取1个字节,否则mark标记失效,但实际运行结果不是这样
            System.out.println("mark");
            
            // 置为1 则连续两个字节则报异常,为2则连续三个字节即报错
            bis.mark(3);

            /*
             * 连续读取两个字节,超过了readlimit的大小,mark标记仍有效
             */
            // 连续读取两个字节
            System.out.print(bis.read()+",");
            System.out.print(bis.read()+",");
            // 调用reset方法,未发生异常,说明mark标记仍有效。
            // 因为,虽然readlimit参数为1,但是这个BufferedInputStream类的缓冲区大小为2,
            // 所以允许读取2字节
            System.out.println("reset");
            bis.reset();

            /*
             * 连续读取3个字节,超过了缓冲区大小,mark标记失效。
             * 在这个例子中BufferedInputStream类的缓冲区大小大于readlimit,
             * mark标记由缓冲区大小决定
             */
            // reset重置后连续读取3个字节,超过了BufferedInputStream类的缓冲区大小
            System.out.print(bis.read()+",");
            System.out.print(bis.read()+",");
            System.out.print(bis.read()+",");
            // 再次调用reset重置,抛出异常,说明mark后读取3个字节,mark标记失效
            System.out.println("reset again");
            bis.reset();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    @Test
	public void markTest(  ){
        try {
            FileInputStream fis = new FileInputStream(
                    "/path/of/file");
            BufferedInputStream bis = new BufferedInputStream(fis,2);
            int c = 0;

            System.out.println((char)bis.read());

            /*在当前输入流的当前位置上做一个标志,允许最多再读入100个字节*/
            bis.mark(2);
            for(int i=0; i<=6&& (c=bis.read())!=-1;i++){
                System.out.print((char)c+" ");
            }
            System.out.println();
            /*把输入指针返回到以前所做的标志处*/
            bis.reset();
            for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
                System.out.print((char)c+" ");
            }
            bis.close();
        } catch (IOException e) {e.printStackTrace();}
    }
}

JAVA中mark()和reset()用法

3. 转换流

注: 字节流 <===> 字符流

(1)构造方法:public FileOutputStream(String name,boolean append) throws FileNotFoundException
如果append为True,输出字节流就写入文件的末尾,而不是开头(覆盖原来的内容);
如果append为False,输出字节流就写入文件的开头,即覆盖原来的内容从文件开始处写内容。
(2)构造方法:public FileOutputStream(String name) throws FileNotFoundException 每次覆盖原文件的内容,从文件开始处写内容。

 public  static  void  main(String[] args) throws IOException{
//        while(true){
//            System.out.println(new InputStreamReader(System.in).read());}
        BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufw=new BufferedWriter(new FileWriter("readin.txt"));
        String line=null;
        while((line=bufr.readLine())!=null)
        {
            if("over".equals(line)){ break;}
            bufw.write(line);
            bufw.newLine();
        }
        bufw.close();
        bufr.close();
    }

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序堵塞。
等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容.

4. 对象流

在对象属性前面加transient关键字,则该对象的属性不会被序列化。

读取顺序和写入顺序一定要一致,不然会读取出错

@Test
    public void objectReadTest(){
        try {
            InputStream in = new FileInputStream("object.tmp");
            BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
            ObjectInputStream objectInputStream = new ObjectInputStream(bufferedInputStream);
           Person p =  (Person)objectInputStream.readObject();
           System.out.println("tmp Object::"+p);
           objectInputStream.close();
           bufferedInputStream.close();
           in.close();
        }catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void objectWriteTest(){
        FileOutputStream fw = null;
        BufferedOutputStream bufferedOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try{
            // 文件序列化
            fw = new FileOutputStream("object.tmp");
            bufferedOutputStream = new BufferedOutputStream(fw);
            objectOutputStream = new ObjectOutputStream(bufferedOutputStream);
            objectOutputStream.writeObject(new Person("name",10));

            objectOutputStream.close();
            bufferedOutputStream.close();
            fw.close();
        }catch (java.io.IOException e){
            e.printStackTrace();
        }
    }

5. 数据流

数据流:DataInputStream和DataOutputStream
(0)DataInputStream和DataOutputStream是面向字节的,因此要使用InputStream和OutputStream。
(1)DataInputStream和DataOutputStream分别继承InputStream和OutputStream,
它们属于处理流,需要分别“套接”在InputStream和OutputStream类型的节点流上。
(2)DataInputStream和DataOutputStream提供了可以存取与机器无关的Java原始类数据(如:int,double等)的方法。

(3)DataInputStream和DataOutputStream的构造方法

	/**
    	*要保证先写入的先读出来的原则,否则会出现错误。
        *    因此,我们在写代码的时候,我们必须:
        *         要么为文件中的数据采用固定的格式;
        *         要么将额外的信息保存到文件中,以便能够对其进行解析以确定数据的寻访位置。
		*/
import java.io.*;
public class TestDataStream
{
    public static void main(String[] args) throws IOException
    {
        FileOutputStream fout = new FileOutputStream("D:/JavaProject/demo13_IO/DataStream/demo.txt",true);
        BufferedOutputStream bout = new BufferedOutputStream(fout);
        DataOutputStream dout = new DataOutputStream(bout);
        /*DataOutputStream,BufferedOutputStream,FileOutputStream这里使用了流栈。*/

        dout.writeInt(110);
        dout.writeUTF("hello,中国");
        dout.writeFloat(3.14f);
        dout.writeChar(97);/*97对应的是'a'*/
        dout.close();/*如果正在使用一个流栈,程序关闭最上面的一个流也就自动的关闭了栈中的所有底层流。*/

        FileInputStream fin = new FileInputStream("D:/JavaProject/demo13_IO/DataStream/demo.txt");
        BufferedInputStream bin = new BufferedInputStream(fin);
        DataInputStream din = new DataInputStream(bin);

        int i = din.readInt();
        String str = din.readUTF();
        float f = din.readFloat();
        char c = din.readChar();
        fin.close();/*如果正在使用一个流栈,程序关闭最上面的一个流也就自动的关闭了栈中的所有底层流。*/
        System.out.println("int:"+i+"\nString:"+str+"\nfloat:"+f+"\nchar:"+c);
    }

}

参考

IO流 之 节点流与处理流(2)

IO体系的学习总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值