JAVA中的File类,文件流,字节流和字符流超级详解(1.8万字干货 )

1.File类

在Java中,File 类是 java.io 包中的一个重要类,它提供了与文件或目录路径名相关的一系列操作。File 类可以用来创建、删除、重命名文件和目录,也可以用来获取文件或目录的属性,比如大小、最后修改时间等。

File类的常用方法方法

方法名称功能描述
getName()获取文件名字
getParent()获取文件的父路径字符串
getPath()获取文件的相对路径的字符串
getAbsolutePath()获取文件的绝对路径字符串
exists()判断文件或文件夹是否存在
canRead()判断文件是否可读
isFile()判断文件是否是一个正常的文件,而不是目录
canWrite()判断文件是否可以被写入
idDirectory()判断是不是文件夹类型

 2.创建File对象的几种方式

(1)File(String pathname):

该构造方法通过指定的文件路径字符串创建一个新File实例对象

语法格式如下:

pathname:文件路径字符串,包括文件名称,就是将一个代表路径的字符串转换为抽象的路径。 

绝对路径:一个完整的路径,以盘符为开头

相对路径:一个简化的路径,不以盘符为开头

  1. 路径是不区分大小写的
  2. 路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

import java.io.File;
public class MyFile {
	public static void main(String[] args) {
		File file1 = new File("a.txt");            // 相对路径  
		File file2 = new File("E:\\image\\1.jpg"); // 绝对路径
		File file3 = new File("E:\\image");
 
		// getPath():获取构造方法的参数
		System.out.println(file1.getPath());
		System.out.println(file2.getPath());
		System.out.println(file3.getPath());
	}

运行结果:

a.txt
E:\image\1.jpg
E:\image

 (2)File(String path,String filename):

该构造方法根据指定的父路径字符串和子路径字符串(包括文件名称)创建File类的实例对象

  • path:父路径字符串
  • filename:子路径字符串,不能为空
public class MyFile {
	public static void main(String[] args) {
		String parentDir = "E:\\image";             // 父目录
		String childFile = "1.jpg";                 // 子文件
		File file = new File(parentDir, childFile); // 根据父目录和子文件创建File对象
 
		String childDir = "aaa";                    // 子目录
		File dir = new File(parentDir, childDir);   // 根据父目录和子目录创建File对象
		System.out.println(file.getName());         // getName()获取文件名
		System.out.println(dir.getName());          // getName()获取目录名
	}
}

 运行结果:

1.jpg
aaa

(3)mkdir()和mkdirs()以及creatNewFile

1、mkdir()
  • mkdir()方法用于创建一个新目录
  • 如果指定的目录已经存在,并且是一个目录,则此方法将返回false
  • 如果父目录不存在,将会抛出java.io.IOException异常
2、mkdirs()
  • mkdirs()方法用于创建一个新的目录,并创建所有必须的父目录
  • 如果目录已经存在,midirs()将会返回true,而不会抛出异常。
  • 与mkdir()不同,mkdirs()会递归创建所有不存在的父目录,直到村建了指定的目录

 虽然两个方法类似,但是mkdir()只能创建一级目录,而mkdirs()可以创建多级目录,而且mkdir()能干的mkdirs()也能干,所以开发中都是用mkdirs()。

3、createNewFile()
  • 文件不存在,创建一个新的空文件并返回,文件存在,不创建文件返回false
4、delete() 
  • delete方法,如果此File表示目录,则目录必须为空才能被删除
  • 如果文件不存在,则返回false
public class FileGet{
    public static void main(String[] args) throws IOException {
        File f =new File("aaa.txt");
        System.out.println("是否存在:"+f.exists());//false
        System.out.println("是否创建:"+f.createNewFile());//true
        System.out.println("是否创建:"+f.createNewFile());//false
        System.out.println("是否存在:"+f.exists());//true

        //目录的创建
        File f2 =new File("newDir");
        System.out.println("是否创建:"+f2.mkdir());//false
        System.out.println("是否存在:"+f2.mkdir());//true
        System.out.println("是否存在:"+f2.exists());//true
        
        //创建多级项目
        File f3 =new File("newDira\\newDirb");
        System.out.println(f3.mkdir());//false
        File f4 =new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());//true

               
        //文件的删除
        System.out.println(f.delete());//true
        
        //目录的删除
        System.out.println(f2.delete());//true
        System.out.println(f4.delete());//false

          
      }
}

