java IO流

代码

文章中用到的代码都在这
https://gitee.com/lai_di_hao/java-io-exercise.git

概念:内存与存储设备之间的传输数据的通道。

流的分类

按方向分

  • 输入流:将<存储设备>中的内容写入到<内存>中。
  • 输出流:将<内存>中的内容写入到<存储设备>中。
    在这里插入图片描述

按单位分

  • 字节流:以字节为单位,可以读写所有数据。
  • 字符流:以字符为单位,只能读写文本数据。

按功能分

  • 节点流:具有实际传输数据的读写功能。
  • 过滤流:在节点流的基础之上增强功能。

字节流

字节流的父类(抽象类)

  • InputStream 字节输入流
    public int read () {}
    public int read (byte[] b) {}
    public int read(byte[] b, int off, int len) {}
  • OutputStream 字节输出流
    public void write (int n) {}
    public void write (byte[] b) {}
    public void write (byte[] b, int off, int len) {}

文件字节流

  • FileInputStream :
    public int read (byte[] b) // 从流中读取多个字节,将读到内容存入b数组,返回实际读到的字节数;如果达到文件的尾部,则返回-1

在文件读取时,如果不指定输出的类型,则会输出内容对应的字符编码:
错误示范
要输出字符的应该强制转换输出类型:
强制转换后输出正确
还有多个字节的读取方法:
多个字节的读取方法
代码:

package com.test5;

import java.io.FileInputStream;

/**
 * 文件字节输入流
 */
public class FileInputStreamTest {
    public static void main(String[] args) throws Exception {
        //创建FileInputStream,并指定文件路径
        FileInputStream fis = new FileInputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        //读取文件(单个字节读取)
//        int data = 0;
//        while ((data = fis.read()) != -1){
//            System.out.println((char) data);
//        }
        //读取文件(多个字节)
        byte[] buf = new byte[6];
        int count = 0;
        while ((count =fis.read(buf)) != -1){
            System.out.println(new String(buf,0,count));
        }

        //关闭
        fis.close();
        System.out.println("关闭");
    }
}


  • FileOutputStream :
    public void write (byte[] b) // 一次写多个字节,将b数组中的所有字节,写入输出流。

单字节写入文件:
单字节写入文件
多字节写入文件:
在这里插入图片描述
如果FileOutputStream对象中的append方法为true,则写入的内容不覆盖,而是接在内容尾部:
在这里插入图片描述
代码:

package com.test5;

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

/**
 * 文件字节输出流
 */
public class FileOutputStreamTest {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:\\Users\\LDH\\Desktop\\test.txt",true);
        //写入文件
//        fos.write(97); //这里写入的是字符编码,'a'
//        fos.write('b');
//        fos.write('c');
        String s = "hhhhhhh";
        fos.write(s.getBytes(StandardCharsets.UTF_8));
        //关闭
        fos.close();
        System.out.println("关闭");

    }
}


文件复制案例:

package com.test5;

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

/**
 * 文件复制
 */
public class FileCopy {
    public static void main(String[] args) throws Exception {
        //文件字节输入流,被复制的文件
        FileInputStream fis = new FileInputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        //文件字节输出流,复制后的文件
        FileOutputStream fos = new FileOutputStream("C:\\Users\\LDH\\Desktop\\test2.txt");
        //一边读,一边写
        byte[] buf = new byte[1024];
        int count = 0;
        while ((count = fis.read(buf)) != -1){
            fos.write(buf,0,count);
        }
        //关闭
        fis.close();
        fos.close();
    }
}


字节缓冲流

  • BufferedInputStream / BufferedOutputStream
  • 提高IO效率,减少访问磁盘的次数
  • 数据存储在缓冲区中,flush是将缓冲区的内容写入文件中,也可以直接close。

BufferedInputStream 用法:

package com.test5;

import java.io.BufferedInputStream;
import java.io.FileInputStream;

/**
 * 字节缓冲流
 */
public class BufferedInputStreamTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        BufferedInputStream bis = new BufferedInputStream(fis); //内部有8k的缓冲区
        //读取
        int data = 0;
        while ((data = bis.read()) != -1){
            System.out.println((char) data);
        }
        //自定义缓冲区读取
        byte[] buf = new byte[1024];
        int count = 0;
        while ((count =fis.read(buf)) != -1){
            System.out.println(new String(buf,0,count));
        }
        bis.close();
    }
}

