IO流-节点流和处理流

流的分类

流的抽象基类

字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

流的具体分类

按照操作数据的单位分类:
字节流:操作数据的单位为1byte,用于字节数据(如图片数据)的传输;
字符流:操作数据的单位为2byte,用于字符数据(如文本数据)的传输。

按照数据流向分类:
输入流
输出流

按照流的作用对象分类:
节点流:直接作用于数据;
处理流:作用于数据的传输,即作用于节点流。

节点流

概述

节点流是直接读写文件的IO流,传输路径为:文件内存输入流内存文件输出流。因此节点流又称为文件流

文本文件与非文本文件

文本文件

文本文件的数据存储单元为字符,文件中的每一个字符单元都可在相应的字符集中找到,如ASCIIUTF-8。对于ASCII字符集,字符单元的长度为1byte;对于UTF-8字符集,字符单元的长度是1~4byte的可变长度。文本文件的常见格式有:

.txt/.java/.c/.h/.cpp/.dat/.html/.xml

非文本文件

非文本文件的数据存储单元为字节。非文本文件的常见格式有:

.png/.jpeg/.mp3/.mp4/.doc/.ppt/.pdf

字节流

字节流用于读写非文本文件
字节流也可以对文本文件进行操作,因为字符单元可以完整拆分为一个或多个字节单元,在二进制层面,不会产生缺位或多位。
字节流不能对文本文件进行操作,因为文本文件的正确解码方式是字符解码,而字节流的读操作只能进行单字节解码。对于文本文件中的多字节字符,使用单字节解码会出现乱码。对于文本文件中的单字节字符,依然可以正确解码。

FileInputStream

常用构造器

/* 通过打开与本地文件的连接创建输入流,
   该本地文件由 File对象 指定。 */
public FileInputStream(File file) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建输入流,
   该本地文件由 路径字符串 指定。 */
public FileInputStream(String name) throws FileNotFoundException {}

常用方法

/* 返回可以从该输入流中读取(或跳过)而不阻塞的字节数的估计数,
   该估计数可能为0,或者在检测到流结束时为0。*/
public int available() throws IOException {}

/* 每次读取1byte.返回int类型结果。
   到达文件末尾,返回-1. */
/* 注意:
   1.返回类型为int,注意进行类型转换(byte);
   2.该方法线程安全,调用该方法会导致其它操作相同资源的线程阻塞,
     直到检测到流的末尾或抛出异常。 */
public int read() throws IOException {}

/* 每次读取多个字节,并将多个字节保存到指定的byte[]数组中,
   每次读取的字节数等于或小于(到达文件末尾时)指定byte[]数组的长度。 
   返回读取到的字节数,没有更多的字节可以读取时,返回-1. */
/* 注意:
   该方法线程安全,调用该方法会导致其它操作相同资源的线程阻塞,
   直到检测到流的末尾或抛出异常。 */
public int read(byte[] b) throws IOException {}

/* 每次读取多个字节,并将多个字节保存到指定的byte[]数组中。
   参数off指定本次读取在byte[]数组中存储的起始偏移量,
   参数len指定每次读取的最大字节数。 
   本次读取存储在byte[]数组中的索引位置为 off-1 ~ off+len-1. 
   返回读取到的字节数,没有更多的字节可以读取时,返回-1. */
/* 注意:
   该方法线程安全,调用该方法(len不为0时)会导致其它操作相同资源的线程阻塞,
   直到检测到流的末尾或抛出异常。 */
public int read(byte[] b,int off,int len) throws IOException {}

/* 跳过并丢弃指定的字节数n。如果n为负,skip()将尝试从当前位置向后跳过。
   由于种种原因,实际跳过的字节数可能不等于指定字节数,
   skip()将返回实际跳过的字节数。向前跳过,返回相应正值;
   向后跳过,返回相应负值。*/
/* 目标文件不支持向后跳转而试图向后跳转时,抛IOException. */
public long skip(long n) throws IOException {}

/* 关闭输入流并且释放所有与该输入流相关联的本地文件。*/
/* 注意:
   1.流使用完毕后,必须调用close()关闭流,否则可能导致内存泄露;
   2.close()方法的调用最好置于finally{}代码块中,保证流最终一定关闭。 */
public void close() throws IOException {}

FileOutputStream

常用构造器

/* 通过打开与本地文件的连接创建输出流,
   该本地文件由 File对象 指定。 */
/* 注意:
   1.使用此构造器创建的输出流向文件写入数据时,
     若目标文件本地已存在,新数据将覆盖原有数据。
   2.若目标文件本地不存在,创建输出流对象将创建该文件;
     若目标文件的父级路径本地不存在,将抛FileNotFoundException. */
public FileOutputStream(File file) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建输出流,
   该本地文件由 路径字符串 指定。 */
/* 注意:
   1.使用此构造器创建的输出流向文件写入数据时,
     若目标文件本地已存在,新数据将覆盖原有数据。
   2.若目标文件本地不存在,创建输出流对象将创建该文件;
     若目标文件的父级路径本地不存在,将抛FileNotFoundException. */
public FileOutputStream(String name) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建输出流,
   该本地文件由 File对象 指定。 
   参数append指定新数据是否向文件末尾追加:
   append为true,新数据在原有数据末尾追加;
   append为false,新数据覆盖原有数据。*/
/* 注意:
   1.使用此构造器创建的输出流向文件写入数据时,
     若目标文件本地已存在,新数据将覆盖原有数据。
   2.若目标文件本地不存在,创建输出流对象将创建该文件;
     若目标文件的父级路径本地不存在,将抛FileNotFoundException. */
public FileOutputStream(File file,boolean append) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建输出流,
   该本地文件由 路径字符串 指定。 
   参数append指定新数据是否向文件末尾追加:
   append为true,新数据在原有数据末尾追加;
   append为false,新数据覆盖原有数据。*/
/* 注意:
   1.使用此构造器创建的输出流向文件写入数据时,
     若目标文件本地已存在,新数据将覆盖原有数据。
   2.若目标文件本地不存在,创建输出流对象将创建该文件;
     若目标文件的父级路径本地不存在,将抛FileNotFoundException. */
public FileOutputStream(String name,boolean append) throws FileNotFoundException {}

常用方法

/* 每次向目标文件写入单个字节。*/
/* 注意:需要传入int型参数,注意进行类型转换。*/
public void write(int b) throws IOException {}

/* 每次向目标文件写入指定byte[]数组中的全部数据。*/
public void write(byte[] b) throws IOException {}

/* 每次向目标文件中写入指定byte[]数组中的部分数据,
   具体部分为指定byte[]数组中的索引位置 off-1 ~ off+len-1. */
public void write(byte[] b,int off,int len) throws IOException {}

/* 关闭输出流并且释放所有与该输出流相关联的本地文件。*/
/* 注意:
   1.流使用完毕后,必须调用close()关闭流,否则可能导致内存泄露;
   2.close()方法的调用最好置于finally{}代码块中,保证流最终一定关闭。 */
public void close() throws IOException {}

/* 刷新流 */
public void flush() throws IOException {}