(4)目录的遍历

  •  public String[] list():返回一个String数组,表示该file目录中所有子文件或者目录
  •  public File[] listFiles():返回一个file数组,表示该file目录中所有子文件或者目录
  • listFiles在获取指定目录下的文件或者文件夹时必须满足下面两个条件
  1. 指定的必须是目录。否则容易引发返回数组为null,出现NullPointerException异常
  2. 指定的目录必须存在
public class FileGet{
    public static void main(String[] args) throws IOException {
        File f =new File("aaa.txt");

        //获取当前目录下的文件以及文件夹名称
        String[] names =f.list();
        for (String name:names){
            System.out.println(name);
        }
        //获取当下目录下的文件以及文件夹对象,只要拿到了文件对象那么就可以获取更多信息
        File[] files =f.listFiles();
        for (File file:files){
            System.out.println(file);
        }
    }
}

 (5)递归目录

import java.io.File;
import java.io.IOException;

public class FileGet{
   public static void main(String[] args){
       File f=new File("D:\\java专属io测试");
       Recursion(f);
   }

    private static void Recursion(File f) {
       //判断传入是否是目录
       if (!f.isDirectory()){
           //不是目录直接退出
           return;
       }
       //已经确保了传入的file是目录
        File[] files =f.listFiles();
       //遍历files
        for(File file:files){
            //如果该目录下文件还是文件夹就再进行递归遍历其子目录
            if (f.isDirectory()){
                //递归
                Recursion(f);
            }else{
                //如果该目录在文件是个文件,则打印对应的名字
                System.out.println(f.getName());
            }
            
        }
    }

}

 2.IO流

2.1 IO流的基本概念

  1. Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
  2. Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
  3. 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
  4. Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。

流(Steam)是一组有序的数据序列。根据操作的类型,分为输入流和输出流两种。输入流的指向称为源,程序从指向原的输入流中读取源中的数据。当程序需要读取数据是,就会开启一个数据源的流,这个数据源可以是文件,可以是,内存或网络连接。输出流的指向是字节要去的目的地,程序通过输出流中写入数据把信息传递到目的地。当程序需要写入数据时,就会开启一个通向目的地的流

 java中的io操作主要是指使用java.io包下的内容,进行输入,输出操作。输入也就读取数据,输出也叫做写出数据。

可能有点抽象,我们这里举个例子就明白了,我们可以将IO流想象成一个水管中的管道和水龙头。

  1. 水管(io流):水管是用于连接水源和用水点的通道,就像JAVA中的IO流是连接数据源(如文件,网络等)和程序的通道。
  2. 水龙头(流的类):水龙头控制水流,可以打开或者关闭水流,调节水流的大小。在java中。各种流的类(如FileInputStream,BufferedReader等)就相当于水龙头,它们控制数据的读取和写入

2.2 io的分类

  1. 输入流(InputStream):用于数据源读取数据
  2. 输出流(OutputStream):用于向数据目标写入数据
  3. 字节流(Byte Stream):处理字节数据,如果InputStream和OutStream
  4. 字符流(Character Stream):处理字符数据,如reader和writer。字符流通常用于处理文本数据,它会自动处理字符编码的转换

 以下是分类后的超类,就是4大基类,其他都是子类。

输入流输出流
字节流字节输入流InputStream字节输出流OutputStream
字符流字符输入流Reader字节输出流Writer

各自的子类名称都是以其父类名作为子类名的后缀。

比如:

  • InputStream的子类FileInputStream。
  • Reader的子类FileReader。

 

2.3 文件的世界都为字节

一切的文件数据(文本,图片,视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,在传输的过程也是这样。所以字节流可以传输任意的文件数据。在操作流的时候,我们需要记住,无论使用什么样的流对象,底层都是二进制的数据。

2.4 InputStream类

InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

public void close()关闭输入流并释放与此流相关联的任何系统资源
public abstract int read()从输入流读取数据的下一个字节
public int read(byte[] b)该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1

 在InputStream类的方法中,read()方法被定义成抽象方法,目的是为了让继承InputStream类的子类可以针对不同的外部设备实现不同的read()方法。

  • 该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
  • 有多种构造方法可用来创建对象。
  • 可以使用字符串类型的文件名来创建一个输入流对象来读取文件:

FileOutputStream类是OutputStream的子类,它实现了文件的写入,能够以字节的形式写入文件中,该类的所有方法都是OutputStream类继承并重写。 

FileInputStream的构造方法

FileInputStream(File file):通过打开与实际文件的联建来创建一个FileInputStream,该文件由文件系统中的File对象file命名

File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);

