ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

尚学堂视频笔记五:IO流

2019-08-10 13:40:11  阅读:250  来源: 互联网

标签:src IO System 笔记 学堂 File new public String


IO流技术

1. IO介绍

​ 流:流动、流向,从一段流到另一端。流是一个抽象、动态的概念,是一连串连续动态的数据集合。(其中,一段指程序,另一端为源头)

​ 数据源:data source。提供原始数据的原是媒介,常见的:数据库、文件、其他程序、内存、网络连接、IOS设备。

​ 一切以为程序为重。

​ 在Java程序中,对于数据的输入/输出操作以流 “stream” 方式进行;

​ J2SDK提供了各种各样的 “流” 类,用以获取不同种类的数据;程序中通过标准的方法输入或输出数据。

​ Java的流类型一般位于java.io包中

1.1 核心类

​ 在整个Java.io包中最重要的就是5个类和3个接口,掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了。

说明
File 文件类
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流
Closeable 关闭流接口
Flushable 刷新流接口
Serializable 序列化接口

1.2 流分类

按照流向来分

  • 输入流:数据源到程序
  • 输出流:程序到目的地

在这里插入图片描述

​ 两个流的对接就是文件的拷贝。

1.2.1 按照功能来分

  • 节点流:可以直接从数据源或目的地读写程序

在这里插入图片描述

  • 处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能。

在这里插入图片描述

  • 节点流和处理流的关系
    • 节点流处于io操作的第一线,所有操作必须通过他们进行。
    • 处理流可以对其他流进行处理(提高效率或操作灵活性)。

1.2.2 按数据分:

  • 字节流:按照字节读取数据(InputStream、OutputStream)

  • 字符流:按照字符读取数据(Reader、Writer),因为文件编码的不同,从而有了对字符进行高效操作的字符流对象。

    原理:底层还是基于字节流操作,自动搜寻了指定的码表

1.2.3 对IOStream的实现类:

在这里插入图片描述

在这里插入图片描述

2. File——常用API及文件编码

​ Java中对数据源文件的操作,使用File类来完成。这里的File不光表示文件,还表示文件夹。

​ File类并不是构建完成就存在了。Java不能直接操作硬盘,必须让虚拟机与操作系统进行交互,让系统去操作硬盘。这里的File只是在硬盘和Java之间建立一个联系。所以这里的文件,可能不存在,可能是一个文件夹,也可能是一个文件。File只是一个抽象的表述方式。

package com.io;
import java.io.File;
/**
 * 相对路径和绝对路径
 *
 */
public class PathDemo02 {

    public static void main(String[] args) {
        // 绝对路径
        String path = "/home/qianqian/IdeaProjects/HelloWorld/1.jpg";
        // 相对路径
        String path2 = "1.jpg";
        File src = new File(path);
        System.out.println(System.getProperty("user.dir"));

        // 构建一个不存在的文件
        src = new File("aaa/IO2.png");
        System.out.println(src.getAbsolutePath());
    }
}

2.1 File存在三种构造方法,分别是:

  1. 通过路径字符串转换为抽象路径来构造一个File对象
  2. 通过父路径字符串和子路径字符串来构造一个File对象
  3. 通过父抽象名称和子路径字符串来构造一个File对象
import java.io.File;

public class PathDemo01 {
    /**
     * 构建File对象
     * @param args
     */
    public static void main(String[] args) {
        String path = "1.jpg";

        // 1.构建File对象
        File src = new File(path);
        System.out.println(src.length());

        // 2.构建File对象
        src = new File("/home/qianqian/IdeaProjects/HelloWorld", "1.jpg");
        System.out.println(src.length());

        // 3.构建File对象
        src = new File(new File("/home/qianqian/IdeaProjects/HelloWorld"), "1.jpg");
    }
}

2.2 File类中的重点API

API 说明
pathSeparator separator 路径|路径分隔符
File(String parent, String child)
File(File parent, String child)
File(String name)
构造器
没有盘符以user.dir作为
相对目录
getName()
getPath()
getAbsolutePath()
getParent()
文件名、路径名
exists()
isFile()
isDirectory()
判断状态
length() 文件长度,如果长度为0
1.不存在
文件是一个文件夹,则
长度未指定
createNewFile()
delete()
创建新文件
删除文件

2.2.1 第一部分:文件名、路径名

​ 其中需要注意的是,getPath() 获取的路径,如果你给与File类的构造函数的字符串是相对路径,输出的就是相对路径;如果是绝对路径,那么就是绝对路径。

getAbsolute() 获取的无论构造函数传入的参数是相对路径还是绝对路径都会输出该文件的绝对路径。

​ getParent() 函数获取的就是分隔符前面的内容,如果是相对路径,则为空。

import java.io.File;

/**
 * 名称或路径:
 * getName()
 * getPath()
 * getAbsolutePath()
 * getParent() : 只是将名称分隔符前面的内容拿出来
 */

public class FileDemo03 {

    public static void main(String[] args) {
        // 基本信息
        File src = new File("1.jpg");
        System.out.println("名称:" + src.getName());
        System.out.println("路径:" + src.getPath());
        System.out.println("绝对路径:" + src.getAbsolutePath());
        System.out.println("父目录:" + src.getParent());
    }

}

2.2.2 第二部分:文件状态

import java.io.File;
/**
 * 文件状态:
 * 1. 不存在
 * 2. 存在:
 *      文件:isFile
 *      文件夹:isDirectory
 */
public class FileDemo04 {

    public static void main(String[] args) {
        File src = new File("1.jpg");
        System.out.println("是否存在:"+src.exists());
        System.out.println("是否是文件夹:"+src.isDirectory());
        System.out.println("是否是文件:" + src.isFile());
    }

}	

​ 这里需要主义的是,如果是相对路径,那么在使用时,java会自动地将user.dir补在相对路径之前。所以如果你的相对路径有错误,那么,是否存在就会为空。

import java.io.File;
/**
 * 文件状态:
 * 1. 不存在
 * 2. 存在:
 *      文件:isFile
 *      文件夹:isDirectory
 */
public class FileDemo04 {

    public static void main(String[] args) {
        File src = new File("1.jpg");
        System.out.println("是否存在:"+src.exists());
        System.out.println("是否是文件夹:"+src.isDirectory());
        System.out.println("是否是文件:" + src.isFile());
        System.out.println("---------------------");
        src = new File("HelloWorld/1.jpg");
        System.out.println("是否存在:"+src.exists());
        System.out.println("是否是文件夹:"+src.isDirectory());
        System.out.println("是否是文件:" + src.isFile());
        System.out.println("---------------------");
        src = new File("/home/qianqian/IdeaProjects/HelloWorld");
        System.out.println("是否存在:"+src.exists());
        System.out.println("是否是文件夹:"+src.isDirectory());
        System.out.println("是否是文件:" + src.isFile());
        
        // 文件状态的标准代码
        src = new File("xxx");
        if(null == src || !src.exists()) {
            System.out.println("文件不存在");
        }else {
            if(src.isFile()) {
                System.out.println("文件操作");
            }else {
                System.out.println("文件夹操作");
            }
        }
    }
}
/**
是否存在:true
是否是文件夹:false
是否是文件:true
---------------------
是否存在:false
是否是文件夹:false
是否是文件:false
---------------------
是否存在:true
是否是文件夹:true
是否是文件:false
*/

​ length() 方法用来返回由此抽象路径名表示的文件的长度。由于是文件的长度。所以当该文件是文件夹时,返回为一个不确定的数。

import java.io.File;

/**
 * 其他信息
 * length():返回字节数,必须确保是文件。
 *
 *
 */
public class FileDemo05 {

    public static void main(String[] args) {
        File src = new File("1.jpg");
        System.out.println("长度: " + src.length());
        src = new File("/home/qianqian/IdeaProjects/HelloWorld");
        System.out.println("长度: " + src.length());
        src = new File("/home/qianqian/IdeaProjects/HelloWorld1");
        System.out.println("长度: " + src.length());
    }

}

​ createNewFile() 方法用来创建新的文件。强调一下,只能用来创建新的文件而不能用来创建新的文件夹。并且这个方法也是文件不存在才创建,存在不创建。

​ delete() 用来删除此choux9iang路径名表示的文件或者目录。如果路径名表示为目录,则目录必须为空才能删除。

import java.io.File;
import java.io.IOException;

/**
 * 其他信息:
 * crateNewFile() : 不存在才创建。 不能创建文件夹
 * delete() : 删除已经存在的文件
 */
public class FileDemo06 {

    public static void main(String[] args) throws IOException {
        File src = new File("2.txt");
        boolean flag = src.createNewFile();
        System.out.println(flag);

        flag = src.delete();
        System.out.println(flag);

        // 补充: con con3是操作系统的设备名,不能正确创建。
        // linux可以正确创建
        src = new File("con3");
        src.delete();
        src = new File("con2");
        src.delete();
        src = new File("con");
        src.delete();
    }

}

2.3 如何操作文件夹

API 说明
mkdir()
mkdirs()
创建目录,必须保证上级目录存在
如果父目录链不存在一同创建
list() 下级名称
listFiles() 下级File
lsitRoots() 根路径

​ mkdir() 方法只有当上级目录存在时,才能创建。

​ 而mkdires() 当上级目录不存在时,会将上级目录连同要创建的目录文件一起删除。

​ list() 方法只能获取下级名称,而不是子孙文件的名称。当想要获取全部的子孙文件时,需要使用递归方法。

​ listFiles() 列出下级File对象。

import java.io.File;

public class DirDemo01 {

    public static void main(String[] args) {
        File dir = new File("/home/qianqian/IdeaProjects/HelloWorld/dir/test");
        // 创建目录
        boolean flag = dir.mkdir();
        System.out.println(flag);
        // 使用mkdirs()
        flag = dir.mkdirs();
        System.out.println(flag);
        System.out.println("--------------");
        // 列出下级名称
        dir = new File("/home/qianqian/IdeaProjects/HelloWorld");
        String[] subNames = dir.list();
        for (String s : subNames) {
            System.out.println(s);
        }
        System.out.println("--------------");
        // 下级对象 listFile()
        File[] subFiles = dir.listFiles();
        for (File s : subFiles) {
            System.out.println(s.getAbsolutePath());
        }
        System.out.println("--------------");
        // 所有盘符
        File[] roots = dir.listRoots();
        for (File f : roots) {
            System.out.println(f.getAbsolutePath());
        }
    }

}
/*
false
false
--------------
web
1.jpg
dir
out
src
HelloWorld.iml
sxt
.idea
gg.txt
--------------
/home/qianqian/IdeaProjects/HelloWorld/web
/home/qianqian/IdeaProjects/HelloWorld/1.jpg
/home/qianqian/IdeaProjects/HedaxiaolloWorld/dir
/home/qianqian/IdeaProjects/HelloWorld/out
/home/qianqian/IdeaProjects/HelloWorld/src
/home/qianqian/IdeaProjects/HelloWorld/HelloWorld.iml
/home/qianqian/IdeaProjects/HelloWorld/sxt
/home/qianqian/IdeaProjects/HelloWorld/.idea
/home/qianqian/IdeaProjects/HelloWorld/gg.txt
--------------
/
*/

2.3.1 案例:打印子孙及目录名称

import java.io.File;

/**
 * 打印子孙及目录名称
 */
public class DirDemo02 {

    public static void main(String[] args) {
        File src = new File("/home/qianqian/IdeaProjects/HelloWorld");
        printName(src, 0);
    }
	// 打印子孙级目录和文件的名称
    public static void printName(File src, int deep) {
        // 控制层次感
        for (int i =0; i < deep; i++) {
            System.out.print(" ");
        }
        // 打印名称
        System.out.println(src.getName());
        if(null == src || !src.exists()) { // 递归头
            return ;
        }else if(src.isDirectory()) {
            for (File s : src.listFiles())  { // 递归体
                printName(s, deep + 1);
            }
        }
    }

}

2.3.2 案例:统计文件夹的大小

import java.io.File;

public class DirCount {
    private long len; // 大小
    private String path; // 路径
    private File src;
    private int fileSize;
    private int dirSize;

    public int getFileSize() {
        return fileSize;
    }

    public int getDirSize() {
        return dirSize;
    }


    public long getLen() {
        return len;
    }

    public String getPath() {
        return path;
    }

    public File getSrc() {
        return src;
    }

    public DirCount(String path) {
        this.path = path;
        this.src = new File(path);
        count(this.src);
    }

