11. Java的IO流

Java —— IO流

        • 1. Flie
        • 2. IO
          • 2.1 IO字节低级流
            • 2.1.1 文件输出流 FileOutputStream
            • 2.1.2 文件输入流 FileInputStream
            • 2.1.3 文件复制(字节输入流与字节输出流的应用)
          • 2.2 OI字节高级流
            • 2.2.1 缓冲字节输入流 BufferedInputStream
            • 2.2.2 缓冲字节输出流 BufferedOutputStream
            • 2.2.3 对象输出流 ObjectOutputStream
            • 2.2.4 对象输入流 ObjectInputStream
          • 2.3 IO字符流
            • 2.3.1 转换输入流 InputStreamReader
            • 2.3.2 转换输出流 OutputStreamWriter
            • 2.3.3 缓冲字符输入流 BufferedReader
            • 2.3.4 缓冲字符输出流 BufferedWriter
            • 2.3.5 字符输出流 PrintWriter
        • 3. 字节流与字符流的区别
        • 4. IO流的不同分类

1. Flie
File类:用来操作(创建、删除、重命名、判断存在、获取属性等)文件和目录的类(通过File类在程序中操作硬盘上的文件和目录)
作用:将给定路径字符串转换成抽象路径名来创建一个新的File实例

File类:只用于表示文件/目录的信息(大小,名称),不能对文件内容进行访问

  • File没有无参构造器,在创建对象时要传入以字符串形式的路径: File(String path)
  • File的构造方法之一:File(File parent, String child)
    • parent:一个 File 对象,表示父路径
    • child:一个字符串,表示子路径
      • 父路径可以是一个目录或文件,子路径可以是相对路径或绝对路径
      • 当子路径是相对路径时,它将被解析为相对于父路径的路径
    • 绝对路径:从操作系统要求的根目录开始
      • Windows:对应的硬盘根目录通常就是:c:/d:/等
      • Linux和macOS:根目录:/
    • 相对路径:
      • Windows:反斜杠(\)作为路径分隔符:.\folder\file.txt或者./folder/file.txt
      • macOS和Linux:正斜杠(/)作为路径分隔符:./folder/file.txt

抽象路径尽量使用相对路径,层级分隔符不要直接写/或\
System.getProperty("file.separator") 方法:获取当前操作系统的目录层次分隔符

File文件信息
   ▶ String getName():返回文件或目录名字
   ▶ long length():返回文件的长度,单位是字节
   ▶ lboolean canRead():是否可读
   ▶ boolean isHidden:是否隐藏

File的常用方法
boolean exists():测试此抽象路径名表示的文件或目录是否存在

  • 返回值:若文件或目录存在则返回true,否则返回false

boolean createNewFile():创建一个新的空文件

  • 返回值:如果文件不存在并成功创建,返回true;如果指定文件存在,则返回false

boolean delete():删除此抽象路径名表示的文件或目录

  • 返回值:当且仅当成功删除文件或目录时,返回true,否则返回false
    • 当删除的对象为一个非空目录时,只有此目录为空(目录中不含任何文件或子目录)才能成功删除

boolean mkdir():创建指定路径的目录(只能创建单层目录,即指定路径下的最后一级目录)

  • 返回值:已创建目录时,返回true;目录已经存在或无法创建返回false

boolean mkdirs():创建指定路径的多级目录(包括所有需要但不存在的父目录)

  • 返回值:当成功创建了所有所需目录时,返回true,当返回false时,有可能创建了部分父目录

boolean isFile():检测当前文件是否存在且是一个普通文件(不是目录)

  • 返回值:文件存在且为普通文件则为true,否则为false

boolean isDirectory():检测当前文件是否存在且是一个目录

  • 返回值:该文件存在且为目录,则返回true,否则返回false

File[] listFiles():获取一个目录中的所有文件和子目录的数组

  • 返回一个File类型的数组,包含目录中的所有文件和子目录的路径;如果目录为空或者该对象不是目录则返回null

FileFilter:是一个接口,用于过滤文件和目录

  • 包含一个方法 accept(File pathname),给定的文件或目录是否满足过滤条件
  • 通常需要创建一个实现了 FileFilter 接口的类,并在其中实现 accept() 方法。在 accept() 方法中,可以定义自己的过滤逻辑来确定是否接受给定的文件或目录
  • 练习代码
package file;

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

