【Java学习】IO流

前言

本文为IO流的学习笔记,刚开始学习Java,有错误及不完善的地方还请大佬们指出~

1. 文件

1.1 概念

保存数据的地方,如word、txt、excel、pdf、jpg、mp4、avi等

1.1.1 文件流

文件在程序是以流的形式操作的

1.1.2 流

数据在数据源(文件)和程序(内存)之间的路径

1.1.3 输入流

文件→内存

1.1.4 输出流

内存→文件

1.1.5 序列化和反序列化

想将对象存入文件或从文件中读取对象,则需要将基本数据类型或对象进行序列化和反序列化操作

  • 序列化:保存数据时,保存数据的值和数据类型
  • 反序列化:恢复数据时,恢复数据的值和数据类型
    而实现序列化机制:让类是可序列化的,即实现以下两个接口之一
    • Serializable
    • Externalizable

1.2 常用操作

1.2.1 创建文件对象相关的构造器和方法

  • new File(String pathname) //根据路径构建一个File对象
  • new File(File parent, String child) //根据父目录文件+子路径构建
  • new File(String parent, String child) //根据父目录+子路径构建

1.2.2 创建新文件

createNewFile();

练习

在D盘分别用三种方式从创建a.txt、b.txt和c.txt文件

  • 方式一
  File file = new File("d:/a.txt");
  file.createNewFile(); //需要抛出异常
  • 方式二
  File parentFile = new File("d:/");
  String fileName = "b.txt";
  File file = new File(parentFile,fileName);
  file.createNewFile();
  • 方式三
  String parentPath = "d:/";
  String fileName = "c.txt";
  File file = new File(parentPath,fileName);
  file.createNewFile();

1.2.3 获取文件相关信息

  • getName:获取文件名
  • getAbsolutePath:获取文件绝对路径
  • getParent:获取文件父级目录
  • length:获取文件大小(字节)
  • exists:判断文件是否存在
  • isFile:判断是否为文件
  • isDirectory:判断是否为目录
    例:
//先创建文件对象
File file = new File("d:/a.txt");

//调用相应方法
sout(file.getName());

1.2.4 目录的操作和文件删除

  • mkdir:创建一级目录,并返回boolean类型值
  • mkdirs:创建多级目录,并返回boolean类型值
  • delete:删除空目录或文件,并返回boolean类型值
    例:
//判断"d:/a.txt"是否存在,存在则删除文件
File file = new File("d:/a.txt");
	if (file.exists()) {
		if (file.delete()) {
			System.out.println("删除成功");
		} else {
			System.out.println("删除失败");
		}
	} else {
		System.out.println("文件不存在");
    }
//判断"d:/demo/a/b/c"是否存在,存在则提示,否则创建
File file = new File("d:/demo/a/b/c");
	if (file.exists()) {
		System.out.println("目录存在");
	} else {
        if(file.mkdirs()) {
            System.out.println("该目录创建成功");
        } else {
            System.out.println("创建失败");
        }
    }

2. IO流原理及流分类

2.1 IO流原理

  • I/O是Input/Output的缩写,用于处理数据传输
  • Java程序中对于数据的输入输出操作以“流(Stream)”的方式进行
  • java.io包下提供了各种流类和接口,用于获取不同类型数据,并通过方法输入或输出数据
  • 输入Input:读取外部输入到程序
  • 输出Output:将程序输出到外部

2.2 流分类

  • 按操作数据单位分:字节流(8bit),字符流
  • 按数据流的流向分:输入流,输出流
  • 按流的角色分:节点流,处理流/包装流
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

3. 节点流和处理流

3.1 节点流

可以从一个特定的数据源读写数据,如:FileReader、FileWriter

3.2 处理流

也叫包装流,是连接在已经存在的流(节点流或处理流)上,为程序提供更为强大的读写功能,如:BufferedReader、BufferedWriter

3.3 分类

分类字节输入流字节输出流字符输入流字符输出流流类型
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter节点流
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter节点流
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWrter节点流
访问字符串StringReaderStringWriter节点流
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter处理流
转换流InputStreamReaderOutputStreamWriter处理流
对象流ObjectInputStreamObjectOutputStream处理流
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter处理流
打印流PrintStreamPrintWriter处理流
推回输入流PushbackInputStreamPushbackReader处理流
特殊流DataInputStreamDataOutputStream处理流

3.4 区别和联系

  • 区别:
    • 节点流是底层流/低级流,直接跟数据源相接
    • 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
  • 联系:
    • 处理流包装节点流,既可以消除不同节点流的现实差异,也可以提供更方便的方法来完成输入输出

4. 输入流

4.1 InputStream

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

4.1.1 FileInputStream

  • 构造函数:
    • FileInputStream(File file):通过文件对象(file)创建文件输入字节流
    • FileInputStream(String name):通过实际文件的路径字符串创建输入流对象
  • 方法:
    • close:关闭文件输入流并释放资源
    • finalize:清除与文件的链接
    • read:按单个字节读取文件
    • read(byte[] b):一次读取最多b.length个字节的数据,返回实际读取的字节数
    • read(byte[] b,int start,int end):从b数组的指定位置读入字节数据

例:

//文件输入字节流读取文件,并输出在控制台
public void readFile01() {
    String filePath = "d:/hello.txt";
    FileInputStream fileInputStream = null;
    try{
        fileInputStream = new FileInputStream(filePath);//需要异常处理
        int readData = 0;
    	while (readData != -1) {
        	readData = fileInputStream.read();//逐个字节读入,需要异常处理        
        	System.out.println((char) readData);//读入的数据为字节,转换为字符输出
    	}
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
        fileInputStream.close();//关闭文件流释放资源,关闭需要异常处理
        } catch (IOException e) {
            e.printStackTrance();
        }
    }
}

//使用read(byte[] b)读取文件,提高效率
public void readFile02() {
    String filePath = "d:/hello.txt";
    byte[] buf = new byte[8];//一次读取8个字节
    FileInputStream fileInputStream = null;
    try{
        fileInputStream = new FileInputStream(filePath);//需要异常处理
        //如果读取正常,read(byte[] b)返回的是实际读取的字节数(最多读8个字节,不够8个字节有几个字节就读几个)
        int readLen = 0;//用于存放实际读取到的字节数
    	while ((readLen = fileInputStream.read(buf)) != -1) {//读数据,需要异常处理    
        	System.out.println(new String(buf,0,readLen));//转换为字符串输出
    	}
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
        fileInputStream.close();//关闭文件流释放资源,关闭需要异常处理
        } catch (IOException e) {
            e.printStackTrance();
        }
    }
}

4.1.2 BufferedInputStream

  • 构造函数:
    • BufferedInputStream(InputStream in):将InputStream包装为BufferedInputStream
    • BufferedInputStream(InputStream in, int size):将InputStream包装为BufferedInputStream,并指定缓冲区大小
  • 方法:
    • close:关闭输入流并释放与流相关的系统资源
    • read:按单个字节读取文件
    • read(byte[] b,int off,int len):从给定偏移开始,将字节输入流中的字节读入指定的字节数组

4.1.3 ObjectInputStream

提供反序列化,读取数据时需要与序列化时顺序一致

  • 构造函数:
    • ObjectInputStream(InputStream in):将InputStream包装为ObjectInputStream
  • 方法:
    • close:关闭流并释放资源
    • read:按单个字节读取文件
    • read(byte[] b,int off,int len):从给定偏移开始,将字节输入流中的字节读入指定的字节数组
    • readInt:读取一个32位整型
    • readByte:读取一个8位字节
    • readShort:读取一个16位短整型
    • readLong:读取一个64位长整型
    • readChar:读取一个字符
    • readDouble:读取一个64位小数
    • readFloat:读取一个32位浮点型
    • readBoolean:读取一个布尔类型
    • readObject:读取一个对象

例:

/**
 * @ClassName: ObjectInputStreamDemo
 * @Description: 反序列化恢复data.dat文件
 */
public class ObjectInputStreamDemo {

    public static void main(String[] args) {
        //指定反序列化的文件
        String filePath = "data.dat";

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

            //读取,反序列化时需要和序列化顺序一致
            System.out.println(objectInputStream.readInt());
            System.out.println(objectInputStream.readBoolean());
            System.out.println(objectInputStream.readChar());
            //读取Dog对象,Dog实体类在ObjectOutputStreamDemo的内部类中,同包内可以调用
            Object dog = objectInputStream.readObject(); //找不到实体类时会报异常ClassNotFoundException
            System.out.println(dog); 

            //调用dog的方法需要向下转型(Dog) dog
            System.out.println(((Dog) dog).getName());

            //关闭流
            objectInputStream.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

tips】对象输入输出流注意事项和细节:

  • 读写顺序要一致
  • 要求实现序列化和反序列化对象,需要实现Serializable
  • 序列化的类中建议添加SerialVersionUID以提高版本的兼容性
  • 序列化对象时,默认将里面的所有属性都进行序列化,但除了static或transient修饰的成员
  • 序列化对象时,要求里面属性的类型也需要实现序列化接口
  • 序列化具备可继承性,即如果某类已经实现序列化,则它的所有子类也已经默认实现了序列化

4.2 Reader

4.2.1 FileReader

  • 构造函数:

    • FileReader(File file):通过文件对象创建文件字符输入流对象
    • FileReader(String fileName):通过文件路径创建文件字符输入流对象
  • 相关方法:

    • read:读取字符

例:

//单个字符读取story.txt文件内容
public void readFile01() {
    String filePath = "d/story.txt";
    FileReader fileReader = null;
    int data = 0;
    try {
        fileReader = new FileReader(filePath);
        while ((data = fileReader.read()) != -1) {
            System.out.println((char) data);
        }
    } catch (IOException e) {
        e.printStackTrance();
    } finally {
        try {
            if (fileReader != null) {
                fileReader.close();
            }
        } catch (IOException e) {
            e.printStackTrance;
        }
    }
}

//字符数组读取文件
public void readFile02() {
    String filePath = "d/story.txt";
    FileReader fileReader = null;
    int readLen = 0;
    char[] buf = new char[8];
    try {
        fileReader = new FileReader(filePath);
        //循环读入,返回的是实际读取到的字符数
        while ((readLen = fileReader.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
    } catch (IOException e) {
        e.printStackTrance();
    } finally {
        try {
            if (fileReader != null) {
                fileReader.close();
            }
        } catch (IOException e) {
            e.printStackTrance;
        }
    }
}

4.2.2 BufferedReader

属于字符流,按照字符读取数据,关闭处理流时只需关闭外层流

  • 构造函数:
    • BufferedReader(Reader in):创建使用默认大小输入缓冲区的缓冲字符输入流
    • BufferedReader(Reader in, int size):创建使用指定大小输入缓冲区的缓冲字符输入流
  • 方法:
    • close:关闭流并释放资源
    • read:读入一个字符
    • readLine:读入一行字符

例:

public class BufferedReaderDemo{

    public static void main(String[] args) throws IOException {
        String filePath = "src/FileWriter01.java";
        //创建BufferedReader对象
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //按行读取,当返回值为空时说明文件读取完毕
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }

        //关闭流
        bufferedReader.close();
    }
}

4.2.3 InputStreamReader

将InputStream(字节流)包装成Reader(字符流)

  • 构造函数:
    • InputStreamReader(InputStream in):创建一个使用默认字符集的InputStreamReader
    • InputStreamReader(InputStream in, Charset cs):创建一个指定字符集的InputStreamReader
  • 方法:
    • close:关闭流并释放资源
    • read:读入一个字符
    • read(char[], int off, int len):读入字符数组的指定部分

例:

public class InputStreamReaderDemo {

    /**
     * @Description: 将字节流FileInputStream转换为字符流InputStreamReader
     * @Param: [args]
     * @Return: [void]
     */
    public static void main(String[] args) {
        String filePath = "a.txt";
        try {
            //把FileInputStream转换为InputStreamReader,并指定编码格式为utf-8
            InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath), "utf-8");
            //将InputStreamReader传入BufferedReader
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            //读取
            String str = bufferedReader.readLine();
            System.out.println("读取内容:" + str);
            //关闭流
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. 输出流

5.1 OutputStream

5.1.1 FileOutputStream

  • 构造函数:
    • FileOutputStream(File file):通过文件对象创建文件输出字节流对象
    • FileOutputStream(String name):通过文件路径创建输出流对象
    • FileOutputStream(File file,boolean append):append为true则输出时追加到文件末尾,否则覆盖文件内容
    • FileOutputStream(String name,boolean append):同上
  • 方法:
    • close:关闭输出流并释放资源
    • finalize:清除与文件的链接
    • write(int b):写入单个字节的数据
    • write(byte[] b):将b.length个字节的数据从指定的字节数组输出到文件
    • write(byte[] b,int start,int end):将字符串数组b中指定位置的字节数据输出到文件

例:

//在a.txt中写入hello,world 文件不存在时自动创建文件
pulic void writeFile() {
    String filePath = "d:/a.txt";
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(filePath);
        fileOutputStream.write('H');//写入单个字节,char会自动转换为int
        String str = "hello,world";
        fileOutputStream.write(str.getBytes());//getBytes方法可以把字符串转换为一个字节数组
        fileOutputStream.write(str.getBytes(),0,str.length);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileOutputStream.close();
        }
    }
}

5.1.2 BufferedOutputStream

  • 构造函数:
    • BufferedOutputStream(OutputStream os):创建一个缓冲输出流
    • BufferedOutputStream(OutputStream os, int size):指定缓冲区大小的缓冲输出流
  • 方法:
    • write(int b):将指定字节写入输出流
    • write(byte[], int off, int len):将数组指定段落写入输出流

5.1.3 ObjectOutputStream

提供序列化,实现了Serializable接口

  • 构造函数:

    • ObjectOutputStream(OutputStream os):将OutputStream包装为ObjectOutputStream
  • 方法:

    • close:关闭流并释放资源
    • write:按单个字节输出
    • writeInt:输出一个32位整型
    • writeByte:输出一个8位字节
    • writeShort:输出一个16位短整型
    • writeLong:输出一个64位长整型
    • writeChar:输出一个字符
    • writeDouble:输出一个64位小数
    • writeFloat:输出一个32位浮点型
    • writeBoolean:输出一个布尔类型
    • writeObject:输出一个对象

例:

/**
 * @ClassName: ObjectOutputStreamDemo
 * @Description: 使用ObjectOutputStream序列化基本数据类型和一个Dog对象(name, age),并保存到data.dat文件中
 */
public class ObjectOutputStreamDemo {

    public static void main(String[] args) {
        //文件路径:相对路径,即工程文件目录下data.dat文件,也可以使用.txt文件保存
        String filePath = "data.dat";

        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));

            //序列化数据到文件中
            objectOutputStream.writeInt(100); //自动装箱成Integer,实现了Serializable
            objectOutputStream.writeBoolean(true); //自动装箱成Boolean,实现了Serializable
            objectOutputStream.writeChar('a'); //自动装箱成Character,实现了Serializable
            //保存Dog对象
            objectOutputStream.writeObject(new Dog("旺财",5)); //需要在实体类实现Serializable接口

            //关闭流
            objectOutputStream.close();
            System.out.println("数据保存完毕(序列化形式)");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

/**
 * @ClassName: Dog
 * @Description: Dog实体类(内部类),需要实现Serializable接口
 */
class Dog implements Serializable {

    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

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

5.2 Writer

5.2.1 FileWriter

  • 构造函数:

    • FileWriter(File file):通过文件对象创建文件输出字符流对象
    • FileWriter(String str):通过字符串创建文件输出字符流对象
    • FileWriter(File file, boolean append):append为true则输出时追加到文件末尾,否则覆盖文件内容
    • FileWriter(String str, boolean append): 同上
  • 方法:

    • write(int):写入单个字符
    • write(char[]):写入指定数组
    • write(char[], off, len):写入指定数组的指定部分
    • write(String):写入整个字符串
    • write(String, off, len):写入字符串指定部分

tips】FileWriter使用后必须关闭(close)或刷新(flush)

例:

public class FileWriterDemo {

    public static void main(String[] args) {
        String filePath = "d:/note.txt";

        //创建FileWriter对象
        FileWriter fileWriter = null;
        char[] chars = {'a','b','c'};
        try {
            fileWriter = new FileWriter(filePath);

//          write(int):写入单个字符
            fileWriter.write("H");
//          write(char[]):写入指定数组
            fileWriter.write(chars);
//          write(char[], off, len):写入指定数组的指定部分
            fileWriter.write(chars, 0, 2);
//          write(String):写入整个字符串
            fileWriter.write("213");
//          write(String, off, len):写入字符串指定部分
            fileWriter.write("4dfk3",2,2);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert fileWriter != null;
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println("程序结束");
    }
}

5.2.2 BufferedWriter

  • 构造函数:

    • BufferedWriter(Writer out):创建缓冲字符输出流
    • BufferedWriter(Writer out, int size):创建指定缓冲区大小的缓冲字符输出流
  • 方法:

    • close:关闭流并释放资源
    • newLine:输出并插入一个和系统相关的换行符
    • write:输出一个字符
    • write(char[], int off, int len):输出数组指定部分
    • write(String s, int off, int len):输出字符串指定部分

例:

public class BufferedWriterDemo {

    public static void main(String[] args) {
        String filePath = ("BufferedWriterTest.txt");
        //创建bufferedWriter对象
        try {
            //BufferedWriter本身没有追加,需要追加时在节点流后加true,即:FileWriter(filePath,true)
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
            bufferedWriter.write("Hello World");

            //插入一个和系统相关的换行符
            bufferedWriter.newLine();

            //关闭流
            bufferedWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5.2.3 OutputStreamWriter

将OutputStream(字节流)包装成Writer(字符流)

  • 构造函数:

    • OutputStreamWriter(OutputStream os):默认字符集的OutputStreamWriter
    • OutputStreamWriter(OutputStream os, Charset cs):指定字符集的OutputStreamWriter
  • 方法:

    • close:关闭流并释放资源
    • write:输出一个字符

例:

public class OutputStreamWriterDemo {

    /**
     * @Description: 将字节流FileOutputStream包装为字符流OutputStreamWriter,并指定保存文件的编码格式为gbk
     * @Param: [args]
     * @Return: [void]
     */
    public static void main(String[] args) {

        String filePath = "OutputStreamWriterDemo.txt";
        try {
            //创建流对象
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(filePath),"gbk");
            //写入
            outputStreamWriter.write("测试中文编码");
            //关闭流
            outputStreamWriter.close();

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

6. Properties类

用于处理.properties文件

  • 方法:
    • load:加载配置文件的键值对到Properties对象
    • list:将数据显示到指定的设备/流对象
    • getProperty(key):根据键获取值
    • setProperty(key, value):设置键值对到Properties对象
    • store:将Properties中的键值对存储到配置文件

例:

public class PropertiesDemo {

    public static void main(String[] args) {
        //创建Properties对象
        Properties properties = new Properties();

        try {
            //加载配置文件
            properties.load(new FileReader("demo.properties"));

            //键值对打印到控制台
            properties.list(System.out);

            //根据key获取对应value
            String userName = properties.getProperty("userName");
            String password = properties.getProperty("password");
            System.out.println("用户名:" + userName + " 密码:" + password);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

demo.properties

ip=192.168.100.100
userName=root
password=root
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值