【Java高级编程】IO流

在这里插入图片描述

1、File类的使用

1.1、File类的理解

  • File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
  • File类声明在java.io包下
  • File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
  • 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的“终点”

1.2、File的实例化

  • 常见构造器
    • File(String filePath)
    • File(String parentPath, String childPath)
    • File(File parentFile, String childPath)
  • 路径的分类
    • 相对路径:相较于某个路径下,指明的路径
    • 绝对路径:包含盘符在内的文件或文件目录的路径
  • 路径分隔符
    • public static final String separator:根据操作系统,动态的提供分隔符

1.3、常用方法

  • File类的获取功能
    • public String getAbsolutePath():获取绝对路径
    • public String getPath():获取路径
    • public String getName():获取名称
    • public String getParent():获取上层文件目录。若无,返回null
    • public long length():获取文件长度(即:字节数)。不能获取目录的长度。
    • public long lastModified():获取最后一次的修改时间,毫秒值
    • public String[] list():获取指定目录下的所有文件或文件目录的名称数组
    • public File[] listFiles():获取指定目录下的所有文件或文件目录的File数组
  • File类的重命名功能
    • public boolean renameTo(File dest):把文件重命名为指定的文件路径。要想保证返回true,需要源文件在硬盘中存在,且目标文件不能在硬盘中存在
  • File类的判断功能
    • public boolean isDirectory():判断是否是文件目录
    • public boolean isFile():判断是否是文件
    • public boolean exists():判断是否存在
    • public boolean canRead():判断是否可读
    • public boolean canWrite():判断是否可写
    • public boolean isHidden():判断是否隐藏
  • File类的创建功能
    • public boolean creatNewFile():创建文件。若文件存在,则不创建,返回false
    • public boolean mkdir():创建文件目录。如果文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
    • public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建
  • File类的删除功能
    • public boolean delete():删除文件或文件夹。要删除一个文件目录,请注意该文件目录内不能包含文件或文件目录

2、IO原理及流的分类

2.1、流的分类

  • 按操作数据单位不同分为:字节流、字符流
  • 按数据流的流向不同分为:输入流、输出流
  • 按流的角色的不同分为:节点流、处理流

2.2、流的体系结构

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream
FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream

2.3、输入、输出的标准化过程

  • 输入过程
    • 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
    • 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
    • 具体的读入过程
    • 关闭流资源
  • 输出过程
    • 创建File类的对象,指明写出的数据的位置。(不要求此文件一定要存在)
    • 创建相应的输出流,将File类的对象作为参数,传入流的构造器中
    • 具体的写出过程
    • 关闭流资源

说明:程序中出现的异常需要使用try-catch-finally处理

3、节点流(或文件流)

3.1、FileReader的使用

  • read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
  • 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
  • 读入的文件一定要存在,否则就会报FileNotFoundException
