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:文件路径字符串,包括文件名称,就是将一个代表路径的字符串转换为抽象的路径。
绝对路径:一个完整的路径,以盘符为开头
相对路径:一个简化的路径,不以盘符为开头
- 路径是不区分大小写的
- 路径中的文件名称分隔符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());//falseSystem.out.println("是否创建:"+f.createNewFile());//trueSystem.out.println("是否创建:"+f.createNewFile());//falseSystem.out.println("是否存在:"+f.exists());//true//目录的创建File f2 =new File("newDir");System.out.println("是否创建:"+f2.mkdir());//falseSystem.out.println("是否存在:"+f2.mkdir());//trueSystem.out.println("是否存在:"+f2.exists());//true//创建多级项目File f3 =new File("newDira\\newDirb");System.out.println(f3.mkdir());//falseFile f4 =new File("newDira\\newDirb");System.out.println(f4.mkdirs());//true//文件的删除System.out.println(f.delete());//true//目录的删除System.out.println(f2.delete());//trueSystem.out.println(f4.delete());//false}
}
(4)目录的遍历
- public String[] list():返回一个String数组,表示该file目录中所有子文件或者目录
- public File[] listFiles():返回一个file数组,表示该file目录中所有子文件或者目录
- listFiles在获取指定目录下的文件或者文件夹时必须满足下面两个条件
- 指定的必须是目录。否则容易引发返回数组为null,出现NullPointerException异常
- 指定的目录必须存在
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();//遍历filesfor(File file:files){//如果该目录下文件还是文件夹就再进行递归遍历其子目录if (f.isDirectory()){//递归Recursion(f);}else{//如果该目录在文件是个文件,则打印对应的名字System.out.println(f.getName());}}}}
2.IO流
2.1 IO流的基本概念
- Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
- Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
- 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
- Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
流(Steam)是一组有序的数据序列。根据操作的类型,分为输入流和输出流两种。输入流的指向称为源,程序从指向原的输入流中读取源中的数据。当程序需要读取数据是,就会开启一个数据源的流,这个数据源可以是文件,可以是,内存或网络连接。输出流的指向是字节要去的目的地,程序通过输出流中写入数据把信息传递到目的地。当程序需要写入数据时,就会开启一个通向目的地的流
java中的io操作主要是指使用java.io包下的内容,进行输入,输出操作。输入也就读取数据,输出也叫做写出数据。
可能有点抽象,我们这里举个例子就明白了,我们可以将IO流想象成一个水管中的管道和水龙头。
- 水管(io流):水管是用于连接水源和用水点的通道,就像JAVA中的IO流是连接数据源(如文件,网络等)和程序的通道。
- 水龙头(流的类):水龙头控制水流,可以打开或者关闭水流,调节水流的大小。在java中。各种流的类(如FileInputStream,BufferedReader等)就相当于水龙头,它们控制数据的读取和写入
2.2 io的分类
- 输入流(InputStream):用于数据源读取数据
- 输出流(OutputStream):用于向数据目标写入数据
- 字节流(Byte Stream):处理字节数据,如果InputStream和OutStream
- 字符流(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”
}
- 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
- 流操作完毕之后,必须释放系统资源,因为如果不释放资源,可能会导致资源的泄露,即应用程序占用的资源没有被正确释放,可能会导致系统资源耗尽。
写出字节数组: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实现数据追加续写,换行
经过上面的代码测试,每次测序运行时, 每次创建输出流对象,都会清空目标文件中的数据。那么我们该如果保留目标文件中的数据,还能继续追加新的数据呢,并且实现换行,其实很简单,这时候就需要另外两个构造方法了。
- public FileOutputStream(File file,boolean appead)
- 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;elsedefaultCharset = 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,因为他是输出流//写出数据,通过flushfw.write('刷');fw.flush();fw.write('新');fw.flush();//写出数据,通过closefw.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();
}