理解Java之IO流

流是一种抽象概念,它代表了数据的无结构化传递。用来进行输入输出操作的流就称为IO流。

IO流结构

流的分类方式

按流向分:从文件/网络/内存等(数据源)到程序是输入流

从程序到文件/网络/内存等(数据源)是输出流

按数据处理单位分

字节流:以字节为单位传输数据的流,以Stream结尾的都是字节流。

字符流:以字符为单位传输数据的流,以Reader结尾的都是输入字符流,以Writer结尾的都是输出字符流。

按功能(层次)分

节点流:用于直接操作目标设备的流

处理流(也叫过滤流):是对一个已存在的流的连接和封装,通过对数据的处理为程序提供更为强大、灵活的读写功能。

IO流的结构

如下图所示:

注意:

所有的字节输入流类都是InputStream的子类;所有的字节符输入流类都是Reader的子类;所有的字节节输出流类都是OutputStream的子类;所有的字节符输出流类都是Writer的子类,且他们都为抽象类。

IO流四大抽象类

InputStream的基本方法:

 public abstract int read() throws IOException {}//从输入流中读取数据的下一个字节, 返回读到的字节值.若遇到流的末尾,返回-1
 public int read(byte[] b) throws IOException {}//从输入流中读取 b.length 个字节的数据并存储到缓冲区数组b中.返回的是实际读到的字节总数
 public int read(byte[] b, int off, int len) throws IOException {}//读取 len 个字节的数据,并从数组b的off位置开始写入到这个数组中
 public void close() throws IOException {}//关闭此输入流并释放与此流关联的所有系统资源
 public int available() throws IOException {}//返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数
 public long skip(long n) throws IOException {}//跳过和丢弃此输入流中数据的 n 个字节,返回实现路过的字节数。

OutputStream的基本方法:

public abstract void write(int b) throws IOException {}//将指定的字节写入此输出流。
public void write(byte[] b) throws IOException {}// 将 b.length 个字节从指定的 byte 数组写入此输出流。
public void write(byte[] b, int off, int len) throws IOException {}//将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
public void flush() throws IOException {}//刷新此输出流并强制写出所有缓冲的输出字节。
pulbic void close() throws IOException {}//关闭此输出流并释放与此流有关的所有系统资源。

Reader的基本方法:

public int read() throws IOException {}//读取单个字符,返回作为整数读取的字符,如果已到达流的末尾返回-1
public int read(char[] cbuf) throws IOException {}//将字符读入数组,返回读取的字符数
public abstract int read(char[] cbuf, int off, int len) throws IOException {}//读取 len 个字符的数据,并从数组cbuf的off位置开始写入到这个数组中
public abstract void close() throws IOException {}//关闭该流并释放与之关联的所有资源
public long skip(long n) throws IOException {}//跳过n个字符。
public int available()  //还可以有多少能读到的字节数

Writer的基本方法:

public void write(int c) throws IOException {} //写入单个字符
public void write(char[] cbuf) throws IOException {} //写入字符数组
public abstract void write(char[] cbuf, int off, int len) throws IOException {} //写入字符数组的某一部分
public void write(String str) throws IOException {} //写入字符串
public void write(String str, int off, int len) throws IOException {}//写字符串的某一部分
public abstract void close() throws IOException {}  //关闭此流,但要先刷新它
public abstract void flush() throws IOException {}  //刷新该流的缓冲,将缓冲的数据全写到目的地

IO流的具体使用

FileInputStream 和 FileOutputStream

