09、IO流—File类与IO流

举报
长路 发表于 2022/11/22 21:40:48 2022/11/22
【摘要】 文章目录一、File类基本认识实用方法获取功能重命名功能(包含剪切)判断功能创建、删除文件实际小案例二、IO流1、认识IO流2、IO流基类介绍字节流基类介绍字符流基类介绍三、节点流与处理流1、节点流介绍说明实际小案例总结2、缓冲流缓冲流介绍使用缓冲流3、转换流认识转换流实际小案例4、标准输入、输出流实际小案例5、打印流(PrintStream与PrintWriter)基本介绍小案例6、数据流7、对

@[toc]


一、File类

基本认识

File类:java.io.File,也是io流中的一部分,File类能够新建、删除、重命名文件与目录,但是不能访问文件本身,若是需要访问文件内容需要使用到输入输出流。

创建对象:通过构造器获取,介绍三个

  • File(String pathname):可填入绝对路径与相当路径,相对路径就是在本项目目录下。
  • File(String parent, String child):parent是父路径、child是子文件路径。
  • File(File parent, String child):父File对象与子文件路径。
@Test
public void test01(){
    //第一个构造器:File(String pathname)
    File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner");
    //第二个构造器:File(String parent, String child)
    File file1 = new File("C:\\Users\\93997\\Desktop\\fileexer","javahoner");
    //第三个构造器:File(File parent, String child)
    File file2 = new File(new File("C:\\Users\\93997\\Desktop\\fileexer"),"javahoner");
    System.out.println(file);
    System.out.println(file1);
    System.out.println(file2);
}
  • 第三个构造器第一个参数File parent:当其不为空时,实际上也是取该对象属性string path
  • println()输出路径:File类其toString()方法就是打印了String path参数。

image-20210131090330410


路径分隔符:就是上面构造器中的那个\\,不同操作系统使用的也不同

  • windows与Dos系统默认:\

  • Unix与URL使用:/

  • 因为Java跨平台,所以对于路径分隔符需慎用,Java也给出通用的:File.separator,我们在创建实例时可以使用这个参数来代替我们手写/或\。(这个属性是通过调用方法来获取到本地文件系统的默认分隔符)

    • //相当于C:\Users\93997\Desktop\fileexer\javahoner 
      File file = new File("C:\\Users\\93997\\Desktop\\fileexer\"+File.separator+\"javahoner");
      


实用方法

获取功能

获取文件对象的相关信息

String getAbsolutePath():获取文件的绝对路径

String getPath:获取路径(也是绝对路径)

String getName:获取路径下的最后一个文件名

String getParent():获取上层文件目录路径,若无,返回null

Long length():获取文件长度(字节数)

Long lastModified():获取最后一次修改文件的时间戳(毫秒值)

String[] list():获取指定目录下所有文件的名称

File[] listFiles():获取指定目录下所有文件,以对象形式返回

  • 若调用方法的抽象路径名不表示目录,返回值为null

实际使用:

@Test
public void test01(){
    //第一个构造器:File(String pathname)
    File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner");
    System.out.println(file.getAbsolutePath());//C:\Users\93997\Desktop\fileexer\javahoner
    System.out.println(file.getPath());//C:\Users\93997\Desktop\fileexer\javahoner
    System.out.println(file.getName());//javahoner
    System.out.println(file.getParent());//C:\Users\93997\Desktop\fileexer
    System.out.println(file.length());//0
    System.out.println(file.lastModified());//1612056310161
    for (String childFile : file.list()) {
    System.out.print(childFile+" ");
    }//文件1 文件2
    System.out.println();
    for (File listFile : file.listFiles()) {
    System.out.println(listFile);
    }
    //C:\Users\93997\Desktop\fileexer\javahoner\文件1
    //C:\Users\93997\Desktop\fileexer\javahoner\文件2
}


重命名功能(包含剪切)

方法介绍

public boolean renameTo(File dest):剪切文件改名到指定路径(也可以实现重命名效果)

  • 调用该方法的File,其指定路径下是否有文件都没事;注意dest的文件路径应该是不存在的
  • 返回值:dest的路径下应该不存在文件,若存在返回false。

实际应用:重命名与剪切功能

场景1:想将javahoner目录下的文件名为文件修改为长路锅锅

File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件");
boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\重命名的文件"));
System.out.println(b);
  • 实际上就是将C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件先剪切到C:\\Users\\93997\\Desktop\\fileexer\\javahoner路径下再改名为不存在的文件名为重命名文件。实现了修改名称效果

