飞道的博客

Java 基础 — 数据流、文件和 I/O

346人阅读  评论(0)

概述

1、数据流

在电脑上的数据有三种存储方式:外存、内存和缓存。数据流是一组有序、有起点和终点的字节的数据序列,包括输入流和输出流。Java 数据流分为两种:字节流(Byte)和字符流(Character)。采用数据流的目的就是使得输出输入独立于设备。

(1)数据流分类

① 字节流

以8位为单位对二进制数据进行操作。这些类都是InputStream和OutputStream的子类。
字节流中最小的数据单元是字节(8位)。

② 字符流

以字符为单位对数据进行操作,在读的时候,将二进制数据转换为字符,在写时,将字符转换为二进制数据。这些类都是Reader和Writer的子类。
字符流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节(16位)。

两者区别:

字节流可用于任何类型的对象,包括二进制对象,处理单元为1个字节,操作字节和字节数组;字符流只能处理字符或者字符串,处理单元为2个字节的Unicode字符,操作字符、字符数组或字符串。
② 如果是处理音频文件、图片、歌曲等,建议使用字节流;如果处理纯文本数据,建议使用字符流。
所有文件的储存是都是字节(byte)的储存。字符流在写入文本数据时,先把字符编码成字节,再储存这些字节到磁盘;在读取文本文件时,先将字节序列转换成成字节序列再处理。

(2)输入流与输出流

一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

① 输入流(Input Stream)

程序从输入流读取数据源(数据:外界——>程序)。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道。

Input Stream 不关心数据源来自何种设备(键盘,文件,网络)。

② 输出流(out Stream)

程序向输出流写入数据(数据:程序——>外界)。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

Output Stream 不关心数据的目的是何种设备(键盘,文件,网络)。

2、Java 的 I/O 体系


在整个Java.io包中最重要的就是5个类和一个接口:5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。

Java I/O主要包括如下几个层次,包含三个部分:

(1)流式部分――IO的主体部分
如:InputStream类(二进制格式操作)、OutputStream(二进制格式操作)、Reader类(文件格式操作)和Writer类(文件格式操作)。

(2)非流式部分――主要包含一些辅助流式部分的类
如:File类(文件特征与管理)、RandomAccessFile类(随机文件操作)和FileDescriptor等类;

(3)其他类–文件读取部分的与安全相关的类
如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。
Java 中的 I/O 操作主要是基于数据流进行操作的,输入流用于从源读取数据,输出流用于向目标写数据。

一、字符数据流(Writer/Reader)

字符流就是在字节流的基础上,加上编码形成的数据流。在读的时候将二进制数据转换为字符,在写时将字符转换为二进制数据。
,字符流适用于处理字符数据(诸如文本文件)
字符流出现的原因:因为字节流在操作字符时,可能会有中文导致的乱码,所以由字节流引申出了字符流

1、Reader抽象类

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

主要成员方法:

测试流是否可以读取

boolean ready() throws IOException;

读取一个字符,返回值为读取的字符

 public int read() throws IOException;  

读取 cbuf.length 长度的数据放到 cbuf 数组中,返回值为实际读取的字符的数量

 public int read(char cbuf[]) throws IOException;  

读取 len 个字符,存放到偏移量为off的 cbuf 数组中,返回值为实际读取的字符数量,该方法必须由子类实现

 public abstract int read(char cbuf[],int off,int len) throws IOException;  

关闭数据流

public void close() throws IOException; 

(1)FileReader(外存文件读入数据)

文件字符输入流。创建一个 FileReader 对象指向要读取的文件,用于把硬盘文件中的数据以字符的方式读取到内存中

构造函数:

// 1、以文件路径的字符串作为参数 
FileReader f = new FileReader(“c:/temp.txt”); 
// 2、将File对象作为其参数
File f = new file(“c:/temp.txt”); 
FileReader f1 = new FileReader(f); 

(2)InputStreamReader

从输入流读取字节,在将它们转换成字符 。

构造函数

  Public inputstreamReader(inputstream is);

(3)CharArrayReader(从内存字符数组读入数据)

CharArrayReader 是一个把字符数组作为源的输入流的实现。该类实现了一个将内存中的字符数组用作字符输入流的字符缓冲区(数据源),即该类可利用字符缓冲区当做字符输入流进行读取工作。

构造函数:

CharArrayReader(char array[ ]) 
CharArrayReader(char array[ ], int start, int numChars) 

示例:

public class Main {
   
