Java输入输出流

相关概念

流的概念:

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。流的源端和目的端可简单地看成是数据的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。

流的划分:

按照流向划分数据流可分为输入流和输出流。

输入流:从磁盘,光盘,网络,键盘输入数据到内存。io包中的输入流都继承自InputStream或Reader。

输出流:从内存输出数据到磁盘,磁带,显示器等地。io包中的输出流都继承自OutputStream或Writer。

按照数据组织形式划分数据流可分为字节流和字符流。

字节流:byte为数据的最小单位。io包中的输入流都继承自InputStream或OutputStream。

字符流:char为数据的最小单位。io包中的输入流都继承自Reader或Writer。

按照功能划分数据流可分为低级流(节点流)和高级流(处理流)。

低级流(节点流):节点流是可以直接从/向一个特定的数据源(例如磁盘、内存、网络)读/写数据的流。

高级流(处理流):高级流不可直接从数据源读/写数据,而是连接在已存在的流(可以是低级流或高级流)之上,为流添加额外的扩展功能。

输入输出流总体划分

字节流分类及其作用

字符流分类及其作用

通用API(黑体为常用API)

OutputStream

public abstract void write(int b)              //向输出流写入单个字节

public void write(byte[] data)           //将字节数组data中数据写入到输出流中

public void write(byte[] data, int offset, int length)     //将字节数组data中“offset开始,共length长“的数据写入到输出流中

public void flush()        //刷新该输出流,强制数据写入到输出流

void close()      //关闭输出流并释放与该流关联的所有系统资源

InputStream

int available()      //可以不受阻塞地从此输入流读取(或跳过)的估计字节数

void close()     //关闭此输入流并释放与该流关联的所有系统资源

void mark(int readlimit)     //在此输入流中标记当前的位置,如果读取字节数超过readlimit,则不标记任何字节

boolean markSupported()     //测试此输入流是否支持 mark 和 reset 方法

abstract int read()     //从输入流中读取数据的下一个字节

int read(byte[] b)     //从输入流中读取一定数量的字节,并将其存储在字节数组 b 中

int read(byte[] b, int off, int len)     //将输入流中从"off开始的最多 len字节数据"读入 byte 数组

void reset()     //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置

long skip(long n)     //跳过和丢弃此输入流中数据的 n 个字节

Reader

abstract  void  close()     //关闭该流并释放与之关联的所有资源

void  mark(int readAheadLimit)     //标记流中的当前位置

boolean  markSupported()     //判断此流是否支持 mark() 操作

int  read()     //读取单个字符

int  read(char[] cbuf)     //将字符读入数组

abstract  int  read(char[] cbuf, int off, int len)     //将字符读入数组的“offset开始,共length长”的部分

int  read(CharBuffer target)     //试图将字符读入指定的字符缓冲区

boolean  ready()     //判断是否准备读取此流

void  reset()     //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置

long  skip(long n)     //跳过个字符

Writer

Writer  append(char c)     //将单个字符添加到输出流                追加模式

Writer  append(CharSequence csq)     //将指定字符序列添加到输出

Writer  append(CharSequence csq, int start, int end)     //将指定字符序列的子序列添加到输出流

abstract  void  close()     //关闭此流

abstract  void  flush()     //刷新该流的缓冲

void  write(char[] cbuf)     //写入字符数组

abstract  void  write(char[] cbuf, int off, int len)     //写入字符数组的某一部分

void  write(int c)     //写入单个字符

void  write(String str)     //写入字符串

void  write(String str, int off, int len)     //写入字符串的某一部分

部分流的特点

ByteArrayOutputStream:写入ByteArrayOutputStream的数据被写入到一个byte数组,缓冲区会随着数据的不断写入而自动增长;可使用toByteArray()和toString()获取数据;无法关闭,调用close方法仍然可以之后向该流写入数据。(ByteArrayInputStream类似)

public static void main(String args[]) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream(1);  //缓冲区长度为4
    baos.write("helloworld".getBytes());   //可写入超过1字节数据
    byte b[] = baos.toByteArray();
    String str = baos.toString();     //toByteArray和toString方法
    baos.close();      //尝试关闭
    baos.write("!".getBytes());  //仍可写入
    baos.writeTo(System.out);    //写到特定输出流中
    //结果:helloworld!
}

FileoutputStream:用于向文件进行写入操作,getFD方法可获取文件描述符,getChannel方法可获取文件通道。

FileOutputStream fos = new FileOutputStream("fa.txt");
FileChannel fc = fos.getChannel();
FileDescriptor fd = fos.getFD();

ObjectOutputStream:组合ObjectInputStream来进行对基本数据或者对象的持久存储

PipedOutputStream:和PipedInputStream一起使用,能实现多线程间的管道通信

DataOutputStream:装饰其他的输出流,允许应用程序以与机器无关方式向底层写入基本Java数据类型。(DataInputStream类似效果)

DataOutputStream dos = new DataOutputStream(new ByteArrayOutputStream());
dos.writeDouble(3.14);   //写入double类型
dos.writeBoolean(true);   //写入布尔类型
dos.writeChar(97);   //写入char类型

BufferedOutputStream:为另一个输出流添加缓冲功能,BufferedInputStream类似效果。

PrintStream:装饰其他输出流,为其他输出流添加功能,方便的打印各种数据值。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
ps.format("%-3.2f",3.1415926);
baos.writeTo(System.out);
//结果:3.14

PushbackInputStream:允许从流中读取数据,然后在需要时推回该流。

byte[] b = "heo".getBytes();
PushbackInputStream pis = new PushbackInputStream(newByteArrayInputStream(b),3); //回推缓存三字节
byte[] bb = new byte[5];
pis.read(bb,0,2);   //将b中数据he读入输入流中
pis.unread("ll".getBytes());   //将ll放入回推缓存
pis.read(bb,2,3);     //读取回推缓存数据后再读取b中数据
System.out.println(new String(bb));
//结果
//hello

OutputStreamWriter:从字符流到字节流的桥接,自动将要写入流中的字符编码成字节。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos,"utf-8");  //指定编码
osw.write("Sakura\n");
osw.write("最好了");
osw.flush(); 
baos.writeTo(System.out);
//结果
//Sakura
//最好了

各种流的使用场景

一,数据输入还是输出
1) 输入:使用Reader、InputStream 类型的子类。
2) 输出:使用Writer、OutputStream 类型的子类。

二,数据格式

1) 二进制格式:使用InputStream、OutputStream 及其所有带 Stream结尾的子类。
2) 纯文本格式:使用Reader、Writer 及其所有带 Reader、Writer 的子类。

三,数据源

1) 文件:字节流使用FileInputStream和FileOutputStream;对于字符流使用FileReader和 FileWriter。
2) 字节数组:则使用ByteArrayInputStream和ByteArrayOutputStream。
3) 字符数组:则使用CharArrayReader和CharArrayWriter。
4) String对象:字节流使用StringBufferInputStream和StringBufferOuputStream;字符流使用StringReader和StringWriter。

六、特殊需要
1) 转换流:InputStreamReader、OutputStreamWriter。
2) 对象输入输出:ObjectInputStream、ObjectOutputStream。
3) 线程间通信:PipeInputStream、PipeOutputStream、PipeReader、PipeWriter。
4) 合并输入:SequenceInputStream。
5) 格式化输出,则使用PrintStream或PrintWriter。

6)更特殊的需要:PushbackInputStream、PushbackReader、LineNumberInputStream、LineNumberReader。

七、缓冲

字节流使用BufferedInputStream和BufferedOutputStream;字节流使用BufferedReader和BufferedWriter。

一个实例

复制文件夹的类

import java.io.*;

public class Test{
    public static void main(String[] args) {
        copyClass cc = new copyClass("src\\old","src\\new");
        try {
            long start = System.currentTimeMillis();
            cc.copyDir(cc.getSource(),cc.getDestination(),false);
            long end = System.currentTimeMillis();
            System.out.println(end-start);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class copyClass{
    private File source;
    private File destination;
    copyClass(String  source, String destination){
        this.source = new File(source);
        this.destination = new File(destination);
    }

    void copyContent(File source,File destination,boolean byByte) throws IOException {
        if(byByte){       //字节数组复制
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination));
            byte[] b = new byte[1024];
            while (bis.available()!= 0){
                bis.read(b);
                bos.write(b);
                bos.flush();
            }
            bis.close();
            bos.close();
        }
        else {     //单个字节复制
            FileInputStream fis = new FileInputStream(source);
            FileOutputStream fos = new FileOutputStream(destination);
            while (fis.available()!=0){
                int i = fis.read();
                fos.write(i);
            }
            fis.close();
            fos.close();
        }
    }

    void copyDir(File source, File destination,boolean byBytes) throws IOException{
        if (!source.isDirectory()){  //是文件则复制其内容
            copyContent(source,destination,byBytes);
        }
        else {
            if (destination.mkdirs()){   //是文件夹则递归复制
                File[] fs = source.listFiles();
                for (int i = 0; i < fs.length; i++) {
                    String fileName = fs[i].getName();
                    File newDestination = new File(destination.getPath() + File.separator + fileName);
                    copyDir(fs[i], newDestination,byBytes);
                }
            }
            else {
                System.out.print("Creat dirs failed!");
            }
        }

    }
    
    public File getSource() {
        return source;
    }

    public void setSource(File source) {
        this.source = source;
    }

    public File getDestination() {
        return destination;
    }

    public void setDestination(File destination) {
        this.destination = destination;
    }
}

结果

//采用单个字节复制方法耗时
1317
//采用缓冲区字节数组复制方法耗时
52
//减少了超过95%的时间

至关重要的是:除非数据流非常小,否则都应该使用数组来进行流的处理,必要时可用BufferedInputStream或BufferedOutputStream或BufferedWriter/BufferedReader来进行缓冲处理。

posted @ 2019-08-05 21:23  Sakura_lht  阅读(403)  评论(0编辑  收藏  举报