一、流的概念

  流是字节序列的抽象概念。流和文件的差别:文件是数据的静态存储形式,而流是指数据传输时的形态。文件只是流的操作对象之一。流按其操作的对象不同可以分为文件流、网络流、内存流、磁带流等。Java流类可分为两个大类:节点流类和过滤流类(也叫处理流类)

Java流输入输出基本如原理

Java把这些不同来源和目标的数据都统一抽象为数据流。

Java流的分类

  按流向分:

      输入流: 程序可以从中读取数据的流(读取流)。

      输出流: 程序能向其中写入数据的流(写出流)。

  按数据传输单位分:

       字节流: 以字节为单位传输数据的流

        字符流: 以字符为单位传输数据的流

   按功能分:

       低级流:(节点流) 用于直接操作目标设备的流 ,有明确的来源和去向。比如我们用一个类和一个文件或网络相关联,那么这个类就叫做节点流类,这个文件或网络就叫做流的节点。

        高级流:(过滤(处理)流) 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。 不能独立存在,需要处理另一个流。

java.io常用类

JDK所提供的所有流类位于java.io包中,都分别继承自以下四种抽象流类。

InputStream:继承自InputStream的流都是用于向程序中输入数据的,且数据单位都是字节(8位)。

OutputSteam:继承自OutputStream的流都是程序用于向外输出数据的,且数据单位都是字节(8位)。

Reader:继承自Reader的流都是用于向程序中输入数据的,且数据单位都是字符(16位)。

Writer:继承自Writer的流都是程序用于向外输出数据的,且数据单位都是字符(16位)。

接口摘要

Closeable
Closeable 是可以关闭的数据源或目标。
DataInput
DataInput 接口用于从二进制流中读取字节,并根据所有 Java 基本类型数据进行重构。
DataOutput
DataOutput 接口用于将数据从任意 Java 基本类型转换为一系列字节,并将这些字节写入二进制流。
Externalizable
Externalizable 实例类的唯一特性是可以被写入序列化流中,该类负责保存和恢复实例内容。
FileFilter
用于抽象路径名的过滤器。
FilenameFilter
实现此接口的类实例可用于过滤器文件名。
Flushable
Flushable 是可刷新数据的目标地。
ObjectInput
ObjectInput 扩展 DataInput 接口以包含对象的读操作。
ObjectInputValidation
允许验证图形中对象的回调接口。
ObjectOutput
ObjectOutput 扩展 DataOutput 接口以包含对象的写入操作。
ObjectStreamConstants
写入 Object Serialization Stream 的常量。
Serializable
类通过实现 java.io.Serializable 接口以启用其序列化功能。

类摘要


BufferedInputStream
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。
BufferedOutputStream
该类实现缓冲的输出流。
BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
ByteArrayInputStream
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。
ByteArrayOutputStream
此类实现了一个输出流,其中的数据被写入一个 byte 数组。
CharArrayReader
此类实现一个可用作字符输入流的字符缓冲区。
CharArrayWriter
此类实现一个可用作 Writer 的字符缓冲区。
Console
此类包含多个方法,可访问与当前 Java 虚拟机关联的基于字符的控制台设备(如果有)。
DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
DataOutputStream
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
File
文件和目录路径名的抽象表示形式。
FileDescriptor
文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。
FileInputStream
FileInputStream 从文件系统中的某个文件中获得输入字节。
FileOutputStream
文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。
FilePermission
此类表示对文件和目录的访问。
FileReader
用来读取字符文件的便捷类。
FileWriter
用来写入字符文件的便捷类。
FilterInputStream
FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
FilterOutputStream
此类是过滤输出流的所有类的超类。
FilterReader
用于读取已过滤的字符流的抽象类。
FilterWriter
用于写入已过滤的字符流的抽象类。
InputStream
此抽象类是表示字节输入流的所有类的超类。
InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。
LineNumberInputStream
已过时。此类错误假定字节能充分表示字符。


(一)InputStream和OutputStream类

  1、InputStream类:程序可以从中连续读取字节的对象叫输入流,在Java中,用InputStream类来描述所有输入流的抽象概念。这个类是抽象类。

  2、OutputStream类:程序可以向其连续写入字节的对象叫输出流,在Java中,用OutputStream类来描述所有输出流的抽象概念。这个类也是抽象类。

(二)、FileInputStream和FileOutputStream类

  1、这两个类分别用来创建磁盘文件的输入流和输出流对象,通过他们的构造函数来指定文件路径和文件名。

  2、有两种方法创建FileInputStream

  (1)、FileInputStream inFirst = new FileInputStream("test.txt");

  (2)、File f = new File("test.txt");

FileInputStream inSecond = new FileInputStream(f);

(三)、Reader和Writer类

  1、上面的几个类,他们在读取或写入的时候,都是进行字节操作,要显示出来还要进行字符串转换。

  2、用Reader和Writer类就可以直接进行字符串读取,简化了字符串的输入输出编程。但他们都是抽象类,用他们的子类FileReader、FileWriter就可以直接使用。

(四)、PipedInputStream和PipedOutputStream类

PipedInputStream和PipedOutputStream类用于在应用程序中创建管道通信。一般用在进程间通信。

(五)、PipedWriter和PipedReader类

这两个类用于在管道中的字符串通信。

(六)、ByteArrayInputStream和ByteArrayOutputStream类

ByteArrayInputStream和ByteArrayOutputStream,用于以IO流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或者内存映像文件的功能。

二、文件的输入输出流

   FileInputStream和FileOutputStream类

  这两个类分别用来创建磁盘文件的输入流和输出流对象,通过他们的构造函数来指定文件路径和文件名。

  有两种方法创建FileInputStream

  (1)、FileInputStream inFirst = new FileInputStream("test.txt");

  (2)、File f = new File("test.txt");

  FileInputStream inSecond = new FileInputStream(f);

1、FileOutputStream

构造方法:

FileOutputStream(File file) 创建向指定文件中写数据的文件输出流。

FileOutputStream(String str) 创建一个向具有指定名称的文件中写入数据的输出文件流。

若想打开文件后继续在文件末尾追加内容,而不是重写文件,我们可以使用两个参数的构造方法。

第二个是boolean,当为true时候则是追加内容。

* FileOutputStream(File file, boolean append)

* FileOutputStream(String str, boolean append)


例:
/**
* write(int b)
* write(byte[] buff)
* write(byte[] buff,int off,int len)
*/
File file = new File("file.txt");
/**
* 创建文件输入流
* 构造方法
* FileOutputStream(File file)
* 创建向指定文件中写数据的文件输出流
* FileOutputStream(String str)
* 创建一个向具有指定名称的文件中写入数据的输出文件流。
*/
//FileOutputStream fos = new FileOutputStream(file);
FileOutputStream fos = new FileOutputStream("file.txt");
/**
* 若想打开文件后继续在文件末尾追加内容,而不是重写文件,我们可以使用两个参数的构造方法。
* 第二个是boolean,当为true时候则是追加内容。
* FileOutputStream(File file, boolean append)
* FileOutputStream(String str, boolean append)
*/
int a=123456789;
fos.write(a>>>24);
fos.write(a>>>16);
fos.write(a>>>8);
fos.write(a);
fos.close();


2、FileInputStream

构造方法:

FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。

FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

例:


File file = new File("file.txt");
//FileInputStream fis = new FileInputStream(file);
FileInputStream fis = new FileInputStream("file.txt");
//读取的整数
int num = 0;
int data = fis.read();
//对应的output 这里就要左移
/**
* 1.
* num 00000000 00000000 00000000 00000000
* data 00000000 00000000 00000000 01111111
* data<<24 01111111 00000000 00000000 00000000
*/
num = num | (data<<24);
data = fis.read();
num = num | (data<<16);
data = fis.read();
num = num | (data<<8);
data = fis.read();
num = num | data;
                                                                                     
System.out.println(num);
fis.close();
                                                                                     
/**
* 使用 fis 和 fos 实现文件的COPY
*/
public static void copy(File src,File desc) throws IOException{
    if(src==null){
        throw new RuntimeException("源文件对象为空");
    }
    if(!src.exists()){
        throw new RuntimeException("源文件不存在");
    }
    if (desc == null) {
        throw new RuntimeException("目标对象为空");
    }
    if(!desc.exists()){
        /** 创建文件需要强制捕获IOException */
        desc.createNewFile();
    }
    /**
    * 为了保证效率,不要一个个的字节读写
    * 创建字节数组,批量读写。
    */
    byte[] buf = new byte[1024*10];//10k
    int len = -1;//记录每次真实的读取字节数
    FileInputStream fis = new FileInputStream(src);
    FileOutputStream fos = new FileOutputStream(desc);
    while ((len=fis.read(buf))>0) {
        fos.write(buf, 0, len);
    }
    fis.close();
    fos.close();
}



三、节点流和过滤流

节点流:从特定的地方读写的流类,如:磁盘或一块内存区域。

过滤流:使用节点流作为输入输出,过滤流是使用一个已经存在的输入流或输出流连接创建的。


比如:

FileInputStream和FileOutputStream,节点流 属于低级流。

BufferedInputStream和BufferedOutputStream,过滤流 属于高级流。

DataInputStream 和 DataOutputStream,过滤流,需要使用已经存在的节点流来构造,提供读写Java中的基本数据类型的功能。

PipedInputStream 和 PipedOutputStream,管道流,用于线程间的通信,一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。要使用管道流,必须同时构造管道输入流和管道输出流。


缓冲字节输入流与缓冲字节输出流。BIS/BOS,内部维护者一个缓冲区的内存。


BufferedInputStream是一个带有怀冲去的输入流,通常使用它可以提高我们的读取效率。

1)每次调用read方法的时候,它首先尝试从缓冲区里读取数据。

2)若读取失败(缓冲区没有可读数据)

3)则选择从物理数据源(文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区

4)在后在将缓冲区中的内容部分或全部返回给用户,由于从缓冲区里读取数据远比直接从物理数据源读取数度快 。

BufferedOutputStream为IO操作提供了缓冲区,写入操作时候,加上缓冲流,这种流模式是为了提高IO的性能。

属于高级流,我们使用要创建有的低级流。

例:


/**
* 使用 缓冲流 来进行 文件复制 BOS BIS
*/
public static void copy1(File src,File desc) throws IOException{
    if (src == null) {
        /**
        * IllegalArgumentException 是 RuntimeException的子类
        * 描述无效的参数异常
        */
        throw new IllegalArgumentException("源文件对象为空");
    }
    if (!src.exists()) {
        throw new IllegalArgumentException("源文件不存在");
    }
    if (desc == null) {
        throw new IllegalArgumentException("目标对象为空");
    }
    if (!desc.exists()) {
        /**
        * 创建文件需要强制捕获IOException
        */
        desc.createNewFile();
    }
                                                                                         
    /**
    * 使用缓冲字节输入输出流复制文件
    */
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;  
    try {
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(desc);
        bis=new BufferedInputStream(fis);
        bos=new BufferedOutputStream(fos);      
        int b=0;
        while ((b=bis.read())!=-1) {
            bos.write(b);
        }
    } finally{
        /**
        * 应当关闭放在 finally中
        * 确保流的关闭。
        */
        if(bis != null){
            bis.close();
        }
        if(bos != null){
            bos.flush();//将缓冲区的内容全部写出么
            bos.close();
        }
    }
}


区分高级流的最显著特点是:看构造方法,若需要传入其他流,那就是高级流。

高级流不能独立存在,一维没有意义,高级流的作用就是对它处理的流中的数据进行加工方便我们读写的。


三、IO-高级流之DataInputStream和DataOutputStream

可以方便的对基本类型数据进行读写。

是对“流”功能的扩展,可以更方面的读取int,long,字符等类型数据。也就是基本类型序列化/反序列化方法。

1、常用方法

int readInt() 读取一个输入字节返回一个int值

double readDouble() 读取八个输入字节并返回一个double

void writeInt(int) 将一个int值写入输出流

void writeDouble(double e) 将一个double值写入输出流

void writeUTF()将表示长度信息的两个字节写入输出流,后跟字符串s中每个字符的utf-8修改版表示形式。

例子:


/**
* 向文件中写入一个int
*/
public static void writeInt(File file, int num) throws IOException{
    DataOutputStream dos = null;
    try {
        FileOutputStream fos = new FileOutputStream(file);
        /**
        * 可以先将文件字节流转换成一个缓冲字节输出流
        * 这样就可以批量写出,提高效率。
        */
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        dos = new DataOutputStream(bos);
        //dos = new DataOutputStream(fos);
        dos.write(num);
    } finally {
        if(dos != null){
            dos.close();
        }
    }
}


四、IO-字符流

以字符为单位来读写数据。字符的底层任然是字节流。

字符输入流以Reader结尾;字符输出流以Writer结尾。 (高级流)

InputStreamReader 完成byte流解析为char流,按照编码解析。

OutputStreamWriter 提供char流到byte流,按照编码处理。


1、InputStreamReader 和 OutputStreamWriter(ISR/OSW)

在创建流的时候可以指定编码,这样可以指定的编码集来读取和写入。


/**
* 以字符为单位写入数据
*/
public static void writer() throws IOException {
    OutputStreamWriter writer = null;
    try {
        FileOutputStream fos = new FileOutputStream(new File("data.dat"));
        // 以字符为单位写入数据
        //指定了字符集,不指定就是是用系统默认的。
        writer = new OutputStreamWriter(fos,"UTF-8");
        /**
        * writer(char[] data) 将一个char数组中的所有字符一次写入
        * writer(String s) 将字符串一次性写入
        */
        writer.write("你好!");      
    } finally {
        if (writer != null) {
            writer.close();
        }
    }
}
/**
* 以字符为单位读取数据
*/
public static void reader() throws IOException {
    InputStreamReader reader = null;
    try {
        FileInputStream fis = new FileInputStream(new File("data.dat"));
        // 以字符为单位读取数据
        //根据字符集来读取
        reader = new InputStreamReader(fis);
        int data = -1;
        while ((data = reader.read()) != -1) {
            /**
            * 每次读取一个char
            */
            System.out.print((char) data);
            }
        } finally {
            if (reader != null) {
            reader.close();
        }
    }
}