  public static void main(String[] args) throws Exception {
   

    char[] ch = {
    'H', 'E', 'L', 'L', 'O' };  // 定义一个字符数组作为数据源
    CharArrayReader car = new CharArrayReader(ch);  // 以数据源作为参数创建字符缓冲区对象
    int value = 0;
    while ((value = car.read()) != -1) {
   
      char c = (char) value;
      System.out.print(c + " : "); 
      System.out.println(value);
    }
  }
}

输出结果:

(4)StringReader(从内存字符串读入)

StringReader 是一个把字符串作为源的输入流的实现。该类实现了一个将内存中的字符串用作字符输入流的字符缓冲区(数据源),即该类可利用字符缓冲区当做字符输入流进行读取工作。

构造函数:

StringReader(String str);

(5)BufferedReader(提供缓冲功能)

BufferedReader 是缓冲字符输入流,作用是为其他字符输入流提供缓冲功能(拥有 8192 字符的缓冲区),而且提供了很实用的 readLine,读取一个文本行(reader.readLine()方法返回的一行字符中不包含换行符,所以输出的时候要自己加上换行符),从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。
为什么需要缓冲呢?缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。

源码关键字段

    // 字符输入流
    private Reader in;
    // 字符缓冲区
    private char cb[]; 
    //读取字符存储的最末下标+1
    private int nChars; 
    //读取字符存储的起始下标
    private int nextChar;
 
    private static final int INVALIDATED = -2;
    private static final int UNMARKED = -1;
    private int markedChar = UNMARKED;
 
    // 仅在markedChar为0时有效
    private int readAheadLimit = 0; 
    // 如果下个字符是换行符,则跳过--专用于readLine()方法里面控制
    private boolean skipLF = false; 
    // 设置标志时的markedSkipLF--用于mark()方法的变量
    private boolean markedSkipLF = false;
 
    // 默认的字符缓冲大小
    private static int defaultCharBufferSize =8192;    
    //用于readLine()方法时初始化StringBuffer的初始容量
    private static int defaultExpectedLineLength = 80;

构造函数

public BufferedReader(Reader in);  //创建缓冲区字符输入流
public BufferedReader(Reader in,int size);  //创建输入流并设置缓冲区大小

示例:

FileReader fd = new FileReader("java.txt");  // 创建一个 FileReader 对象
BufferReader mbr = new BufferedReader(fd);   // 以 FileReader 对象为参数,创建 BufferReader 对象
String line = null;
while((line = mbr.myReadLine())!= null) {
     //读取文本行
     System.out.println(line)
     }

(6)PipedReader

管道字符输入流,用于读取对应绑定的管道字符输出流写入其内置字符缓存数组buffer中的字符,借此来实现线程之间的通信、pr中专门有两个方法供pw调用、receive(char c)、receive(char[] b, int off, intlen)、使得pw可以将字符或者字符数组写入pr的buffer中。

构造方法

PipedReader(PipedWriter src)	// 使用默认的buf的大小和传入的pw构造pr
PipedReader(PipedWriter src, int pipeSize)		// 使用指定的buf的大小和传入的pw构造pr
PipedReader()		// 使用默认大小构造pr
PipedReader(int pipeSize)		// 使用指定大小构造pr

源码关键字段:

public class PipedReader extends Reader {
   
	// 标记PipedWriter是否关闭
    boolean closedByWriter = false;
    // 标记PipedReader是否关闭
    boolean closedByReader = false;
    // 标记PipedWriter与标记PipedReader是否关闭的连接是否关闭
    boolean connected = false;
    // 拥有PipedReader的线程
    Thread readSide;
    // 拥有PipedWriter的线程
    Thread writeSide;
  
	//用于循环存放PipedWriter写入的字符数组的默认大小 
    private static final int DEFAULT_PIPE_SIZE = 1024;
    
    //用于循环存放PipedWriter写入的字符数组     
    char buffer[];
 
    // buf中下一个存放PipedWriter调用此PipedReader的receive(int c)时、c在buf中存放的位置的下标。
    // in为-1时、说明buf中没有可读取字符、in=out时已经存满了。
    int in = -1;
 
    //  buf中下一个被读取的字符的下标
    int out = 0;
}

2、Writer 抽象类

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

主要成员方法:

将指定字符添加到此 writer,返回流对象本身(也就是可以写成这种格式: append().append())

public Writer append(char c) ; 

将指定字符序列添加到此 writer,返回流对象本身(也就是可以写成这种格式: append().append())

 public Writer append(CharSequence csq) ; 

将指定字符序列的子序列添加到此 writer.Appendable,返回流对象本身(也就是可以写成这种格式: append().append())

public Writer append(CharSequence csq, int start, int end) ; 

将整型值c的低16位写入输出流

public void write(int c) throws IOException; 