FileInputStream(String name):通过打开与实际文件的联建来创建一个FileInputStream,该文件由文件系统中的路径名name命名

InputStream f = new FileInputStream("C:/java/hello");

 推荐使用第二种构造方法,当你创建一个流对象时。该路径在,如果没有该文件,会抛出FIleNotFoundException。

FileInputStream读取字节数据

1.读取字节:read()方法可以读取一个字节的数据,提升为int类型时,读取到文件末尾,返回-1

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
       try(FileInputStream fis =new FileInputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");){
           int content;
           while ((content=fis.read())!=-1){
               System.out.println((char)content);
           }
       }catch (IOException e){
           e.printStackTrace();
       }
       //运行结果:
        //a
        //b
        //c
        //d
        //e
        
    }
}

 2.使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组当中,返回读取到的有效字节个数,读取到末尾时,返回-1,代码演示:


public class Main {
    public static void main(String[] args) throws IOException {
        FileInputStream fis =new FileInputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        int len;
        //定义字节数组,作为装字节数据的容器
        byte[] b =new byte[2];
        //循环读取
        while ((len=fis.read(b))!=-1){
            //每次读取完之后,把数组变成字符串打印
            System.out.println(new String(b));
        }
        fis.close();

    }
}
//输出结果
//ab
//cd
//ed

有没有发现最后一个读取到的时候ed?而这个d是怎么读取到的,因为在读取字节中只有一个e,数组中,上一个数据没有被完全替换掉,所以通过len,获取有效字节。

 我们需要改进一下:

public class Main {
    public static void main(String[] args) throws IOException {
        FileInputStream fis =new FileInputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        int len;
        //定义字节数组,作为装字节数据的容器
        byte[] b =new byte[2];
        //循环读取
        while ((len=fis.read(b))!=-1){
            //每次读取完之后,把数组变成字符串打印
            System.out.println(new String(b,0,len));
        }
        fis.close();

    }
}
//输出结果
//ab
//cd
//e

 在开发中一般强烈推荐使用数组读取文件,代码如下:

public class Main {
    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = null;
       
        try{
            inputStream =new FileInputStream("a.txt");
            byte[] bys =new byte[1024];
            int len ;
            while ((len=inputStream.read(bys))!=-1){
                System.out.println(new String(bys,0,len));
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try{
                inputStream.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

字节流FileInputStream复制图片

复制图片的原理 


import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      //创建流对象
        //1、1指定数据源
        FileInputStream fis =new FileInputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        //1、2指定目的地
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\PTui\\images\\公孙离_11.jpg");

        //读写数据
        //定义数组
        byte[] b =new byte[1024];
        int len;
        while ((len=fis.read(b))!=-1){
            fos.write(b,0,len);
        }
        //关闭资源
        fos.close();
        fis.close();
    }
}

 注:复制文本,图片,MP3,视频等方式一样

2.5 OutputStream类

OutputStream抽象类是表示字节输出流的所有列的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

public void close()关闭此输出流并释放于此流相关的任何系统资源
public void flush()刷新此输出流并强制任何缓冲的输出字节被写出
public void write(byte[] b)将b.length个字节从指定的字节数组写入此输出流
public abstract void write(int b)将指定字节输出流

 FileOutputStream构造方法

public FileOutputStream(File file):根据File对象参数创建对象

File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);

pulbic FileOutputStream(String name):根据名称字符串来创建对象

OutputStream f = new FileOutputStream("C:/java/hello")

 下面是使用write的三个方法

public void write(int b)
public void write(byte[] b)
public void write(byte[] b,int off,int len)

写出字节:write(int b)方法,每次可以写出一个字节数据,代码如下:

package Text;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        fos.write(97);//写出第一个字节
        fos.write(98);
        fos.write(99);
        //关闭资源
        fos.close();
    }
    //输出结果“abc”
}
  1. 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
  2. 流操作完毕之后,必须释放系统资源,因为如果不释放资源,可能会导致资源的泄露,即应用程序占用的资源没有被正确释放,可能会导致系统资源耗尽。

写出字节数组:write(byte[] b),每次写入数组中的数据

import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        byte[] b ="你好,我叫大帅比".getBytes();
        fos.write(b);
        //关闭资源
        fos.close();
    }
}