public static void fileInputStreamTest() {
    File f1 = new File("D:\\in.txt");
    File f2 = new File("D:\\out.txt");
    try {
        FileInputStream fi = new FileInputStream(f1);
        FileOutputStream fo = new FileOutputStream(f2);
        byte[] buf = new byte[521];
        int len = 0;
        while((len = fi.read(buf)) != -1){
            fo.write(buf, 0, len);
        }
        fo.flush();
        fo.close();
        fi.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

PipedOutputStream和PipedintputStream

Java里的管道输入流PipedInputStream与管道输出流PipedOutputStream实现了类似管道的功能,用于不同线程之间的相互通信。

Java的管道输入与输出实际上使用的是一个循环缓冲数组来实现,这个数组默认大小为1024字节。输入流PipedInputStream从这个循环缓冲数组中读数据,输出流PipedOutputStream往这个循环缓冲数组中写入数据。当这个缓冲数组已满的时候,输出流PipedOutputStream所在的线程将阻塞;当这个缓冲数组首次为空的时候,输入流PipedInputStream所在的线程将阻塞。Java在它的jdk文档中提到不要在一个线程中同时使用PipeInpuStream和PipeOutputStream,这会造成死锁。

public class WriteThread implements Runnable{
    private PipedOutputStream pout;  

    WriteThread(PipedOutputStream pout){
      this.pout=  pout;
    }  

    @Override
    public void run() {
        try {
            System.out.println("W:开始将数据写入:但等个5秒让我们观察...");
            Thread.sleep(5000);  //释放cpu执行权5秒
            pout.write("writePiped 数据...".getBytes());  //管道输出流
            pout.close();
          } catch(Exception e) {
            throw new RuntimeException("W:WriteThread写入失败...");
          }
    }
}

public class ReadThread implements Runnable{
    private PipedInputStream pin;  

    ReadThread(PipedInputStream pin) {
      this.pin=pin;
    }  

    @Override
    public void run() {  //由于必须要覆盖run方法,所以这里不能抛,只能try
      try {
            System.out.println("R:读取前没有数据,阻塞中...等待数据传过来再输出到控制台...");
            byte[] buf = new byte[1024];
            int len = pin.read(buf);  //read阻塞
            System.out.println("R:读取数据成功,阻塞解除...");
            String s= new String(buf,0,len);
            System.out.println(s);  //将读取的数据流用字符串以字符串打印出来
            pin.close();
      }  catch(Exception e)  {
            throw new RuntimeException("R:管道读取流失败!");
      }
    }
}

public class Test {
    public static void main(String[] args) throws IOException {
          PipedInputStream pin = new PipedInputStream();
          PipedOutputStream pout = new PipedOutputStream();
          pin.connect(pout);  //输入流与输出流连接  

          ReadThread readTh   = new ReadThread(pin);
          WriteThread writeTh = new WriteThread(pout);
          new Thread(readTh).start();
          new Thread(writeTh).start();
    }
}

BufferedInputStream和BufferedOutputStream

FileInputStream和FileOutputStream 在使用时,我们介绍了可以用byte数组作为数据读入的缓存区,以读文件为列,读取硬盘的速度远远低于读取内存的数据,为了减少对硬盘的读取,通常从文件中一次读取一定长度的数据,把数据存入缓存中,在写入的时候也是一次写入一定长度的数据,这样可以增加文件的读取效率。我们在使用FileInputStream的时候是用byte数组来做了缓存,而BufferedInputStream and BufferedOutputStream已经为我们增加了这个缓存功能。

public static void fileBufferTest() {
    File f1 = new File("D:\\in.txt");
    File f2 = new File("D:\\out.txt");
    try {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(f1));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(f2));
        byte[] data = new byte[1];
        while(bufferedInputStream.read(data)!=-1) {
            bufferedOutputStream.write(data);
        }
        //将缓冲区中的数据全部写出
        bufferedOutputStream.flush();
        bufferedInputStream.close();
        bufferedOutputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

读写对象ObjectInputStream和ObjectOutputStream

public class ObjectStream {
    public static void main(String[] args) {
        ObjectStream.objectStreamTest();
    }

    public static void objectStreamTest() {
        Demo newObject;
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:/123.obj")));
            oos.writeObject(new Demo());
            oos.flush();
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:/123.obj")));
            newObject = (Demo)ois.readObject();
            System.out.println(newObject.num);
            ois.close();
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Demo implements Serializable{
    private static final long serialVersionUID = 1L;
    int num = 30;
}

SequenceInputStream

合并流,将与之相连接的流集组合成一个输入流并从第一个输入流开始读取, 直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 合并流的作用是将多个源合并合一个源。可接收枚举类所封闭的多个字节流对象。

public class Test {
      public static void main(String[] args) {
             doSequence();
      }  

      private static void doSequence() {
         SequenceInputStream sis = null;  // 创建一个合并流的对象
         BufferedOutputStream bos = null;   // 创建输出流。
         try {
            // 构建流集合
            Vector<InputStream> vector = new Vector<InputStream>();
            vector.addElement(new FileInputStream("D:/in.txt"));
            vector.addElement(new FileInputStream("D:/out.txt"));
            Enumeration<InputStream> e = vector.elements();
            sis = new SequenceInputStream(e);
            bos = new BufferedOutputStream(new FileOutputStream("/Users/zhengchao/cctv/File_OUT.txt"));
            // 读写数据
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = sis.read(buf)) != -1) {
               bos.write(buf, 0, len);
               bos.flush();
            }
         } catch (Exception e1) {
            e1.printStackTrace();
         } finally {
            try {
               if (sis != null)
                  sis.close();
            } catch (IOException e) {
               e.printStackTrace();
            }
            try {
               if (bos != null)
                  bos.close();
            } catch (IOException e) {
               e.printStackTrace();
            }
         }
      }
}

FileReader与FileWriter

public static void fileReaderTest() {
    File f1 = new File("D:\\in.txt");
    File f2 = new File("D:\\out.txt");
    try {
        FileReader fr = new FileReader(f1);
        FileWriter fw = new FileWriter(f2);
        char[] ch = new char[512];
        int len = 0;
        while((len = fr.read(ch)) != -1){
            fw.write(ch, 0, ch.length);
        }
        fw.flush();
        fw.close();
        fr.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

BufferedReader和BufferedWriter

public static void fileBufferReaderWriteTest() {
    File f1 = new File("D:\\in.txt");
    File f2 = new File("D:\\out.txt");
    try {
        BufferedReader br=new BufferedReader(new FileReader(f1));
        BufferedWriter bw=new BufferedWriter(new FileWriter(f2));

        String s=br.readLine();
        while(null!=s) {
            bw.write(s);
            //由于BufferedReader的readLine()是不读入换行符的,所以写入换行时须用newLine()方法
            bw.newLine();
            s=br.readLine();
        }
        br.close();
        bw.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

注:在使用过程中应注意将编码格式设置为UTF-8,否则文件写入会乱码。

以上只是IO流的部分类,用于对IO流相关知识进行查阅,后面再实际的工作中也会根据实际需求不断添加。

参考自:

https://blog.csdn.net/zhengchao1991/article/details/53033137

https://blog.csdn.net/SilenceOO/article/details/50995062

https://blog.csdn.net/Yue_Chen/article/details/72772445

原文地址:https://www.cnblogs.com/liuyi6/p/10086651.html

时间: 2024-10-11 03:58:44

理解Java之IO流的相关文章

Java 输入/输出——理解Java的IO流

1.流的分类 (1)输入流和输出流(划分输入/输出流时是从程序运行所在内存的角度来考虑的) 输入流:只能从中读取数据,而不能向其写入数据. 输出流:只能向其写入数据,而不能从中读取数据. 输入流主要由InputStream和Reader作为基类,输出流主要由OutputStream和Writer作为基类.它们都是抽象基类,无法直接创建实例. (2)字节流和字符流 字节流和字符流的用法几乎完全一样,区别在于字节流和字符流操作的数据单元不同--字节流操作的数据单元是8-bit的字节,而字符流操作的数

Java之IO流---字节流

1.1 IO流的引入 IO流在很多语言已有体现,诸如C语言的stdio.h,C++中的iostream.Java中的IO流大抵是用于在控制台.磁盘.内存上进行数据的读写操作,完成数据的传递. 我们可以对它进行如下分类: 按处理的数据类型可分为字节流与字符流 按流的流向可分为输入流(in)与输出流(out) 按流的功能可分为节点流(Node)和过滤流(Filter) 本篇侧重于梳理字节流相关的知识,毕竟作为字符流的前辈,它还是非常重要的.下篇继续梳理字符流. 1.2 IO流的继承体系图 大概描述了

深入理解 Java中的 流 (Stream)

首先,流是什么? 流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以"流"的方式进行.设备可以是文件,网络,内存等. 流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流. 可以将流想象成一个"水流管道",水流就在这管道中形成了,自然就出现了方向的概念. 当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件.内存或网络等等.

java常用IO流数据流小结

  类名 常用方法 说明 输入流 InputStream int read(); 只能读字节流,虽然返回值是int,但只有低8位起作用. DataInputStream Type readType(); 可以读二进制流,可以读byte,short,int,long,double等二进制流. BufferedReader String readLine(); 可以读文本行. 输出流 OutputStream void write(int); 只能写字节流,虽然形参是int,但只有低8为起作用. D

【Java】IO流简单分辨

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/5827509.html Java的IO流体系十分庞大,并且体系层次稍复杂,很容易记混或记错.在此,我把平时经常用到的几个流类的简易区分以及体系层次整理出来,方便记忆与辨析.本人对IO了解尚浅,文章如有错漏,恳请前辈指正,感激不尽! 字节流体系: 基类:InputStream/outputStream(抽象类,不能new) 子类: 文件IO字节流:FileInputStream/FileoutputStream

JAVA中IO流总结

本文是在学习中的总结,欢迎转载但请注明出处:http://write.blog.csdn.net/postedit/42119261 我想你对JAVA的IO流有所了解,平时使用的也比较的多,但是对于其具体分类和继承体系可能知道的并不多,可能也很少去看相关的API文档,找出其中的关系和各自的应用情形.本文简单对常用的IO流进行分类整理,并简单举例说明其应用.希望本文对你有所帮助. (A)IO流大致分为两种: (1)字节流:对应抽象类为InputStream(输入流)和 OutputStream(输

【JAVA的 IO流之FileInputStream和FileOutputStream】

java的 IO流即输入输出流,流是一组有顺序的,有起点和终点的字节结合,是对数据传输的总称.即数据在两设备间的传输称为流,流的本质是数据传输. IO流可以分为字节流和字符流.给出相应的IO结构图: 在接下来的一段时间里,将会慢慢介绍各种流的使用,本篇博客先介绍字节流的FileOutputStream和相对应的FileInputStream. 一.FileOutputStream(文件输出流) OutputStream是一个抽象类,抽象类必须通过子类实现.现在要向文件里输出就要用FileOutp

java的IO流,字节流和字符流

java操作文件都是通过流来处理的,(其实其他很多语言也是这样) 第一:java的IO流,分为:输入流 和 输出流(这真是废话,这是从流向的角度来说的) 第二:java的所有IO流,只分为:字节流 和 字符流(其实就是传输的颗粒,传输的基本单位) 总结:凡是处理纯文本的优先考虑字符流:其他的才考虑使用字节流

Java笔记-IO流的运用

1.InputStream和System.in(Scanner) InputStream 输出流以字节为单位来获取数据,且需要复杂的判断并创建字节数组作为缓冲 另外字节转换为字符时容易出现中文乱码的情况:Scanner Java扫描器类,可以从输入流中读取指定类型的数据或字符串. 对于字符数据的读取,应该使用Scanner扫描器进行封装,然后获取字符串类型的数据 2. out和err out和err是System类的两个static类成员变量: out:主要是输出调试信息的输出流,以黑色显示 e