2、BufferedReader 和 BufferedWriter

以行为单位进行读写数据,是对读写功能扩展,极大的方面了文本读写操作。


BufferedReader reader = null;
    BufferedWriter writer = null;
    try{
        /**
        * BufferedReader 要处理一个字符流
        * 而我们读取文件时创建的是一个字节输入流。
        * 所以我们要先将字节流转换为字符输入流,
        * 在将字符输入流转换为缓冲字符输入流。
        */
        //1.创建文件的字节输入流
        FileInputStream fis = new FileInputStream("data.dat");
        //2.将字节流转换为字符流
        InputStreamReader isr = new InputStreamReader(fis);
        //如果需要编码就要加上编码集,文件是什么编码我们就要用什么编码来读取 new InputStreamReader(fis, "GBK");
        //至于写入的时候我们也可以更加需要,这样就完成了转码工作。
        //3.将字符输入流转换为按行读取的缓冲字符输入流
        reader = new BufferedReader(isr);
        writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("Data1.dat"))));
                                                                                  
        /**
        * 每次读取一行数据,保存在一个字符串中.
        * reader.readLine()方法,一次读取一行字符。
        * 一行是指读取到“\n”为止,若方法返回null说明EOF(末尾)
        */
        String lineData = null;
        while ((lineData=reader.readLine())!=null) {
            writer.write(lineData);//写入到字符输出流,写入文件。
            writer.write("\n");//保持格式一致,换行这些
        }
    } finally {
        if(reader != null){
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(writer != null){
            try {
                writer.flush();//遇到缓冲区的问题关闭之前flush下使其缓冲区里的数据全部写入到文件在关闭。
                writer.close();
            } catch (IOException e) {
               e.printStackTrace();
            }
        }
    }


3、FileReader与FileWriter

文件字符输入流和文件字符输出流,用于文本文件读写。

这两个类只是能快捷的方式读写文本文件,不支持字符集。

如果字符集不同需要转码,就要使用InputStreamReader.

/** 创建读写文本文件的字符输入流 */


FileReader reader = new FileReader("print.txt");
FileWriter writer = new FileWriter("copy_prin.txt");
int data = -1;
while ((data=reader.read())!=-1) {
    writer.write(data);
}
try{}
finally{
    reader.close();
    writer.flush();
    writer.close();
}


4、PrintWriter

另一种缓冲字符输出流。

有很多构造方法:

PrintWriter(File file) 创建向给定文件写字符的字符输出流。

PrintWriter(OutputStream out) 常见一个基于字节输出流的缓冲字符输出流。

PrintWriter(Writer writer) 将字符输出流转换为缓冲字符输出流。

PrintWriter(String fileName) 创建给定路径的文件的字符输出流。


/** 创建向文件pw.txt写入数据的字符输出流 */
PrintWriter pw = new PrintWriter("pw.txt");
pw.println("Hello Word!");
pw.flush();
pw.close();
/**
* 在某些情况下,我们需要在每次输入之后都晴空缓冲区,
* 而这时候我们都需要调用flush方法,这样比较麻烦,
* 这时候我们可以调用PrintWriter的重载构造方法建立一个带有自动刷新的输出流。
* PrintWriter(OutputStream out,boolean autoFlush)
* PrintWriter(Writer writer,boolean autoFlush)
* 这样当我们调用println方法后,会自动flush()
*/
PrintWriter pw=new PrintWriter(new FileOutputStream("pw.txt"),true);
pw.println("Hello Word!");
pw.close();


5、访问控制台


/**
* 将键盘数据写到 文本文件中
*/
//获取来自键盘的字节输入流
//这是一个低级流,有明确来源
InputStream input = System.in;
//转为字符流
InputStreamReader isr = new InputStreamReader(input);
//转换为缓冲字符输入流
BufferedReader reader = new BufferedReader(isr);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("print.txt")));
String line = null;
while ((line=reader.readLine())!=null) {
    if("exit".equals(line))
    break;
    writer.write(line+"\n");
    /*System.out.println("print:"+line);*/
}
try{}
finally{
    writer.flush();
    writer.close();
}