使用流实现字节文件的复制

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        File from = new File("F:"+sp+"Desktop"+sp+"pic.png");
        //pic_1.png本地已存在
        File to_1 = new File("F:"+sp+"Desktop"+sp+"pic_1.png");
        //pic_2.png本地不存在
        File to_2 = new File("F:"+sp+"Desktop"+sp+"pic_2.png");
        //父级目录dir本地不存在
        File to_3 = new File("F:"+sp+"Desktop"+sp+"dir"+sp+"pic_3.png");
        
        //复制成功,原pic_1.png内容被覆盖
        byteCopy(from,to_1);
        //复制成功,pic_2.png为本次新建
        byteCopy(from,to_2);
        //复制失败,抛异常FileNotFoundException,因为父级目录dir本地不存在
        byteCopy(from,to_3);
    }
    
    public static void byteCopy(File copyFrom,File copyDest) {
        FileInputStream from = null;
        FileOutputStream dest = null;
        byte[] buffer = new byte[1024];
        int len;
        try{
            from = new FileInputStream(copyFrom);
            //新数据覆盖原有数据
            dest = new FileOutputStream(copyDest,false);
            long startT = System.currentTimeMillis();
            while((len = from.read(buffer)) != -1){
                dest.write(buffer,0,len);
                dest.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("-------------------------");
            System.out.println("Copy file from " + copyFrom.getPath() + " to "
                               + copyDest.getPath() + " finished.\n"
                               + "Takes time " + (endT-startT) + "ms.");  
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            if(from != null){
                try{
                    from.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(dest != null){
                try{
                    dest.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

-------------------------
Copy file from F:\Desktop\pic.png to F:\Desktop\pic_1.png finished.
Takes time 267ms.
-------------------------
Copy file from F:\Desktop\pic.png to F:\Desktop\pic_2.png finished.
Takes time 258ms.
java.io.FileNotFoundException: F:\Desktop\dir\pic_3.png (系统找不到指定的路径。)

字符流

字符流用于读写文本文件
字符流不能用于读写非文本文件非文本文件的存储单位是1字节,字符流的读写单位可能是多字节。使用字符流写入非文本文件时可能会导致二进制层面的多位(1字节转成多字节,不足位补0),导致非文本文件不可用;使用字符流读出非文本文件时,可能会将非文本文件中的多个1字节数据错误组织编码成多字节数据,导致读出乱码。

FileReader

常用构造器

/* 通过打开与本地文件的连接创建字符输入流,
   该本地文件由 File对象 指定。
   使用平台默认字符集。*/
public FileReader(File file) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建字符输入流,
   该本地文件由 路径字符串 指定。
   使用平台默认字符集。*/
public FileReader(String fileName) throws FileNotFoundException {}

/* 通过打开与本地文件的连接创建字符输入流,
   该本地文件由 File对象 指定。
   使用参数charset指定的字符集。*/
/* 字符集可使用常量类 StandardCharsets 中的标准字符集,
   例如:StandardCharsets.UTF_8. 
   也可以使用Charset的静态方法forName()获取,例如:
   Charset charset = Charset.forName("utf-8"); */
public FileReader(File file,Charset charset) throws IOException {}

/* 通过打开与本地文件的连接创建字符输入流,
   该本地文件由 路径字符串 指定。
   使用参数charset指定的字符集。*/
/* 字符集可使用常量类 StandardCharsets 中的标准字符集,
   例如:StandardCharsets.UTF_8. */
public FileReader(String fileName,Charset charset) throws IOException {}

常用方法

/* 返回输入流使用的字符集(字符串名)。 */
public String getEncoding() {}

/* 每次读取1个字符,返回int类型结果。
   到达文件末尾,返回-1. */
/* 注意:
   1.返回类型为int,注意进行类型转换(char);
   2.该方法线程安全,调用该方法会导致其它操作相同资源的线程阻塞,
     直到检测到流的末尾或抛出异常。 */
public int read() throw IOException {}

/* 每次读取多个字符,并将多个字符保存到指定的char[]数组中,
   每次读取的字符数等于或小于(到达文件末尾时)指定char[]数组的长度。 
   返回读取到的字符数,没有更多的字符可以读取时,返回-1. */
/* 注意:
   该方法线程安全,调用该方法会导致其它操作相同资源的线程阻塞,
   直到检测到流的末尾或抛出异常。 */
public int read(char[] cbuf) throws IOException {}

/* 每次读取多个字符,并将多个字符保存到指定的char[]数组中。
   参数off指定本次读取在char[]数组中存储的起始偏移量,
   参数len指定每次读取的最大字节数。 
   本次读取存储在char[]数组中的索引位置为 off-1 ~ off+len-1. 
   返回读取到的字节数,没有更多的字节可以读取时,返回-1. */
/* 注意:
   该方法线程安全,调用该方法(len不为0时)会导致其它操作相同资源的线程阻塞,
   直到检测到流的末尾或抛出异常。 */
public int read(char[] cbuf,int off,int len) throws IOException {}

/* 判断输入流是否准本就绪 */
public boolean ready() throws IOException {}

/* 关闭输入流并且释放所有与该输入流相关联的本地文件。*/
/* 注意:
   1.流使用完毕后,必须调用close()关闭流,否则可能导致内存泄露;
   2.close()方法的调用最好置于finally{}代码块中,保证流最终一定关闭。 */
public void close() throws IOException {}

FileWriter

常用构造器

public FileWriter(File file) throws IOException {}
public FileWriter(File file,Charset charset) throws IOException {}
public FileWriter(File file,boolean append) throws IOException {}
public FileWriter(File file,Charset charset,boolean append) throws IOException {}
public FileWriter(String fileName) throws IOException {}
public FileWriter(String fileName,Charset charset) throws IOException {}
public FileWriter(String fileName,boolean append) throws IOException {}
public FileWriter(String fileName,Charset charset,boolean append) throws IOException {}

常用方法

public String getEncoding() {}
/* 刷新输出流 */
public void flush() throws IOException {}
public void write(int c) throws IOException {}
public void write(char[] cbuf,int off,int len) throws IOException {}
public void write(String str,int off,int len) throws IOException {}
public void close() throws IOException {}

使用流实现字节文件的复制

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        //使用utf-8字符集
        final Charset charset = StandardCharsets.UTF_8;
        File from = new File("F:"+sp+"Desktop"+sp+"Main.java");
        //Test_1.java本地已存在
        File to_1 = new File("F:"+sp+"Desktop"+sp+"Test_1.java");
        //Test_2.java本地不存在
        File to_2 = new File("F:"+sp+"Desktop"+sp+"Test_2.java");
        //父级目录dir本地不存在
        File to_3 = new File("F:"+sp+"Desktop"+sp+"dir"+sp+"Test_3.java");
        
        //复制成功,原Test_1.java内容被覆盖
        charCopy(from,to_1,charset);
        //复制成功,Test_2.java为本次新建
        charCopy(from,to_2,charset);
        //复制失败,抛异常FileNotFoundException,因为父级目录dir本地不存在
        charCopy(from,to_3,charset);
    }
    
    public static void charCopy(File copyFrom,File copyDest,Charset charset){
        FileReader reader = null;
        FileWriter writer = null;
        char[] buffer = new char[100];
        int len;
        try{
            reader = new FileReader(copyFrom,charset);
            //新数据覆盖原有数据
            writer = new FileWriter(copyDest,charset,false);
            long startT = System.currentTimeMillis();
            while((len = reader.read(buffer)) != -1) {
                writer.write(buffer,0,len);
                writer.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("-------------------------");
            System.out.println("Copy file from " + copyFrom.getPath() + " to "
                               + copyDest.getPath() + " finished.\n"
                               + "Takes time " + (endT-startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            if(reader != null){
                try{
                    reader.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try{
                    writer.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }    
}

运行结果:

-------------------------
Copy file from F:\Desktop\Main.java to F:\Desktop\Test_1.java finished.
Takes time 0ms.
-------------------------
Copy file from F:\Desktop\Main.java to F:\Desktop\Test_2.java finished.
Takes time 0ms.
java.io.FileNotFoundException: F:\Desktop\dir\Test_3.java (系统找不到指定的路径。)

处理流

处理流处理流的作用对象是节点流(文件流),并非直接用于传输文件。

缓冲流

缓冲流处理流的一种,用于提升文件传输的效率。

字节缓冲流

BufferedInputStream

常用构造器:

public BufferedInputStream(InputStream in) {}

常用方法:

/* 返回可以从该输入流读取(或跳过)的字节数的估计 */
public int available() throws IOException {}
public int read() throws IOException {}
public int read(byte[] b) throws IOException {}
public int read(byte[] b,int off,int len) throws IOException {}

BufferedInputStream的创建步骤:

FileInputStream fis = null;
BufferedInputStream bis = null;
try{
    /* 1.创建节点流:*/
    fis = new FileInputStream(file);
    /* 2.创建缓冲流:*/
    bis = new BufferedInputStream(bis);
    /* 3.读操作:*/
    bis.read();
} catch(IOException e){
    e.printStackTrace();
} finally{
    /* 4.先关闭外层流(缓冲流):*/
    if(bis != null){
        try{
            bis.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
    /* 5.后关闭内层流(文件流):
         无需此步操作,因为关闭外层流会自动关闭内层流 */
    if(fis != null){
        try{
            fis.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

BufferedOutputStream

常用构造器:

public BufferedOutputStream(OutputStream out) {}

常用方法:

/* 刷新流 */
public void flush() throws IOException {}
public void write(int b) throws IOException {}
public void write(byte[] b,int off,int len) throws IOException {}

BufferedOutputStream的创建步骤:

FileOutputStream fos = null;
BufferedOutputStream bos = null;
try{
    /* 1.创建节点流:*/
    fos = new FileOutputStream(file,false);
    /* 2.创建缓冲流:*/
    bos = new BufferedOutputStream(bos);
    /* 3.写操作:*/
    bos.write();
} catch(IOException e){
    e.printStackTrace();
} finally{
    /* 4.先关闭外层流(缓冲流):*/
    if(bos != null){
        try{
            bos.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
    /* 5.后关闭内层流(文件流):
         无需此步操作,因为关闭外层流会自动关闭内层流 */
    if(fos != null){
        try{
            fos.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

字节型文件流和缓冲流的效率对比

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        File from = new File("F:"+sp+"Desktop"+sp+"Faded.flac");
        File to_1 = new File("F:"+sp+"Desktop"+sp+"Faded_1.flac");
        File to_2 = new File("F:"+sp+"Desktop"+sp+"Faded_2.flac");
        copy(from,to_1);
        bufferedCopy(from,to_2);
    }
    public static void copy(File from,File to){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        byte[] buffer = new byte[1024];
        int len;
        try{
            fis = new FileInputStream(from);
            //false指定:不追加,覆盖原文件
            fos = new FileOutputStream(to,false);
            long startT = System.currentTimeMillis();
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
                fos.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("----------use file stream----------");
            System.out.println("Copy from " + from.getPath()
                               + " to " + to.getPath() + " finished.");
            System.out.println("Takes " + (endT - startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(fis != null){
                try{
                    fis.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try{
                    fos.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static void bufferedCopy(File from,File to){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        byte[] buffer = new byte[1024];
        int len;
        try{
            bis = new BufferedInputStream(new FileInputStream(from));
            //false指定:不追加,覆盖原文件
            bos = new BufferedOutputStream(new FileOutputStream(to,false));
            long startT = System.currentTimeMillis();
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);
                bos.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("----------use buffered stream----------");
            System.out.println("Copy from " + from.getPath()
                               + " to " + to.getPath() + " finished.");
            System.out.println("Takes " + (endT - startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(bis != null){
                try{
                    bis.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(bos != null){
                try{
                    bos.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

----------use file stream----------
Copy from F:\Desktop\Faded.flac to F:\Desktop\Faded_1.flac finished.
Takes 691ms.
----------use buffered stream----------
Copy from F:\Desktop\Faded.flac to F:\Desktop\Faded_2.flac finished.
Takes 64ms.

字符缓冲流

BufferedReader

常用构造器:

public BufferedReader(Reader in) {}

常用方法:

public boolean ready() throws IOException {}
public int read() throws IOException {}
public int read(char[] cbuf,int off,int len) throws IOException {}

/* 读取一行文本。
   从上一个换行(或回车)到下一个换行(或回车)为一行,
   或者从上一个换行(或回车)到文件末尾为一行。*/
public String readLine() throws IOException {}

BufferedReader的创建步骤:

FileReader fr = null;
BufferedReader br = null;
try{
    /* 1.创建节点流:*/
    fr = new FileReader(file,charset);
    /* 2.创建缓冲流:*/
    br = new BufferedReader(br);
    /* 3.读操作:*/
    br.read();
} catch(IOException e){
    e.printStackTrace();
} finally{
    /* 4.先关闭外层流(缓冲流):*/
    if(br != null){
        try{
            br.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
    /* 5.后关闭内层流(文件流):
         无需此步操作,因为关闭外层流会自动关闭内层流 */
    if(fr != null){
        try{
            fr.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

BufferedWriter

常用构造器:

public BufferedWriter(Writer out) {}

常用方法:

/* 刷新流 */
public void flush() throws IOException {}

/* 写一个行分隔符。行分隔符字符串由系统属性行定义,
   不一定是单个换行符('\n'). */
public void newLine() throws IOException {}

public void write(int c) throws IOException {}
public void write(char[] cbuf,int off,int len) throws IOException {}
public void write(String s,int off,int len) throws IOException {}

BufferedWriter的创建步骤:

FileWriter fw = null;
BufferedWriter bw = null;
try{
    /* 1.创建节点流:*/
    fw = new FileWriter(file,charset,append);
    /* 2.创建缓冲流:*/
    bw = new BufferedWriter(bw);
    /* 3.写操作:*/
    bw.write();
    bw.flush();  //刷新流
} catch(IOException e){
    e.printStackTrace();
} finally{
    /* 4.先关闭外层流(缓冲流):*/
    if(bw != null){
        try{
            bw.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
    /* 5.后关闭内层流(文件流):
         无需此步操作,因为关闭外层流会自动关闭内层流 */
    if(fw != null){
        try{
            fw.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

字符型文件流和缓冲流的效率对比

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        //使用utf-8字符集
        final Charset charset = StandardCharsets.UTF_8;
        File from = new File("F:"+sp+"Desktop"+sp+"helpme.md");
        File to_1 = new File("F:"+sp+"Desktop"+sp+"helpme_1.md");
        File to_2 = new File("F:"+sp+"Desktop"+sp+"helpme_2.md");
        copy(from,to_1,charset);
        bufferedCopy(from,to_2,charset);
    }
    public static void copy(File from,File to,Charset charset){
        FileReader fr = null;
        FileWriter fw = null;
        char[] buffer = new char[100];
        int len;
        try{
            fr = new FileReader(from,charset);
            //false指定:不追加,覆盖原文件
            fw = new FileWriter(to,charset,false);
            long startT = System.currentTimeMillis();
            while((len = fr.read(buffer)) != -1){
                fw.write(buffer,0,len);
                fw.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("----------use file stream----------");
            System.out.println("Copy from " + from.getPath()
                               + " to " + to.getPath() + " finished.");
            System.out.println("Takes " + (endT - startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(fr != null){
                try{
                    fr.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(fw != null){
                try{
                    fw.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static void bufferedCopy(File from,File to,Charset charset){
        BufferedReader br = null;
        BufferedWriter bw = null;
        char[] buffer = new char[100];
        int len;
        try{
            br = new BufferedReader(new FileReader(from,charset));
            //false指定:不追加,覆盖原文件
            bw = new BufferedWriter(new FileWriter(to,charset,false));
            long startT = System.currentTimeMillis();
            while((len = br.read(buffer)) != -1){
                bw.write(buffer,0,len);
                bw.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("----------use buffered stream----------");
            System.out.println("Copy from " + from.getPath()
                               + " to " + to.getPath() + " finished.");
            System.out.println("Takes " + (endT - startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(br != null){
                try{
                    br.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(bw != null){
                try{
                    bw.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

----------use file stream----------
Copy from F:\Desktop\helpme.md to F:\Desktop\helpme_1.md finished.
Takes 53ms.
----------use buffered stream----------
Copy from F:\Desktop\helpme.md to F:\Desktop\helpme_2.md finished.
Takes 17ms.

转换流

转换流处理流的一种,用于将字节流转换为字符流(解码decode),或者将字符流转换为字节流(编码encode)。

InputStreamReader

InputStreamReader用于将字节流转换为字符流,即解码(decode)。

常用构造器

/* 将指定字节流转换为字符流(解码),使用平台默认字符集解码 */
public InputStreamReader(InputStream in) {}

/* 将指定字节流转换为字符流(解码),使用Charset对象指定的字符集解码 */
public InputStreamReader(InputStream in,Charset cs) {}

/* 将指定字节流转换为字符流(解码),
   使用字符集名字符串(不区分大小写)指定的字符集解码,例如"utf-8". */
public InputStreamReader(InputStream in,String charsetName) 
                        throws UnsupportedEncodingException {}

常用方法

public String getEncoding() {}
public boolean ready() throws IOException {}
public int read() throw IOException {}
public int read(char[] cbuf,int off,int len) throws IOException {}
public void close() throws IOException {}

OutputStreamWriter

OutputStreamWriter用于将字符流转换为字节流,即编码(encode)。

常用构造器

/* 将指定字符流转换为字节流(编码),使用平台默认字符集编码 */
public OutputStreamWriter(OutputStream out) {}

/* 将指定字符流转换为字节流(编码),使用Charset对象指定的字符集编码 */
public OutputStreamWriter(OutputStream out,Charset cs) {}

/* 将指定字符流转换为字节流(编码),
   使用字符集名字符串(不区分大小写)指定的字符集编码,例如"utf-8". */
public OutputStreamWriter(OutputStream out,String charsetName)
                         throws UnsupportedEncodingException {}

常用方法

public String getEncoding() {}
/* 刷新流 */
public void flush() throws IOException {}
public void write(int c) throws IOException {}
public void write(char[] cbuf,int off,int len) throws IOException {}
public void write(String str,int off,int len) throws IOException {}

使用转换流改变文本的字符集

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        File from = new File("F:"+sp+"Desktop"+sp+"from.txt");
        File dest = new File("F:"+sp+"Desktop"+sp+"dest.txt");
        encode(from,dest,StandardCharsets.UTF_8,StandardCharsets.UTF_16);
    }
    
    public static void encode(File from,File dest,Charset de,Charset en) {
        InputStreamReader reader = null;
        OutputStreamWriter writer = null;
        char[] buffer = new char[100];
        int len;
        try{
            reader = new InputStreamReader(new FileInputStream(from),de);
            //false指定:不追加,覆盖原文件
            writer = new OutputStreamWriter(new FileOutputStream(dest,false),en);
            long startT = System.currentTimeMillis();
            while((len = reader.read(buffer)) != -1){
                writer.write(buffer,0,len);
                writer.flush();  //刷新流
            }
            long endT = System.currentTimeMillis();
            System.out.println("-------------------------");
            System.out.println("Encode file from " + from.getPath()
                               + " by " + en.name() + " to "
                               + dest.getPath() + " finished.");
            System.out.println("Takes " + (endT - startT) + "ms.");
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            if(reader != null){
                try{
                    reader.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try{
                    writer.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

-------------------------
Encode file from F:\Desktop\from.txt by UTF-16 to F:\Desktop\dest.txt finished.
Takes 75ms.

数据流

数据流处理流的一种,用于传输基本数据或字符串

DataOutputStream

DataOutputStream的作用是输出内存中指定类型的数据(基本类型或字符串)。

构造器

public DataOutputStream(OutputStream out) {}

常用方法

public void write(int b) throws IOException {}
public void write(byte[] b,int off,int len) throws IOException {}
/* 刷新流 */
public void flush() throws IOException {}

/* 返回目前为止写入此数据输出流的字节数 */
public final int size() {}

public final void writeInt(int v) throws IOException {}
public final void writeFloat(float v) throws IOException {}
public final void writeDouble(double v) throws IOException {}
public final void writeChar(char v) throws IOException {}
public final void writeBoolean(boolean v) throws IOException {}
public final void writeLong(long v) throws IOException {}
public final void writeShort(short v) throws IOException {}
public final void writeByte(int v) throws IOException {}

/* 将字符串作为字节序列写入流。
   字符串中的每个字符通过丢弃高8位按顺序写入。
   如果没有抛出异常,则写入的计数器将以s的长度递增。 */
public final void writeBytes(String s) throws IOException {}

/* 将字符串作为字符序列写入流。
   字符串中的每个字符按顺序完整写入。
   如果没有抛出异常,则写入的计数器将以s的长度递增。 */
public final void writeChars(String s) throws IOException {}

/* 使用UTF-8编码以与机器无关的方式将字符串写入流。 */
public final void writeUTF(String str) throws IOException {}

DataInputStream

DataInputStream的作用是读入指定类型的数据(基本类型或字符串)到内存。

构造器

public DataInputStream(InputStream in) {}

常用方法

public final int read(byte[] b) throws IOException {}
public final int read(byte[] b,int off,int len) throws IOException {}
public final int readInt() throws IOException {}
public final float readFloat() throws IOException {}
public final double readDouble() throws IOException {}
public final char readChar() throws IOException {}
public final boolean readBoolean() throws IOException {}
public final long readLong() throws IOException {}
public final short readShort() throws IOException {}
public final byte readByte() throws IOException {}
public final String readUTF() throws IOException {}

注意:
使用DataInputStream读取数据时,必须按照DataOutputStream写入时的顺序将数据保存到对应的变量

使用数据流保存重要数据到本地

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        File file = new File("F:"+sp+"Desktop"+sp+"dest.txt");
        SaveData data = null;
        String name = "Tony Stark";
        float height = 1.75f;
        int weight = 130;
        try{
            //写入
            data = new SaveData(file);
            data.save(name);
            data.save(height);
            data.save(weight);
            
            //按照写入顺序读出
            String name_1 = data.takeUTF();
            float height_1 = data.takeFloat();
            int weight_1 = data.takeInt();
            System.out.println(name_1 + " : " + height_1 
                               + "m " + weight_1 + "(1/2Kg).");
            
            //错误示例:读出顺序与写入顺序不同,将抛异常
            data.save(name);
            data.save(height);
            data.save(weight);
            
            //按照错误顺序读出
            float height_2 = data.takeFloat();
            String name_2 = data.takeUTF();
            int weight_2 = data.takeInt();
            System.out.println(name_2 + " : " + height_2 
                               + "m " + weight_2 + "(1/2Kg).");
            
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(data != null){
                try{
                    data.allFinish();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
class SaveData {
    private DataOutputStream dos = null;
    private DataInputStream dis = null;
    public SaveData(File file) throws IOException {
        //false指定:不追加,覆盖原文件
        dos = new DataOutputStream(new FileOutputStream(file,false));
        dis = new DataInputStream(new FileInputStream(file));
        dos.flush();  //刷新流
    }
    public <V> void save(V v) throws IOException {
        if(v.getClass().equals(Integer.class)) {dos.writeInt((int)v);}
        else if(v.getClass().equals(Float.class)) {dos.writeFloat((float)v);}
        else if(v.getClass().equals(Double.class)) {dos.writeDouble((double)v);}
        else if(v.getClass().equals(Character.class)) {dos.writeChar((char)v);}
        else if(v.getClass().equals(String.class)) {dos.writeUTF((String)v);}
        else {throw new IOException("type not supported.");}
        dos.flush();  //刷新流
    }
    public int takeInt() throws IOException {return dis.readInt();}
    public float takeFloat() throws IOException {return dis.readFloat();}
    public double takeDouble() throws IOException {return dis.readDouble();}
    public char takeChar() throws IOException {return dis.readChar();}
    public String takeUTF() throws IOException {return dis.readUTF();}
    public void allFinish() throws IOException {
        if(dos != null) {dos.close();}
        if(dis != null) {dis.close();}
    }
}

运行结果:

Tony Stark : 1.75m 130(1/2Kg).
java.io.EOFException

RandomAccessFile

概述

RandomAccessFile是一种特殊的字节型文件流RandomAccessFile即可以创建输入流对象,也可以创建输出流对象。
RandomAccessFile直接继承于Object类,并且实现DataInputDataOutput接口。

构造器

/* 通过连接由路径字符串指定的文件创建流,
   mode指定读写模式。 */
/* 注意:
   1.mode指定流的读写模式:
     mode = "r" : 以 只读 模式连接文件;
     mode = "rw" : 以 读写 模式连接文件;
     mode = "rwd" : 以 读写 模式连接文件,并且每次读写都同步更新本地文件数据;
     mode = "rws" : 以 读写 模式连接文件,并且每次读写都同步更新本地文件数据和元数据;
   2.读写模式(mode = "rw"/"rwd"/"rws")下,如果指定文件本地不存在,
     创建RandomAccessFile对像将创建指定文件;
   3.只读模式(mode = "r")下,如果指定文件本地不存在,
     创建RandomAccessFile对像将抛FileNotFoundException;
   4.如果指定文件的父级目录本地不存在,将抛FileNotFoundException. */
public RandomAccessFile(String name,String mode) throws FileNotFoundException {}

/* 通过连接由抽象路径名指定的文件创建流,
   mode指定读写模式。 */
/* 注意:
   1.mode指定流的读写模式:
     mode = "r" : 以 只读 模式连接文件;
     mode = "rw" : 以 读写 模式连接文件;
     mode = "rwd" : 以 读写 模式连接文件,并且每次读写都同步更新本地文件数据;
     mode = "rws" : 以 读写 模式连接文件,并且每次读写都同步更新本地文件数据和元数据;
   2.读写模式(mode = "rw"/"rwd"/"rws")下,如果指定文件本地不存在,
     创建RandomAccessFile对像将创建指定文件;
   3.只读模式(mode = "r")下,如果指定文件本地不存在,
     创建RandomAccessFile对像将抛FileNotFoundException;
   4.如果指定文件的父级目录本地不存在,将抛FileNotFoundException. */
public RandomAccessFile(File file,String mode) throws FileNotFoundException {}

常用方法

/* DataInput */
public final int read(byte[] b) throws IOException {}
public final int read(byte[] b,int off,int len) throws IOException {}
public final int readInt() throws IOException {}
public final float readFloat() throws IOException {}
public final double readDouble() throws IOException {}
public final char readChar() throws IOException {}
public final boolean readBoolean() throws IOException {}
public final long readLong() throws IOException {}
public final short readShort() throws IOException {}
public final byte readByte() throws IOException {}
public final String readUTF() throws IOException {}
public final String readLine() throws IOException {}

/* DataOutput */
/* 注意:写入文件的新字节会覆盖文件指针所在位置及之后的原有字节,
        覆盖范围等于写入的新字节数。 */
public void write(int b) throws IOException {}
public void write(byte[] b,int off,int len) throws IOException {}
public final void writeInt(int v) throws IOException {}
public final void writeFloat(float v) throws IOException {}
public final void writeDouble(double v) throws IOException {}
public final void writeChar(char v) throws IOException {}
public final void writeBoolean(boolean v) throws IOException {}
public final void writeLong(long v) throws IOException {}
public final void writeShort(short v) throws IOException {}
public final void writeByte(int v) throws IOException {}
public final void writeBytes(String s) throws IOException {}
public final void writeChars(String s) throws IOException {}
public final void writeUTF(String str) throws IOException {}

public void close() throws IOException {}
/* 返回目前文件中的字节数 */
public long length() throws IOException {}
/* 如果length()返回的文件的当前字节数大于newLength参数,则该文件将被截断。
   在这种情况下,如果getFilePointer()返回的文件偏移量大于newLength,
   那么在该方法返回之后,偏移量将等于newLength。
   如果length()返回的文件的当前字节数小于newLength参数,
   那么文件将被扩展。在这种情况下,文件扩展部分的内容未定义。*/
public void setLength(long newLength) throws IOException {}

/* new */
/* 设置文件指针偏移量(字节数),从该文件的开头开始计算,下一次读或写发生在该偏移量处。
   偏移量可以设置在文件结束之后。设置超过文件末尾的偏移量不会改变文件长度。
   只有在将偏移量设置到文件末尾之后,才会通过写入来更改文件长度。 */
/* 注意:文件指针偏移量的起始索引为0. */
public void seek(long pos) throws IOException {}

/* 返回当前文件指针偏移量(字节数),从该文件的开头开始计算 */
/* 注意:文件指针偏移量的起始索引为0. */
public void getFilePointer() throws IOException {}

在文件的指定字节位置之后插入新字节

Main.java

public class Main {
    public static void main(String[] args) {
        final String sp = File.separator;
        File file = new File("F:"+sp+"Desktop"+sp+"data.bat");
        fileBytePrint(file);
        byte[] bytes = {' ','I',' ','l','o','v','e',' ','J','a','v','a',' ','!',' '};
        insertBytes(file,bytes,5);
        fileBytePrint(file);
    }
    public static void insertBytes(File file,byte[] bytes,long pos) {
        RandomAccessFile in = null;
        RandomAccessFile out = null;
        ArrayList<Byte> listAfter = new ArrayList<>();
        ArrayList<Byte> listInsert = new ArrayList<>();
        try{
            in = new RandomAccessFile(file,"r");
            out = new RandomAccessFile(file,"rw");
            in.seek(pos);
            //暂存pos之后的数据
            for(int i = 0;i < (file.length()-pos);i++){
                listAfter.add((byte)in.read());
            }
            for(byte b : bytes){
                listInsert.add(b);
            }
            out.seek(pos);
            //拼接将要插入的数据和之后的数据
            listInsert.addAll(listAfter);
            for(byte b : listInsert){
                out.write(b);
            }
            System.out.println("insert bytes finished.");
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(in != null){
                try{
                    in.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(out != null){
                try{
                    out.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static void fileBytePrint(File file){
        RandomAccessFile reader = null;
        int value;
        try{
            reader = new RandomAccessFile(file,"r");
            while((value = reader.read()) != -1){
                System.out.print((char)((byte)value));
            }
            System.out.println();
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(reader != null){
                try{
                    reader.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

0123456789
insert bytes finished.
01234 I love Java ! 56789
展开阅读全文
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值