IO流

154 阅读17分钟

File类的使用

java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关

File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)

File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流(IO流)。后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”终点“。

想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个 File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。

File对象可以作为参数传递给流的构造器

常用构造器

  • public File(String pathname)

    以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

    • 绝对路径:是一个固定的路径,从盘符开始
    • 相对路径:是相对于某个位置开始
  • public File(String parent,String child)

    以parent为父路径,child为子路径创建File对象。

  • public File(File parent,String child)

    根据一个父File对象和子文件路径创建File对象

路径分隔符

  • 路径中的每级目录之间用一个路径分隔符隔开。

  • 路径分隔符和系统有关:

    • windows和DOS系统默认使用“”来表示
    • UNIX和URL使用“/”来表示
  • Java程序支持跨平台运行,因此路径分隔符要慎用

  • 为了解决这个隐患,File类提供了一个常量:

    public static final String separator。根据操作系统,动态的提供分隔符。

  • 举例:

 File file1 = new File("E:\Develop\test.txt");
 File file2 = new File("E:"+File.separator+"Develop"+File.separator + "test.txt");
 File file3 = new File("E:/Develop");

说明:

在IDEA中:

如果开发使用JUnit中的单元测试方法测试,相对路径即为当前Module下。

如果使用main()测试,相对路径即为当前的Project下。

在Eclipse中:

无论使用单元测试方法还是使用main()测试,相对路径都是当前的Project下。

常用方法

File类的获取功能

  • public String getAbsolutePath():获取绝对路径
  • public String getPath():获取路径
  • public String getName():获取名称
  • public String getParent():获取上层文件目录路径。若无,返回null
  • public long length():获取文件长度(即:字节数)。不能获取目录的长度。
  • public long lastModified():获取最后一次的修改时间,毫秒值
  • public String[ ] list():获取指定目录下的所有文件或者文件目录的名称数组
  • public File[ ] listFiles():获取指定目录下的所有文件或者文件目录的File数组

File类的重命名功能

  • public boolean renameTo(File dest):把文件重命名为指定的文件路径
 @Test
 public void test(){
     File file1 = new File("test.txt");
     File file2 = new File("E:\Develop\testing.txt");
     
     boollean renameTo = file1.renameTO(file2); 
     System.out.println(renameTo);
 }
 ​
 要想保证返回true,需要file1在硬盘中存在,且file2在硬盘中不存在。

File类的判断功能

  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile():判断是否是文件
  • public boolean exists():判断是否存在
  • public boolean canRead():判断是否可读
  • public boolean canWrite() :判断是否可写
  • public boolean isHidden():判断是否隐藏

File类的创建功能

  • public boolean createNewFile():创建文件。若文件存在,则不创建,返回false
 @Test
 public void test() throws IOException {
     File file = new File("test.txt");
     if(!file.exists()){
         file.createNewFile();
         System.out.println("创建成功!");
     }else{
         boolean delete = file.delete();
         if(delete){
             System.out.println("删除成功!");
         }else{
             System.out.println("删除失败!");
         }
     }
 }
  • public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
  • public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建
 @Test
 public void test() throws IOException {
     //文件目录的创建
     File file = new File("E:\Developer\Test");
     boolean mkdir = file.mkdir();
     if (mkdir) {
         System.out.println("Test创建成功!");
         file.delete();
     }else{
         System.out.println("Test创建失败!");
     }
 ​
     File file1 = new File("E:\Developer\test");
     boolean mkdirs = file1.mkdirs();
     if(mkdirs){
         System.out.println("test创建成功!");
         file1.delete();
     }else{
         System.out.println("Test创建失败!");
     }
     file = new File("E:\Developer");
     file.delete();
 }

注意事项:如果你创建文件或者文件目录没有与盘付路径,那么,默认在项目路径下。

File类的删除功能

  • public boolean delete():删除文件或者文件夹

删除注意事项:

Java中的删除不走回收站。

要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录


IO流

Java IO原理

I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于 处理设备之间的数据传输 。如读/写文件,网络通讯等。

Java程序中,对于数据的输入/输出操作以 “流(stream)” 的方式进行。