/**
 * 文件访问
 * File的每一个实例用于表示使盘上的一个文件或目录(实际保存的是对应的路径)
 * 使用File可以:
 * 1:访问该文件或目录的属性信息(名字,大小,修改时间,权限等等)
 * 2:可以创建或删除文件和目录
 * 3:访问一个目录中的子项信息
 * 但是不能访问文件数据
 *
 * @author LongYongchuan
 */
public class Files {
    public static void main(String[] args) throws IOException {
        String separator = System.getProperty("file.separator");
        System.out.println("获取操作系统路径分隔符:" + separator);

        // 绝对路径:"D:/Tedu/APIs/lyc/src/main/java/file/demo.txt"
        File file = new File("./document/demo.txt");
        System.out.println("获取文件名:" + file.getName());
        System.out.println("获取字节量(返回类型long):" + file.length());

        // 权限查看
        System.out.println("是否可读:" + file.canRead());
        System.out.println("是否可写:" + file.canWrite());
        System.out.println("是否隐藏:" + file.isHidden());

        // 文件不存在则创建文件
        File files = new File("./document/demos.txt");
        // 当且仅当该文件成功创建则返回true(文件存在返回false)
        // boolean result = files.createNewFile();

        // 判断文件是否存在,不存在则创建文件
        if (files.exists()) {
            System.out.println("该文件已存在!");
        } else {
            files.createNewFile();
            System.out.println("文件创建成功!");
        }


        // 创建多个文件
        for (int i = 1; i <= 100; i++) {
            File test = new File("./document/test/test%d.txt".formatted(i));
            if (test.exists()) {
                System.out.println("文件已存在!");
            } else {
                test.createNewFile();
                System.out.println("文件test" + i + ".txt已创建成功!");
            }
        }
        System.out.println("创建结束!");


        // 删除多个文件
        for (int i = 11; i <= 100; i++) {
            File delete = new File("./document/test/test%d.txt".formatted(i));
            if (delete.exists()) {
                delete.delete();
                System.out.println("文件test" + i + ".txt已删除!");
            } else {
                System.out.println("文件test" + i + ".txt不存在!");
            }
        }
        System.out.println("删除完成!");

        // 创建目录
        File dir = new File("./document/dir/a/b/c/d");
        if (dir.exists()) {
            System.out.println("目录已存在!");
        } else {
            // 创建目录(需要存在当前目录,不然创建的成功)
            dir.mkdir();
            // 创建多级目录(不存在也能创建)
            dir.mkdirs();
            System.out.println("目录已创建完成!");
        }

        // 删除目录(删除d目录)
        File deleteDir = new File("./document/dir/a/b/c/d");
        if (deleteDir.exists()) {
            deleteDir.delete();
            System.out.println("目录已删除!");
        } else {
            System.out.println("目录不存在!");
        }


        // FileFilter过滤器,过滤指定文件 --- 获取当前项目之下以“.”开头的子项
        File directory = new File(".");
        // 实现Filefilter接口,创建匿名内部类(过滤条件:接收以“.”开头的子项)---- // 获取过滤之前的元素名字并进行过滤
        FileFilter fileFilter = sub -> sub.getName().startsWith(".");
        // 对文件进行过滤
        File[] subs = directory.listFiles(fileFilter);
        System.out.println("一共有:" + subs.length + "个子项");
        // 循环输出子项
        for (File sub : subs
        ) {
            System.out.println(sub);
        }
    }
}
  • 加强练习
package file;

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

public class ListFile {
    public static void main(String[] args) throws IOException {
        // 获取当前目录中的所有子项
        File dir = new File("./document");
        /*
         * File[]listFiles()
         * 获取一个目录中的所有子项每一个子项使用一个File对象表示,
         * 最终以数组的形式返回所有子项
         */
        if (dir.isDirectory()) {
            File[] subs = dir.listFiles();
            System.out.println("一共有" + subs.length + "个子项");
            for (File sub : subs
            ) {
                System.out.println("子项:" + sub.getName());
            }
        }

        // =======================创建test目录=======================
        String path = "./document/test";
        File testDir = new File(path);
        if (testDir.exists()) {
            System.out.println("文件夹已存在!");
            createFiles(path, 10);
        } else {
            testDir.mkdirs();
            createFiles(path, 10);
            System.out.println("文件夹已创建!");
        }

        // 删除目录及文件
        // deleteFiles(path);

        // 文件过滤
        fileFilters(".");
    }

    /**
     * 创建指定个数的txt文件
     * @param dir 文件存放目录
     * @param n   文件个数
     * @throws IOException
     */
    public static void createFiles(String dir, int n) throws IOException {
        for (int i = 1; i <= n; i++) {
            File testFile = new File(dir, "test%d.txt".formatted(i));
            if (testFile.exists()) {
                System.out.println("文件" + testFile.getName() + "已存在");
            } else {
                testFile.createNewFile();
                System.out.println("文件已创建!");
            }
        }
    }

    /**
     * 删除文件及目录
     * @param path 删除的目录及路径
     */
    public static void deleteFiles(String path) {
        File dir = new File(path);
        if (dir.isDirectory()) {
            File[] subs = dir.listFiles();
            for (File sub : subs
            ) {
                // 删除文件
                sub.delete();
            }
            System.out.println("该目录:" + dir.getName() + "下的文件已删除!");
        }
        // 删除文件夹
        dir.delete();
        System.out.println("该目录:" + dir.getName() + "已删除!");
    }


    // ======================重载listFile方法===================================
    public static void fileFilters(String path){
        File file = new File(path);
        File[] subs = file.listFiles(sub -> sub.getName().startsWith("."));
        System.out.println("子项" + subs);
        for (File sub:subs
             ) {
            System.out.println(sub.getName());
        }
    }
}
2. IO
IO:(Input 输入/Output 输出)指的是用于处理数据流的机制和类库;java将IO比喻为“流",即:stream.就像生活中的“电流",“水流“一样
实质理解:以同一个方向顺序移动的过程.只不过这里流动的是字节(2进制数据).所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的“管道”,用于获取或发送数据到另一端
输入:从外部数据源(如文件、键盘、网络等)读取数据到程序中进行处理
输出:将程序中的数据写入到外部数据源(如文件、屏幕、网络等)
特性:IO操作可以是同步的或异步的
  • java IO流基本结构
    11. Java的IO流_第1张图片
2.1 IO字节低级流
2.1.1 文件输出流 FileOutputStream
▶ 文件输出流 FileOutputStream:文件流是实际连接程序与硬盘中文件的“管道”,用于读写文件数据的流,属于低级流
              文件流以字节为单位读取文件数据
write(int data):将一个字节的数据写入文件,数据为整数类型,实际写入文件时将转换为字节类型
write(byte[] buffer):将一个字节数组的数据写入文件
作用:文件输出流用于将数据写入文件

常用构造器
  FileOutputStream(String filename)
    创建一个向具有指定名称的文件中写出数据的文件输出流,在目录存在的情况下,自动创建文件
  FileOutputStream(File file)
    创建一个向指定ile对象表示的文件中写出数据的文件输出流
    文件输出流创建时如果指定的文件不存在则会将该文件创建出来
    如果指定的文件所在的目录不存在会抛出异常:fileNatFoundException
    如果指定的文件已经存在并包含内容,那么文件流创建时会将原有数据全部清除

文件输出流继承自java.io.OutputStream(提供了写出字节的相关方法)
  写出一个字节:void write(int d) —— int值d对应2进制的“低八位”
  块写操作:void write(byte[] data) —— 一次性将给定字节数组data中所有字节写出
  块写操作:void write(byte[] data,int offset,int len) —— 一次性将给定字节数组data中从下标offset处开始连续len个字节写出
  刷新输出流:flush() —— 确保所有缓冲区内容都被写入文件(flush方法调用了close方法)
流使用完之后,需要关闭,调用close()方法关闭

  • 演示代码
package io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件输出流
 */
public class FOStream {
    public static void main(String[] args) throws IOException {
        // 第一种创建方法
        FileOutputStream fos = new FileOutputStream("./document/fos.txt");
        // 第二种方法创建
        File file = new File("./document/fosa.dat");
        FileOutputStream fose = new FileOutputStream(file);


        // 写入文件(向fos文件中写入内容)
        fos.write(2);
        // 内容写完之后要关闭
        fos.close();

        // 第一种创建方法
        FileOutputStream fos0 = new FileOutputStream("./document/fos.txt");
        // 向文件中写入所有小写字母
        for (int i = 0; i < 26; i++) {
            fos0.write(97 + i);
        }
        System.out.println("写出完毕");
        fos0.close();
    }
}
2.1.2 文件输入流 FileInputStream
▶ 文件输入流 FileInputStream文件输入流继承自java.io.lnputStream,文件输入流用于从文件中读取数据
read()方法:读取文件的内容,返回一个整数(读取到的字节数据),当读取到文件末尾时,返回-1
read(byte[] buffer):从输入流中读取多个字节的数据,并将其存储到指定的字节数组buffer中,返回实际读取的字节数

常用构造器
  FilelnputStream(String filename)
    创建一个从具有指定名称的文件中读取数据的文件输入流
  FilelnputStream(File file)
    创建一个从指定File对象表示的文件中读即据的文件输入流
读取字节的方法
  int read():读取一个字节,返回的int值d表示读取的1字节数据内容(2进制的“低八位“);果返回值为整数-1,则表示流读取到了末尾(EOF即:End Of File)
  int read(byte[] data):块读操作,一次性读取给定字节数组data总长度的数据量并从数组第一个字节位置开始存入到数组中,返回值表示实际读取到的数据量;如果返回值为-1则表示流读取到了未尾

  • 演示代码 – 文件复制
package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件输入流
 */
public class FIStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("./document/fos.txt");
        FileOutputStream fos = new FileOutputStream("./document/fos.txt");
        fos.write(-1);
        fos.write(256);
        // 读取第一个字节
        int d = fis.read();
        System.out.println(d);
        d = fis.read();
        System.out.println(d);
        // fis.close();

        // 写入字母
        for (int i = 0; i < 26; i++) {
            // 写入所有小写字母
            fos.write(97 + i);
            // 读出整数(强转为字符输出)
            System.out.println((char) fis.read());
        }
    }
}
2.1.3 文件复制(字节输入流与字节输出流的应用)
文件复制:利用文件入流和文件输出流实现对文件的复制

实现步骤:对图片进行复制(不用缓冲流)
  ① 利用文件输入流FileInputStream创建一个输入流对象fis
  ② 利用文件输出流FileOutputStream创建一个输出流对象fos
  ③ 以while循环对fis读取字节进行复制;
  ④ 关闭流。

  • 演示代码
package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFile {
    public static void main(String[] args) throws IOException {
        // 文件复制
        FileInputStream fis = new FileInputStream("./document/0.jpg");
        FileOutputStream fos = new FileOutputStream("./document/1.jpg");

        int d;
        // 获取当前时间戳
        long time = System.currentTimeMillis();
        
        long start = System.currentTimeMillis();
        while ((d = fis.read()) != -1){
            fos.write(d);
        }
        System.out.println("复制完毕");
        long end = System.currentTimeMillis();
        System.out.println("复制用时:" + (end - start) + "毫秒");
        fis.close();
        fos.close();
    }
}

文件读写

package io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 向文件中字符形式写入字符串数据
 */
public class WriteString {
    public static void main(String[] args) throws IOException {
        // ================写入===================
        // 创建fos文件流对象
        FileOutputStream fos = new FileOutputStream("./document/fos.txt");
        String line = "ObjectInputStream 可以读取由 ObjectOutputStream 写入的对象,也能其还原为原始的 Java 对象";
        // 利用String提供的方法,将字符转化为字节
        byte[] data = line.getBytes(StandardCharsets.UTF_8);
        // 将字节文件写入文件
        fos.write(data);
        fos.close();
        System.out.println("写入完成!");


        // =================读取=================
        File file = new File("./document/fis.txt");
        // 创建文件输入流
        FileInputStream fis = new FileInputStream(file);
        // 一次性将文件内容读取到数组中(创建于文件等长的数组)
        byte[] readLine = new byte[(int)file.length()];
        // 将字节数组读入
        fis.read(readLine);
        // 以utf-8转换为String
        String content = new String(data, StandardCharsets.UTF_8);
        System.out.println(content);
        fis.close();
    }
}


文件流的两种创建模式:① 覆盖模式;② 追加模式
  ① 覆盖模式:对应流创建时,如果指定的文件已存在,则会将文件内容清空
     FileOutputStream(String fileName)
     FileOutputStream(File file)
  ② 追加模式:以追加的模式将需要写入的新内容写入文件
     FileOutputStream(String fileName, boolean append)
     FileOutputStream(File file, boolean append)

