Java基础14-缓冲区字节流;File类

作业解析

  1. 阐述BufferedReader和BufferedWriter的工作原理,

    是否缓冲区读写器的性能恒大于非缓冲区读写器的性能,为什么,请举例说明?

    答: BufferedReader对Reader类进行了装饰,即在成员变量中声明一个Reader成员变量,在构造时将该成员变量进行初始化,BufferedReader在读取文件时,将读取到的数据存储在字符数组中,下一次读取时,从字符数组中取出对应的数据,避免对物理文件的频繁访问;BufferedWriter对Writer类进行了装饰,在写入数据时,先写入到字符数组中,当字符数组写满时,才将字符数组中的数据清理到物理文件中去。

    根据上述工作原理可以看到缓冲区读写器的性能不一定大于非缓冲区读写器的性能。对BufferedReader来说,如果FileReader读取时采用字符数组读取,则会比BufferedReader要快;对BufferedWriter来说,如果一次写入的数据量很大,则FileWriter要快。

  2. 写入数据1~1000000数字到文件中,分别使用FileWriter和BufferedWriter实现,考察其效率的不同

     package com.kokojia.io;
    
     import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException; public class FileWriterBufWriterDemo { public static void main(String[] args) throws Exception {
    /**
    * 写入数据1~1000000数字到文件中,分别使用FileWriter和BufferedWriter实现,考察其效率的不同
    */ //非缓冲区字符输出流
    FileWriter fw = new FileWriter("f.txt");
    long startTime = System.currentTimeMillis();
    for(int i=1;i<=1000000;i++) {
    fw.write(i+"");
    }
    System.out.println("FileWriter over!Cost time: "+(System.currentTimeMillis()-startTime));//460
    fw.close();
    //缓冲区字符输出流
    BufferedWriter bw = new BufferedWriter(new FileWriter("f.txt"));
    startTime = System.currentTimeMillis();
    for(int i=1;i<=1000000;i++) {
    bw.write(i+"");
    }
    System.out.println("BufWriter over!Cost time: "+(System.currentTimeMillis()-startTime));//264
    bw.close();
    }
    }
  3. 文件切割:

    把较大的文件切割成20k一个的小文件

     /**
    * 文件切割:把较大的文件切割成size大小的小文件
    * @param srcFile
    * @param size
    * @throws Exception
    */
    private static void fileSplit(String srcFile,int size) throws Exception { FileInputStream fis = new FileInputStream(srcFile); //获取源文件名
    String fileName = srcFile.substring(srcFile.indexOf("/")+1,srcFile.indexOf("."));
    //获取源文件后缀名
    String sufName = srcFile.substring(srcFile.indexOf(".")+1);
    //获得源文件大小
    int srcFileSize = fis.available();
    //计算切割的数量
    int nCount = srcFileSize/size+1; FileOutputStream fos = null; byte[] buf = null; int len = -1;
    for(int i=1;i<=nCount;i++) {
    fos = new FileOutputStream(srcFile.substring(0,srcFile.indexOf("/")+1)+fileName+"-"+i+"."+sufName);
    if(i==nCount) {
    int bufLength =srcFileSize-size*(nCount-1);
    buf = new byte[bufLength];
    fis.read(buf);
    fos.write(buf);
    }
    else {
    int bufLength = size;
    buf = new byte[bufLength];
    fis.read(buf);
    fos.write(buf);
    }
    fos.close();
    }
    fis.close();
    }
  4. 文件合成:

    把小文件合并成大文件

     /**
    * 文件合成,把小文件合成大文件
    * @param srcFiles
    * @param string
    * @throws Exception
    */
    private static void fileCombine(String[] srcFiles, String destFile) throws Exception {
    FileOutputStream fos = new FileOutputStream(destFile);
    FileInputStream fis = null;
    for(String s: srcFiles) {
    fis = new FileInputStream(s);
    int len = -1;
    byte[] buf = new byte[1024];
    while((len=fis.read(buf))!=-1) {
    fos.write(buf,0,len);
    }
    fis.close();
    }
    fos.close();
    }
  5. 文件归档解档:

    0=txt,1=jpg,2=avi,3=gif

    解档归档工具类

    package com.kokojia.io;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream; public class Archive { /**
    * 新建归档文件
    * @param srcFiles
    * @param destFile
    * @throws Exception
    */ public void newArchiver(String[] srcFiles, String destFile) throws Exception {
    FileOutputStream fos = new FileOutputStream(destFile);
    for(String srcFile: srcFiles) {
    addNewFile(srcFile,fos);
    }
    fos.close();
    } private void addNewFile(String srcFile, FileOutputStream fos) throws Exception {
    FileInputStream fis = new FileInputStream(srcFile);
    //获取文件后缀名
    String sufName = srcFile.substring(srcFile.lastIndexOf(".")+1);
    //写入文件后缀名
    if(sufName.equals("txt")) {
    fos.write((byte)0);
    }
    if(sufName.equals("mkv")){
    fos.write((byte)1);
    }
    if(sufName.equals("jpg")){
    fos.write((byte)2);
    }
    //获取文件大小,写入文件大小
    int fileLength = fis.available();
    byte[] fileSizes = int2bytes(fileLength);
    fos.write(fileSizes); //写入文件内容
    int len = -1;
    byte[] buf = new byte[1024];
    while((len=fis.read(buf))!=-1) {
    fos.write(buf,0,len);
    }
    fis.close();
    } /**
    * int转化为字节数组
    */
    private byte[] int2bytes(int fileLength) {
    byte[] fileSizes = new byte[4];
    fileSizes[0] = (byte)fileLength;
    fileSizes[1] = (byte)(fileLength>>8);
    fileSizes[2] = (byte)(fileLength>>16);
    fileSizes[3] = (byte)(fileLength>>24);
    return fileSizes;
    } /**
    * 文件解档
    * @param string
    * @param string2
    * @throws Exception
    */
    public void unArchiver(String yarPath, String destPath) throws Exception {
    FileInputStream fis = new FileInputStream(yarPath); byte bytes[] = new byte[1];
    int len = -1;
    int fileNo = 1;//记数文件个数
    String sufNames[] = new String[] {"txt","mkv","jpg"} ;
    while((len=fis.read(bytes))!=-1) { //获取文件大小
    byte fileSize[] = new byte[4];
    fis.read(fileSize);
    int fileLength = bytes2int(fileSize); //获取文件后缀名
    String sufName = sufNames[bytes[0]]; //创建输出文件
    FileOutputStream fos = new FileOutputStream(destPath+"/"+fileNo+"."+sufName); byte buf[] = new byte[1024];
    //计算读取次数
    int nCount = fileLength/buf.length+1; for(int i = 1; i<=nCount;i++) {
    if(i==nCount) {
    byte buf2[] = new byte[fileLength-buf.length*(nCount-1)];
    fis.read(buf2);
    fos.write(buf2);
    }
    else {
    fis.read(buf);
    fos.write(buf);
    }
    }
    fileNo++;
    fos.close();
    }
    fis.close();
    } /**
    * byte数组转化为int
    */
    private int bytes2int(byte[] fileSize) {
    int fileLength = 0;
    fileLength = (fileSize[0] & 0xff) | (fileSize[1] & 0xff)<<8 | (fileSize[2] & 0xff)<<16 | (fileSize[3] & 0xff)<<24;
    return fileLength;
    }
    }

    测试类

    public class ArchiveDemo {
    @Test
    public void ArchiverTest() throws Exception {
    Archive archive = new Archive();
    String[] srcFiles = new String[] {"1.txt","2.2 logistic 回归.mkv","test.jpg"}; archive.newArchiver(srcFiles,"all.yar");
    System.out.println("over");
    } @Test
    public void unArchiverTest() throws Exception {
    Archive archive = new Archive();
    archive.unArchiver("all.yar","1");
    }
    }
  6. 阐述HashSet与HashMap的异同。

    答:HashSet的本质是HashMap,其value项为一个占位符(垃圾变量)。添加元素时,通过key的hash值确定其理应添加的数组位置。依次与当前数组中的元素进行对比,比对原则有三个:(hashcode1hashcode2) && ( o1o2 || (o1.equals(o2)) ),如果没有元素与之相同,则添加到该区间元素的末尾。通过key查询时,先得到key对应的hashcode, 然后定位到所在区间,简化了查询,最后按照上述的三个原则进行比对

  7. Charset类操作: isSupport()

    3.1) 通过该类验证平台是否支持以下字符集:

    gb2312

    GB2312

    gbk

    GBK

    utf-8

    utf8

    iso8859-1

    iso-8859-1

    3.2) 取出平台默认的字符集

     System.out.println("默认字符集:"+Charset.defaultCharset());
    System.out.println("是否支持gb2312: "+Charset.isSupported("gb2312"));
    System.out.println("是否支持GB2312: "+Charset.isSupported("GB2312"));
    System.out.println("是否支持gbk: "+Charset.isSupported("gbk"));
    System.out.println("是否支持GBK: "+Charset.isSupported("GBK"));
    System.out.println("是否支持utf-8: "+Charset.isSupported("utf-8"));
    System.out.println("是否支持utf8: "+Charset.isSupported("utf8"));
    System.out.println("是否支持iso8859-1: "+Charset.isSupported("iso8859-1"));
  8. 使用FileInputStream + FileOutputStream / BufferedInputStream + BufferedOuputStream

    实现大文件复制,比较复制效率。

     import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.IOException; public class CopyBigFile {
    /**
    * 使用FileInputStream + FileOutputStream / BufferedInputStream + BufferedOuputStream
    实现大文件复制,比较复制效率。
    * @param args
    * @throws IOException
    */
    public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("1.txt");
    for(int i = 0;i<1024*1024;i++) {
    fw.write(i+",");
    }
    fw.close(); //非缓冲区字节输入流
    FileInputStream fis=new FileInputStream("test.jpg");
    //缓冲区字节输入流
    BufferedInputStream bis=new BufferedInputStream(new FileInputStream("test.jpg")); //非缓冲区字节输出流
    FileOutputStream fos=new FileOutputStream("FileOutput.jpg");
    //缓冲区字节输入流
    BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("BufOutput.jpg")); int len=-1;
    byte bytes[] = new byte[1024]; //非缓冲区复制文件
    long startTime = System.currentTimeMillis();
    while((len=fis.read(bytes))!=-1) {
    fos.write(bytes, 0, len);
    }
    fis.close();
    fos.close();
    System.out.println("非缓冲区复制完成!耗时:"+(System.currentTimeMillis()-startTime)); //缓冲区复制文件
    startTime = System.currentTimeMillis();
    while((len=bis.read(bytes))!=-1) {
    bos.write(bytes, 0, len);
    }
    bis.close();
    bos.close();
    System.out.println("缓冲区复制完成!耗时:"+(System.currentTimeMillis()-startTime)); System.out.println(System.lineSeparator());
    System.out.println("over");
    }
    }
  9. 阐述对象回收的前提条件。

    答:当内存中没有一个引用可以指向该对象时,该对象将会被回收。在Set集合中,添加对象后,如果将对象的引用置空,对象也不会被回收,因为Set集合中仍然存在该对象的引用。

