IO流

主要涉及文件的操作

File类

位于java.io.

File类中的一个对象通常代表一个目录或者一个文件

File类适用于文件的创建、删除操作,如果读取、写入需要IO流

构造器

目录无特殊说明,都是字符串

构造器1

方式1:File 对象名 = new File(目录)

目录可以为相对目录也可以为绝对目录

相对路径:相对于当前程序的目录,如果时main()中的File类,那么此时路径是相当于整个项目文件的

绝对路径:加上盘符的完整的路径

使用\\标识每级路径或者/

可以print文件类的对象名,默认是输出填写的目录名

构造器2

方式2:File 对象名 = new File(上级目录, 下级目录)

构造器3

方式3:File 对象名 = new File(File类型的目录, 下级目录的字符串)

方法

方法含义
f.getAbsolutePath()返回String类型,获取对象的绝对路径
f.getPath()返回String类型,填写的路径
f.getName()返回String类型,获取文件名
f.getParent()返回String类型,获取上级目录
f.length()返回long类型,获取文件大小
f.lastModified()返回long类型,最后的修改时间,毫秒数
f.list()返回String[],获取f所有的文件、目录名称
f.listFiles()返回File[],获取f所有的文件、目录
f.renameTo(File类型)返回布尔类型,将文件改名和移动到File的位置
f.isDirectory()返回布尔类型,判断是否是个目录
f.isFile()返回布尔类型,判断是否是个文件
f.exists()返回布尔类型,判断是否存在,中文为存在,读音iɡˈzists
f.canRead()返回布尔类型,判断是否可读
f.canWrite()返回布尔类型,判断是否可写
f.isHidden()返回布尔类型,判断是否隐藏
f.createNewFile()返回布尔类型,仅当f不存在时创建文件,需要使用try-catch环绕,并且目录必须要存在
f.mkdir()返回布尔类型,创建文件目录,仅当上层文件目录存在时创建,如果目录存在或者上层目录不存在都不会创建
f.mkdirs()返回布尔类型,创建文件目录,如果不存在上层目录,将一并创建
f.delete()返回布尔类型,删除文件夹,这个目录内不能有文件或者文件目录

renameTo

将文件改名和移动到File的位置,名字改为File类型所打开的文件名

需保证形式参数所打开的文件不存在才能修改名称+移动 成功

创建文件夹

有两个方法mkdirmkdirs,在new对象中即使填入带后缀的文件名也会当成文件夹处理

例如

  		File fp2 = new File("D:6.txt");
        fp2.mkdir();

实际效果

image-20210807195743246

流stream

Java中对数据的输入/输出操作以流的形式进行

分类:

  • 按照数据单位的不同,分为
    • 字节流8bit,可以读二进制,按照字节读
    • 字符流16bit
  • 按照数据流向的不同,分为
    • 输入流(输入到程序)
    • 输出流(程序输出到数据)
  • 按照角色的不同,分为
    • 节点流
    • 处理流

流的抽象基类

抽象类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

Java的IO流涉及到40多个类,但都是从以上4个类中派生的

image-20210808181330099

节点流(文件流)

分为

  • FileInputStream
  • FileOutputStream
  • FileReader
  • FileWriter

FileReader类

针对于字符的读取

FileReader 变量名 = new FileReader(File类型),如果文件不存在,返回null

实例化FileReader类时,需要使用try-catch环绕或者让实例化的函数抛出异常

方法含义
f.read()会抛出异常,返回int,读取一个字符并返回,如果读取到末尾,则返回-1,可以使用强制类型转换来输出char类型的数据
f.close()关闭流,JVM不会自动回收已经打开的流,也会抛出异常
f.read(char[]数组)也会抛出异常,功能是每次读取char[]数组长度的数据,并存入到数组中,返回int每次读取的长度,当读取到末尾时,返回-1,如果最后一个读取的长度小于数组的长度,那么,超出的部分时上次的内容,即数组内容不会被清空,只会被替换

为保证f.close()方法始终被执行,可以采用try-catch-finally的方式进行环绕

格式

