Java 基础-IO

200 阅读9分钟

概述

流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径

WeChatb946e98015df2db07e8e71cb932d389c.png

流的分类

  • 按操作数据单位不同分为:字节流(8 bit)二进制文件,字符流(按字符)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流,包装流
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
  2. 由这个四个类派生出来的子类名称都是以其父类名作为子类名后缀

文件操作

  • 常用方式

    API说明
    File(String pathname)根据路径构建一个file对象
    File(File parent String child)根据父目录文件 + 子路径构建
    File(String parent String child)根据父目录路径 + 子路径构建

    注意调用 file.createNewFile() 才能真正创建物理文件

    File file = new File(System.getProperty("user.dir")+"/src/IOExample/files/testFile1.txt");
    try {
        file.createNewFile();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    
  • 常用文件操作

    • 获取文件相关信息

      API说明
      file.getName()获取文件的名称
      file.getAbsolutePath()文件的绝对路径
      file.getParent()获取父目录
      file.length()获取文件大小 按照编码字节
      file.exists判断文件是否是存在
      file.isFile判断是否是文件
      file.isDirectory判断是不是一个目录
    • 目录的操作和文件删除

      API说明
      file.mkdir()创建一级目录 new File("D:\\h").mkdir()
      file.mkdirs()创建多级目录 new File("D:\\h\z\k").mkdirs()
      file.delete()删除空目录(如果目录下有文件需要先去清空)

IO流

IO.jpg

节点流和处理流的区别和关系

  1. 节点流底层流(低级流)可以从一个特定的数据源读写数据,直接跟数据源相接
  2. 处理流(也叫包装流)是 连接 在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入
  3. 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连 (使用修饰器设计模式)
  • 处理流的功能主要体现在以下两个方面
  1. 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
  2. 操作的边界:处理流可以提供一系列边界的方法来一次输入输出大批量的数据,使用更加灵活方便
分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter节点流
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter处理流
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriter
推回输入流PushBackInputStreamPushBackInputReader
特殊流DataInputStreamDataOutputStream

InputStream

InputStream 字节输入流抽象类是所有类字节输入的超

FileInputStream 文件输入流

// 单字节获取
@Test
public void fileInputStreamDemo1(){
    String filePath = System.getProperty("user.dir") + "/src/IOExample/files/testFile1.txt";
    int readData = 0;
    FileInputStream fileInputStream = null;
    try {
        // 从该输入流读取一个字节数据,如果没有输入可用,字方法将阻止
        // 如果返回 -1 表示读取完毕
        // 如果读取正常,返回实际读取的字节数
        fileInputStream = new FileInputStream(filePath);
        while ((readData = fileInputStream.read()) != -1) {
     
            System.out.println(readData);
            // 只能处理英文,无法转成中文
            System.out.println((char)readData);
            
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
// 多字节获取
@Test
public void fileInputStreamDemo2(){
    String filePath = System.getProperty("user.dir") + "/src/IOExample/files/testFile1.txt";
    // 一次读取8个字节
    byte[] bytes = new byte[8];
    int readLen = 0;
    FileInputStream fileInputStream = null;
    try {
        // 从该输入流读取一个字节数据,如果没有输入可用,字方法将阻止
        // 如果返回 -1 表示读取完毕
        // 字母遵循 ASCII
        fileInputStream = new FileInputStream(filePath);
        while ((readLen = fileInputStream.read(bytes)) != -1) {
            System.out.println(new String(bytes , 0 , readLen));
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

ByteArrayInputStream 缓冲字节输入流

ObjectInputStream 序列化输入流

OutputStream

FileOutputStream 文件输出流

    @Test
    public void fileOutputStreamDemo(){

        // 如果文件不存在,则创建文件
        // 如果有文件则覆盖原信息
        String filePath = System.getProperty("user.dir") + "/src/IOExample/files/testFile1.txt";
        FileOutputStream fileOutputStream = null;
        String str = "hahhahahaah";
        try {
            // 覆盖的方式
            fileOutputStream = new FileOutputStream(filePath);
            // 追加的方式
//            fileOutputStream = new FileOutputStream(filePath,true);
            fileOutputStream.write(str.getBytes());
            // 也可以通过长度输入 等价于上面  0 表示第几个位置,str.length() 表示偏移量
//            fileOutputStream.write(str.getBytes(),0,str.length());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }

Reader

@Test
public void fileReaderDemo1(){
    String filePath = System.getProperty("user.dir") + "/src/IOExample/files/fileDemo.txt";
    FileReader fileReader = null;
    int data = 0;
    try {
        fileReader = new FileReader(filePath);
        while ((data = fileReader.read()) != -1){
            System.out.println( data +":"+(char)data);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Test
public void fileReaderDemo2(){
    String filePath = System.getProperty("user.dir") + "/src/IOExample/files/fileDemo.txt";
    FileReader fileReader = null;
    char[] chars = new char[8];
    int readLen = 0;
    try {
        fileReader = new FileReader(filePath);
        // read(chars) 返回的是实际读取的字符数
        while ((readLen = fileReader.read(chars)) != -1){
            System.out.println(new String(chars,0,readLen));
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Writer

注意在 FileWriter 使用后,必须要关闭(close)或刷新(flush),否则写入不到指定文件!close 等于 flush+关闭

@Test
public void fileWriterStreamDemo(){

    // 如果文件不存在,则创建文件
    // 如果有文件则覆盖原信息
    String filePath = System.getProperty("user.dir") + "/src/IOExample/files/fileDemo.txt";
    FileWriter fileWriter = null;
    String str = "你好哈哈哈哈哈哈哈1";
    try {
        // 覆盖的方式
        fileWriter = new FileWriter(filePath);
        // 追加的方式
        //  fileWriter = new FileWriter(filePath,true);

        fileWriter.write(str);
        // 1) fileWriter.write(char); 写入单个字符
        // 2) fileWriter.write(char[]); 写入字符数组
        // 3) fileWriter.write(char[],off,len); 写入字符数组的指定部分
        // 4) fileWriter.write(String,off,len); 写入字符串的指定部分

    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            fileWriter.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

处理流

BufferedReader & BufferedWriter

字符流一般处理字符类型的文件

/**
 * Buffered 字符流 copy
 */
@Test
public void copyFileBufferedDemo() throws IOException {
    String inputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/fileDemo.txt";
    String outputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/copyfileDemo.txt";

    BufferedReader bufferedReader = new BufferedReader(new FileReader(inputFilePath));
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFilePath));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        bufferedWriter.write(line);
        //  bufferedWriter.newLine();
        //  bufferedWriter.write("\r\n");
    }
    bufferedReader.close();
    bufferedWriter.close();
}

BufferedInputStream & BufferedOutputStream

字节流一般处理图片,视频的二进制文件

/**
 * Buffered 字节流 copy
 */
@Test
public void copyStreamBufferedDemo() {
    String inputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/gtx.jpg";
    String outputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/copyGtx.jpg";
    BufferedInputStream bufferedInputStream = null;
    BufferedOutputStream bufferedOutputStream = null;
    try {
         bufferedInputStream = new BufferedInputStream(new FileInputStream(inputFilePath));
         bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outputFilePath));
        byte[] bytes = new byte[1024];
        int readLen = 0;

        while ((readLen = bufferedInputStream.read(bytes)) != -1) {
            System.out.println(bytes);
            bufferedOutputStream.write(bytes,0 ,readLen);
        }


    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {

        try {
            if(bufferedInputStream != null){
                bufferedInputStream.close();
            }

            if (bufferedOutputStream != null) {
                bufferedOutputStream.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

ObjectInputStream & ObjectOutputStream

  • 序列化和反序列化说明
  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型
  3. 需要让某个对象支持序列化机制,则必须让其类是可以序列化的,为了让某个类型可序列化的,该类必须实现如下两个接口之一 Serializable,Externalizable
  • 注意
  1. 读写顺序要一致
  2. 要求实现序列化或反序列化,需要实现 Serializable
  3. 序列化的类中建议添加 SerialVersionUID,为了提高版本的兼容性(jvm 认为是一个升级版,不是一个新的类)
  4. 序列化对象时,默认将里面所有的属性都都进行序列化,但除了 statictransient修饰的成员
  5. 序列化对象时,要求里面属性的类型也需要实现序列化接口
  6. 序列化具备可继承性,也就是如果某个类已经实现了序列化,则它的所有子类也已经默认实现了序列化
  7. 序列化的类中可以定义属性是实现类,依然可以序列化
// 序列化
@Test
public void  objectOutputStreamDemo(){
    String outputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/dog.dat";
    ObjectOutputStream objectOutputStream = null;
    try {
         objectOutputStream = new ObjectOutputStream(new FileOutputStream(outputFilePath));

         objectOutputStream.writeInt(10); // int -> Integer
         objectOutputStream.writeBoolean(true); // boolena -> Boolena
         objectOutputStream.writeChar('你'); // char -> Character
        objectOutputStream.writeChars("吼吼吼");
         objectOutputStream.writeDouble(9.5); // double -> Double
         objectOutputStream.writeUTF("哈哈哈");
        objectOutputStream.writeObject(new Dog("旺财",10));
    } catch (IOException e) {
        throw new RuntimeException(e);
    }finally {
        try {
            objectOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

// 反序列化
@Test
public void  objectInputStreamDemo(){
    String outputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/dog.dat";
    ObjectInputStream objectInputStream = null;

    try {
        objectInputStream = new ObjectInputStream(new FileInputStream(outputFilePath));

        System.out.println(objectInputStream.readInt());
        System.out.println(objectInputStream.readBoolean());
        System.out.println(objectInputStream.readChar());
        // writeChars 需要循环读
        System.out.println(objectInputStream.readChar());
        System.out.println(objectInputStream.readChar());
        System.out.println(objectInputStream.readChar());

        System.out.println(objectInputStream.readDouble());
        System.out.println(objectInputStream.readUTF());
        System.out.println(objectInputStream.readObject());
        
       // 1. 如果需要使用实现类,需要向下转型
       // 2. 需要我们将Dog类的定义,拷贝到可以引用的位置

    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }finally {
        try {
            objectInputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

System.in & System.out

  • 转换流说明
  1. System.inSystem.out 是 System 类中的一个属性
        System 类 的 public final static InputStream in = null
        System 类 的 public final static InputStream out = null
    
  2. System.in 编译类型是 InputStream ,运行类型 BufferedInputStram,标准输入 键盘
  3. System.out 编译类型是 PrintSteam ,运行类型 PrintSteam,表示标准输出,显示器

InputStreamReader & OutputStreamWriter

  • 说明
  1. InputStreamReaderReader的子类,可以将InputStream(字节流)包装成Reader(字符流)
  2. OutputStreamReaderWriter的子类,可以将OutputStream(字节流)包装成Writer(字符流)
  3. 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效的解决中文问题,所以建议将字节流转成字符流
  4. 可以使用指定的编码格式(utf-8,gbk,gb2312,IOS8859-1 ...)
/**
 * 转换流
 * @throws IOException
 */
@Test
public  void transformStreamDemo() throws IOException {
    String inputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/fileDemo.txt";
    String outputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/copyTransformFileDemo.txt";

    InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(inputFilePath),"utf-8");
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(outputFilePath), "utf-8");

    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
    String line;
    while ((line = bufferedReader.readLine()) != null){
        System.out.println(line);
        bufferedWriter.write(line);
        bufferedWriter.newLine();
    }

    bufferedReader.close();
    bufferedWriter.close();
}

PrintStream & PrintWriter

  • 说明
  1. 打印流只有 PrintStream(字节打印流),PrintWriter(字符打印流)
  2. PrintStream 默认情况下 输出数据的位置是标准输出,即显示器
   // 字节打印流
  @Test
    public void printStreamDemo() throws IOException {
        PrintStream out = System.out;
        out.print("哈哈");
        // out.print 本质就只 out.write
        out.write("楼吼吼".getBytes());
        out.close();
        String inputFilePath = System.getProperty("user.dir") + "/src/IOExample/files/printFileDemo.txt";
        // 可以修改打印流输出的位置/设备

//        public static void setOut(PrintStream out) {
//            checkIO();
//            setOut0(out); // native 方法,修改了 out
//        }
        System.setOut(new PrintStream(inputFilePath));
        System.out.println("哈哈哈。。。你好");

    }
   // 字符打印流
    @Test
    public void printWriterDemo() throws IOException {
        PrintWriter printWriter = new PrintWriter(System.out);
        printWriter.println("hi,你好");
        printWriter.close();
        // 可以写入到文件
        PrintWriter printWriter1 = new PrintWriter(System.getProperty("user.dir") + "/src/IOExample/files/printFileDemo.txt");
        printWriter1.println("hi,你好世界");
        printWriter1.close();
    }

Properties

  • 说明
  1. load
API说明
load加载配置文件的键值对到 Properties 对象
list将数据显示到设备
getProperty根据key获取去值
setProperty设置键值对到 Properties 对象
store将 Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为 unicode 码
 @Test
    public void propertiesDemo() throws IOException {

        Properties properties = new Properties();
        properties.load(new FileReader(System.getProperty("user.dir") + "/src/IOExample/files/sql.properties"));

        /*
        * 在输出非追加模式下
        * 如果Key不存在则追加,如果存在则修改
        * 底层是 Hashtable, 具体原因可以看 Hashtable 中的 put 方法
        * */

        properties.setProperty("email","122");
        properties.store(new FileOutputStream(System.getProperty("user.dir") + "/src/IOExample/files/sql.properties"),null);

        properties.list(System.out);
        properties.getProperty("user");

        // 可以打印入文件
        PrintWriter printWriter = new PrintWriter(System.getProperty("user.dir") + "/src/IOExample/files/printFileDemo.txt");
        properties.list(printWriter);
        printWriter.close();

    }