写出指定长度字节数组:write(byte[] b,int off,int len),每次写出从off索引开始,len个字节,代码如下:

import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        byte[] b ="abcdef".getBytes();
        fos.write(b,2,2);
        //关闭资源
        fos.close();//输出结果为cd
    }
}

 FileOutputStream实现数据追加续写,换行

经过上面的代码测试,每次测序运行时, 每次创建输出流对象,都会清空目标文件中的数据。那么我们该如果保留目标文件中的数据,还能继续追加新的数据呢,并且实现换行,其实很简单,这时候就需要另外两个构造方法了。

  1. public FileOutputStream(File file,boolean appead)
  2. public FileOutputStream(String name,boolean appead)

 这两个构造方法,第二个参数中都需要传入一个boolean类型的值,true表示追加数据,false表示不追加也就是清空原有的数据。这样创建的输出流对象,就可以指定是否追加续写了,至于Windows换行则是\n\r。

实现数据追加续写代码如下:

import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt",true);
        //将字符串转换为字节
        byte[] b ="abcdef".getBytes();
        fos.write(b);
        fos.close();//输出结果为cdabcde
    }
}

在Windows系统里,换行符号是\r\n,具体代码如下:

import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt",true);
        //定义字节数组
        byte[] words={97,98,99,100,101};
        //遍历数组
        for (int i =0;i<words.length;i++){
            fos.write(words[i]);
            fos.write("\r\n".getBytes());
        }
        fos.close();


    }
}

3 FileReader类和FileWriter类

3.1FileReader类

字符流的由来:因为数据编码的不同,因而有了对象字符进行高效操作的流对象,字符流本质其实就是基于字节流读取时,去查看了指定的编码,而字节流直接读取数据会有编码的问题(读中文会乱码)

public class Main {
    public static void main(String[] args) throws IOException {
      //创建流对象
        //1、1指定数据源
        FileInputStream fis =new FileInputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        FileOutputStream fos =new FileOutputStream("C:\\Users\\86130\\helloworld\\idea202303825\\src\\Text\\fos.txt");
        //先将这几句话转化为字节
        byte[] b ="你好,我叫大帅比".getBytes();
        fos.write(b);
        //定义数组
        byte[] c =new byte[1024];
        int len;
        while ((len=fis.read(c))!=-1){
            System.out.println((char) len);
        }//运行结果是乱码的

    }
}

字节流读取中文字符时,可能不会显示完整的字符,因为一个中文字符占用了多个字节存储。

当我们使用默认的字符编码(见上例)读取一个包含中文字符的文本文件时,就会出现乱码。因为默认的字符编码通常是 ASCII 编码,它只能表示英文字符,而不能正确地解析中文字符。

那使用字节流该如何正确地读出中文呢?见下例。

try (FileInputStream inputStream = new FileInputStream("a.txt")) {
    byte[] bytes = new byte[1024];
    int len;
    while ((len = inputStream.read(bytes)) != -1) {
        System.out.print(new String(bytes, 0, len));
    }
}

因为我们拿 String 类进行了解码,查看new String(byte bytes[], int offset, int length)的源码就可以发现,该构造方法有解码功能:

public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}
public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            if (cs != null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}
static char[] decode(byte[] ba, int off, int len) {
    String csn = Charset.defaultCharset().name();
    try {
        // use charset name decode() variant which provides caching.
        return decode(csn, ba, off, len);
    } catch (UnsupportedEncodingException x) {
        warnUnsupportedCharset(csn);
    }
}

在 Java 中,常用的字符编码有 ASCII、ISO-8859-1、UTF-8、UTF-16 等。其中,ASCII 和 ISO-8859-1 只能表示部分字符,而 UTF-8 和 UTF-16 可以表示所有的 Unicode 字符,包括中文字符。

当我们使用 new String(byte bytes[], int offset, int length) 将字节流转换为字符串时,Java 会根据 UTF-8 的规则将每 3 个字节解码为一个中文字符,从而正确地解码出中文。

尽管字节流也有办法解决乱码问题,但不够直接,于是就有了字符流,专门用于处理文本文件(音频、图片、视频等为非文本文件)。

从另一角度来说:字符流 = 字节流 + 编码表

3.2 字符输入流