2.2 OI字节高级流
节点低级流(FileOutputStream,FileInputStream)的局限:中转站太小,速度慢,效率低
▶ 缓冲流的引入:为提高读写效率,减少系统调用等
2.2.1 缓冲字节输入流 BufferedInputStream
▶ 缓冲字节输入流 BufferedInputStream:一个字节输入流,继承自 InputStream 类,并使用内部缓冲区来提高读取数据的效率
int read():读从输入流中读取下一个字节数据并返回(若已到达流的末尾,则返回 -1)
int read(byte[] buffer, int offset, int length):从输入流中最多读取 length 个字节数据,并将其存储到 buffer 数组中,存储位置从 offset 索引开始,末尾返回-1
long skip(long n):从输入流中跳过 n 个字节的数据,返回实际跳过的字节数
void mark(int readLimit):在当前位置设置标记,并指定在调用 reset() 方法后可以重新读取的字节数限制
void reset():将输入流的位置重置为最近一次调用 mark() 方法设置的位置


  缓冲流高效原理:以(缓冲区读写数据)一定大小的字符数组读写数据,减少实际的读写次数以提高读写效率
  BufferedInputStream 的默认内部缓冲区大小为 8192 字节(8KB);第二参数可自定义大小,每次读取10KB:byte[] data = new byte[1024 * 10];
  读写块操作:一次读写一组数据
构造方法:
  BufferedInputStream(InputStream in):指定的输入流 in 作为其来源流
  BufferedInputStream(InputStream in, int size):指定的输入流 in 作为其来源流,及内部缓冲区的大小为 size 字节

2.2.2 缓冲字节输出流 BufferedOutputStream

常用方法
  void write(int b):将指定的字节b写入输出流
  void write(byte[] buffer, int offset, int length):将指定的字节数组中从 offset 索引开始的 length 个字节写入输出流
    参数 buffer:要写入的字节数组
    参数 offset:写入起始位置
    参数 length:要写入的字节数
  void flush():刷新输出流,将缓冲区中的数据立即写入目标流

  • 原理:演示代码 – 高性能文件复制
package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 块读写
 */
public class CopyFileBlock {
    public static void main(String[] args) throws IOException {
        // 文件复制
        FileInputStream fis = new FileInputStream("./document/0.jpg");
        FileOutputStream fos = new FileOutputStream("./document/1.jpg");

        int len = 0;
        // 每次读取10kb
        byte[] data = new byte[1024 * 10];
        long start = System.currentTimeMillis();
        while ((len = fis.read(data)) != -1) {
            fos.write(data);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完成,耗时:" + (end - start) + "毫秒");
        fis.close();
        fos.close();
    }
}
  • 演示代码 – 缓冲流(字节)
package io;

import java.io.*;

/**
 * 缓冲流(高级流)
 * @author LongYongchuan
 */
public class BufferStream {
    public static void main(String[] args) throws IOException {
        // 输入流
        FileInputStream fis = new FileInputStream("./document/0.jpg");
        // 输入缓冲流
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("./document/1.jpg");
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        int d;
        long start = System.currentTimeMillis();
        while ((d = bis.read()) != -1) {
            bos.write(d);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完成,耗时:" + (end - start) + "毫秒");
        bis.close();
        bos.close();
    }
}

刷新输出流:flush()方法:
  当缓冲区的数据满足写出条件时,此时缓冲区不会执;即需要满足条件(默认8KB)时,缓冲区才会执行读写操作。所以为了解决在文件最后读写的数据不能满足缓冲区条件之前也能执行读写操作,使程序继续执行,java设计了flush方法;当调用此方法,无论缓冲区中的数据量是否达到要求均会执行读写操作。

2.2.3 对象输出流 ObjectOutputStream
▶ 对象输出流 ObjectOutputStream:一个输出流,它用于将对象进行序列化并写入流中
void writeObject(Object obj):将对象进行序列化并写入输出流
void flush():刷新输出流,将缓冲区中的数据立即写入目标流
  • 演示代码 – Person对象(要实现Serializable接口)
package io;

import java.io.Serializable;
import java.util.Arrays;

public class Person implements Serializable {
    private String name;
    private int age;
    private char gender;
    private String[] otherInfo;

    public Person() {
    }