    public void count(File src) {
        // 获取大小
        if (null != src && src.exists()) {
            if (src.isFile()) { // 大小
                len += src.length();
                this.fileSize++;
            } else {
                this.dirSize++;
                for (File s : src.listFiles()) {
                    count(s);
                }
            }
        }
    }


    public static void main(String[] args) {
        DirCount dir = new DirCount("/home/qianqian/IdeaProjects/HelloWorld");
        System.out.println(dir.getLen());
        System.out.println(dir.getDirSize());
        System.out.println(dir.getFileSize());
    }
}
/*
716762
58
133
*/

2.4 File字符集

​ 编码:将人类认识使用的字符集转换成计算机认识的字节码

​ 解码:将计算机的字节码转换成人类能够认识字符集

​ 字符集:Java字符使用16位的双字节存储,但是在实际文件存储的数据有各种字符集,需要正确操作,否则就有乱码。

字符集 说明
US-ASCII 即英文的ASCII码
ISO-8859-1 Latin-1 拉丁字符,包含中文、日文等。
UTF-8 变长Unicode字符 ( 1-3 字节 ),国际通用
UTF-16BE 定长Unicode字符 ( 2 个字节 ),大端Big-endian表示
UTF-16LE 定长Unicode字符( 2 个字节 ),小端Big-endian表示
UTF-16 文件中开头指定大端还是小端表示方式,
即BOM ( Byte-Order-Mark ):
FE FF 表示大端, FF FE 表示小端

2.5 文件编码

​ 字符串如何转换成字节数组,字符数组如何转换成字符串。

​ 这里使用getBytes()方法,根据不同的字符集进行编码。

import java.io.UnsupportedEncodingException;

/**
 * 字符串 --> 字节   (编码)
 */
public class ContentEncode {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String msg = "姓名 生命 使命";
        // 编码:字符编码
        byte[] datas = msg.getBytes(); // 默认使用工程的字符集
        System.out.println(datas.length);

        // 编码其他字符集
        datas = msg.getBytes("UTF-16LE");
        System.out.println(datas.length);

        datas = msg.getBytes("GBK");
        System.out.println(datas.length);
    }

}

​ 在解码的时候,容易出现乱码,乱码的原因有一下两个

  1. 字节数不够
  2. 字符集不统一
import java.io.UnsupportedEncodingException;

/**
 * 解码
 */
public class ContentDecode {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String msg = "姓名 生命 使命";
        byte[] datas = msg.getBytes(); // 默认使用工程字符集

        // 解码:字符串
        msg = new String(datas, 0, datas.length, "utf-8");
        System.out.println(msg);

        // 乱码:
        // 1. 字节数不够
        msg = new String(datas, 0, datas.length - 2, "utf-8");
        System.out.println(msg);

        // 2.字符集不统一
        msg = new String(datas, 0, datas.length, "GBK");
        System.out.println(msg);
    }
    
}

3. IO流——流读写操作

3.1 字节流

​ 我们会发现,有关IO流的具体实现类有很多。但由于Java多态的特性,我们只需要把握这些具体实现类的父类,就可以了。具体实现类我们只需要关注其中的某些特点就可以了。

​ 从处理数据来分,可以分为字节流和字符流。比如,以后如果处理纯文本,而文本中都是英文和中文,我们就可以使用字符流。如果我们处理的是音频视频,推荐使用字节流,因为我们不方便处理。

​ 这里需要注意的是,能使用字符流的地方,都能使用字节流。反过来不一定。

​ 按照流向来分,分为输入流和输出流。但是都要以程序为中心。

​ 按照功能来分,分为节点流和处理流。 始终处于第一线的流,成为节点流。在节点流的基础上为了提升性能而进行包装,成为处理流。处理流必须依赖于节点流。

3.1.1 四个抽象类

抽象类 说明 常用方法
InputStream 字节输入流的父类,数据单位为字节 Int read()
void close()
OutputStream 字节输出流的父类,数据单位为字节 void write(int)
void flush()
void close()
Reader 字符输入流的父类,数据单位为字符 int read()
void close()
Writer 字符输出流的父类,数据单位为字符 void write(String)
void flush()
void close()

​ FileInputStream

​ FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频等),全字符请考虑FileReader。

package com.io2;

import java.io.*;

/**
 * 这是标准
 * 1. 创建源
 * 2. 选择源
 * 3. 操作
 * 4. 释放
 */
public class IOTest03 {