缓冲区字节流

  1. BufferedOutputStream:缓冲区字节输出流

    • Object --> OutputStream --> FilterOutputStream --> BufferedOutputStream
    • 使用装饰模式
  2. BufferedInputStream : 缓冲区输入流

    • Object --> InputStream --> FilterInputStream --> BufferedInputStream
    • close():释放与之关联的系统资源。多次关闭没有影响。手动关闭时,从外部向里面关闭。
  3. 字节数组输出流:ByteArrayOutputStream

    • Object --> OutputStream --> ByteArrayOutputStream
    • 在内存中开辟空间,存放数据。不是缓冲区流
  4. 字节数组输入流 : ByteArrayInputStream

    • Object --> InputStream --> ByteArrayInputStream

IO小结

  1. 流向

    InputStream

    OutputStream

  2. 类型划分

    a. 字符流(文本文件)

    Reader | Writer

    FileReader | FileWriter

    b. 字节流(任何文件)

    InputStream | OutputStream

    FileInputStream | FileOutputStream

  3. 性能划分

    BufferedWriter: 缓冲区流(装饰模式,flush清理) | 非缓冲区流

    BufferedReader: 缓冲区,read(fill)

    readLine() LineNumberReader

    BufferedInputStream.read(): -128 ~ 127 -1

    BufferedOutputStream