    public Person(String name, int age, char gender, String[] otherInfo) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.otherInfo = otherInfo;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) throws Exception{
        // 异常处理throws关键字
        if (name.length() < 0 || name.length() >= 5){
            throw new Exception("名字长度不符合汉文名字");
        }
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // 抛出异常throw关键字
        if (age < 0 || age >100){
            throw new RuntimeException("年龄不合法");
        }
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

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

    public String[] getOtherInfo() {
        return otherInfo;
    }

    public void setOtherInfo(String[] otherInfo) {
        this.otherInfo = otherInfo;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", otherInfo=" + Arrays.toString(otherInfo) +
                '}';
    }
}
  • 演示代码 – 对Person对象序列化
package io;

import java.io.*;

/**
 * 对象流:将Person序列化
 */
public class OOPStream{
    public static void main(String[] args) throws IOException {
        Person person = new Person();
        try {
            person.setName("李明");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        person.setAge(20);
        person.setGender('女');
        String[] info = {"地址:xxx", "电话:xxx-xxx"};
        person.setOtherInfo(info);
        System.out.println(person);

        // 创建文件输出字节流
        FileOutputStream fos = null;
        // 创建对象输出流
        ObjectOutputStream oos = null;

        fos = new FileOutputStream("./document/Person.obj");
        oos = new ObjectOutputStream(fos);
        // 使用ObjectOutputStream提供的方法进行序列化
        oos.writeObject(person);
        oos.close();
    }
}
2.2.4 对象输入流 ObjectInputStream
▶ 对象输入流 ObjectInputStream:一个输入流,可以用于反序列化对象
作用:ObjectInputStream 可以读取由 ObjectOutputStream 写入的对象,也能其还原为原始的 Java 对象
Object readObject():从输入流中读取并反序列化一个对象,返回为Object类型的对象
int available():获取当前可读取的字节数,返回可读取的字节数
  • 演示代码 – 反序列化
package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * 对象反序列化
 */
public class OIStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建文件输入流
        FileInputStream fis = new FileInputStream("./document/Person.obj");
        // 创建对象输入流
        ObjectInputStream ois = new ObjectInputStream(fis);