@Test
public void testFileReader1 () {
    FileReader fr = null;
    try {
        // 1、File类的实例化
        File file = new File("hello.txt");

        // 2、FileReader流的实例化
        fr = new FileReader(file);

        // 3、读入的操作
        // read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾则返回-1
        char[] cbuf = new char[5];
        int len;
        while ((len = fr.read(cbuf)) != -1) {
            String str = new String(cbuf, 0, len);
            System.out.println(str);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fr != null) {
            // 4、关闭资源
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2、FileWriter的使用

  • 输出操作,对应的File可以不存在。并不会报异常
  • File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
  • File对应的硬盘中的文件如果存在:
    • 如果流使用的构造器是:FileWriter(file, false) / FileWriter(file):对原有文件的覆盖
    • 如果流使用的构造器是:FileWriter(file, true):不会对原有文件覆盖,而是在原有文件基础上追加内容
@Test
public void testFileWriter () {
    FileWriter fw = null;
    try {
        // 1、提供File类的对象,指明写出到的文件
        File file = new File("hello1.txt");

        // 2、提供FileWriter的对象,用于数据的写出
        fw = new FileWriter(file, false);

        // 3、写出的操作
        fw.write("I have a dream!\n");
        fw.write("you need to have a dream!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4、流资源的关闭
        if (fw != null) {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.3、文本文件的复制

@Test
public void testFileReaderFileWriter () {
    FileReader fr = null;
    FileWriter fw = null;

    try {
        // 1、创建File类的对象,指明读入和写出的文件
        File srcFile = new File("hello.txt");
        File destFile = new File("hello2.txt");

        // 2、创建输入流和输出流的对象
        fr = new FileReader(srcFile);
        fw = new FileWriter(destFile);

        // 3、数据的读入和写出操作
        char[] cbuf = new char[5];
        int len; // 记录每次读入到cbuf数组中的字符的个数
        while ((len = fr.read(cbuf)) != -1) {
            // 每次写出len个字符
            fw.write(cbuf, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4、关闭流资源
        if (fw != null) {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fr != null) {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.4、FileInputStream/FileOutputStream的使用

  • 对于文本文件(.txt, .java, .c, .cpp),使用字符流处理
  • 对于非文本文件(.jpg, .mp3, .mp4, .avi, .doc, .ppt,…),使用字节流处理
/**
  * 实现对图片的复制操作
  */
@Test
public void testFileInputOutputStream () {
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
        // 1、造文件
        File srcFile = new File("爱情与友情.jpg");
        File destFile = new File("爱情与友情2.jpg");
        // 2、造流
        fis = new FileInputStream(srcFile);
        fos = new FileOutputStream(destFile);
        // 3、复制的过程
        byte[] buffer = new byte[5];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4、关闭流
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

说明:
IDEA中:如果大家开发使用JUnit中的单元测试方法测试,相对路径即为当前Module下。如果使用main()测试,相对路径即为当前的Project下。
Eclipse中:不管使用单元测试方法还是使用main()测试,相对路径都是当前的Project下

4、缓冲流

4.1、缓冲流涉及到的类

  • BufferedInputStream
  • BufferedOutputSteam
  • BufferedReader
  • BufferedWriter

4.2、作用

  • 提高流的读取、写入的速度。
  • 提高读写速度的原因:内部提供了一个缓冲区。默认情况下是8kb
public class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;

}

4.3、典型代码

  • 使用BufferedInputStream和BufferedOutputStream:处理非文本文件
@Test
public void copyFileWithBuffered(String srcPath, String destPath) {
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        // 1、造文件
        File srcFile = new File(srcPath);
        File destFile = new File(destPath);
        // 2、造流
        // 2.1、造节点流
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        // 2.2、造缓冲流
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);
        // 3、复制的细节:读取、写入
        byte[] buffer = new byte[10];
        int len;
        while ((len = bis.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
            // bos.flush(); //刷新缓冲区
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4、资源关闭
        // 要求:先关闭外层的流,再关闭内层的流
        if (bis != null) {
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bos != null) {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略
        // fos.close();
        // fis.close();
    }
}
  • 使用BufferedReader和BufferedWriter:处理文本文件
@Test
public void testBufferedReaderBufferedWriter () {
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        // 创建文件和相应的流
        br = new BufferedReader(new FileReader(new File("dbcp.txt")));
        bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

        // 读写操作
        // 方式一:使用char[]数组
        // char[] cbuf = new char[1024];
        // int len;
        // while ((len = br.read(cbuf)) != -1) {
        //     bw.write(cbuf, 0, len);
        // }
        // 方式二:使用String
        String data;
        while ((data = br.readLine()) != null) {
            // 方法一:
            // bw.write(data + "\n"); // data中不包含换行符
            // 方法二:
            bw.write(data);
            bw.newLine(); // 提供换行的操作
        }
    } 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();
            }
        }
    }

}

5、转换流

5.1、转换流涉及到的类:属于字符流

  • InputStreamReader:将一个字节的输入流转换为字符的输入流
    • 解码:字节、字节数组 —> 字符数组、字符串
  • OutputStreamWriter:将一个字符的输出流转换为字节的输出流
    • 编码:字符数组、字符串 —> 字节、字节数组

说明:编码决定了解码的方式

5.2、作用

  • 提供字节流与字符流之间的转换

5.3、典型实现

  • 文件编码的方式(比如:GBK),决定了解析时使用的字符集(也只能是GBK)
@Test
public void test2 () {
    InputStreamReader isr = null;
    OutputStreamWriter osw = null;
    try {
        // 1、造文件、造流
        File file1 = new File("dbcp.txt");
        File file2 = new File("dbcp_gbk.txt");

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        // isr = new InputStreamReader(fis); // 使用系统默认的字符集
        isr = new InputStreamReader(fis, "utf-8");
        osw = new OutputStreamWriter(fos, "gbk");

        // 2、读写过程
        char[] cbuf = new char[20];
        int len;
        while ((len = isr.read(cbuf)) != -1) {
            osw.write(cbuf, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 3、关闭资源
        if (isr != null) {
            try {
                isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (osw != null) {
            try {
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5.4、常见的编码表

  • ASCII:美国标准信息交换码。用一个字节的7位可以表示。
  • ISO8859-1:拉丁码、欧洲码表。用一个字节的8位表示
  • GB2312:中国的中文编码表。最多两个字节编码所有字符
  • GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
  • Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
  • UTF-8:变长的编码方法,可用1-4个字节来表示一个字符。

6、其它的流的使用

6.1、标准输入、输出流

  • System.in:标准的输入流,默认从键盘输入
  • System.out:标准的输出流,默认从控制台输出
  • 修改默认的输入和输出行为
    • System类的setIn(InputStream is)/setOut(PrintStream ps)方式重新指定输入和输出的流

6.2、打印流

  • PrintStream和PrintWriter
  • 提供了一些列重载的print()和println()方法,用于多种数据类型的输出
  • System.out返回的是PrintStream的实例

6.3、数据流

  • DataInputStream和DataOutputStream
  • 用于读取或写出基本数据类型的变量或字符串
@Test
public void test3() {
    DataOutputStream dos = null;
    try {
        dos = new DataOutputStream(new FileOutputStream("data.txt"));

        dos.writeUTF("刘建辰");
        dos.flush(); // 刷新操作,将内存中的数据写入文件
        dos.writeInt(23);
        dos.flush();
        dos.writeBoolean(true);
        dos.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (dos != null) {
            try {
                dos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
  * 注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致
  */
@Test
public void test4() {
    DataInputStream dis = null;
    try {
        dis = new DataInputStream(new FileInputStream("data.txt"));

        String name = dis.readUTF();
        int age = dis.readInt();
        boolean isMale = dis.readBoolean();

        System.out.println(name);
        System.out.println(age);
        System.out.println(isMale);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (dis != null) {
            try {
                dis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

7、对象流

7.1、对象流

  • ObjectInputStream:内存中的对象 —> 存储中的文件、通过网络传输出去(序列化过程)
  • ObjectOutputSteam:存储中的文件、通过网络接受过来 —> 内存中的对象(反序列化过程)

7.2、对象的序列化机制

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
  • 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

7.3、序列化代码实现

@Test
public void testObjectOutputSteam () {
    ObjectOutputStream oos = null;
    try {
        // 1、造流
        oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
        // 2、读写过程
        oos.writeObject(new String("我爱北京天安门"));
        oos.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 3、关闭资源
        if (oos != null) {
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

7.4、反序列化代码实现

@Test
public void testObjectInputSteam () {
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("object.dat"));

        Object obj = ois.readObject();
        String str = (String) obj;

        System.out.println(str);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (ois != null) {
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

7.5、实现序列化的对象所属的类需要满足的条件

  • 需要实现接口:Serializable
  • 当前类提供一个全局常量:serialVersionUID
  • 除了当前类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)

补充:ObjectOutputSteam和ObjectInputStream不能序列化static和transient修饰的成员变量

8、随机存取文件流

8.1、随机存取文件流

  • RandomAccessFile

8.2、使用说明

  • RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
  • RandomAccessFile既可以作为一个输入流,又可以作为一个输出流
  • 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原文件内容进行覆盖。(默认情况下,从头覆盖)
  • 可以通过相关的操作,实现RandomAccessFile“插入”数据的效果。seek(int pos)

8.3、典型代码

/**
 * 实现复制功能
 */
@Test
public void test1 () {
    RandomAccessFile raf1 = null;
    RandomAccessFile raf2 = null;
    try {
        // 1、造流
        raf1 = new RandomAccessFile(new File("爱情与友情.jpg"), "r");
        raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"), "rw");
        // 2、读写过程
        byte[] buffer = new byte[1024];
        int len;
        while ((len = raf1.read(buffer)) != -1) {
            raf2.write(buffer, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 3、关闭资源
        if (raf1 != null) {
            try {
                raf1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (raf2 != null) {
            try {
                raf2.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
/**
 * 使用RandomAccessFile实现数据的插入效果
 */
@Test
public void test3 () {
    RandomAccessFile raf1 = null;
    try {
        raf1 = new RandomAccessFile("hello.txt", "rw");

        raf1.seek(3); // 将指针调到角标为3的位置
        // 保存指针3后面的所有数据到StringBuilder中
        StringBuilder buildr = new StringBuilder((int) new File("hello.txt").length());
        byte[] buffer = new byte[20];
        int len;
        while ((len = raf1.read(buffer)) != -1) {
            buildr.append(new String(buffer, 0, len));
        }

        // 调回指针,写入“xyz”
        raf1.seek(3);
        raf1.write("xyz".getBytes());

        // 将StringBuilder中的数据写入到文件中
        raf1.write(buildr.toString().getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (raf1 != null) {
            try {
                raf1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

9、NIO.2中Path、Paths、Files类的使用

9.1、NIO的使用说明

  • Java NIO(New IO, Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。
  • NIO与原来的IO同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作
  • NIO将以更高效的方式进行文件的读写操作
  • 随着JDK 7的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为NIO.2

9.2、Path的使用

  • Path替换原有的File类
  • Paths类提供了静态get()方法用来获取Path对象:
    • static Path get(String first, String … more):用于将多个字符串串连成路径
    • static Path get(URI uri):返回指定uri对应的Parh路径
  • 常用方法:
    • String toString():返回调用Path对象的字符串表示形式
    • boolean startsWith(String path):判断是否以path路径开始
    • boolean endsWith(String path):判断是否以path路径结束
    • boolean isAbsolute():判断是否是绝对路径
    • Path getParent():返回path对象包含整个路径,不包含Path对象指定的文件路径
    • Path getRoot():返回调用Path对象的根路径
    • Path getFileName():返回与调用Path对象关联的文件名
    • int getNameCount():返回Path根目录后面元素的数量
    • Path getName(int idx):返回指定索引位置idx的路径名称
    • Path toAbsolutePath():作为绝对路径返回调用Path对象
    • Path resolve(Path p):合并两个路径,返回合并后的路径对应的Path对象
    • File toFile():将Path转换为File类的对象

9.3、Files工具类

  • 作用:操作文件或文件目录的工具类
  • 常用方法:
    • Path copy(Path src, Path dest, CopyOption … how):文件的复制
    • Path createDirectory(Path path, FileAttribute<?> … attr):创建一个目录
    • Path createFile(Path path, FileAttribute<?> … arr):创建一个文件
    • void delete(Path path):删除一个文件/目录,如果不存在,执行报错
    • void deleteIfExists(Path path):Path对应的文件/目录如果存在,执行删除
    • Path move(Path src, Path dest, CopyOption … how):将src移动到dest位置
    • long size(Path path):返回指定文件的大小
  • 用于判断:
    • boolean exists(Path path, LinkOption … opts):判断文件是否存在
    • boolean isDirectory(Path path, LinkOption … opts):判断是否是目录
    • boolean isRegularFile(Path path, LinkOption … opts):判断是否是文件
    • boolean isHidden(Path path):判断是否是隐藏文件
    • boolean isReadable(Path path):判断文件是否可读
    • boolean isWritable(Path path):判断文件是否可写
    • boolean notExists(Path path, LinkOption … opts):判断文件是否不存在
  • 用于操作内存
    • SeekableByteChannel newByteChannel(Path path, OpenOption … how):获取与指定文件的连接,how指定打开方式
    • DirectoryStream<Path> newDirectoryStream(Path path):打开path指定的目录
    • InputStream newInputStream(Path path, OpenOption … how):获取InputStream对象
    • OutputStream newOutputStream(Path path, OpenOption … how):获取OutputStream对象

猜你喜欢

转载自blog.csdn.net/qq_51808107/article/details/131443242
今日推荐