try{
    FileReader 变量名 = new FileReader(参数);//这里读入的可能为null
    //其他的相关操作
}catch(IOException e){
    e.printStackTrace();
}finally{
    try{
        if(f != null){
        	f.close();//如果为空时,不能直接关闭
        }
    }catch (IOException e) {
        e.printStackTrace();
    }
}

读取数据

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

FileWriter类

针对于字符的写入

FileWriter 变量名 = new FileWriter(File类型)

FileWriter 变量名 = new FileWriter(File类型, 布尔类型)布尔类型会决定如果文件中存在内容时 写入文件是否要替换,当为true时追加,为false时,替换文件

方法含义
f.write(字符串)也会抛出异常,将内容写入到文件,如果文件不存在,则创建新文件并写入,如果文件中存在内容,按照构造器给出的参数决定是否替换。在未执行f.close()方法前,每次写入数据都写入到末尾,如果文件夹不存在,则抛出异常
f.close()关闭流,JVM不会自动回收已经打开的流,也会抛出异常
f.write(char[]数组, 开始位置, 个数)即在char[]数组中的开始位置下标处读取相应的个数

FileInputStream类

逐字节读入,如果在内存层面操作过,读中文可能会乱码

f.read()方法包含多个重载

f.read()读取一个字节,返回int,当读取到末尾时返回-1

f.read(byte[]数组, 开始位置, 长度),在byte[]数组中,每次读取相应的长度,从开始位置开始存放

f.readAllBytes()返回一个byte[]数组,包含所有的信息

FileOutputStream类

逐字节写入

f.write()方法也有多个重载

f.write(int),写入一个字节

f.write(byte[]数组),将一个byte[]数组写入

f.write(byte[]数组, 开始位置, 写入长度)byte[]数组中的开始位置下标处读取相应的个数

处理流

包裹着已有的流的基础上的流

缓冲流

是处理流的一种,是为了提高文件读写的效率

处理字节的BufferedInputStreamBufferedOutputStream

处理字符的BufferedWriterBufferedReader

步骤:

  • 打开文件
  • 造一个节点流/文件流
  • 造缓冲流
  • 相应的操作
  • 关闭流,关闭流时,可以只关闭外部的流,内部的流会自动关闭
    public static void main(String[] args) throws IOException {
        //打开文件
        File file = new File("C:\\Users\\singx\\OneDrive\\图片\\背景图片\\wallhaven-8oky1j.jpg");
        File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\2.jpg");
        //造节点流/文件流
        FileInputStream fileInputStream = new FileInputStream(file);
        FileOutputStream fileOutputStream = new FileOutputStream(file2);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        //其他的操作
        int b;
        while((b = fileInputStream.read())!=-1){
            fileOutputStream.write(b);
        }
        fileInputStream.close();//关闭外部的流
        fileOutputStream.close();//关闭外部的流
    }

使用数组

    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\OneDrive\\图片\\背景图片\\wallhaven-8oky1j.jpg");
        File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\22.jpg");
        FileInputStream fileInputStream = new FileInputStream(file);
        FileOutputStream fileOutputStream = new FileOutputStream(file2);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        byte[] b = new byte[512];
        int len;
        while((len = fileInputStream.read(b)) != -1){
            fileOutputStream.write(b, 0, len);
        }
        fileInputStream.close();
        fileOutputStream.close();
    }

f.flush()写入到文件并清空缓冲区,flush中文未冲洗,缓冲流快的原因是其内部有一个大小为8192的数组,当达到这个数组的大小时,一次性的写出

BufferedWriterBufferedReaderBufferedInputStreamBufferedOutputStream步骤一致,造文件、造节点/缓冲流、执行操作、关闭文件,但只能处理文本文件

也可以使用套娃的方式创建

BufferedXXXX 对象名 = newBuffered(new FileXXXX(new File(路径)))

  • BufferedReader可以一次读一行,即使用.readLine()方法,返回String类型,当读到最后时,返回null,使用此方法无法读入换行
    • 在写入时需要手动添加换行或者是用BufferedWriter类的实例中的nextLine()方法进行写入一个换行
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
        File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\222.c");
        BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file2));
        String data;
        int cnt = 0;
        while ((data = bufferedReader.readLine()) != null) {//每次读一行
            bufferedWriter.write(data + "\n");//手动添加换行
        }
        bufferedReader.close();
        bufferedWriter.close();
    }