结果:
在这里插入图片描述

BufferedOutputStream 使用:

package com.test5;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;

/**
 * 字节输出流写入文件
 */
public class BufferedOutputStreamTest {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //写入文件
        for (int i = 0; i < 10; i++) {
            bos.write(("hellow"+i+"\r\n").getBytes(StandardCharsets.UTF_8)); //写入缓冲区,内置有8k缓冲区
            bos.flush(); //刷新文件
        }
        //关闭,内部只用flush方法
        bos.close();
    }
}

结果:
在这里插入图片描述
注意:
写入缓冲区之后一定要刷新,不然内容不会显示。

bos.flush(); //刷新文件

对象流

  • ObjectOutputStream / ObjectInputStream
  • 增强了缓冲区功能
  • 增强了读写8种基本数据类型和字符串功能
  • 增强了读写对象功能:
    readObject() 从流中读取一个对象 ----反序列化
    writeObject(Object obj) 向流中写入一个对象 ----序列化

ObjectOutputStream 使用

package com.test5;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

/**
 * 实现对象的序列化
 */
public class ObjectOutputStreamTest {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //序列化(写入操作)
        Students s1 = new Students("张三",20);
        oos.writeObject(s1);
        oos.close();
        System.out.println("序列化ok");
    }
}

这里直接运行会报错:

Exception in thread "main" java.io.NotSerializableException: com.test5.Students
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.test5.ObjectOutputStreamTest.main(ObjectOutputStreamTest.java:15)

因为创建的Students 类没有实现序列化接口,所以需要在类里实现Serializable接口,表示这个类可以序列化:
在这里插入图片描述
再运行上面的代码,发现对象写入成功:
在这里插入图片描述


ObjectInputStream 使用

package com.test5;

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

/**
 * 实现反序列化(读取重构成对象)
 */
public class ObjectInputStreamTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //读取文件
        Students s = (Students) ois.readObject();
        //关闭
        ois.close();
        System.out.println("执行完毕");
        System.out.println(s.toString());
    }
}

结果:
在这里插入图片描述


注意

1、序列化的类必须实现Serializable接口,包括类中的属性,例如private Address address;
2、类实现Serializable接口后要添加一个常量private static final long serialVersionUID,即对象序列化版本号

2020之前的idea要自动对象序列化版本号的设置可以参考这篇文章
我自己是2020idea,设置有些许改变,可以参考这篇文章

我现在添加了序列化版本号之后:
在这里插入图片描述

再运行反序列化读取之前的内容会报错,因为两个对象的序列化版本号不一致。

Exception in thread "main" java.io.InvalidClassException: com.test5.Students; local class incompatible: stream classdesc serialVersionUID = 100, local class serialVersionUID = 120
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1963)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1829)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2120)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1646)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
	at com.test5.ObjectInputStreamTest.main(ObjectInputStreamTest.java:14)

在新的序列化版本号的基础上再序列化一次对象,或者修改对象类内部的版本号后再次执行反序列化,结果恢复正常:
在这里插入图片描述
3、在属性前加 transient 可以避免被序列化,如private transient int age;,那这个age就不会被序列化,例如:
被序列化的对象:

Students s1 = new Students("张三",20);

序列化后,再反序列化读取结果:

执行完毕
Students{name='张三', age=0}

可以看到age不会被存入文件,自然在读取的时候也不会被读取到。

4、静态属性也不能被序列化

例如我在学生类中加一条静态属性:

public static String code = "200";

在进行序列化和反序列化之后得到的结果:

执行完毕
Students{name='张三', age=0}

5、序列化多个对象,可以用集合的形式进行序列化和反序列化
例如,被序列化的对象:

        Students s1 = new Students("张三",20);
        Students s2 = new Students("李四",20);

序列化集合:

        ArrayList<Students> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        oos.writeObject(list);

反序列化集合:

        //读取文件
//        Students s = (Students) ois.readObject();
        ArrayList<Students> list = (ArrayList<Students>) ois.readObject();
        //关闭
        ois.close();
        System.out.println("执行完毕");
        System.out.println(list.toString());