将字符数组 cbuf[] 写入输出流 ,从而将其输出到 Writer 对象指定的位置

public void write(char cbuf[]) throws IOException; 

将字符数组 cbuf[] 中的从索引为off的位置处开始的len个字符写入输出流 ,从而将其输出到 Writer 对象指定的位置

public abstract void write(char cbuf[],int off,int len) throws IOException; 

将字符串str中的字符写入输出流 ,从而将其输出到 Writer 对象指定的位置

public void write(String str) throws IOException; 

将字符串str 中从索引off开始处的len个字符写入输出流 ,从而将其输出到 Writer 对象指定的位置

public void write(String str,int off,int len) throws IOException; 

刷空输出流,并输出所有被缓存的字节。

flush( )  

关闭流 ,释放系统资源,关闭前会刷新缓冲区。

close()    

(1)FileWrite(将数据写入外存文件)

FileWriter 类对象,用于将字符类型数据写入文件。构造函数第一个参数表明创建对象时要指定输出的地址第二个参数表明我在输出的时候是否要覆盖源内容(默认为false覆盖),可以加一个参数true表示追加不覆盖。

构造函数:

FileWriter(File file)      // 给一个File对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append)     // 给一个File对象构造一个 FileWriter 对象。
FileWriter(FileDescriptor fd)      // 构造与文件描述符关联的 FileWriter 对象。
FileWriter(String fileName)      // 根据一个给定文件的路径创建 FileWriter 对象。
FileWriter(String fileName, boolean append)   // 根据一个给定文件的路径构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据

示例:

File file = new File("D:\\www\\love.txt");  // 定义一个File类的对象
Writer out = new FileWriter(file);   // 多态的写法
out.write(97);        // 97-->对应的字符a  输出a
out.write("\r\n");    // 换行
out.write('a');      // a
out.write('我');     // 我
out.write("我爱中国");    // 我爱中国
out.write("我爱中国",2,2);   // 中国
//定义一个字符数组
char[] ch = {
   '我','爱','世','界'};    
out.write(ch);          // 我爱世界
out.write(ch,2,2);    // 世界
out.flush();       // 刷新输出流
out.write("可以继续输出");   //可以继续输出
out.close();      // 关闭流
out.write(97);    // 会报错,因为此时的流已经关闭。java.io.IOException: Stream closed

(2)CharArrayWrite(向内存字符数组写入数据)

字符数组输出流,用于将字符写入到内存中的字符缓存数组 char[] buf 中当此数组存满时会自动扩容
此类上调用 close() 无效,即 close 方法对CharArrayWriter没有影响,可以在流关闭后继续调用此类的方法而不生成IOException

源码关键字段:

 public class CharArrayWriter extends Writer {
   
    // 字符数组缓冲
    protected char buf[];
    // 字符数组缓冲大小
    protected int count;
    }

构造函数:

CharArrayWriter caw = new CharArrayWriter();   // 创建使用默认大小的CharArrayWriter对象
CharArrayWriter caw = new CharArrayWriter(int initialSize);   // 创建大小为 initialSize 的CharArrayWriter对象

示例:

char[] ArrayLetters = new char[] {
   'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
// 创建CharArrayWriter字符输出流
CharArrayWriter caw = new CharArrayWriter(); 
 // 写入“A”个字符
caw.write('A');
// 写入字符串“BC”个字符
caw.write("BC");
//System.out.printf("caw=%s\n", caw);
// 将ArrayLetters数组中从“3”开始的后5个字符(defgh)写入到caw中。
caw.write(ArrayLetters, 3, 5);
//System.out.printf("caw=%s\n", caw); 
// (01) 写入字符0
// (02) 然后接着写入“123456789”
// (03) 再接着写入ArrayLetters中第8-12个字符(ijkl)
caw.append('0').append("123456789").append(String.valueOf(ArrayLetters), 8, 12);
System.out.printf("caw=%s\n", caw);

输出:

(3)StringWriter

StringWriter是将一个 StringBuffer 对象作为输出目的地,并且可以在构造函数指定 buffer 的大小。
StringWriter只是将数据写入到一个 buffer 对象中,而不是实质的物理介质,这个对象最后又可以转换成字符串(toString)
在字符串缓冲区中收集输出的字符流,可用于构造字符串, 关闭流无效,关闭后调用其他方法不会报异常

源码关键字段:

public class StringWriter extends Writer {
   
	// StringBuffer 字符串缓冲
	private StringBuffer buf;
	}

构造函数:

StringWriter = new StringWriter();   // 创建使用默认大小的 StringWriter 对象
StringWriter = new StringWriter(int initialSize);   // 创建大小为 initialSize 的 StringWriter 对象

示例:

StringWriter stringWriter = new StringWriter();
stringWriter.write("中国");
stringWriter.append("人民");
String str = stringWriter.toString();  // 将 stringWriter 对象最后又可以转换成字符串
System.out.println(str);

输出:

(4)BufferedWriter

BufferedWriter 是缓冲字符输出流,作用是为其他字符输出流提供缓冲功能(拥有 8192 字符的缓冲区)
使用 BufferedWriter 时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出
关闭此流时,要先刷新它,强行将缓冲区中的数据写出。否则可能无法写出数据。

源码关键字段:

public class BufferedWriter extends Writer {
   
     //字符输出流
    private Writer out;
    //字符缓冲区
    private char cb[];    
    //设置的字符缓冲区大小变量
    private int nChars;    
    //字符缓冲区中的已存储元素的位置
    private int  nextChar; 
    //默认字符缓冲区大小
    private static int defaultCharBufferSize = 8192; 
    /**
     * 行分割字符串-
     * property at the moment that the stream was created.
     */
    private String lineSeparator;
    }

构造函数:

BufferedWriter(Writer out);   // 默认缓冲区大小构造字符缓冲输出流对象
BufferedWriter(Writer out,int size);  // 指定缓冲区大小

(5)PipedWriter

管道字符输出流,用于将当前线程的指定字符写入到与此线程对应的管道字符输入流中去、所以PipedReader(pr)、PipedWriter(pw)必须配套使用、缺一不可。管道字符输出流的本质就是调用pr中的方法将字符或者字符数组写入到pr中、这一点是与众不同的地方。所以pw中的方法很少也很简单、主要就是负责将传入的pr与本身绑定、配对使用、然后就是调用绑定的pr的写入方法、将字符或者字符数组写入到pr的缓存字符数组中

源码关键字段:

public class PipedWriter extends Writer {
   
	
	//与此PipedWriter绑定的PipedReader
    private PipedReader sink;
 
    //标示此流是否关闭。
    private boolean closed = false;
}

构造方法

PipedReader(PipedWriter src)	// 使用默认的buf的大小和传入的pw构造pr
PipedReader(PipedWriter src, int pipeSize)		// 使用指定的buf的大小和传入的pw构造pr
PipedReader()		// 使用默认大小构造pr
PipedReader(int pipeSize)		// 使用指定大小构造pr

二、字节数据流(InputStream/OutputStream)

在Java中,字节流一般适用于处理字节数据(诸如图片、视频),InputStream 和 OutputStream
分别是字节输入流与字节输出流的基类,它们的子类都是字节流,主要用在按字节来处理二进制数据。

1、InputStream 类

InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit)。

主要方法

返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。

int available()  

关闭此输入流并释放与该流关联的所有系统资源。

  void close()  

在此输入流中标记当前的位置。

  void mark(int readlimit)  

测试此输入流是否支持 mark 和 reset 方法。

 boolean markSupported()  

从输入流中读取数据的下一个字节。

 abstract  int read() 

从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。

  int read(byte[] b)  

将输入流中最多 len 个数据字节读入 byte 数组。

 int read(byte[] b, int off, int len) 

将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。

  void reset()  

跳过和丢弃此输入流中数据的 n 个字节。

 long skip(long n)  

(1)FileInputStream

从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。

构造函数

FileInputStream(File file)  // 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
FileInputStream(FileDescriptor fdObj)  // 通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 
FileInputStream(String name)   // 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 

(2)ObjectInputStream

对象输入流,对使用 ObjectOutputStream 写入的对象进行反序列化
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。
ObjectInputStream 用于恢复那些以前序列化的对象。用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取
对象要能从流中读取,它必须是可序列化的;读取的顺序应该与写入时的顺序是一致的

构造方法

  ObjectInputStream(InputStream in);   // 创建从指定 InputStream 读取的 ObjectInputStream。 

示例

private static void read() throws FileNotFoundException, IOException, ClassNotFoundException {
   
        FileInputStream fis = new FileInputStream("t.tmp");
        ObjectInputStream ois = new ObjectInputStream(fis);
        // 顺序读取
        int readInt = ois.readInt();
        String string = (String) ois.readObject();
        Date date=(Date) ois.readObject();

        System.out.println("readInt="+readInt);
        System.out.println("string="+string);
        System.out.println("date="+date);

        ois.close();
    }

(3)ByteArrayInputStream

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可继续读取,而不会产生任何 IOException

构造方法

ByteArrayInputStream(byte[] buf)   // 创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。 
ByteArrayInputStream(byte[] buf, int offset, int length)  // 创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。 