转换流

提供了字节流和字符流之间的转换,也是属于处理流,还属于字符流,目的时转换格式

有两个,分别为

InputStreamReader:将一个字节的输入流,转换为字符的输入流

OutputStreamWriter:将字符的输出流,转换为字节的输出流

使用InputStreamReader

  • 造文件
  • 造文件流/字节流
  • 造转换流:InputStreamReader 对象名 = new InputStreamReader(字节流/文件流, 文件编码格式字符串),文件编码格式可以省略,默认为系统指定的
  • 相应的操作
  • 关闭文件
public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
        File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\222.c");
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
        int a;
        while((a = inputStreamReader.read()) != -1){
            System.out.print((char)a);
        }
        inputStreamReader.close();
    }

使用特定的字符集进行存储

public class IoTest {
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
        File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\2.c");
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file2), "gbk");//存储为GBK格式
        int a;
        while((a = inputStreamReader.read()) != -1){
            outputStreamWriter.write(a);
        }
        inputStreamReader.close();
//        outputStreamWriter.close();
    }
}

image-20210811180455436

标准输入/输出流

System中有System.out标准输出流、System.in标准输入流

System.in标准输入流:默认从键盘输入:

  • 可通过System类中的setIn指定输入的流

System.out标准输出流:默认输出到控制台:

  • 可通过System类中的setOut指定输出的流

Scanner也是可以读取文件的

Scanner 变量名 = new Scanner(File类型)

后续调用变量名.nextXXX()就可以读取了

打印流

PrintStreamPrintWriter在Java中的System.out.printXXX()方法就是一个打印流

用法:

  • 造文件
  • PrintStream 变量名 = new PrintStream(文件);有多个重载的构造函数
  • 可以使用变量名.printXXX(内容)的形式写入到文件

使用打印流也可以指定System.out.printXXX()指定输出流

System.setOut(printStream)

例如

public static void main(String[] args) throws IOException {
    File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
    PrintStream ps = new PrintStream(file);
    System.setOut(ps);//指定输出流,此后的所有System.out.printXXX()都输出到文件
    System.out.println("ddddddddddd");
}

数据流

作用是操作基本数据类型和String

分别为DataInputStreamDataOutputStream

​ 变量值写入到文件(二进制)变量值从文件读取(二进制)

也就是保存变量中的值,运行期间变量是在内存当中,一旦结束运行,变量的值就自动释放,可以使用数据量进行保存

读取顺序要和写入相同!

image-20210811193928332

例子

public class IoTest {
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
        dataOutputStream.writeInt(3);//写入
        dataOutputStream.writeDouble(77.8);//写入
        dataOutputStream.close();
    }

}
class hhhh{
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        DataInputStream dataInputStream = new DataInputStream(fileInputStream);
        int a = dataInputStream.readInt();//按顺序读取
        double b = dataInputStream.readDouble();//按顺序读取
        System.out.println(a);
        System.out.println(b);
        dataInputStream.close();
    }
}

对象流

用来存储/读取对象、基本数据类型的值,以二进制形式存储,与数据流对应

ObjectInputStreamObjectOutputStream,属于字节流

可以将对象写入到文件中

序列化:用ObjectOutputStream类保存基本数据类型或者对象

反序列化:用ObjectInputStream类读取保存的基本数据类型或者对象

不能序列化static修饰的成员变量

序列化过程

一个类想要被序列化,需要满足实现

  • 其内部属性是可序列化的

  • Serializable接口(位于java.io. )

  • 或者实现Externalizable接口

  • 还需要提供一个全局常量,自定义异常类中的UIDpublic static final long serialVersionUID = XXXXXL,UID可以不写,Java会自动随机生成

  • 当不写UID时,如果类保存后,类中的代码修改了或者增加了其他属性,那么,该类再次读取文件时,会出错

  • 如果写了UID,可以正常读取,并且新增的属性会自动置为默认值