结果:

执行完毕
[Students{name='张三', age=0}, Students{name='李四', age=0}]

6


字符流

  • Reader (抽象类):字符输入流:
    public int read() {}
    public int read(char[] c) {}
    public int read(char[] c, int off, int len) {}

  • Writer (抽象类):字符输出流:
    public void write(int n) {}
    public void write(Strint str) {}
    public void write(char[] c) {}

小案例

在这里插入图片描述
在这里插入图片描述
这里两次测试分别用字节流读取英文和中文,英文的正常,中文的乱码。这是因为在txt中默认是UTF-8的形式保存的,在UTF-8中一个汉字三个字节,而字节流是一个字节一个字节的读取,所以当一个汉字被拆成三分来输出的时候肯定会变成乱码。

FileReader 使用

文本内容:
在这里插入图片描述

创建FileReader,文件字符输入流:

        FileReader fr = new FileReader("C:\\Users\\LDH\\Desktop\\test2.txt");

单个字符读取文件:

        int data = 0;
        while ((data = fr.read()) != -1){
            System.out.print((char) data);
        }

结果:

你好你好你好

多个字符读取:

        char[] buf = new char[2];
        int count = 0;
        while ((count = fr.read(buf)) != -1){
            System.out.println(new String(buf,0,count));
        }

结果:

你好
你好
你好

FileWriter 使用

代码:

package com.test5;

import java.io.FileWriter;

public class FileWriterTest {
    public static void main(String[] args) throws Exception {
        //创建FileWriter对象
        FileWriter fw = new FileWriter("C:\\Users\\LDH\\Desktop\\test2.txt");
        //写入
        for (int i = 0; i < 10; i++) {
            fw.write("java你好\r\n");
            fw.flush();
        }
        //关闭
        fw.close();
        System.out.println("执行完毕");
    }
}

结果:
在这里插入图片描述

字符流实现文本文件复制

package com.test5;

import java.io.FileReader;
import java.io.FileWriter;

/**
 * 使用FileReader和FileWriter复制文件
 * 只能复制文本文件,不能复制图片或者二进制文件
 */
public class FileCopy2 {
    public static void main(String[] args) throws Exception {
        //创建FileReader和FileWriter
        FileReader fr = new FileReader("C:\\Users\\LDH\\Desktop\\test2.txt");
        FileWriter fw = new FileWriter("C:\\Users\\LDH\\Desktop\\test.txt");
        //读、写
        int data = 0;
        while ((data = fr.read()) != -1){
            fw.write(data);
            fw.flush();
        }
        //关闭
        fr.close();
        fw.close();
        System.out.println("执行完毕");
    }
}

结果:
复制成功
注意:
二进制没有字符编码,所以字符流copy二进制的时候无法进行正确的解析,就会造成乱码,而图片里也是二进制,所以字符流操作无法复制图片。

在这里插入图片描述

字符缓冲流

  • BufferedReader / BufferedWriter
  • 高效读写
  • 支持输入换行符
  • 可一次写一行、读一行

BufferedReader 使用:

package com.test5;

import java.io.BufferedReader;
import java.io.FileReader;

public class BufferedReaderTest {
    public static void main(String[] args) throws Exception {
        //创建缓冲流
        FileReader fr = new FileReader("C:\\Users\\LDH\\Desktop\\test2.txt");
        BufferedReader br = new BufferedReader(fr);
        //读取
        //缓冲区读取
        char[] buf = new char[1024];
        int count = 0;
        while ((count = br.read(buf)) != -1){
            System.out.println(new String(buf,0,count));
        }
        //一行一行的读取
        String line = null;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        //关闭
        br.close();
    }
}

两种方式结果一致:

java你好
java你好
java你好
java你好
java你好
java你好
java你好
java你好
java你好
java你好

BufferedWriter 使用:

package com.test5;

import java.io.BufferedWriter;
import java.io.FileWriter;