场景2:想将javahoner目录下的文件长路锅锅移动到1(2)文件中

File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\长路锅锅");
boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\1(2)\\长路锅锅"));
System.out.println(b);
  • 其实原理操作与上面一样的,需要注意1(2)要不存在长路锅锅文件。

总结:秒呀这个方法,看了下源码最终定位到一个WinNTFileSystemprivate native boolean rename0(File f1, File f2);根据native关键字,说明这个方法是一个原生函数,是使用c/c++来实现并编译成DLL文件由java去调用。



判断功能

这里仅仅介绍下方法使用

public boolean isDirectory:判断是否是文件目录

public boolean isFile():判断是否是文件

public boolean exists():判断是否存在

public boolean canRead():判断是否可读

public boolean canWrite():判断是否可写

public boolean is Hidden():判断是否隐藏



创建、删除文件

创建功能

public boolean createNewFile():文件存在不创建返回false。

public boolean mkdir():文件目录存在不创建,若此文件目录的上级目录不存在也不创建。

public boolean mkdirs():不仅仅是此目录文件,若是此目录的上层文件目录不存在一并创建。


删除功能

public boolean delete():删除此抽象路径名表示的文件或目录,若是此路径名表示目录,则目录必须为空才能删除。



实际小案例

案例:删除指定路径下所有文件

public class Main {

    //生成num个空白字符串,若是num非0最后添加-
    public static String blankStr(int num){
        StringBuilder str = new StringBuilder();
        for (int i=0;i<num;i++){
            str.append(" ");
        }
        if(num != 0){
            str.append("-");
        }
        return str.toString();
    }



    //删除路径下内容(包含层级关系)
    public static void deleteAllFile(File file,int tier) throws RuntimeException{
        //如果该路径是一个文件直接删除
        if(file.isFile()){
            System.out.println(file.delete()?blankStr(tier)+file.getName()+"已删除":blankStr(tier)+file.getName()+"删除失败");
            return;
        }
        //该路径是目录
        File[] files = file.listFiles();
        //空目录直接删除
        if(files == null || files.length == 0){
            System.out.println(file.delete()?blankStr(tier)+"成功删除"+file.getName():blankStr(tier)+file.getName()+"空目录删除失败");
            return;
        }
        //目录不为空,遍历当前文件
        for (File dict : files) {
            //目录:删除目录中内容+删除自己本身
            if(dict.isDirectory()){
                //获取目录下的所有文件
                File[] files2 = dict.listFiles();
                //如果目录为空直接删除
                if(files2 == null || files2.length == 0){
                    System.out.println(dict.delete()?blankStr(tier)+"成功删除"+dict.getName():blankStr(tier)+dict.getName()+"空目录删除失败");
                }else{
                    //非空目录情况
                    deleteAllFile(dict,tier+1);
                    System.out.println(dict.delete()?blankStr(tier)+"成功删除"+dict.getName():blankStr(tier)+dict.getName()+"空目录删除失败");
                }
            }else{
                //非目录:无提示直接调用
                deleteAllFile(dict,tier+1);
            }
        }
    }

    //测试
    @Test
    public void test01(){
        File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件1");
        try {
            deleteAllFile(file,0);
        } catch (RuntimeException e) {
            System.out.println("删除非空文件时出现异常!!!");
        }
        System.out.println("该文件路径下内容已被删除");
    }
}
  • 调用deleteAllFile(File file,int tier)即可:参数1就是删除指定文件路径,参数2表示层级关系(设置为0最高级)


二、IO流

1、认识IO流

Google将I/O誉为"开放中创新",input/output:二进制1,0

IO原理:是Input/Output的缩写,I/O技术是很实用的技术,用来处理设备之间的数据传输。例如读/写文件,网络通讯等。

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

在Java程序中,对于数据的输入/输出操作以"流(stream)"的方式进行。java.io包下提供了各种"流"类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。


流的分类

根据数据单位不同分为两类:字节流与字符流

  • 字节流:最基本单位是字节byte的流,通常用来传输视频、图片(非文本的数据)
  • 字符流:最基本单位是字符char的流,通常用来文本输入输出。
    • 说明:中文字体在不同编码下占不同字节数,在UTF-8中文所占字节不确定可能是2,3,4个字节,所以建议进行文本输入输出选择字符流。
  • 区别(重要):字节流按字节读数据,不需要编码解码;字符流按字符读数据,一次读两个字节,并返回这两个字节所对应的int型数值)(编码)。