        Person person = (Person) ois.readObject();
        System.out.println(person);
        ois.close();
    }
}

序列化的意义
  持久化存储:通过序列化,可以将对象保存到磁盘或数据库中,以便在稍后重新加载和使用
  数据传输:序列化使得对象可以在网络间传输(在不同的平台和编程语言之间进行通信)
  分布式计算:序列化在分布式系统中起着重要作用
  缓存和性能优化:序列化还可以用于缓存数据,以提高应用程序的性能

总结 字节输入输出流使用的是字节(byte)数据类型对二进制文件进行操作,所以字节流主要是对二进制文件(图片,音频,视频,压缩包等)操作,容易出现乱码问题

2.3 IO字符流
▶ 字符输入流Reader:专门用于对文本文件进行操作流(弥补字节流对文本文件操作时容易出现乱码的为题)
          Reader是字符输入的父类(抽象类),所有字符输入流都要直接或间接继承Reader类,流中数据类型为char
▶ 字符输出流Writer:Writer字符输入流是所有字符输入流父类,所有字符输入流都要直接或间接继承与Writer
  • Reader抽象类
public abstract class Reader extends Object implements Readable, Closeable
  • Writer抽象类
public abstract class Writer extends Object implements Appendable, Closeable, Flushable
2.3.1 转换输入流 InputStreamReader
▶ InputStreamReader 字节字符转换输入流:将字节流转换为字符流,并且可以设置文件编码集
                     InputStreamReader是FileReader的直接父类

构造方法
  ① InputStreamReader(InputStream in),构建InputStreamReader流对象并使用的是默认编码集
  ② InputStreamReader(InputStream in, Charset cs),构建InputStreamReader流对象并用过Charset设置读取文件编码集, Charset.forName(“编码集名字”)
  ③ InputStreamReader(InputStream in, String charsetName),构建InputStreamReader流并用过字符串设置读取文件编码集,“编码集名字”

返回值 常用方法
void close() 关闭流并释放与它相关联的任何系统资源
String getEncoding() 返回此流使用的字符编码的名称
int read() 读取单个字符
int read(char[] cbuf, int offset, int length) 将字符读入一个数组的一部分
int read(char[]cbuf) 将读取的数据存储到字符数组中
2.3.2 转换输出流 OutputStreamWriter
▶ OutputStreamWriter 字节字符转换输出流:将字符流转换为字节流输出,并可以设置编码集

构造方法
  ① OutputStreamWriter(OutputStream out),创建OutputStreamWriter对象并使用默认编码集写出数据
  ② OutputStreamWriter(OutputStream out, Charset cs),创建OutputStreamWriter对象并使用Charset设置写出文件编码集, Charset.forName(“编码集名字”)
  ③ OutputStreamWriter(OutputStream out, String charsetName),创建OutputStreamWriter对象并使用String类型设置写出文件编码集,“编码集名字”
OutputStreamWriter继承Writer中的String类方法,所以提供直接输出字符串的方法

返回值 常用方法
void close() 关闭流并释放与它相关联的任何系统资源
String getEncoding() 返回此流使用的字符编码的名称
void write(char[] cbuf, int off, int len) 写入一个字符数组的一部分
void write(int c) 写一个字符
void write(String str, int off, int len) 写入字符串的一部分
void flush() 冲流
2.3.3 缓冲字符输入流 BufferedReader
▶ 原始流:InputStream和OutputStream 或者 Reader和Writer不使用缓冲区的流
▶ 缓冲流(Buffered Streams):是一种在读写数据时提供缓冲功能的输入输出流。它通过在内存中创建一个缓冲区来减少磁盘或网络访问次数,从而提高读写数据的效率
▶ BufferedReader缓冲字符输入流:是Reader字节输入的子类,提供 8192K缓冲区,可以包装一个普通的字符输入流提高操作效率
int available():获取当前可读取的字节数,返回可读取的字节数

构造方法
  ① BufferedReader(Reader in),建一个使用默认大小输入缓冲区的缓冲字符输入流
  ② BufferedReader(Reader in, int sz),创建一个使用指定大小的输入缓冲区的缓冲字符输入流

返回值 常用方法
String readLine() 读一行文本
package io;


import java.io.*;

/**
 * 缓冲流(高级流)
 * @author LongYongchuan
 */
public class BufferStream {
    public static void main(String[] args) throws IOException {
        // 输入流
        FileInputStream fis = new FileInputStream("./document/0.jpg");
        // 输入缓冲流
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("./document/1.jpg");
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        int d;
        long start = System.currentTimeMillis();
        while ((d = bis.read()) != -1) {
            bos.write(d);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完成,耗时:" + (end - start) + "毫秒");
        bis.close();
        bos.close();
    }
}

2.3.4 缓冲字符输出流 BufferedWriter

构造方法
  ① BufferedWriter(Writer out),创建一个使用默认大小输出缓冲区的缓冲字符输出流
  ② BufferedWriter(Writer out, int sz),创建一个新的缓冲字符输出流,该流使用给定大小的输出缓冲区

返回值 常用方法
void write(Stringstr) 写一个字符串
void newLine() 写行分隔符
  • 使用write(String str) 将数据写出时需要配合使用newLine()这个方法以保证写出文件正确格式
package io;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 在流中创建PrintWrite
 */
public class PWriteDemo {
    public static void main(String[] args) throws FileNotFoundException {
        FileOutputStream fos = new FileOutputStream("pw.txt");

        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);

        BufferedWriter bw = new BufferedWriter(osw);

        PrintWriter pw = new PrintWriter(bw);

