理解java之io流

紫焱      2022-04-21     528

关键词:

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

一、IO流结构

1、流的分类方式

按流向分
从文件/网络/内存等(数据源)到程序是输入流;从程序到文件/网络/内存等(数据源)是输出流
按数据处理单位分
以字节为单位传输数据的流,以Stream结尾的是字节流;以字符为单位传输数据的流,以Reader结尾的是输入字符流,以Writer结尾的都是输出字符流。
按功能(层次)分
用于直接操作目标设备的流是节点流;处理流(也叫过滤流)是对一个已存在的流的连接和封装,通过对数据的处理为程序提供更为强大、灵活的读写功能。

2、IO流的结构

如下图所示:

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

3、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流的具体使用

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

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

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

4、读写对象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;
}

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

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

7、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

java之io流

一、File类的使用1.File类的理解File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)。防城港SEO(网站优化)File类声明在java.io包下:文件和文件路径的抽象表示形式,与平台无关。File类中涉及到关于文件或文件目录... 查看详情

java从零开始之javastreamfileio

...比如:基本类型、对象、本地化字符集等等。一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。Java为I/O提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中... 查看详情

io系列之字符流

java中io流系统庞大,知识点众多,作为小白通过五天的视频书籍学习后,总结了io系列的随笔,以便将来复习查看。本篇为此系列随笔的第一篇:io系列之字符流。 IO流:对数据的传输流向进行操作,java中将这种操作行为本... 查看详情

java之io流

java之IO流IO流的介绍输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。输出:将程序(内存)数据输出到磁盘、光盘等存储设备中Java的IO流主要包括输入、输出两种IO流,... 查看详情

java基础之io流

...理设备(硬盘,控制台,内存)间的数据。   (2)java中对数据的操作都是通过流的方式。   (3)java用于操作流的类都在io包中。   (4)按照流操作的数据的类型不同:分为字节流和字符流。字符流是为了... 查看详情

java编程基础之io流(代码片段)

IO流IO流基类InputStream输入流基类readOutputStream输出流基类writeIO流分类流向分类输入输出文件操作处理单元分类字节流和字符流FileInputStream文件操作输入字节流FileOutputStream文件操作输出字节流FileReader文件操作输入字符流FileWriter文... 查看详情

java基础之io流(代码片段)

...两个小问题字节输入流字节缓冲流前言IO流什么是IO?Java中I/O操作主要是指使用Java进行输入,输出操作.Java所有的I/O机制都是基于数据流 查看详情

java输入/输出——理解java的io流

1、流的分类  (1)输入流和输出流(划分输入/输出流时是从程序运行所在内存的角度来考虑的)    输入流:只能从中读取数据,而不能向其写入数据。    输出流:只能向其写入数据,而不能从中读取数据。  ... 查看详情

java基础之io流

JavaIO一、什么是IO?  Java中I/O操作主要是指使用Java进行输入,输出操作.。Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java... 查看详情

java基础知识回顾之六-----io流

前言在上一篇文章中,回顾了Java的多线程。而在本篇文章中主要介绍JavaIO的相关知识。IO的介绍什么是IO?IO的名称又来是Input与Output的缩写,也就是输入流和输出流。输入流用于从源读取数据,输出流用于向目标写数据。可以从... 查看详情

java基础之io流(输入/出字符流)(代码片段)

字符基流FileReader/FileWriter 代码示例packageIOTest;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.FileWriter;/***ClassName:FileReaderDemo<br/>*Description:<br/>*date:2 查看详情

java之io

总结iO流用来处理设备之间的数据传输。Java程序中,对于数据的输入/输出操作以”流(stream)”的方式进行输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。输出output:将程序(内存)数据输出到磁盘... 查看详情

java高级之io流

1.1,什么是io流?  流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。1.2... 查看详情

java之数据io流-datainputstream

...允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。二、构造方法DataInputStream(InputStreamin)    使用指定的底层InputStream创建一个DataInputStream... 查看详情

java开发知识io知识之输入输出流以及文件

java开发知识IO知识之输入输出流以及文件一丶流概述流十一组有序的数据序列.根据操作的类型,可以分为输入流跟输出流两种.IO(input/output)输入/输出流提供了一条通道程序.可以使用这条通道把源的字节序列送到目的地简单来说.... 查看详情

java之io流(代码片段)

...File类注意,\\在文件中是路径的分隔符,但是在java编程中一个\\的意思时转义符,在java中\\\\或者/才是文件的分隔符文件类里面的基础方法文件的新建,删除创建单层目录,多级目录返回当前文件的子集流的... 查看详情

java基础知识(java之io流)

   学习JavaIO,不得不提到的就是JavaIO流。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方... 查看详情

java之io流(字节流,字符流)

...。单独的程序一般是让系统为它们完成大部分的工作。在Java编程中,一直使用流的方式完成I/O。所有I/O都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。流I/O用于与外部世界接触。它也在内部使 查看详情