示例

private static void test1() {
   
        byte[] buf=new byte[]{
   'h','e','l','l','o'};
        // 用一个字节数组来构造一个ByteArrayInputStream
        ByteArrayInputStream bais=new ByteArrayInputStream(buf,0,3);
        byte[] buff=new byte[bais.available()-1];
        try {
   
            bais.read(buff);
            System.out.println(new String(buff));
        } catch (IOException e) {
   
            e.printStackTrace();
        }finally{
   
            try {
   
                bais.close(); // 关闭 ByteArrayInputStream 无效,是一个空实现
            } catch (IOException e) {
   
                e.printStackTrace();
            }
        }   

        // 仍然可以继续读
        int read = bais.read();
        System.out.println((char)read);
    }

(4)BufferInputStream

BufferedInputStream 是 FilterInputStream 的子类,作为过滤器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。并且支持 mark() 标记和 reset() 重置方法,mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。
BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置
不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

源码关键字段

//内置缓存字节数组的大小 8KB
private static int defaultBufferSize = 8192;	
//内置缓存字节数组	
protected volatile byte buf[];	
//当前buf中的字节总数、注意不是底层字节输入流的源中字节总数
protected int count;		
//当前buf中下一个被读取的字节下标	
protected int pos;		
//最后一次调用mark(int readLimit)方法记录的buf中下一个被读取的字节的位置
protected int markpos = -1;	
//调用mark后、在后续调用reset()方法失败之前云寻的从in中读取的最大数据量、用于限制被标记后buffer的最大值
protected int marklimit;	

 //返回底层流对应的源中有效可供读取的字节数 
int available();        
//关闭此流、释放与此流有关的所有资源  
void close();  
//查看此流是否支持mark
boolean markSupport();  
//标记当前buf中读取下一个字节的下标    
void mark(int readLimit); 
//读取buf中下一个字节    
int read();  
//读取buf中下一个字节    
int read(byte[] b, int off, int len);  
//重置最后一次调用mark标记的buf中的位子    
void reset();   
//跳过n个字节、 不仅仅是buf中的有效字节、也包括in的源中的字节   
long skip(long n);  

构造函数

BufferedInputStream(InputStream in)  // 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 
BufferedInputStream(InputStream in, int size)   // 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

(5)PipedInputStream

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。 通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

管道连接的方法

①在构造时以管道输出流对象做参数来构造,即使用PipedInputStream(PipedOutputStream src) 构造;
②使用connect(PipedOutputStream src) 方法来建立连接;

构造函数

PipedInputStream()  // 创建尚未连接的 PipedInputStream。 
PipedInputStream(int pipeSize)  // 创建一个尚未连接的 PipedInputStream,并对管道缓冲区使用指定的管道大小。 
PipedInputStream(PipedOutputStream src)   // 创建 PipedInputStream,使其连接到管道输出流 src。 
PipedInputStream(PipedOutputStream src, int pipeSize)   // 创建一个 PipedInputStream,使其连接到管道输出流 src,并对管道缓冲区使用指定的管道大小。 

示例

public class PipedStream {
   
    public static void main(String[] args) throws IOException {
   

        PipedInputStream input = new PipedInputStream();
        PipedOutputStream output = new PipedOutputStream();

        // 使输入管道流与输出管道流联通
        input.connect(output);

		// 将其放在两个线程里,避免发生死锁
        new Thread(new Input(input)).start();
        new Thread(new Output(output)).start();

    }

}

class Input implements Runnable {
   

    private PipedInputStream in;

    Input(PipedInputStream in) {
   
        this.in = in;
    }

    public void run() {
   

        try {
   
            byte[] buf = new byte[1024];
            int len = -1;
            while ((len = in.read(buf)) != -1) {
    //read 读取从管道输出流写入的数据,没有读到数据则阻塞
                String s = new String(buf, 0, len);
                System.out.println("s=" + s);
            }
            in.close();
        } catch (Exception e) {
   
            e.printStackTrace();
        }

    }
}

class Output implements Runnable {
   
    private PipedOutputStream out;

    Output(PipedOutputStream out) {
   
        this.out = out;
    }

    public void run() {
   

        try {
   
            while (true) {
     // 模拟数据写入
                Thread.sleep(3000);
                out.write("Output管道写入的数据!".getBytes());
            }
        } catch (Exception e) {
   
            e.printStackTrace();
        }finally{
   
            try {
   
                out.close();
            } catch (IOException e) {
   
                e.printStackTrace();
            }
        }
    }
}

2、OutputStream 类


转载:https://blog.csdn.net/IT__learning/article/details/116743206
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场