        pw.println("你好");
        pw.println("hello");
        System.out.println("写出完成!");
        pw.close();
    }
}
2.3.5 字符输出流 PrintWriter
▶ 字符输出流 PrintWriter:用来创建一个文件并向文本文件写入数据
▶ 缓冲流(Buffered Streams):是一种在读写数据时提供缓冲功能的输入输出流。它通过在内存中创建一个缓冲区来减少磁盘或网络访问次数,从而提高读写数据的效率
▶ BufferedReader缓冲字符输入流:是Reader字节输入的子类,提供 8192K缓冲区,可以包装一个普通的字符输入流提高操作效率
int available():获取当前可读取的字节数,返回可读取的字节数

构造方法
  ① PrintWriter(File file),将输出写入到指定的文件
  ② PrintWriter(OutputStream out),将输出写入到指定的输出流
  ③ PrintWriter(String fileName),将输出写入到具有指定文件名的文件
  ④ PrintWriter(Writer out),将输出写入到指定的Writer对象

常用方法 作用
print()和println() 打印给定的数据到输出流。这些方法可以接受任意类型的参数,并将其转换为字符串后输出,println()方法会在打印完成后自动换行
printf() 使用指定的格式字符串进行格式化输出。类似于C语言中的printf()函数,可以使用占位符来指定输出格式
write() 将给定字符或字符数组写入输出流
flush() 刷新输出流,将缓冲区的内容立即写入到输出流中
close() 关闭输出流,同时会自动调用flush()方法
package io;

import java.io.FileNotFoundException;
import java.io.PrintWriter;

public class PWriter {
    public static void main(String[] args) throws FileNotFoundException {
        PrintWriter pw = new PrintWriter("./document/pw.txt");
        pw.println("用于实现字节流到字符流的转换。它们允许将字节流转换成字符流,并提供了字符编码和解码的功能");
        pw.println("加密、解密、计算校验和等来提供额外的处理能力");
        pw.print("1111");
        System.out.println("写出完毕!");
        pw.close();
    }
}
3. 字节流与字符流的区别

本质区别 字节输入输出流(InputStream和OutputStream直接对文件自身操作)和字符输入输出流(Reader和Writer经过缓冲区对文件操作)
  ① 流数据不同:字节流流中的数据 byte
          字符流流中数据 char
  ② 处理文件类型不同:字节流主要处理是二进制文件 (图片、音频、视频、压缩包等),处理文本文件时容易乱码

Unicode Unicode(统一码)是一个国际标准,由 Unicode 联盟(Unicode Consortium)制定和维护
  Unicode 联盟的目标:为全球所有的字符集提供一个统一的编码方案,使得可以准确地表示和处理世界上几乎所有的字符
  Unicode联盟:一个非营利组织,成立于1987年,致力于推动和发展Unicode标准(成员包括计算机和软件公司、学术机构、国际化专家、语言学家等相关领域的专家和组织)
常用字符编码集
  ① ASCII码: 占一个字节,只能包含128个符号,不能表示汉字
  ② ISO-8859-1( Latin-1):占一个字节,西欧字符集,不能表示汉字
  ③ GB18030/GBK: 占两个字节,支持中文
  ④ UTF-8(万国码):一种针对Unicode的可变长字符编码,UTF-8是Unicode的实现方式之一,兼容ASCII码 能表示汉字
UTF(Unicode Transformation Format)家族
  UTF-8:UTF-8 是最常用的 Unicode 编码方案之一。它使用可变长度的编码单元,可以表示 Unicode 的所有字符
  UTF-16:UTF-16 使用 16 位字节(即两个字节)来表示 Unicode 字符。它可以表示 Unicode 的所有字符,包括 BMP(基本多文种平面)和增补平面字符
  UTF-32:UTF-32 是一种固定长度的编码方案,使用 32 位字节(即四个字节)来表示每个 Unicode 字符。UTF-32 可以直接表示 Unicode 的所有字符,无需使用变长编码

4. IO流的不同分类

数据类型
  字节流(Byte Stream):以字节为单位进行输入输出操作,常用于处理图像、音频、视频等二进制文件
      InputStream和OutputStream是字节流的基类,具有处理字节数据的通用方法
      FileInputStream和FileOutputStream是文件字节流,用于从文件读取字节数据和将字节数据写入文件
      ByteArrayInputStream和ByteArrayOutputStream是数组字节流,用于从字节数组读取数据和将数据写入字节数组
  字符流(Character Stream):以字符为单位进行输入输出操作,常用于处理文本文件
      Reader和Writer是字符流的基类,具有处理字符数据的通用方法
      FileReader和FileWriter是文件字符流,用于从文件读取字符数据和将字符数据写入文件
      CharArrayReader和CharArrayWriter是数组字符流,用于从字符数组读取数据和将数据写入字符数组

数据传输方向
  输入流(Input Stream):用于从数据源读取数据
      InputStream和Reader是输入流的基类,具有读取数据的通用方法
      FileInputStream和FileReader是文件输入流,用于从文件读取数据
  输出流(Output Stream):用于将数据写入目标
      OutputStream和Writer是输出流的基类,具有写入数据的通用方法
      FileOutputStream和FileWriter是文件输出流,用于将数据写入到文件中

数据大小
  缓冲流(Buffered Stream):通过内部缓冲区减少对基础流的访问次数,从而提高读取或写入效率
      BufferedInputStream和BufferedOutputStream是字节缓冲流
      BufferedReader和BufferedWriter是字符缓冲流
  数据流(Data Stream):可以直接处理Java基础类型数据及其对象,将它们转换为字节流进行传输
      DataInputStream和DataOutputStream是字节数据流,用于读取和写入基本数据类型和字符串等数据
      ObjectInputStream和ObjectOutputStream是对象数据流,用于读取和写入Java对象及其图形化表示

你可能感兴趣的:(java网络编程,java,开发语言)