public class BufferedWriterTest {
    public static void main(String[] args) throws Exception {
        FileWriter fw = new FileWriter("C:\\Users\\LDH\\Desktop\\test2.txt");
        BufferedWriter buw = new BufferedWriter(fw);
        //写入
        for (int i = 0; i < 10; i++) {
            buw.write("BufferedWriter");
            buw.newLine(); //写入换行符 Windows \r\n , Linux \n
            buw.flush();
        }
        //关闭
        buw.close();
        System.out.println("执行完毕");
    }
}

结果:
在这里插入图片描述

打印流 PrintWriter

  • 封装了print () / println () 方法,支持写入后换行
  • 支持数据原样打印

示例:

package com.test5;

import java.io.PrintWriter;

public class PrintWriterTest {
    public static void main(String[] args) throws Exception {
        //创建打印流
        PrintWriter pw = new PrintWriter("C:\\Users\\LDH\\Desktop\\test2.txt");
        //打印
        pw.println(97);
        pw.println(true);
        pw.println("a");
        pw.println(3.14);
        //会自动刷新
        //关闭
        pw.close();
    }
}

结果:
在这里插入图片描述

转换流

  • 桥转换流:InputStreamReader / OutputStreamWriter
  • 可将字节流转换为字符流
  • 可设置字符的编码方式

InputStreamReader 使用:

package com.test5;

import java.io.FileInputStream;
import java.io.InputStreamReader;

public class InputStreamReaderTest {
    public static void main(String[] args) throws Exception {
        //创建InputStreamReader
        FileInputStream fis = new FileInputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        //读取文件
        int data = 0;
        while ((data = isr.read()) != -1){
            System.out.print((char) data);
        }
        //关闭
        isr.close();
    }
}

new InputStreamReader(fis,"utf-8");中的第二个参数是要使用的编码形式,如果设置的编码形式和被读取文件的编码形式不一致,就会导致乱码。

OutStreamWriter 使用:

package com.test5;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class OutputStreamWriterTest {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:\\Users\\LDH\\Desktop\\test.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
        //写入
        for (int i = 0; i < 10; i++) {
            osw.write("人生若只如初见\r\n");
            osw.flush();
        }
        //关闭
        osw.close();
        System.out.println("执行完毕");
    }
}

与上面一样,new OutputStreamWriter(fos,"gbk");的第二个参数是指定的编码。
运行结果:
在这里插入图片描述
ANSI就是指各个国家的自己的编码,在中国就是指gbk

File类

概念:代表物理盘符中的一个文件或文件夹

文件操作

分隔符

        System.out.println("路径分隔符:"+ File.pathSeparator);
        System.out.println("名称分隔符:"+File.separator);

结果:

路径分隔符:;
名称分隔符:\

创建文件

        //创建文件
        File file = new File("C:\\Users\\LDH\\Desktop\\test3.txt");
        boolean b = file.createNewFile();
        System.out.println("创建结果:"+b); //创建结果:true

如果再执行的话,会得到结果:创建结果:false

删除文件

        //删除文件
        //1、直接删除
        System.out.println("删除结果:"+file.delete());
        //2、使用jvm退出时删除文件,即程序停止运行时,才会删除指定文件或目录
        file.deleteOnExit();

文件的一些基本方法

        System.out.println("文件绝对路径:"+file.getAbsolutePath());
        System.out.println("获取路径:"+file.getPath()); //这里是你写什么路径就获取什么路径
        System.out.println("获取文件名称:"+file.getName());
        System.out.println("获取文件父目录:"+file.getParent());
        System.out.println("获取文件长度:"+file.length());
        System.out.println("获取文件创建时间:"+new Date(file.lastModified()).toLocaleString());

结果:

文件绝对路径:C:\Users\LDH\Desktop\test3.txt
获取路径:C:\Users\LDH\Desktop\test3.txt
获取文件名称:test3.txt
获取文件父目录:C:\Users\LDH\Desktop
获取文件长度:0
获取文件创建时间:2022-3-26 14:56:56

Process finished with exit code 0

判断

        System.out.println("是否可写:"+file.canWrite());
        System.out.println("是否是文件:"+file.isFile());
        System.out.println("是否隐藏:"+file.isHidden());

结果:

是否可写:true
是否是文件:true
是否隐藏:false

文件夹操作

