Java基础(二十一):IO流

一、文件

1.1 概念

文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
在这里插入图片描述

1.2 常用操作

package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;

public class FileCreate {
    public static void main(String[] args) {
    }
    //一、创建和删除文件
    @Test
    public void create01(){
        //方式一:文件路径
        String filePath = "d:\\file1.txt";
        File file1 = new File(filePath);
        //方式二:父目录文件+子路径
        File parentFile = new File("d:\\");
        String fileName = "file2.txt";
        File file2 = new File(parentFile, fileName);
        //方式三:父目录+子路径
        String parentPath = "d:\\";
        String fileName3 = "file3.txt";
        File file3 = new File(parentPath, fileName3);
        try {
            file1.createNewFile();
            file2.createNewFile();
            file3.createNewFile();
            System.out.println("文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //删除文件
        file1.delete();
    }
    //二、文件的一些常用方法
    @Test
    public void methods(){
        File file = new File("d:\\file1.txt");
        //文件名 绝对目录 父目录 大小(字节) 是否存在、是文件、是目录
        System.out.println(file.getName());
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getParent());
        System.out.println(file.length());
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
        File file1 = new File("d:\\e");
        File file2 = new File("d:\\a\\b\\c");
        //创建一级目录
        file1.mkdir();
        //创建多级目录
        file2.mkdirs();
    }
}

1.3 流的分类

  • 按操作数据单位不同分为:字节流【(8 bit),二进制文件】,字符流【(按字符),文本文件】
  • 按数据流的流向不同分为:输入流、输出流
  • 按流的角色的不同分为:节点流、处理流(包装流)
    在这里插入图片描述

1.4 IO流体系图

在这里插入图片描述

1.5 节点流和处理流

  1. 节点流:可以从一个特定的数据源读写数据,直接对数据操作;
  2. 处理流/包装流:“连接”在已存在的流(节点流或处理流)之上,为程序提供强大的读写功能。
    例如:BufferReader封装了一个属性Reader,可以对Reader的子类进行操作,不会直接操作数据,而是操作文件、数组、管道等节点流。
    在这里插入图片描述

二、文件流(节点流)

2.1 字节流:FileInputStream与FileOutputStream