java.io.Reader 抽象类是字符输入流的所有类的超类(父类),可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

  • public void close():关闭此流并释放与此相关联的任何系统资源
  • public int read():从输入流读取一个字符。
  • pulbic int read(char[] cbuf):从输入流中读取一些字符,并将他们存储到字符数组cbuf中

FileReader类

java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区  

// 使用File对象创建流对象
File file = new File("a.txt");
FileReader fr = new FileReader(file);

// 使用文件名称创建流对象
FileReader fr = new FileReader("b.txt");

FileReader读取字符数据

读取字符:read()方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾时,返回-1,循环读取。      

// 使用文件名称创建流对象
FileReader fr = new FileReader("abc.txt");
// 定义变量,保存数据
int b;
// 循环读取
while ((b = fr.read())!=-1) {
    System.out.println((char)b);
}
// 关闭资源
fr.close();

读取指定长度的字符read(char[] cbuf, int off, int len),并将其存储到字符数组中。其中,cbuf 表示存储读取结果的字符数组,off 表示存储结果的起始位置,len 表示要读取的字符数。代码示例如下:

File textFile = new File("docs/约定.md");
// 给一个 FileReader 的示例
// try-with-resources FileReader
try(FileReader reader = new FileReader(textFile);) {
    // read(char[] cbuf)
    char[] buffer = new char[1024];
    int len;
    while ((len = reader.read(buffer, 0, buffer.length)) != -1) {
        System.out.print(new String(buffer, 0, len));
    }
}

在这个例子中,使用 FileReader 从文件中读取字符数据,并将其存储到一个大小为 1024 的字符数组中。每次读取 len 个字符,然后使用 String 构造方法将其转换为字符串并输出。

FileReader 实现了 AutoCloseable 接口,因此可以使用 try-with-resourcesopen in new window 语句自动关闭资源,避免了手动关闭资源的繁琐操作。

记住FileInputStream和FileReader两者的区别,虽然他们都用于文件的读取,但是他们各自处理的不同类型的数据,并且有着各自使用的场景。

  • FileInputStream主要直接处理字节数据,这对于读取图片,视频,音频等二进制文件非常有用,因为他们不需要进行字节编码的转换
  • FileReader可以自动处理字符编码,这对于读取文件非常有用,因为它可以正确的将字节转换为字符

 3.3 字符输出流(Writer)

java.io.Writer 抽象类是字符输出流的所有类的超类,将指定的字符信息写出到目的地。

下面是字符输出流的基本共性功能方法:

  • 1、write(int c) 写入单个字符。
  • 2、write(char[] cbuf) 写入字符数组。
  • 3、write(char[] cbuf, int off, int len) 写入字符数组的一部分,off为开始索引,len为字符个数。
  • 4、write(String str) 写入字符串。
  • 5、write(String str, int off, int len) 写入字符串的某一部分,off 指定要写入的子串在 str 中的起始位置,len 指定要写入的子串的长度。
  • 6、flush() 刷新该流的缓冲。
  • 7、close() 关闭此流,但要先刷新它。 
FileWriter 构造方法
  • FileWriter(File file): 创建一个新的 FileWriter,参数为要读取的File对象。
  • FileWriter(String fileName): 创建一个新的 FileWriter,参数为要读取的文件的名称
// 第一种:使用File对象创建流对象
File file = new File("a.txt");
FileWriter fw = new FileWriter(file);

// 第二种:使用文件名称创建流对象
FileWriter fw = new FileWriter("b.txt");

 FileWriter写入字符

写入字符write(int b) 方法,每次可以写出一个字符,代码示例如下:

FileWriter fw = null;
try {
    fw = new FileWriter("output.txt");
    fw.write(72); // 写入字符'H'的ASCII码
    fw.write(101); // 写入字符'e'的ASCII码
    fw.write(108); // 写入字符'l'的ASCII码
    fw.write(108); // 写入字符'l'的ASCII码
    fw.write(111); // 写入字符'o'的ASCII码
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (fw != null) {
            fw.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

在这个示例代码中,首先创建一个 FileWriter 对象 fw,并指定要写入的文件路径 "output.txt"。然后使用 fw.write() 方法将字节写入文件中,这里分别写入字符'H'、'e'、'l'、'l'、'o'的 ASCII 码。最后在 finally 块中关闭 FileWriter 对象,释放资源。

需要注意的是,使用 write(int b) 方法写入的是一个字节,而不是一个字符。如果需要写入字符,可以使用 write(char cbuf[]) 或 write(String str) 方法。 

FileWriter fw = null;
try {
    fw = new FileWriter("output.txt");
    char[] chars = {'H', 'e', 'l', 'l', 'o'};
    fw.write(chars); // 将字符数组写入文件
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (fw != null) {
            fw.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

关闭资源时,与FileOutputStream不同。如果不关闭,数据只能保存在缓冲区中,并未保存到文件中

关闭close和刷新flush

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中

但是关闭了流对象,就无法继续写数据了。如果我们既想写入数据,又想继续使用流,就需要 flush 方法了。

flush:刷新缓冲区,流对象可以继续使用

close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了 

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      //源 也就是输入流,读取a.txt文件
        FileReader fr =new FileReader("fos.txt");//必须要存在a.txt文件,否则报FileNotFoundException异常
        //目的流 也就是输出流
        FileWriter fw =new FileWriter("b.txt");//系统会自动创建b.text,因为他是输出流
        int len;
        while((len=fr.read())!=-1){
            fw.write(len);
        }
    }
}

但是b.text文件依旧为空,因为如果不关闭,数据只会被写入缓冲区,没有被保存到文件中。

在以上的代码中再添加下面三句代码,b.txt文件就能复制到源文件的数据了!

fr.close();
fw.flush();
fw.close();

flush()表示这个函数清空的意思,用于清空缓冲区的数据流,进行流的操作,数据先被读到内存中,然后用数据写到文件中,那么当你数据读完后,我们如果这个时候调用close()方法关闭读写流,这个时候可能会造成数据的丢失。


public class Main {
    public static void main(String[] args) throws IOException {
        FileWriter fw =new FileWriter("b.txt");//系统会自动创建b.text,因为他是输出流
        //写出数据,通过flush
        fw.write('刷');
        fw.flush();
        fw.write('新');
        fw.flush();
        
        //写出数据,通过close
        fw.write('关');
        fw.close();//写出第二个字符
        fw.write('闭');//这里会报错了,因为Stream已经被关闭了
        fw.close();
    }
}

即使flush方法写出了数据,操作的最后还是要调用close方法,释放资源。

FileReader和FileWriter类完成文本文件复制

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      FileReader fr =new FileReader("b.txt");//此文件不存在会抛出java.io.FileNotFoundException
        //创建输出流对象
        FileWriter fw=new FileWriter("fos.txt");
        //创建输出流做的工作
        //1,调用系统资源创建一个文件
        //2.创建输出流对象
        //3.把输出流对象指向文件
        //文本文件复制,一次读一个字符
        copyMethod1(fr,fw);
        //文本文件复制,一次读一个字符数组
        copyMethod2(fr,fw);

        fr.close();
        fw.close();
    }

    private static void copyMethod2(FileReader fr, FileWriter fw) throws IOException {
        int ch;
        while((ch=fr.read())!=-1){//读取数据
            fw.write(ch);
        }
        fw.flush();
    }

    private static void copyMethod1(FileReader fr, FileWriter fw) throws IOException {
        char chs[]=new char[1024];
        int len ;
        while((len =fr.read(chs))!=-1){
            fw.write(chs,0,len);
        }
        fw.flush();
    }
}

最后来详细讲解一下IO异常处理

 1.使用finally块

确保在finally块中关闭资源,无论发生异常与否,这都可以防止资源泄露


import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      FileReader fr =new FileReader("b.txt");//此文件不存在会抛出java.io.FileNotFoundException
        //创建输出流对象
        FileWriter fw=new FileWriter("fos.txt");
     try{
         int data;
         while ((data= fr.read())!=-1){
             fw.write(data);
         }
     }catch (FileNotFoundException e){
         System.out.println("文件未找到:"+e.getMessage());
     }catch (IOException e){
         System.out.println("读取或者写入文件时发生的错误"+e.getMessage());
     }finally {
         try {
             if (fr!=null){
                 fr.close();
             }if (fw!=null){
                 fw.close();
             }
         }catch (IOException e){
             System.out.println("关闭文件时发生的错误"+e.getMessage());
         }
     }
    }
}

 2.使用try-with-resources语句

自从java7开始,就可以使用try-with-resources语句自动管理资源,这使得代码更简洁,减少了资源泄露的风险。

try (FileWriter fw = new FileWriter("fw.txt")) {
    // 写出数据
    fw.write("二哥真的帅"); //哥敢摸si
} catch (IOException e) {
    e.printStackTrace();
}

  • 40
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值