转换流

将字节流转换成字符流 ,使用特定字符集读取byte并解码成字符,底层操作是字节流,如果需要将其转换成字符内容处理,就可以使用转换流

  1. InputStreamReader

    是从字节流到字符流的桥梁,读取字节数组并解码(使用指定的字符集)成字符。字符集可以使用名称指定也可以

    指定charset对象,或者平台默认也可以。

    每次read()方法调用,都会引发底层字节流读取一个或者多个字节数据。如果需要高效来处理,可以对其使用

    BufferedReader进行包装。

    • Object --> Reader --> InputStreamReader
    • 不是缓冲区流
    • 使用时,尽量使用BufferedReader对其进行包装,提高效率。
  2. OutputStreamWriter :

    • Object --> Writer --> OutputStreamWriter
    • 不是缓冲区流
    • 使用时,尽量使用BufferedReader对其进行包装,提高效率。

标准IO流

System.out.println();
standard Input //标准输入,默认是键盘,System.in, System.setIn(...)
standard Output //标准输出,默认是控制台,System.out, System.setOut(xxx)

File类

  1. 目录:指文件所在文件夹

  2. 路径:精准定位文件的地址,分为相对路径和绝对路径 . | ..

作业

  1. 定义函数,输出一个byte的二进制字符串。

  2. 定义工具类,完成int数和byte[]之间的相互转换。

  3. 阐述IO流。

    输入输出流

    字符字节流

    缓冲和非缓冲流

    转换流.

  4. 通过File对象打印输出指定路径下的整个目录树结构。

  5. 完善归档和解档程序,文件头包含文件名(文件名+扩展名)+ 长度

    提示:前四个字节文件名长度+文件名+四个字节文件大小+文件内容

  6. 提取指定的第n个文件

  7. 输出文件名列表

  8. 使用文本文件传输媒体文件

    1)将媒体文件的字节数组写到文本中,每个字节转换成了0-255之间的字符

    2)使用BufferedReader读行,读取的每行转换成int, 再转换成byte

    3)将转换成的byte数据写入到文件中

    4)检验

    255 === 1111 1111

    -1 === 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

    216

    97

    bw.write(a); //97

    os.write(255); // ---> byte -128

  9. 使用标准io从控制台读取一行文本,写入到指定文件中,遇到\q字符退出程序

  10. 使用递归遍历指定目录的所有子目录,打印每个目录和文件的绝对路径

    File

    File.listFiles()

    File.getAbsolutePath()

    File.getName()

  11. 阐述归档和解归档的实现原理

  12. 复制文件夹:递归实现

上一篇:ResultHandler的用法


下一篇:【转载】JAVA基础复习与总结<三> Object类的常用方法