  1. 文件输入流FileInputStream
    在这里插入图片描述
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.*;

public class FileInputStream_ {
    public static void main(String[] args) {
    }
    /**
     * read()读取单个字节的读取
     * 返回单个字节,若读取完毕,返回-1
     * 效率比较低
     */
    @Test
    public void readFile() {
        String filePath = "d:\\hello.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //1.创建FileInputStream对象,用于读取文件
            fileInputStream = new FileInputStream(filePath);
            //2.从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1,表示读取完毕
            while((readData = fileInputStream.read()) != -1){
                System.out.print((char)readData);//转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 使用read(byte[] b)读取文件,存到数组中
     * 返回读取的字节数,若读取完毕,返回-1
     * 提高效率
     */
    @Test
    public void readFile02() {
        String filePath = "d:\\hello.txt";
        //1.定义字节数组
        byte[] buf = new byte[8];//一次读取8个字节
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //2.创建FileInputStream对象,用于读取文件
            fileInputStream = new FileInputStream(filePath);
            //3.从该输入流读取最多buf.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1,表示读取完毕
            //如果读取正常,返回实际读取的字节数
            while((readLen = fileInputStream.read(buf)) != -1){
                System.out.print(new String(buf, 0 ,readLen));//转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 文件输出流FileOutputStream
    在这里插入图片描述
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStream_ {
    public static void main(String[] args) {
    }
    /**
     * 演示使用 FileOutputStream 将数据写到文件中,
     * 如果该文件不存在,则创建该文件
     */
    @Test
    public void writeFile() {
        //创建FileOutputtream对象
        String filePath = "d:\\hello.txt";
        FileOutputStream fileOutputStream = null;
        try {
            //1.得到FileOutputStream对象
            //说明
            //1). new FileOutputStream(filePath)创建方式,覆盖原来内容
            //2). new FileOutputStream(filePath, true)创建方式,追加到文件后面
            fileOutputStream = new FileOutputStream(filePath,true);
            
            //2.写入内容
            //1).写入一个字节
            //fileOutputStream.write('W'); //char可以自动转换成int
            //2).写入一个字符串
            String str = "hello,world!";
            //fileOutputStream.write(str.getBytes()); //str.getBytes()这个方法可以把字符串转成字节数组
            //3).写入一个字符串指定位置
            /*
                write(byte[] b, int off, int len)
                将len字节从位于偏移量 off的指定字节数组写入此文件输出流。
             */
            fileOutputStream.write(str.getBytes(), 0 , str.length());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 文件拷贝
package FileExercise;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FIleCopy {
    public static void main(String[] args) {
        String srcfilePath = "d:\\hello1.txt";
        String destfilePath = "d:\\hello2.txt";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream(srcfilePath);
            fileOutputStream = new FileOutputStream(destfilePath);
            byte[] buf = new byte[1024];
            int readLen = 0;
            while((readLen = fileInputStream.read(buf))!=-1){
                fileOutputStream.write(buf,0,readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fileInputStream != null){
                    fileInputStream.close();
                }
                if(fileOutputStream != null){
                    fileOutputStream.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

2.2 字符流:FileReader与FileWriter

  1. FIleReader相关方法
    ①new FIleReader(File/String)
    ②read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
    ③read(char[] ):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
    相关API:
    ①new String(char[]):将char[]转换成String
    ②new String(char[],off,len):将char[]的指定部分转换成String
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.io.IOException;

public class FileReader_ {
    public static void main(String[] args) {
    }
    /**
     * 单个字符读取文件
     */
    @Test
    public void readFile01() {
        String filePath = "d:\\story.txt";
        FileReader fileReader = null;
        int data = 0;
        //1.创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //2.循环读取,使用read,单个字符读取
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 使用字符数组读取文件
     */
    @Test
    public void readFile02() {
        String filePath = "d:\\story.txt";
        FileReader fileReader = null;
        int readLen = 0;
        char[] buf = new char[8];
        //1.创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //2.循环读取,使用read(buf),返回的是实际读取到的字符数
            //如果返回-1,说明到文件结束
            while ((readLen = fileReader.read(buf)) != -1) {
                System.out.print(new String(buf, 0,readLen));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  1. FileWriter常用方法
    ①new FileWriter(File/String):覆盖模式,想当于流的指针在首段
    ②new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
    ③write(int):写入单个字符
    ④write(char[]):写入指定数组
    ⑤write(char[],off,len):写入指定数组的指定部分
    ⑥write(string):写入整个字符串
    ⑦write(string,off,len):写入字符串的指定部分

注意:FileWriter使用后,必须要关闭(close)和刷新(flush),否则写入不到指定的文件!

三、缓冲流(处理流)

3.1 字节流:BufferedInputStream与BufferedOutputStream

package FileExercise;


import java.io.*;

/**
 * 演示BufferedOutputStream 和 BufferedInputStream的使用
 * 思考:字节流可以操作二进制文件,也可以操作文本文件吗? 
 * 答:可以,因为文本文件底层也是二进制
 */
public class BufferedStreamCopy {
    public static void main(String[] args) {
        String srcFilePath = "e:\\a1.java";
        String destFilePath = "e:\\a2.java";

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFilePath));
            bos = new BufferedOutputStream(new FileOutputStream(destFilePath));

            byte[] buff = new byte[1024];
            int readLen = 0;
            while ((readLen = bis.read(buff)) != -1) {
                bos.write(buff, 0, readLen);
            }
            System.out.println("文件拷贝完毕");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                if (bis != null)
                    bis.close();
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2 字符流:BufferedReader与BufferedWriter

package FileExercise;

import java.io.*;

public class BufferedCopy {
    public static void main(String[] args) {
        // BufferedReader和BufferedWriter是操作字符的
        // 不要操作二进制文件
        String srcFilePath = "e:\\a1.java";
        String destFilePath = "e:\\a2.java";
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        String line;
        try {
            bufferedReader = new BufferedReader(new FileReader(srcFilePath));
            bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));
            // 读取一行但是没有换行
            while ((line = bufferedReader.readLine()) != null) {
                bufferedWriter.write(line);
                bufferedWriter.newLine();// 换行
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

四、对象流(处理流)

4.1 使用场景

  1. 数据保存在文件中,也要保存其数据类型,并且可以恢复
  2. 一个Dog对象可以保存在文件中,并且可以从文件中恢复成为一个Dog对象
  3. 上述实质是序列化与反序列化

序列化和反序列化:
1)序列化就是在保存数据时,保存数据的值和数据类型;
2)反序列化就是在恢复数据时,恢复数据的值和数据类型;
3)需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable 和 Externalizable

4.2 字节流:ObjectInputStream与ObjectOutputStream

package FileExercise;
import java.io.*;

public class ObjectStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        new ObjectStream_().ObjectOutputStream_();
        new ObjectStream_().ObjectInputStream_();
    }
    // 写文件
    public void ObjectOutputStream_() throws IOException {
        String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

        // 基本类型已经继承了 Serializable 接口
        oos.writeInt(100);
        oos.writeBoolean(true);
        oos.writeChar('a');
        oos.writeDouble(9.5);
        oos.writeUTF("hello world!");
        // Dog要实现 Serializable 接口
        oos.writeObject(new Dog("旺财", 10));
        // 关闭流
        oos.close();
    }
    // 读文件
    public void ObjectInputStream_() throws IOException, ClassNotFoundException {
        String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        // 读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());
        // 如果我们需要调用Dog的方法,需要向下转型
        // 需要将Dog类的定义,拷贝到引用的位置
        Dog dog = (Dog) ois.readObject();
        System.out.println(dog.getClass());
        dog.sound();

        // 关闭流
        ois.close();
    }
}
class Dog implements Serializable {
    private String name;
    private int age;
    // serialVersionUID 序列化的版本号,提高兼容性
    // 修改Dog后,不会认为它是一个新的类,而是版本的升级
    public Dog(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public void sound(){
        System.out.println("汪汪");
    }
}
输出:
100
true
a
9.5
hello world!
class FileExercise.Dog
汪汪

4.3 字符流:ObjectReader与ObjectWriter

4.4 注意事项

  1. 读写顺序要一致
  2. 要求序列化或反序列化对象 ,需要实现 Serializable 接口;
  3. 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性;
  4. 序列化对象时,默认将里面所有属性都进行序列化,但除了statictransient修饰的成员;
  5. 序列化对象时,要求里面属性的类型也需要实现序列化接口
  6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化。

五、标准输入输出流(处理流)

字节流:System.in和System.out
1)System.in的编译类型 InputStream,运行类型 BufferedInputStream,表示标准输入:键盘
2)System.out的编译类型 PrintStream,运行类型 PrintStream,表示标准输出:显示器

六、转换流(处理流)

InputStreamReader和OutputStreamWriter的目的将字节流转化成字符流
在这里插入图片描述

package FileExercise;

import java.io.*;

/**
 * 系统默认情况下:使用utf-8
 * 转换流:将字节流转换成字符流,用来解决文字乱码,可以指定编码方式
 */
public class Transformation {
    public static void main(String[] args) throws IOException {
        // 一、读取数据
        String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";

        // 1.将 FileInputStream 转成 InputStreamReader
        // 2.指定编码方式:gbk
        InputStreamReader gbk = new InputStreamReader(new FileInputStream(filePath), "gbk");
        // 3.将 InputStreamReader 传入 BufferedReader(目的:效率高)
        //   开发者常常将1-3合在一起写
        BufferedReader bufferedReader = new BufferedReader(gbk);
        // 4.读取数据
        String s = bufferedReader.readLine();
        System.out.println(s);
        bufferedReader.close();

        // 二、写数据
        // 1.将 FileOutputStream 转成 OutputStreamWriter
        OutputStreamWriter gbk1 = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
        // 2.写入数据
        gbk1.write("你好 世界");
        gbk1.close();
    }
}

七、打印流(处理流)

打印流只有输出流

7.1 字节流:PrintStram

package FileExercise;

import java.io.IOException;
import java.io.PrintStream;

public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        PrintStream ps = System.out;
        // 默认情况下,PrintStream输出数据位置是标准输出,即显示器
        ps.println("hello world");
        // println()底层是write
        ps.write("hello world".getBytes());
        ps.close();
        // 修改输出位置
        System.setOut(new PrintStream("C:\\Users\\DELL\\Desktop\\测试文档.txt"));
        System.out.println("hello world");
    }
}

7.2 字符流:PrintWriter

package FileExercise;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriter_ {
    public static void main(String[] args) throws IOException {
        PrintWriter printWriter = new PrintWriter(System.out);
        printWriter.print("打印在显示器");
        // 必须关闭,否则无法写入
        printWriter.close();

        PrintWriter printWriter1 = new PrintWriter(new FileWriter("C:\\Users\\DELL\\Desktop\\测试文档.txt"));
        printWriter1.print("打印在文件中");
        // 必须关闭,否则无法写入
        printWriter1.close();
    }
}

八、Properties类(配置文件)

8.1 基本介绍

  1. 专门用于读写配置文件的集合类,配置文件的格式:键=值
  2. 注意:键值对不需要空格,值不需要用引号,默认类型是String

8.2 使用

package FileExercise;

import java.io.*;
import java.util.Properties;

/**
 * @author 神代言
 * @version 1.0
 */
public class Properties_ {
    public static void main(String[] args) throws IOException {
        // 读取mysql.properties文件,得到ip,user,pwd
        Properties_ properties_ = new Properties_();
        properties_.traditionMethod();
        properties_.propertiesMethod();

    }
    public void traditionMethod() throws IOException{
        System.out.println("******传统方法******");
        BufferedReader bufferedReader = new BufferedReader(new FileReader("src\\mysql.properties"));
        String line = "";
        while((line = bufferedReader.readLine())!=null){
            String[] splite = line.split("=");
            System.out.println(splite[0] + "的值为" +splite[1]);
        }
        bufferedReader.close();
    }
    public void propertiesMethod() throws IOException {
        System.out.println("******Properties类******");
        /*  读取mysql.properties文件,得到ip,user,pwd  */
        //1. 创建Properties对象
        Properties properties = new Properties();
        //2. 加载指定文件
        properties.load(new FileReader("src\\mysql.properties"));
        //3. 把键-值显示在控制台
        properties.list(System.out);
        //4. 根据Key获取Value
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("用户名为" + user);
        System.out.println("密码是" + pwd);

        /*  5.新建/修改一个配置文件  */
        Properties properties2 = new Properties();
        properties2.setProperty("charset","utf-8");
        properties2.setProperty("user","汤姆");//中文在properties中以Unicode码存储
        // 第二个参数表示注释
        properties2.store(new FileOutputStream("src\\mysql2.properties")," This is comment");
    }
}
输出:
******传统方法******
ip的值为192.168.100.100
user的值为root
pwd的值为12345
******Properties******
-- listing properties --
user=root
pwd=12345
ip=192.168.100.100
用户名为root
密码是12345

mysql.properties

ip=192.168.100.100
user=root
pwd=12345

mysql2.properties

# This is comment
#Tue Sep 05 15:55:31 CST 2023
user=\u6C64\u59C6
charset=utf-8

特别说明
本文章是个人整理的学习笔记,参考b站韩顺平老师的课程(【零基础 快速学Java】韩顺平 零基础30天学会Java)。老师讲的非常好,有兴趣的可以去看一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值