数据的流向不同分为:输入流、输出流

角色不同分为:节点流、处理流

Java的IO流全部都是由下面这4个抽象基类派生:

(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

I/O流体系图

image-20210131142003105


关系图

image-20210131142034039



2、IO流基类介绍

字节流基类介绍

对于字节流的输入输出流的基类,首先介绍一下InputSream以及OutputSream

  • InputSream为字节输入流的抽象基类,其基类定义了以下几个通用方法,这里列举几个
    • int read():从流中读取1个字节并返回,如果达到文件末尾返回-1;
    • read(byte b[]):从流中读取b的长度个字节的数据存储到b中,返回的是读取的字节个数,若是返回-1表示到了结尾,没有数据。
    • int read(byte b[], int off, int len):从流中的off位置开始读取len个字节的数据到b中,返回结果是实际读取的字节个数,若是返回-1表示没有数据。
    • void close():关闭输入流
  • OutputSream为字节输出流的抽象基类,这里列举几个该基类常用方法
    • void write(int b):将1个字节写入到输出流中
    • void write(byte b[]):从b的off位置开始,获取len个字节数据,写到输出流中
    • void flush():刷新此输出流并强制任何缓冲的输出字节被写出。(为之后缓冲流提供抽象方法)
    • void close():关闭输出流

输出入流关字节流的含节点流以及各种处理流它们的基类就是这两个。



字符流基类介绍

对于字符流的输入输出流的基类,介绍一下ReaderWriter

  • Reader:是字符输入流的抽象基类 ,定义了几个函数如下
    • int read():读取单个字符,返回结果是一个int,若想要字符显示需要转为char;若是到达流的末尾,返回-1
    • int read(char cbuf[]):从流中读取b的长度个字符的数据存储到b中,返回的是读取的字节个数,若是返回-1表示到了结尾,没有数据。
    • void close():关闭字符输入流
  • writer:是字符输入流的抽象基类 ,定义了几个函数如下
    • void write(int b):将1个字节写入到输出流中
    • void write(byte b[]):从b的off位置开始,获取len个字节数据,写到输出流中
    • void flush():刷新此输出流并强制任何缓冲的输出字节被写出。(为之后缓冲流提供抽象方法)
    • void close():关闭输出流

与之前介绍的字节流大致相同,他们两个区别之一就是它们传递的数据单位不同一个是字节、一个是流。



三、节点流与处理流

1、节点流

介绍说明

节点流也称文件流,对应节点流包含了字节流与字符流

  • 字节流:FileInputSreamFileOutputSream
  • 字符流:FileReaderFileWriter
  • 注意点
    • ①字符流不能用来读取图片、视频等因为字符涉及到编码转换问题,在读取使用字节存储的图片时会对于某些字节转换不到造成问题;
    • ②字节流可以用来读文本文件之类,不过在读取过程中若是打印显示可能会有乱码存在,中文一般占2-4个字节,有时候读取了一半就显示就会出现问题。


实际小案例

案例1:使用字节流来复制图片

import org.junit.Test;
import java.io.*;

public class Main {
    
    @Test
    public void test02(){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //目标图片1234.jpg
            fis = new FileInputStream(new File("1234.jpg"));
            //复制地址
            fos = new FileOutputStream(new File("图片.jpg"));
            byte[] data = new byte[1024];
            int len;
            //读取字节
            while((len = fis.read(data)) != -1){
                //写入data数组中读取到的len个字节
                fos.write(data,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 输入输出流对象创建需要带有File对象或者路径,如果File对应的路径不存在,会自动新建的。
  • 说明:这个案例是将相对路径下的1234.jpg复制一份为图片.jpg。复制视频只要更改路径即可,一样的方法。

案例2:复制文本文件,并且在控制台显示

import org.junit.Test;
import java.io.*;


public class Main {

	//这里主要为了演示就不像之前那么规范,直接抛出异常
    @Test
    public void test02() throws IOException {
        FileReader fr = new FileReader("changlu.txt");;
        FileWriter fw = new FileWriter("cl.txt");
        char[] data = new char[1024];
        int len;
        while((len = fr.read(data)) != -1){
            //显示在控制台中
            System.out.print(new String(data,0,len));
            fw.write(data,0,len);
        }
        fr.close();
        fw.close();
    }
}
  • 这里是复制到指定路径下的文件目录中(覆盖操作)。若是想要追加可使用FileWriter(File file, boolean append)这种构造器方式,第二个参数填true表示数据写入从文件末尾开始。

image-20210131160131283



总结

  1. 对于文本文件(例如.txt,.java,.c,.cpp)尽量使用字符流处理,字节流也是可以的。
  2. 对于非文本文件(例如.jpg,.mp3,.mp4,.avi,.doc,.ppt,…) 之类使用字节流处理。


2、缓冲流

缓冲流介绍

首先看一下缓冲流,前两个是用于传输字节的缓冲流,后两个是传输字符的缓冲流

image-20210131160820342

看一下继承关系

传输字节的两个缓冲流都是继承于FilterInputStream:

image-20210131161006356

传输字符的两个缓冲流都是继承于Writer

image-20210131161126155

缓冲流的作用:提高文件的读写效率

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

  • 对于缓冲流对象其中会创建一个内部缓冲区数组,缺省使用8192个字节(8kb)的缓冲区,以前读取、写入数据每使用read()或write()方法就会写入到文件中,而现在执行会先写到缓冲区中,直到缓冲区写满之后才一次性写到文件里。
  • 这就有个问题:有时候明明去读取或写入了却没有读到或者文件内没有存储,那可能就是存储到缓冲区没有到文件中,可以使用flush()方法强制将缓冲区内容写入到输出流中。

使用了缓冲区与没有使用的图示

image-20210131161950695



使用缓冲流

实际上使用缓冲流很简单,直接在节点流上包一层,缓冲流也是需要进行手动关闭的,关闭的同时会将节点流也关闭。

示例:这里

//字节流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("changlu.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("test.txt")));

//字符流
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("file2.txt"));

针对于缓冲流自己实现了一个方法

  • void newLine():向文件中写入换行符

一般写入完之后我们还需要使用flush()方法来将缓存区的内容手动写入到文件中。

  • 一般只有当缓冲区存满8kb字节时才会写入,若是我们写入内容不足以8kb时就需要我们自己手动写入,养成好的习惯都要写。

测试复制66MB的视频速度

  • 字节流:932ms
  • 缓冲流:189ms

介绍图片加密的方法

//加密data数组中0-len中的字节
public static byte[] encryptChar(byte[] data,int len){
    for (int i = 0; i < len; i++) {
        data[i] ^= 5;//通过异或的方式
    }
    return data;
}

//写入操作 省略了内容
byte[] data = new byte[1024];
int len;
//读取字节
while((len = bis.read(data)) != -1){
    //写入data数组中读取到的len个字节
    bos.write(encryptChar(data,len));
}
  • 如何解密呢?重新读取再次进行异或即可实现解密的效果!!!


3、转换流

认识转换流

转换流提供了在字节流和字符流之间的转换

image-20210131172749165

  • InputStreamReader:将InputStream转换为Reader 字节转字符
  • OutputStreamWriter:将Writer转换为OutputStream 字符转字节

当字节流中的数据都是字符时,转成字符流更高效。大多通过使用转换流来处理文件乱码问题,实现编码和解码功能!

  • 编码:字符串 =》字节数组
  • 解码:字节数组 =》字符串

简单举个例子:将changlu.txt(UTF-8编码)先通过InputStreamReader转为字符,再通过使用OutputStreamWriter指定另一个编码转为长changlu.txt。(gbk编码)

image-20210131173647862

转换流的编码应用

  • 可以将字符按指定编码格式存储
  • 可以对文本数据按指定编码格式来解读
  • 指定编码表的动作由构造器完成


实际小案例

使用转换流将一个UTF-8编码文件转为GBK编码的文件

public class Main {

    public static void main(String[] args) throws IOException {
        //转换流 将一个UTF-8编码的转为GBK编码的
        InputStreamReader isr = new InputStreamReader(new FileInputStream("changlu.txt"), StandardCharsets.UTF_8);
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("changluya.txt"), "GBK");
        //使用缓冲流加速
        BufferedReader br = new BufferedReader(isr);
        BufferedWriter bw = new BufferedWriter(osw);
        char[] data = new char[1024];
        int len;
        while((len = br.read(data)) != -1){
            bw.write(data,0,len);
        }
        br.close();
        bw.close();
    }

}


4、标准输入、输出流

系统标准的输入和输出设备分别为:System.in与System.out。默认输入设备:键盘;默认输出设备:显示器

  • System.in:实际类型为InputStream
  • System.out:实际类型为PrintStream,其次是OutputStream的子类

我们可以更改System的输入输出设备通过System的的setInsetOut方法。



实际小案例

传统我们通过使用Scanner来进行数据的输入获取,在这里不允许使用Scanner,要求从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续 进行输入操作,直至当输入“e”或者“exit”时,退出程序。

public static void main(String[] args) throws IOException {
    //System.in是InputStream实例(字节流),这里包一层转换流转换为字符流
    InputStreamReader is = new InputStreamReader(System.in);
    //包装上一层缓冲流
    BufferedReader br = new BufferedReader(is);
    String str;
    //键盘中每读取一行数据进行循环
    while((str = br.readLine()) != null){
        if("e".equals(str) || "exit".equals(str)){
            System.out.println("安全退出");
            break;
        }
        str = str.toUpperCase();
        //System.out => PrintStream
        System.out.println(str);
        System.out.println("继续输入信息");
    }
    br.close();
}

image-20210131190937116



5、打印流(PrintStream与PrintWriter)

基本介绍

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

image-20210131193339183

  • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出。
  • 这两个打印流都不会抛出IOException异常(受检)。
  • 都有自动flush()功能。
  • PrintStream打印的所有字符都使用平台的默认字符编码转换为字节,在需要写入字符而不是写入字节的情况下应该使用PrintWriter

单个介绍

PrintStream:在实现OutputStream接口上又实现了打印各种数据的print方法,通常使用系统默认的System.out调用方法输出

  • image-20210131191424786 属于字节流
  • 最终输出的总是byte数据

PrintWriter:扩展了Write接口,也实现了许多print打印输出方法

  • image-20210131192010729 属于字符流

  • 最终输出的是char字符

  • //使用方式:配合StringWriter获取数据并打印到控制台
    public static void main(String[] args) throws IOException {
        //内部定义了一个StringBuffer存储数据
        StringWriter str = new StringWriter();
        try (PrintWriter pw = new PrintWriter(str)){
            //将指定内容写入到str中,实际上还是调用了write方法
            pw.println("hello");
            pw.println("changlu");
            pw.println(2020);
        }
        //将StringBuffer对象打印
        System.out.println(str.toString());
    }
    


小案例

案例描述:将原本输出到控制台的内容输入到文件中

思路:更改System中的输出设备(显示屏 =》文件)

public static void main(String[] args) throws IOException {
    //try(声明1;声明2;){ ... }  这种方式会自动执行close()方法
    try(//①创建文件字节流
        FileOutputStream fos = new FileOutputStream("changlu.txt");
        //②PrintStream处理流包裹节点流
        PrintStream ps = new PrintStream(fos);){
        //更改System的输出设备为文件流
        System.setOut(ps);
        //输出到文件中
        System.out.println("长路&林儿");
    }
}

image-20210131195730490

执行结果:成功创建changlu.txt,并输入到文件中。



6、数据流

image-20210202201203423

  • 只有两个流都是字节流,分别"套接"在InputStreamOutputStream子类的流上。

目的:为了方便操作Java的基本数据类型与String类型,可以使用数据流。

这里列举一下DataInputStream的几个方法如下:byte readByte()char readChar()float readFloat()long readLong()int readInt()String readUTF()

  • 例如:int readInt()一次性读出四个字节并将其转为int值读出

OutputStream几个类似read换write即可。

  • 例如:void writeInt(int v)一次写入四个字节并将其转为字节写出

直接上小案例:输出流与输入流配合使用(增添了一个缓冲流来提升速度)

public class Main {

    //使用数据流的输出流存储不同类属数据
    @Test
    public void test01(){
        try(BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream("changlu.data"));
            DataOutputStream dos = new DataOutputStream(bis);){
            dos.writeUTF("长路");
            dos.writeInt(666);
            dos.writeBoolean(false);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //使用数据流的输入流来获取指定顺序的数据类型
    @Test
    public void test02(){
        try(BufferedInputStream bos = new BufferedInputStream(new FileInputStream("changlu.data"));
            DataInputStream dis = new DataInputStream(bos);) {
            String name = dis.readUTF();
            int num = dis.readInt();
            boolean bol = dis.readBoolean();
            System.out.println(name+"\n"+num+"\n"+bol);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


7、对象流(序列化)

详细对象流以及序列化见:IO流—对象序列化



8、随机存取文件流

认识RandomAccessFile

接下来要介绍的随机存取文件流只有一个类RandomAccessFile,它实现了DataOutputDataInput接口,直接继承于Object,说明其实现了读取与写入的功能。

RandomAccessFile类功能描述:

  1. 支持随机访问的方式,程序可以直接跳到文件的任意位置来读、写文件。
  2. 支持只访问文件的部分内容。
  3. 可以向已存在的文件后追加内容。
  4. 包含一个记录指针,用来标示当前读写处的位置。
  5. 可以自由获取并移动指针的位置,例如:long getFilePointer()获取指针的位置,void seek(long pos)将指针定位到pos位置。

构造器介绍RandomAccessFile(String name, String mode)RandomAccessFile(File file, String mode)

  • 第一个参数:File对象,其中name实际上也是创建的File实例;
  • 第二个参数:mode参数指定的是该类的访问模式,访问模式如下:
    • r:只读方式打开。
    • rw:打开以便读取和写入。
    • rwd:打开以便读取和写入;同步文件内容的更新。
    • rws:打开以便读取与写入;同步文件内容和元数据的更新。

注意点:若模式为r(只读),不能够创建文件,只能读取存在的文件,若是不存在就会出现异常;rw模式是若是不存在就会去创建文件。

  • 针对于JDK1.6上面的每次write数据,rw模式,数据不会立即写入到硬盘中,一旦写入过程中有异常数据全部丢失;rwd模式数据会被立即写入硬盘。一旦写数据发生异常,rwd模式中会将已被写入的数据保存到硬盘中。


小案例

案例1:复制图片

@Test
public void test01(){
    //创建两个随机存取流的实例对象,分为来进行读或写的操作
    try(RandomAccessFile rafRead = new RandomAccessFile("1234.jpg", "r");
        RandomAccessFile rafWrite = new RandomAccessFile("changlu.jpg", "rw");){
        byte[] data = new byte[1024];
        int len;
        while((len = rafRead.read(data))!=-1){
            rafWrite.write(data,0,len);
        }
    }catch (IOException e) {
        e.printStackTrace();
    }

}
  • 定义两个实例来进行读与写的操作,该类读取的也是字节,所以大致与之前使用节点流差不多。

案例2:复制一个文件中的内容到另一个文件追加内容

image-20210202221134698

    @Test
    public void test01(){
        //创建两个随机存取流的实例对象,分为来进行读或写的操作
        try(RandomAccessFile rafRead = new RandomAccessFile("changlu.txt", "r");
            RandomAccessFile rafWrite = new RandomAccessFile("changlu222.txt", "rw");){
            //获取其中的字节
            int fileLength = (int) rafRead.length();
            byte[] data = new byte[fileLength];
            for(int i=0;i<data.length;i++){
                data[i] = rafRead.readByte();
            }
            //复制内容到其他文件中
            rafWrite.seek(2);//空两格
            for(int i = 0 ;i<data.length;i++){
                rafWrite.writeByte(data[i]);
            }
            //新增指定内容
//            byte[] bytes = "想对林儿说我想你了".getBytes("utf-8");
//            for(int i=0;i<bytes.length;i++){
//                rafWrite.writeByte(bytes[i]);
//            }
            rafWrite.writeUTF("想对林儿说我想你了");

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

    }

image-20210202221208188 出现乱码,比较迷糊搞不清



NIO扩展

java.nio这个类带来了重要的效能提升并可以充分利用执行程序的机器上的原始容量。

java1.4版新增的输入输出java.nio这个类带来了重要的效能提升并可以充分利用执行程序的机器上的原始容量。

包含一项关键能力是可以直接控制buffer以及nonblocking的输入域输出,它能让你的输入/输出程序代码在没有东西可读取或写入

时不用等在那里。

对于nio可能会引发效能损失,非nio的输入/输出适合九成以上的应用,依旧可以使用FileInputStream并通过getChannel()方法来开始使用nio。

NIO.2中Path、Paths、Files类的使用见语雀-NIO部分



参考资料

[1]. Java中Native关键字的作用

[2]. Java一个汉字占几个字节(详解与原理) 特别详细

[3]. 为什么用字符流复制的图片打不开,而用字节流复制的却可以打开?

[4]. 对比字节流和字符流,回答为什么FileReader不能用来拷贝图片

[5]. Java I/O流之随机流详解,包含随机流读写数据时编码格式问题!

[6]. 尚硅谷Java视频-IO流(宋红康)


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。