zoukankan      html  css  js  c++  java
  • 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
    ");
    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来进行缓冲处理。

  • 相关阅读:
    004-核心技术-netty概述、传统IO、Reactor线程模型
    003-核心技术-IO模型-NIO-基于NIO群聊示例
    002-核心技术-IO模型-NIO【Selector、Channel、Buffer】、零拷贝
    018-redis-命令合计
    【整理】js、python、java分别对url进行编码和解码
    深度 | 翟东升:写在美帝国撤军和阿富汗政权溃散之际
    修改Windows10 命令终端cmd的编码为UTF-8 && IDEA控制台输出中文乱码
    Dockerfile文件中的ENTRYPOINT,CMD命令跟k8s中command,args之间的关系
    服务器带宽,流量之间的关系
    值得收藏的下载地址
  • 原文地址:https://www.cnblogs.com/lht-record/p/11299433.html
Copyright © 2011-2022 走看看