创建目录

        File dir = new File("C:\\Users\\LDH\\Desktop\\aaa\\bbb\\ccc");
        System.out.println(dir.toString());
        if (!dir.exists()){
            System.out.println("创建结果:"+dir.mkdirs()); //创建多级目录
        }

结果:

C:\Users\LDH\Desktop\aaa\bbb\ccc
创建结果:true

删除文件夹

        //删除文件夹
        //1、直接删除(必须是空目录,否则无法删除)
        System.out.println("删除结果:"+dir.delete());
        //jvm退出时删除(无返回值)
        dir.deleteOnExit();

结果:只删除了ccc目录,再执行就会删除bbb目录

删除结果:true

获取文件夹信息

        //获取文件夹信息
        System.out.println("获取结对路径:"+dir.getAbsolutePath());
        System.out.println("获取文件夹路径:"+dir.getPath());
        System.out.println("获取文件夹父目录:"+dir.getParent());
        System.out.println("获取文件夹名称:"+dir.getName());
        System.out.println("获取文件创建时间:"+new Date(dir.lastModified()).toLocaleString());

结果:

获取结对路径:C:\Users\LDH\Desktop\aaa\bbb\ccc
获取文件夹路径:C:\Users\LDH\Desktop\aaa\bbb\ccc
获取文件夹父目录:C:\Users\LDH\Desktop\aaa\bbb
获取文件夹名称:ccc
获取文件创建时间:2022-3-26 15:23:09

Process finished with exit code 0

判断

        //判断
        System.out.println("是否是文件夹:"+dir.isDirectory()); //true
        System.out.println("是否是隐藏:"+dir.isHidden()); //false

遍历文件夹

        //遍历文件夹
        File dir2 = new File("D:\\Pictures\\HZW");
        String[] files = dir2.list();
        System.out.println("---------------------");
        for (String s : files){
            System.out.println(s);
        }

结果:
在这里插入图片描述

FileFilter接口

        System.out.println("-------------------------");
        File[] file2 = dir2.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".jpg")){
                    return true;
                }
                return false;
            }
        });
        for (File file : file2){
            System.out.println(file.getName());
        }

结果:
在这里插入图片描述

遍历文件夹

递归遍历文件夹

    public static void listDir(File dir) {
        File[] files = dir.listFiles();
        System.out.println(dir.getAbsolutePath());
        if (files != null && files.length >0) {
            for (File file : files) {
                if (file.isDirectory()){
                    listDir(file); // 递归
                }
                System.out.println(file.getAbsolutePath());
            }
        }
    }

传入参数:

public static void main(String[] args) {
        listDir(new File("D:\\Pictures"));
    }

结果:
在这里插入图片描述

递归删除文件夹

因为只有空目录可以删除,所以要删除一个文件夹的话必须得先删除文件夹内部的所有文件。

    public static void deleteDir(File dir){
        File[] files = dir.listFiles();
        if (files != null && files.length>0){
            for (File file : files){
                if (file.isDirectory()){
                    deleteDir(file); //递归
                }
                System.out.println(file.getAbsolutePath()+"删除:"+file.delete());
            }
        }
        System.out.println(dir.getAbsolutePath()+"删除:"+dir.delete());
    }

结果:

C:\Users\LDH\Desktop\aaa\bbb\bbbbbbbb\新建文本文档 (2).txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\bbbbbbbb\新建文本文档 (3).txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\bbbbbbbb\新建文本文档.txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\bbbbbbbb删除:true
C:\Users\LDH\Desktop\aaa\bbb\bbbbbbbb删除:false
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹\新建文本文档 (2).txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹\新建文本文档.txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹删除:false
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹 (2)删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文件夹 (2)删除:false
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文本文档 (2).txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc\新建文本文档.txt删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc删除:true
C:\Users\LDH\Desktop\aaa\bbb\ccc删除:false
C:\Users\LDH\Desktop\aaa\bbb\新建文本文档.txt删除:true
C:\Users\LDH\Desktop\aaa\bbb删除:true
C:\Users\LDH\Desktop\aaa\bbb删除:false
C:\Users\LDH\Desktop\aaa\新建文本文档.txt删除:true
C:\Users\LDH\Desktop\aaa删除:true

Process finished with exit code 0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值