Serial中文为顺序的,连续的,依次的,读音ˈsirēəlSerializable中文为可序列化的

External中文为外部的、对外的,读音为ikˈstərnlExternalizable中文为可外部的

Serializable接口中没有任何的抽象方法,因此无需重写任何的方法

class 类名 implements Serializable{
    //无需重写任何的方法
}

使用ObjectOutputStream进行存储到本地

  • 造文件
  • 造文件流
  • new一个ObjectOutputStream类的对象
  • 调用.writeObject(对象)方法,将文件写入到硬盘中
  • 关闭外层流
反序列化的过程

使用ObjectInputStream进行存储到本地

  • 造文件
  • 造文件流
  • new一个ObjectInputStream类的对象
  • 调用.readObject(对象)方法,读取,该方法返回Object类型
  • 关闭外层流

读取时也是按照写入的顺序读取

例子

public class IoTest {
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\object.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
        Dog dog = new Dog("狗狗", 12, true);
        objectOutputStream.writeObject(dog);
        objectOutputStream.close();
    }

}
class hhhh{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\object.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        Dog s = (Dog)(objectInputStream.readObject());
        System.out.println(s);
        objectInputStream.close();
    }
}

class Dog implements Serializable{
    public static final long serialVersionUID = 98089897L;
    String name;
    int weight;
    boolean female;

    public Dog() {
    }

    public Dog(String name, int weight, boolean female) {
        this.name = name;
        this.weight = weight;
        this.female = female;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", weight=" + weight +
                ", female=" + female +
                '}';
    }
}

RandomAccessFile类

access,中文为使用权,读音ˈakˌses,也是在java.io.下,直接继承于Object类,实现了DataInputDataOutput接口,这个类既可以读又可以写

如果文件不存在,创建文件并写入,如果存在,从头挨个覆盖

  • 造文件

  • 不需要造文件流/字节流,直接使用FIle

  • RandomAccessFile 变量名 = new RandomAccessFile(FIle对象/文件路径, 模式字符串)

    • 模式含义
      r只读的方式打开
      rw读写的方式打开,不会立即写入到硬盘,如果发生异常,数据会丢失
      rwd读写的方式打开,立即写入到硬盘,如果发生异常,会被保存好数据
      rws读写的方式打开,
public static void main(String[] args) throws IOException, ClassNotFoundException {
    RandomAccessFile raf1 = new RandomAccessFile("C:\\Users\\singx\\Desktop\\新建文件夹\\2.jpg", "r");
    RandomAccessFile raf2 = new RandomAccessFile("C:\\Users\\singx\\Desktop\\新建文件夹\\23.jpg", "rw");
    byte[] b = new byte[256];
    int len;
    while ((len = raf1.read(b)) != -1) {
        raf2.write(b, 0, len);
    }
    raf1.close();
    raf2.close();

}

例子

文件1

1234567

调用该类写入abc后,文件1会变为

abc456

该类中的实例变量还有其他方法:

  • 设置文件指针的方法:.seek(long),如果设置的文件指针比原有的文件最后的指针要长,那么,从原有文件最后到设置的指针之前要用空格填入

  • 获取当前文件指针的方法:.getFilePointer(),返回long

  • 获取文件大小(最大的指针长度)的方法:.length(),同样返回long

将数据插入到某个位置:

思路是先将文件指针定位到需要插入的位置,该位置之后的数据全部保存下来,再将指针定位到开始插入的位置,将需要写的数据插入,再将保存好的之后的数据插入

		RandomAccessFile raf = new RandomAccessFile("路径", "rw");
        //先将文件指针定位到需要插入的位置
		raf.seek(需要插入的位置);
		//使用byte数组存储该位置之后的数据
        byte[] b;
		//获取该位置之后的数据,长度就是文件的字节数-需要插入的位置
        raf.read((b = new byte[(int)(raf.length() - 需要插入的位置)]));
		//文件指针回滚,将指针定位到开始插入的位置
        raf.seek(需要插入的位置);
		//插入需要插入的数据
        raf.write(需要插入的数据);
		//再插入保存下来的之前得数据
        raf.write(b);
        raf.close();

Q.E.D.


念念不忘,必有回响。