    public static void main(String[] args) {
        // 1. 创建源
        File src = new File("gg.txt");
        // 2. 选择流
        InputStream is = null;
        try {
            is = new FileInputStream(src);
            // 3. 操作 (分段读取)
            byte[] flush = new byte[1024]; // 缓冲容器
            int len = -1; // 接受长度
            while ((len = is.read(flush)) != -1) {
                // 字节数组 --> 字符串 (解码)
                String str = new String(flush, 0, len);
                System.out.println(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

​ FileOutputStream

​ FileOutputStream通过字节的方式写出或追加数据到文件,适合所有类型的文件(图像、视频等),全字符请考虑FileWriter。

package com.io2;

import java.io.*;

/**
 * 字节输出流
 */
public class IOTest04 {

    public static void main(String[] args) {
        // 1. 创建源
        File src = new File("dest.txt");
        // 2. 选择流
        OutputStream os = null;
        try {
            os = new FileOutputStream(src, false);
            // 3. 操作(写出)
            String msg = "IO is so easy";
            byte[] datas = msg.getBytes();
            os.write(datas, 0, datas.length);
            os.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

3.1.2 IO标准步骤

​ 创建源 -> 选择流 -> 操作 -> 释放、

import java.io.*;

/**
 * 这是标准
 * 1. 创建源
 * 2. 选择源
 * 3. 操作
 * 4. 释放
 */
public class IOTest02 {

    public static void main(String[] args) {
        // 1. 创建源
        File src = new File("gg.txt");
        // 2. 选择流
        InputStream is = null;
        try {
            is = new FileInputStream(src);
            // 3. 操作 (读取)
            int temp;
            while ((temp = is.read()) != -1) {
                System.out.println((char)temp);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally { // 释放资源
            try {
                if (null != is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

3.1.3 文件的拷贝

在这里插入图片描述

​ 文件的拷贝,其实就是将IO的输入流和输出流连在一起。一边从输入端读入数据,一边又将数据从程序输出对应的文件地址。

package com.io2;

import java.io.*;

/**
 * 使用文件字节输入输出流达到文件的拷贝
 */

public class Copy {

    public static void main(String[] args) {
        copy("/home/qianqian/IdeaProjects/HelloWorld/src/com/io2/Copy.java","copy.txt");
    }

    public static void copy(String srcPath, String destPath) {
        // 1. 创建源
        File src = new File(srcPath);
        File dest = new File(destPath);
        // 2. 选择流
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(src);
            os = new FileOutputStream(dest);
            byte[] flush = new byte[1024];
            int len = -1;
            while ((len = is.read(flush)) != -1) {
                os.write(flush, 0, len);
                os.flush();
            }
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(os != null) {
                    os.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) {
                    is.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
       zijieliu }

    }

}

3.2 文件字符流

在这里插入图片描述

​ FileReader:通过字符的方式读取文件,仅适合字符文件。

package com.io2;

import java.io.*;

/**
 * 分段读取 文件字符输入流
 */
public class IOTest05 {

    public static void main(String[] args) {
        // 1.创建源
        File src = new File("dest.txt");
        // 2.选择流
        Reader reader = null;
        try {
            reader = new FileReader(src);
            // 3. 操作
            char[] flush = new char[1024];
            int len = -1;
            while ((len = reader.read(flush)) != -1) {
                String str = new String(flush, 0, len);
                System.out.println(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

​ FileWriter: 通过字节的方式写出或追加数据到文件中,仅适合字符文件。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

/**
 * 文件字符输出流
 */
public class IOTest06 {

    public static void main(String[] args) {
        // 1.创建源
        File src = new File("dest.txt");
        // 2.创建流
        Writer writer = null;
        try {
            // 3.操作 写法1
            writer = new FileWriter(src, true);
            String str = "中国永远是最伟大的!";
            char[] datas = str.toCharArray();
            writer.write(datas, 0, datas.length);
            writer.flush();
            // 写法二
            String msg = "凄凄切切求求求求";
            writer.write(msg);
            writer.flush();
            // 写法三
            writer.append("凄凄切切强强强").append("吾问无为谓无");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

​ 如果使用FileReader 和 FileWriter 操作除了字符文件以外的文件,并不报错,但是该文件会遭到破坏,后期无法读取。

3.3 数组流

ByteArrayInputStream&ByteArrayOutputStream

在这里插入图片描述

​ 在之前学过的InputStream、OutputStream、Writer、Reader这些类,处理的流都是存储在硬盘上的资源。这些资源Java虚拟机是无权访问的,必须借助操作系统。当使用完之后,必须人为的通知Java虚拟机去释放资源。而ByteArrayInputStream和ByteArrayOutputStream将数据的源头从之前的硬盘中的文件换成电脑上的一块内存,一串字节数组。这里的内存,可以把它看做是一个计算机上的一块内存,也可以看做互联网上的一块内存,亦可以看做是远程服务器上的一段内存。而Java是可以直接访问内存的,所以这一块资源可以由Java的垃圾回收器来释放资源。所以不用手动释放。释放也没关系,因为这里的close()方法是一个空方法。

​ 所有的东西都可以转换成字符串字符。转成字节数,也就变成了二进制,可以方便我们在网络上进行传输。这些的字节数据流在很多框架的底层都有很多的具体的应用。

​ 文件可以无限制的向其中增加内容,但是内存速度快,量小,所以内存不建议大量的存入。所以,在我们后期处理文件的时候,建议每次处理的内容少一点,防止内存的溢出。

​ 下面是关于ByteArrayInputStream的代码:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 字节数组输入流
 */

public class IOTest07 {

    public static void main(String[] args) {
        // 1.创建源
        byte[] src = "talk is cheap show me the code".getBytes();
        // 2.选择流
        InputStream is = null;
        try {
            is = new ByteArrayInputStream(src);
            // 3. 操作
            byte[] flush = new byte[5];
            int len = -1;
            while ((len = is.read(flush)) != -1) {
                String str = new String(flush, 0, len);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

​ 下面是关于ByteArrayOutputStream的代码:

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 字节数据输出流
 * 创建源:内部维护
 * 选择流:不关联源
 * 操作(写出内容)
 */
public class IOTest08 {

    public static void main(String[] args) {
        byte[] dest = null;
        // 1.选择流(因为要使用新增方法,所以不用多态)
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            byte[] datas = "talk is cheap, show me the code".getBytes();
            baos.write(datas, 0, datas.length);
            baos.flush();
            // 获取数据
            dest = baos.toByteArray();
            System.out.println(dest.length);
            System.out.println(new String(dest,0, dest.length));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

​ 在这里需要注意,与ByteArrayInputStream不同的是,ByteArrayOutputStream的构造方法中,并没有ByteArrayOutputStream(File src) 的构造方法,它的构造方法如下:

ByteArrayOutputStream()

ByteArrayOutputStream(int size)

​ 由于写入的是到一个字节数组中,由于写入的大小的不确定性,导致不知道写入的字节数组的大小。所以,在这个类会构造一个缓冲区,缓冲区会自动增加。可以使用toByteArray()和toString()检索数据。

​ 关闭ByteArrayOutputStream没有任何效果。在关闭之后,可以调用此类中的方法,而不生成IOException。

​ 我们在写完数据之后,我们需要主动地使用toByteArray()来创建一个字节数组获取数据。

3.4 对接流

​ 这里的给出了一个对接流的例子。这个例子主要是将一张图片,首先存入内存中,然后将这张图片从内存中取出放入硬盘中。

import java.io.*;

/**
 * 图片读取到字节数组中
 * 将字节数据写出到文件
 */
public class TestIO09 {

    public static void main(String[] args) {
        byte[] datas = fileToByteArray("1.jpg");
        System.out.println(datas.length);
        byteArrayToFile(datas,"dest.jpg");
    }

    public static byte[] fileToByteArray(String path) {

        // 1. 创建源
        File src = new File(path);
        // 2. 选择流
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            is = new FileInputStream(src);
            baos = new ByteArrayOutputStream();
            // 3. 操作 (分段操作)
            byte[] flush = new byte[1024 * 10]; // 缓冲容器
            int len = -1;
            while ((len = is.read(flush)) != -1) {
                baos.write(flush, 0, len); // 写出到字节数组中
            }
            baos.flush();
            return baos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static void byteArrayToFile(byte[] datas, String path) {

        File src = new File(path);
        OutputStream os = null;
        ByteArrayInputStream bais = null;
        try {
            os = new FileOutputStream(src);
            bais = new ByteArrayInputStream(datas);
            byte[] flush = new byte[1024*10];
            int len = -1;
            while ((len = bais.read(flush)) != -1) {
                os.write(flush, 0, len);
            }
            os.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

​ 虽然这里直接使用 FileInputStream和FileOutputStream比较快,同时也比较能够让人理解。

​ 这里是使用FileInputStream将图片读入,再使用ByteArrayOutputStream将图片以字节写入字节数组,并保存在内存中。

​ 然后,使用FileOutputStream和ByteArrayInputStream。首先使用ByteInputStream从内存中读取字节数据,然后通过FileOutputSteam将其写入硬盘中的某个文件。

3.5 工具类

​ 工具类就是将频繁的使用流进行对接操作,和释放资源操作封装起来。这里的封装需要自己去封装。由于在进行数据库操作时,我经常会封装对数据库的增删改查和打开数据库关闭数据库的代码,所以这里不给参考代码。

// try(is;os) 自动释放资源
// try(FileInputStream is = new FileInputStream(src))
// 可以将对变量的声明丢入try后的括号内,这样可以在函数体中使用该变量,同时系统也可以自动释放资源。

3.6 IO-原理剖析-装饰器设计模式

3.6.1 设计模式

​ GOF设计模式,是用来解决复杂系统,复杂代码的一种方法。是为了解决复杂系统、代码的一种固定的套路和模式。

3.6.2 装饰器设计模式

3.6.2.1 认识装饰器模式

​ 装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类这种方式并不可取。在面向对象的设计中,而我们也应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后再运行期间,根据需要来动态组合的这样一个模式。

3.6.2.2 模式结构和说明

装饰模式的结构如下图所示。

Component:组件对象的接口,可以给这些对象动态的添加职责;

ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;

Decorator:所有装饰器的父类,需要定义一个与组件接口一致的接口(主要是为了实现装饰器功能的复用,即具体的装饰器A可以装饰另外一个具体的装饰器B,因为装饰器类也是一个Component),并持有一个Component对象,该对象其实就是被装饰的对象。如果不继承组件接口类,则只能为某个组件添加单一的功能,即装饰器对象不能在装饰其他的装饰器对象。

ConcreteDecorator:具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

​ 下面是一个列子:

package com.io_decoratte;

public class DecorateTest0 {

    public static void main(String[] args) {
        Person p = new Person();
        p.say();

        Amplifier am = new Amplifier(p);
        am.say();
    }

}

interface Say {
    void say();
}

class Person implements Say {
    private int voice = 50;

    @Override
    public void say() {
        System.out.println("人的声音:" + this.getVoice());
    }

    public Person() {
    }

    public Person(int voice) {
        this.voice = voice;
    }

    public int getVoice() {
        return voice;
    }

    public void setVoice(int voice) {
        this.voice = voice;
    }
}

class Amplifier implements Say {
    private Person p;

    public Amplifier(Person p) {
        this.p = p;
    }

    @Override
    public void say() {
        System.out.println("人的声音: " + p.getVoice()*10);
        System.out.println("噪音,,,,,,");
    }
}

​ 接下来是一个更加复杂的装饰修饰模式

/**
 * 模拟咖啡
 * 1. 抽象组件:需要装饰的抽象对象(接口或父类)
 * 2. 具体组件:需要装饰的对象
 * 3. 抽象装饰类:包含了堆抽象组件的引用以及装饰这共有的方法
 * 4. 具体装饰类:被装饰的对象
 */

public class DecorateTest02 {

    public static void main(String[] args) {

        Drink coffee = new Coffee();
        Drink suger = new Suger(coffee);
        System.out.println(suger.info() + "-->" + suger.cost());
        Drink milk = new Milk(coffee);
        System.out.println(milk.info() + "-->" + milk.cost());

        milk = new Milk(suger);
        System.out.println(milk.info() + "-->" + milk.cost());
    }

}

// 抽象组件
interface Drink {
    double cost();
    String info();
}


// 具体组件
class Coffee implements Drink {

    private String name = "原味咖啡";

    @Override
    public double cost() {
        return 10;
    }

    @Override
    public String info() {
        return this.name;
    }
}

// 装饰抽象类
abstract class Decorate implements Drink {
    // 对抽象组件的引用
    private Drink drink;
    public Decorate(Drink drink) {
        this.drink = drink;
    }
    @Override
    public double cost() {
        return this.drink.cost();
    }

    @Override
    public String info() {
        return this.drink.info();
    }
}

// 具体的装饰类
class Milk extends Decorate {

    public Milk(Drink drink) {
        super(drink);
    }

    @Override
    public double cost() {
        return super.cost()*4;
    }

    @Override
    public String info() {
        return super.info() + "加入了牛奶";
    }
}

// 具体实现类
class Suger extends Decorate {

    public Suger(Drink drink) {
        super(drink);
    }

    @Override
    public double cost() {
        return super.cost()*2;
    }

    @Override
    public String info() {
        return super.info() + "加入了蔗糖";
    }
}

​ 而在IO流中,也是这样的修饰模式:

在这里插入图片描述

3.4 装饰流

3.4.1 字节缓冲流

在这里插入图片描述

​ 字节缓冲流提高了系统处理字节流的效率。字节缓冲流在系统创建了一个缓冲区,提高我们读写的操作。原因是因为,我们只要将想要读取或者写出的数据全部存入缓冲区,再慢慢地读出或者写入,这样避免了大量的访问硬盘,从而提高了效率。

​ 字节缓冲流里面的默认的缓冲区是8k,当然可以自己指定,当满了8k再一起写出去。

​ 如果想要释放资源,是从从内向外释放。而BUfferedInputStream的close(),内部就是释放FileInputStream,所以可以直接调用BufferedInputStream的close()方法即可。

​ 无论什么IO流,底层一定是一个节点流。

​ 随着我们嵌套的IO流越来越多,我们只需要释放最外层的装饰流即可。最外层的流会自动地释放内部的流。如果手动释放,则是从内向外一个个释放。


import java.io.*;

/**
 * 加入缓冲流
 */

public class BufferedTest02 {

    public static void main(String[] args) {
        File src = new File("copy.txt");
        OutputStream os = null;
        InputStream is = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(src, true));
            is = new BufferedInputStream(new FileInputStream(src));
            byte[] datas = "\n The best thing to learn programming is write code".getBytes();
            os.write(datas, 0, datas.length);
            os.flush();
            byte[] flush = new byte[1024*8];
            int len = -1;
            while ((len = is.read(flush)) != -1) {
                String str = new String(flush, 0, len);
                System.out.println(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

3.4.2 字符缓冲流

​ 操作纯文本对象。它提供了相比较字符流更多的方法,所以,在采用新的方法的时,注意不要发生多态。

​ BufferedReader()中新增方法:

​ readLine() 方法,自动寻找换行符,读入一行文字。

​ BufferedWriter()中新增方法:

​ newLine()方法,写一个行分隔符。

import java.io.*;

/**
 * 加入缓冲流
 */

public class BufferedTest04 {

    public static void main(String[] args) {

        File src = new File("copy2.txt");
        File dest = new File("copy.txt");

        try(BufferedReader reader = new BufferedReader(new FileReader(src),1024*8);
        BufferedWriter writer = new BufferedWriter(new FileWriter(dest),1024*8)) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            writer.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}s

3.4.3 数据流

​ DataInputStream&DataOutputStream:用来方便我们处理8大基本数据类型和字符串的。说的简单即是它不光保留我们的数据,还保留我们的数据类型。方便我们后期直接获取该数据类型,不用强转了。

import java.io.*;

/**
 * 数据流
 * 1. 先写出,后读取
 * 2. 读取的顺序与写出保持一致
 */

public class DataTest {

    public static void main(String[] args) throws IOException {
        // 写先出
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
        // 操作数据类型数据
        dos.writeUTF("编码辛酸泪,谁解其中味");
        dos.writeInt(18);
        dos.writeChar('a');
        dos.writeBoolean(false);
        dos.flush();
        byte[] datas = baos.toByteArray();
        System.out.println(datas.length);
        // 再读取
        DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
        String msg = dis.readUTF();
        int age = dis.readInt();
        char ch = dis.readChar();
        boolean flag = dis.readBoolean();
        System.out.println(flag);
    }

}

3.4.4 转换流

​ 由于很多的系统或者框架的底层传回来的是字节流,而字符流处理字符又显得非常的方便而且很多时候系统底层传回的实际上是纯文本,所以这个时候我们就需要一个转换流,可以自动地将字节流转换为字符流。

IputStreamReader&OutputStreamWriter

​ IputStreamReader&OutputStreamWriter:是字节流与字符流之间的桥梁,能将字节流转换为字符流,并且能为字节流指定字符集,可处理一个个的字符。

​ 其中InputStreamReader为解码,OutputStreamWriter为编码。

​ 一下为一部分代码:

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.Buffer;

/**
 * 转换流 InputStreamReader OutputStreamWriter
 * 1. 以字符流的形式操作字节流(处理纯文本)
 * 2. 可以指定字符集
 */
public class ConvertTest02 {

    public static void main(String[] args) {
        test02();
    }

    public static void test01() {
        // 操作System.in 和 System.out
        try {
            // 操作网络流, 下载百度源码
            InputStreamReader is = new InputStreamReader(new URL("http://www.baidu.com").openStream(), "utf-8");
            int temp;
            while((temp = is.read()) != -1) {
                System.out.print((char) temp); 
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void test02() {
        try  (BufferedReader reader = new BufferedReader(new InputStreamReader(new URL("http://www.baidu.com").openStream(), "utf-8"));
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("baidu.html"),"utf-8"))) {
           String msg;
           while((msg = reader.readLine()) != null) {
               writer.write(msg);
               writer.newLine();
           }
           writer.flush();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

3.4.5 对象流

ObjectInputStream&ObjectOputStream

​ 对象流,既可以操作基本数据类型,字符串,还可操作对象。

在这里插入图片描述
​ 这里有两个概念 Serialization( 序列化 )& Deserialization( 反序列化 )

​ Serialization指:将我们的对象输出到字节流里面,然后将它保存在文件中,数据库中,和内存中( 字节数组 )。同时将序列化之后的对象转化成对象称为Deserialization。

​ 关于对象流,这里有3个注意点

  1. 必须先写出后读取。

  2. 读取顺序和写出顺序必须一致。

  3. 并不是所有的对象都能够进行序列化的,必须给出能够序列化的标识,也就是必须实现serializable这个接口。

import java.io.*;
import java.util.Date;

public class ObjectTest02 {

    public static void main(String[] args) {
        // 写出 --> 序列化
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("Object.ser")));
            oos.writeObject("一二三四五");
            oos.writeObject("上山打老虎");
            People XiaoMing = new People("小明", 18, "男");
            oos.writeObject(XiaoMing);
            oos.writeObject(new Date());
            oos.flush();

            ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("Object.ser")));
            Object str1 = ois.readObject();
            Object str2 = ois.readObject();
            Object person = ois.readObject();
            Object date = ois.readObject();
            if (str1 instanceof String) {
                String strObj1 = (String) str1;
                System.out.println(strObj1);
            }
            if (str2 instanceof String) {
                String strObj2 = (String) str2;
                System.out.println(strObj2);
            }
            if (person instanceof People) {
                People personObj = (People) person;
                System.out.println(personObj.getName() + " " + personObj.getAge() + " " + personObj.getGender());
            }
            if (date instanceof Date) {
                Date dateObj = (Date) date;
                System.out.println(dateObj);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

class People implements java.io.Serializable {
    private String name;
    private int age;
    private String gender;

    public People() {

    }

    public People(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

3.4.6 打印流

​ 打印流有两个类一个是PrintStream 还有一个是 PrintWriter 由于不是很常用,所以这里提一下用法。

​ 注:在Java EE PrintWriter 经常用来向网页打印内容。

这里是关于PrintStream的一些用法:


import java.io.*;

/**
 * 打印流
 */

public class PrintTest {

    public static void main(String[] args) {
        // 打印流System.out
        PrintStream ps = System.out;
        ps.println("打印流");
        ps.println(true);

        try {
            ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
            ps.println("打印流");
            ps.println(true);

            // 重定向输出端
            System.setOut(ps);
            System.out.println("change");

            // 重定向回控制台
            System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true));
            System.out.println("i am  back");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

}

​ 这里是关于PrintWriter的一些用法,用法类似于PrintStream

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class PrintTest02 {

    public static void main(String[] args) {

        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream("print.txt")));
            pw.println("打印流");
            pw.println(true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
    }

}

3.5 IO-文件分割-面向对象核心版

随机流和合并流并不是重点,同时在工作中也不是很常用。

3.5.1 RandomAccessFile

​ 我们之前对数据的访问都是顺序访问,而这里的RandomAccessFile提供了一个seak() 方法,这个方法使得我们可以随机地访问文件中的某个点。这里有一个好处就是,可以使我们对一个文件进行分割。

​ 该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。有一种游标,或索引到隐含的数组,称为文件指针 ;输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。如果在读/写模式下创建随机访问文件,则输出操作也可用;输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。写入隐式数组的当前端的输出操作会导致扩展数组。文件指针可以通过读取getFilePointer方法和由设置seek方法。在这个类中的所有读取例程通常都是如果在读取所需的字节数之前到达文件结尾,则抛出一个EOFException (这是一种IOException)。 如果任何字节由于除文件末尾之外的任何原因而无法读取,则抛出IOException以外的EOFException 。 特别地,如果流已经被关闭,则可以抛出IOException 。

​ 它有两个构造方法,分别是:

Constructor 描述
RandomAccessFile(File file, String mode) 创建一个随机访问文件流,从File参数指定的文件读取,并克选择写入文件。
RandomAccessFile(File name, String mode) 创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。

​ 其中 String mode 这个模型,指的是创建访问文件流的操作类型是什么。在RandomAccessFile中给出了4个参数,分别是 : “r”, “rw”, “rws”, 或者"rwd"。

​ 其他操作方式和字节流是一致的。

​ 以下是一些例子:

 // 指定起始位置,读取所有内容
    public static void test1() {
        try {
            RandomAccessFile raf = new RandomAccessFile(new File("copy.txt"), "r");
            // 随机读取
            raf.seek(2);
            byte[] flush = new byte[1024];
            int len = -1;
            while((len = raf.read(flush)) != -1) {
                System.out.println(new String(flush, 0, len));
            }
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

// 这里考虑实际的文本长度和实际读取的大小之间的关系
public static void test2() {
        try {
            RandomAccessFile raf = new RandomAccessFile(new File("copy.txt"), "r");
            // 起始位置
            int beginPos = 2 + 1026;
            // 实际大小
            int actualSize = 1026;
            // 随机读取
            raf.seek(beginPos);
            // 读取
            byte[] flush = new byte[1024];// 缓冲容器
            int len = -1;
            while((len = raf.read(flush)) != -1) {
                if(actualSize < len) {
                    System.out.println(new String(flush, 0, len));
                    actualSize -= len;
                }else {
                    System.out.println(new String(flush, 0, len));
                    break;
                }
            }
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

3.6 IO-文件分割-面向对象终极版

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

/**
 * 面向对象思想封装 分割
 */

public class RandomTest02 {

    // 源头
    private String src;
    // 目的地(文件夹)
    private String destDir;
    // 所有分割后的文件存储路径
    private List<String> destPaths;
    // 每块大写
    private int blockSize;
    // 块数
    private int size;

    public RandomTest02(String src, String destDir, int blockSize) {
        this.src = src;
        this.destDir = destDir;
        this.blockSize = blockSize;
        this.destPaths = new ArrayList<>();
        // 初始化
        init();
    }

    private void init(){
        // 总长度
        long len = this.src.length();
        // 多少块
        this.size = (int) Math.ceil(len*1.1 / this.blockSize);
        // 路径
        for (int i = 0; i < this.size; i ++) {
            this.destPaths.add(this.destDir + "/" + i + "-" + this.src);
        }
    }

    /**
     * 分割
     * 1. 计算每一块的位置和起止大小
     * 2. 分割
     */
    public void split() throws IOException {
        // 文件的大小
        long len = this.src.length();
        // 起始位置
        int beginPos = 0;
        // 实际大小
        int actualSize =(int)(blockSize > len ? len : blockSize);
        for (int i = 0; i < this.size; i++) {
            beginPos = i * blockSize;
            if (i == this.size - 1) {
                actualSize = (int) len; // 最后一块
                splitDetail(i, beginPos, actualSize, destPaths.get(i));
                System.out.println("开始位置:" + beginPos);
                System.out.println("每块大小:" + actualSize);
            } else {
                actualSize = blockSize;
                len -= blockSize; // 剩余量
                splitDetail(i, beginPos, actualSize, destPaths.get(i));
                System.out.println("开始位置:" + beginPos);
                System.out.println("每块大小:" + actualSize);

            }
        }
    }

    private void splitDetail(int i, int beginPos, int actualSize, String destPath) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(this.src, "r");
        RandomAccessFile raf2 = new RandomAccessFile(destPath, "rw");
        raf.seek(beginPos);
        byte[] flush = new byte[actualSize];
        int len = raf.read(flush);
        raf.write(flush, 0, len);
        raf.close();
        raf2.close();
    }
}

// 文件的合并
    public void merge(String destPath) throws IOException {
        // 输出流
        OutputStream os = new BufferedOutputStream(new FileOutputStream(destPath, true));
        // 输入流
        for(int i = 0; i < destPaths.size(); i++) {
            InputStream is = new BufferedInputStream(new FileInputStream(destPaths.get(i)));
            // 拷贝
            byte[] flush = new byte[1024];
            int len = -1;
            while((len = is.read(flush)) != -1) {
                os.write(flush, 0, len);
            }
            os.flush();
            is.close();
        }
        os.close();
    }

3.7 IO-序列流-文件的合并

3.7.1 SequenceInputStream

​ A SequenceInputStream 表示其他输入流的逻辑级联。他从一个有序的输入流集合开始,从第一个读取到文件的结尾,然后从第二个文件读取,以此类推,直到最后一个输入流到达文件的结尾。

构造方法
构造方法 描述
SequenceInputStream(Enumeration<? extends InputStream> e) 初始化新创建SequenceInputStream通过记住参数,它必须是一个Enumeration产生对象,taeny的运行时类型是InputStream。
SequenceInputStream(InputStream s1, InputStream s2) 通过记住两个SequenceInputStream来初始化新创建的SequenceInputStream,这些参数将按照顺序读取,首先是s1,然后是s2,以提供要从此SequenceInputStream读取的字节
以下是一些操作
// 文件的合并
    public void merge(String destPath) throws IOException {
        // 输出流
        OutputStream os = new BufferedOutputStream(new FileOutputStream(destPath, true));
        Vector<InputStream> vi = new Vector<>();
        SequenceInputStream sis = null;
        // 输入流
        for(int i = 0; i < size; i++) {
            vi.add(new BufferedInputStream(new FileInputStream(destPaths.get(i))));
            System.out.println(vi.get(i));
        }
        sis = new SequenceInputStream(vi.elements());
        byte[] flush = new byte[1024];
        int len = -1;
        while((len = sis.read(flush)) != -1) {
            os.write(flush, 0, len);
        }
        os.flush();
        sis.close();
        os.close();
    }

4. CommonsIO

​ CommonsIO是由阿帕奇软件基金会 (APACHE SOFTWARE FOUNDATION)发布和维护的。

4.1 具体的下载过程:

  1. 首先登陆APACHE的官网:

在这里插入图片描述

  1. 然后向下滑动找到开头为C的组件一栏,找到Commons组件:

在这里插入图片描述

  1. 然后找到IO,当时最新的为2017.10更新:

在这里插入图片描述

  1. 找到符合自己JDK版本的CommonsIO版本:

在这里插入图片描述

4.2 CommonsIO最重要的是FileUtils,它包含以下的功能:

​ General file manipulation utilities. Facilities are provide in the following areas:

  • writing to a file
  • reading from a file
  • make a directory including parent directories
  • copying files and directories
  • deleting files and directories
  • converting to and from a URL
  • Listing files and dirctories by filter and extension
  • comparing file content
  • file last changed date
  • calculating a checksum

4.3 一些常用的操作

4.3.1 文件大小

import org.apache.commons.io.FileUtils;

import java.io.File;

/**
 * 文件的大小
 */

public class CIOTest01 {

    public static void main(String[] args) {
        // 文件大小
        long len = FileUtils.sizeOf(new File("baidu.html"));
        // 目录大小
        long len2 = FileUtils.sizeOf(new File("/home/qianqian/IdeaProjects/HelloWorld"));
        System.out.println(len + "  " + len2);
    }
}

4.3.2 遍历子孙集

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.*;

import java.io.File;
import java.util.Collection;


/**
 * 子孙集
 */

public class CIOTest02 {

    public static void main(String[] args) {
        Collection<File> files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), EmptyFileFilter.NOT_EMPTY, null);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
        System.out.println("----------------------------------------------------------");
        files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
        System.out.println("----------------------------------------------------------");
        files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), new SuffixFileFilter("java"), DirectoryFileFilter.INSTANCE);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
        files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), FileFilterUtils.or(new SuffixFileFilter("java"), new SuffixFileFilter("class")), DirectoryFileFilter.INSTANCE);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
        files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), FileFilterUtils.or(new SuffixFileFilter("java"), new SuffixFileFilter("class"), EmptyFileFilter.EMPTY), DirectoryFileFilter.INSTANCE);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
        files =  FileUtils.listFiles(new File("/home/qianqian/IdeaProjects/HelloWorld"), FileFilterUtils.and(new SuffixFileFilter("java"), EmptyFileFilter.NOT_EMPTY), DirectoryFileFilter.INSTANCE);
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
    }
}

4.4 读取文件

4.4.1 读取文件

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * 读取文件
 */

public class CIOTest03 {

    public static void main(String[] args) throws IOException {
        // 读取文件
        String msg = FileUtils.readFileToString(new File("copy2.txt"), "UTF-8");
        System.out.println(msg);
        byte[] datas = FileUtils.readFileToByteArray(new File("copy2.txt"));
        System.out.println(datas.length);

        // 逐行读取
        List<String> msgs = FileUtils.readLines(new File("copy2.txt"), "UTF-8");
        for (String string : msgs) {
            System.out.println(string);
        }
        LineIterator it = FileUtils.lineIterator(new File("copy2.txt"), "UTF-8");
        while(it.hasNext()) {
            System.out.println(it.nextLine());
        }
    }

}

4.4.2 写出文件和写出列表

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 写出内容
 */

public class CIOTest04 {

    public static void main(String[] args) throws IOException {
        // 写出文件
        FileUtils.write(new File("happy.txt"),"学习是一件伟大的事情\r\n");
        FileUtils.writeStringToFile(new File("happy.txt"), "学习是一件伟大的事情\r\n", true);
        FileUtils.writeByteArrayToFile(new File("happy.txt"), "学习是一件幸福的事情\r\n".getBytes("UTF-8"), true);

        // 写出列表
        List<String> datas = new ArrayList<>();
        datas.add("mayun");
        datas.add("mahuateng");
        datas.add("magou");
        datas.add("matianxiang");

        FileUtils.writeLines(new File("happy.txt"), datas, "-",true);
    }

4.5 拷贝文件

以下是一些实例代码:

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 拷贝
 */

public class CIOTest05 {

    public static void main(String[] args) throws IOException {
        // 复制文件
        try {
            FileUtils.copyFile(new File("1.jpg"), new File("p-copy.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 复制文件到目录
        FileUtils.copyFileToDirectory(new File("1.jpg"), new File("lib"));
        // 复制目录到目录
        FileUtils.copyDirectoryToDirectory(new File("lib"), new File("lib2"));
        // 复制目录
        FileUtils.copyDirectory(new File("lib"), new File("lib2"));
        // 拷贝URL内容
        String url = "https://www.baidu.com/img/bd_logo1.png";
        FileUtils.copyURLToFile(new URL(url), new File("baidu.jpg"));
        String datas = IOUtils.toString(new URL("http://www.baidu.com"), "UTF-8");
        System.out.println(datas);
    }

}

5.IO总结

  1. 字节流 — > 字符流 字节流可以转换成字符流
  2. 输入流和输出流 InputStream 和 OutputStream
  3. 节点流、处理流(装饰流)。 处理流是在节点流之上的,用来对节点流进行性能提升时使用的。

操作IO的固定套路:

创建源 —> 选择流 —> 操作 —> 释放资源

其他的流包括:

Data流、 Object流。Object流中存在序列化和反序列化;该对象能否序列化,需要实现Serializable接口。

其他的流了解即可。我们在工作中一般使用别人已经写好的组件。比如CommonsIO等。

标签:src,IO,System,笔记,学堂,File,new,public,String
来源: https://blog.csdn.net/qq_41302594/article/details/99069963

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有