java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过 标准的方法 输入或输出数据。

  • 输入input: 读取外部数据(磁盘、光盘等存储设备的数据到程序(内存)中。
  • 输出output: 将程序(内存)数据输出到磁盘、光盘等存储设备中。

image-20221212200848459.png

流的分类

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

image-20221214201652075.png

IO流体系

image-20221212203630841.png

文件的简单读入操作(字符流)

 @Test
 public void testFileReader(){
     FileReader fileReader = null;
     try {//异常的处理,为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理
         //1.实例化File类的对象,指明要操作的文件
         File file = new File("test.txt");//读入的文件一定要存在,否则就会报FileNotFoundException。
         //2.提供具体的流
         fileReader = new FileReader(file);
         //3.数据的读入
         //read():返回读入的一个字符,如果达到文件末尾,则返回-1
         int data;
         while((data = fileReader.read())!=-1){
             System.out.print((char)data);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         //4.流的关闭操作
         try {
             if(fileReader != null) //防止文件未被读入而导致的空指针异常
                 fileReader.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

升级Read

  //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数,如果达到文件末尾,返回-1。
 char[] cbuf = new char[5];
 int len;
 while ((len = fileReader.read(cbuf)) != -1) {
     System.out.print(cbuf);
 }

文件的简单写出操作(字符流)

 @Test
 public void testFileWriter(){
     FileWriter fileWriter = null;//输出操作,对应的File可以不存在,并不会报异常。
     try {
         //1.提供File类的对象,指明写出的文件
         File file = new File("Testing.txt");//Java忽略大小写
         //2.提供FileWriter的对象,用于数据的写出
         fileWriter = new FileWriter(file);
         // File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
         // File对应的硬盘中的文件如果存在:
         // 如果流使用的构造器是,FileWriter(file,false)/FileWriter(file):对原有文件的覆盖
         // 如果流使用的构造器是,FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
 ​
         //3.写出的操作
         fileWriter.write("There is white moonlight in my heart,\nand there are no stars in my eyes");
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         //4.流资源的关闭
         try {
             if(fileWriter!=null)
                 fileWriter.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

文件的读入与写出操作(字符流)

 @Test
 public void testFileReaderFileWriter(){
     FileReader fileReader = null;
     FileWriter fileWriter = null;
     try {
         //1.创建File类的对象,指明读入和写出的文件
         File srcFile = new File("Testing.txt");
         File destFile = new File("hello.txt");
         //2.创建输入流和输出流的对象
         fileReader = new FileReader(srcFile);
         fileWriter = new FileWriter(destFile);
         //3.数据的读入和写出操作
         char []cbuf = new char[5];
         int len;//记录每次读入到cbuf数组中的字符的个数
         while((len = fileReader.read(cbuf))!=-1){
             //每次写出len个字符
             fileWriter.write(cbuf,0,len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         //4.关闭流资源
         try {
             if(fileWriter != null)
                 fileWriter.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if (fileWriter != null)
                 fileReader.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

字节流的文件读写

 @Test
 public void testFileInputStream(){
     FileInputStream fileInputStream = null;
     FileOutputStream fileOutputStream = null;
     try {
         File srcfile = new File("1667620425430.png");
         File destfile = new File("view.png");
         fileInputStream = new FileInputStream(srcfile);
         fileOutputStream = new FileOutputStream(destfile);
         byte[] buffer = new byte[100000];
         int len;//记录每次读取的字节个数
         while ((len = fileInputStream.read(buffer))!=-1){
             fileOutputStream.write(buffer, 0, len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(fileInputStream != null)
                 fileInputStream.close();
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
         try {
             if (fileInputStream != null)
                 fileInputStream.close();
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
 }

文件的复制操作(节点流)

 public static void copyFile(String srcPath, String destPath){
     FileInputStream fileInputStream = null;
     FileOutputStream fileOutputStream = null;
     try {
         File srcfile = new File(srcPath);
         File destfile = new File(destPath);
         fileInputStream = new FileInputStream(srcfile);
         fileOutputStream = new FileOutputStream(destfile);
         byte[] buffer = new byte[1024];
         int len;
         while ((len = fileInputStream.read(buffer)) != -1) {
             fileOutputStream.write(buffer,0,len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if (fileOutputStream != null)
                 fileInputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if(fileInputStream != null)
                 fileOutputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }
 ​
 @Test
 public void testFileCopy(){
     String src = "Testing.txt";
     String dest = "test.txt";
     long start = System.currentTimeMillis();
     copyFile(src,dest);
     long end = System.currentTimeMillis();
     System.out.println("复制操作花费的时间为:"+(end-start));
 }

文本文件的复制(缓冲流)

 @Test
 public void copyBufferedReaderBufferedWriter(){
     BufferedReader bufferedReader = null;
     BufferedWriter bufferedWriter = null;
     try {
         bufferedReader = new BufferedReader(new FileReader(new File("test.txt")));
         bufferedWriter = new BufferedWriter(new FileWriter(new File("Testing.txt")));
 ​
         char[] buffer = new char[1024];
         int len;
         while ((len = bufferedReader.read(buffer))!=-1){
             bufferedWriter.write(buffer,0,len);
             //          bufferedWriter.flush();
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(bufferedWriter != null)
                 bufferedWriter.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if(bufferedReader != null)
                 bufferedReader.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

非文本文件的复制(缓冲流)

 @Test
 public void copyBufferedStream(String srcFile,String destFile){
     FileInputStream fileInputStream = null;
     FileOutputStream fileOutputStream = null;
     BufferedInputStream bufferedInputStream = null;
     BufferedOutputStream bufferedOutputStream = null;
     try {
         //1.造文件
         File srcFile = new File(srcFile);
         File destFile = new File(destFile);
         //2.造流
         //2.1造节点流
         fileInputStream = new FileInputStream(srcFile);
         fileOutputStream = new FileOutputStream(destFile);
         //2.2造缓冲流
         bufferedInputStream = new BufferedInputStream(fileInputStream);
         bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
         //3.复制的细节:读取、写入
         byte[] buffer = new byte[1024];
         int len;
         while((len = bufferedInputStream.read(buffer))!=-1){
             bufferedOutputStream.write(buffer,0,len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         //4.资源的关闭:先关闭外层的流,再关闭内层的流
         try {
             if (bufferedOutputStream != null)
                 bufferedOutputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if (bufferedInputStream != null)
                 bufferedInputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         //说明:关闭外层流的同时,内层流也会自动关闭
         try {
             if(fileOutputStream!= null)
                 fileOutputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if(fileInputStream != null)
                 fileInputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

提高速度的原因:内部提供一个缓冲区


转换流(属于字符流)

image-20221213133558803.png

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

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

代码实现

 @Test
 public void testInputStreamReaderOutputStreamWriter(){
     InputStreamReader inputStream = null;
     OutputStreamWriter outputStream = null;
     try {
         FileInputStream fileInputStream = new FileInputStream("test.txt");
         FileOutputStream fileOutputStream = new FileOutputStream("test_gdk.txt");
         inputStream = new InputStreamReader(fileInputStream,"utf-8");
         outputStream = new OutputStreamWriter(fileOutputStream,"GBK");
         char [] buffer = new char[1024];
         int len;
         while ((len = inputStream.read(buffer)) != -1) {
             outputStream.write(buffer,0,len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(inputStream != null)
                 inputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         try {
             if(outputStream != null)
                 outputStream.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

文件编码

编码表的由来

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一 一对应,形成一张表。这就是编码表。

常见的编码表

  • ASCIl: 美国标准信息交换码。

    √用一个字节的7位可以表示。

  • lSO8859-1: 拉丁码表。欧洲码表

    √用一个字节的8位表示。

  • GB2312: 中国的中文编码表。最多两个字节编码所有字符

  • GBK: 中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码

  • Unicode: 国际标准码,融合了目前人类使用的所有字符 字符码。所有的文字都用两个字节来表示。

  • UTF-8: 变长的编码方式,可用1 - 4个字节来表示一个字符。

image-20221213174843066.png


其他流

标准的输入、输出流

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

System.out:标准的输出流,默认从控制台输出

 package OtherStream;
 ​
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 ​
 ​
 public class OtherStreamTest {
 /**
  *通过键盘输入字符串,将其中的小写字母转换为大写字母,程序在输入"e"或"exit"时终止(不分大小写)
  *@Version: 1.0
  *
  */
 public static void main (String[] args) {
     test();
 }
     public static void test(){
         BufferedReader bufferedReader = null;
         try {
             InputStreamReader inputStreamReader = new InputStreamReader(System.in);
             bufferedReader = new BufferedReader(inputStreamReader);
             while(true){
                 System.out.println("请输入字符串:");
                 String data = bufferedReader.readLine();
                 if("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
                     System.out.println("程序结束");
                     break;
                 }
                 String upperCase = data.toUpperCase();
                 System.out.println(upperCase);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(bufferedReader != null)
                 bufferedReader.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }

打印流

  • 实现将 基本数据类型 的数据格式转化为 字符串 输出

  • 打印流: PrintStream 和 PrintWriter 

    • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    • PrintStream和PrintWriter的输出不会抛出IOException异常
    • PrintStream和PrintWriter有自动flush功能
    • PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。
    • System.out返回的是PrintStream的实例
 @Test
 public void testPrintStream () {
 ​
     PrintStream printStream = null;
     try {
         FileOutputStream outputStream = new FileOutputStream(new File("test.txt"));
         printStream = new PrintStream(outputStream,true);
         if(printStream!=null){
             System.setOut(printStream);
         }
         for(int i=0;i<=255;i++){
             System.out.print((char)i);
             if(i%10==0){
                 System.out.println();//每10个换行
             }
         }
 ​
     } catch (FileNotFoundException e) {
         e.printStackTrace();
     } finally {
         try {
             if(printStream!=null)
                 printStream.close();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 }

数据流

为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。

  • 数据流有两个类:(用于读取和写出基本数据类型、String类的数据)

    • DatalnputStream和DataOutputStream 
    • 分别“套接”在InputStream和OutputStream子类的流上 

DatalnputStream中的方法

  • boolean readBoolean() 

  • byte readByte() 

  • char readChar() 

  • float readFloat() 

  • double readDouble() 

  • short readShort() 

  • long readLong() 

  • int readlnt() 

  • String readUTF() 

  • void readFully(byte[ ] b) 

  • DataOutputStream中的方法

    • 将上述的方法的read改为相应的write即可。

作用:用于读取或写出基本数据类型的变量或字符串

说明: 数据流所写出到文件中的数据信息,并非是直接能看到的(会出现乱码),必须使用数据流的写入操作按顺序正确写入至内存的变量中,并进行输出操作,才能正常显示。

 @Test
 public void testDataReaderWriter(){
     /**
          *
          * 将基本数据类型和字符串写出并存储到文件中
          *
          */
 ​
     DataOutputStream out = null;
     try {
         out = new DataOutputStream(new FileOutputStream("data.txt"));
         out.writeUTF("何鑫");
         out.flush();//将内存中的数据到文件中
         out.writeInt(18);
         out.flush();//将内存中的数据到文件中
         out.writeBoolean(true);
         out.flush();//将内存中的数据到文件中
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(out != null)
                 out.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 ​
     /**
          *
          * 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。读取不同类型的数据的顺序要与当初写入文件时,保存的数据顺序一致!
          *
          */
 ​
     DataInputStream in = null;
     try {
         in = new DataInputStream(new FileInputStream("data.txt"));
         String s = in.readUTF();
         int i = in.readInt();
         boolean isMale = in.readBoolean();
         System.out.println("name:"+s+"\t"+"age:"+i+"\t"+"isMale:"+isMale);
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(in != null)
                 in.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

对象流

ObjectInputStream和OjbectOutputSteam 

  • 用于存储和读取基本数据类型 数据或对象 的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  • 序列化:用ObjectOutputStream类保存 基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取 基本类型数据或对象的机制

ObjectOutputStream和ObjectInputStream不能序列化 static 和 transient 修饰的成员变量

对象的序列化

  • 对象序列化机制 允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为 字节数据 ,使其在保存和传输时可被还原

  • 序列化是RMI (Remote Method lnvoke --远程方法调用)过程的参数和返回值都必须实现的机制,而RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础

  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常

    • Serializable 
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:

    • private static final long serialVersionUlD; 
    • serialVersionUID用来表明类的不同放本同的兼谷性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
  • 如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUJID可能发生变化。 故建议,显式声明。

  • 简单来说,Java的序列化机制是迎过在运仃的判断尖的Seelo个一证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

 package ObjectInputOutputStream;
 ​
 import org.junit.Test;
 ​
 import java.io.*;
 import java.util.Objects;
 ​
 public class ObjectInputOutputStreamTest {
 /**
  * 序列化过程:将内存中的java对象保存在磁盘中或者通过网络传输出去,使用ObjectOutputStream实现
  *
  */
     @Test
     public void testObjectOutputInputStream(){
         ObjectOutputStream objectOutputStream = null;
         try {
             objectOutputStream = new ObjectOutputStream(new FileOutputStream("object.data"));
             objectOutputStream.writeObject(new String("Love Is Gone,but I love you"));
             objectOutputStream.flush();//刷新操作
             objectOutputStream.writeObject(new Person("WLM",18));
             objectOutputStream.flush();//刷新操作
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(objectOutputStream != null)
                 objectOutputStream.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
         
         /**
          *
          * 反序列化:将磁盘文件中的对象还原为内存中的java对象,使用ObjectInputStream来实现
          *
          */
         ObjectInputStream ois = null;
         try {
             ois = new ObjectInputStream(new FileInputStream("object.data"));
             Object object = ois.readObject();
             System.out.println(object);
             Person person = (Person)ois.readObject();
             System.out.println(person);
         } catch (IOException e) {
             e.printStackTrace();
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(ois != null)
                 ois.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }
 class Person implements Serializable{//Serializable为标识接口
     public static final long serialVersionUID = 4098451642L;
     private String name;
     private int age;
     
     public Person (String name, int age) {
         this.name = name;
         this.age = age;
     }
     
     public Person () {
     }
     
     public String getName() {
         return name;
     }
     
     public void setName(String name) {
         this.name = name;
     }
     
     public int getAge() {
         return age;
     }
     
     public void setAge(int age) {
         this.age = age;
     }
     
     @Override
     public String toString() {
         return "Person{" +
                 "name='" + name + ''' +
                 ", age=" + age +
                 '}';
     }
     
     @Override
     public boolean equals(Object o) {
         System.out.println("Person equals()......");
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         Person person = (Person) o;
         return age == person.age && Objects.equals(name, person.name);
     }
     
     @Override
     public int hashCode() {
         return Objects.hash(name, age);
     }
 }

随机存取文件流(RandomAccessFile类)

RandomAccessFile声明在java.io包下,但直接继承于java.lang.Object类。并且它实现了Datalnput、DataOutput这两个接口,也就意味着这个类既可以读也可以写。

RandomAccessFile类支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读、写文件

  • 支持只访问文件的部分内容
  • 可以向已存在的文件后追加内容

RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置。

RandomAccessFile类对象可以自由移动记录指针:

  • long getFilePointer(): 获取文件记录指针的当前位置
  • void seek(long pos): 将文件记录指针定位到pos位置

构造器

  • public RandomAccessFile(File file, String mode)
  • public RandomAccessFile(String name, String mode)

创建RandomAccessFile类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:

  • r:以只读方式打开 
  • rw:打开以便读取和写入 
  • rwd:打开以便读取和写入;同步文件内容的更新 
  • rws:打开以便读取和写入;同步文件内容和元数据的更新 

如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。

当RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原有文件进行覆盖(默认情况:从头覆盖)

 package RandomAccessFile;
 ​
 import org.junit.Test;
 ​
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 /**
  * RandomAccessFile的使用,直接继承于java.lang.Object类,实现了DataInput和DataOutput接口。既可以作为一个输入流,又可以作为一个输出流
  */
 public class RandomAccessFileTest {
     
     @Test
     public void testRandomAccessFile(){
         RandomAccessFile testFile = null;
         RandomAccessFile testFileWrite = null;
         try {
             testFile = new RandomAccessFile(new File("1667620425430.png"),"r");
             testFileWrite = new RandomAccessFile(new File("1667620425430.png"),"rw");
             byte[] buffer = new byte[1024];
             int len;
             while((len = testFile.read(buffer))!=-1){
                 testFileWrite.write(buffer,0,len);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(testFile != null)
                 testFile.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
             try {
                 if(testFileWrite != null)
                 testFileWrite.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
     @Test
     public void test(){
         RandomAccessFile testFile = null;
         try {
             testFile = new RandomAccessFile("test.txt","rw");
             
 //          testFile.seek(0);//将指针调到角标为0的位置
             testFile.write("Love Is Gone,but I like you!".getBytes());
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(testFile != null)
                 testFile.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
     @Test
     public void testInsertFile(){
         /**
          *
          * 使用RandomAccessFile实现数据的插入效果
          *
          */
         RandomAccessFile testFile = null;
         try {
             testFile = new RandomAccessFile("test.txt","rw");
             testFile.seek(5);
             //保存指针5后面的所有数据到StringBuilder中
             StringBuilder builder = new StringBuilder((int) new File("test.txt").length());
             byte[] buffer = new byte[1024];
             int len;
             while((len = testFile.read(buffer))!=-1){
                 builder.append(new String(buffer,0,len));
             }
             //调回指针,写入“WLM”
             testFile.seek(28);
             testFile.write("WLM ".getBytes());
             //将StringBuilder中的数据写入文件中
             testFile.write(builder.toString().getBytes());
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if(testFile != null)
                 testFile.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }