Java语言使用File类对文件和目录进行操作,查找文件时需要实现FilenameFilter或FileFilter接口。另外,读写文件内容可以通过FileInputStream、FileOutputStream、FileReader和FileWriter类实现。
一、File类
File 类是 Java 处理文件 I/O 的基础。这个抽象既能表示文件,也能表示目录。也就是说文件和目录是可以通过File封装成对象的。
1.1 File类构造函数
- File(String path):如果path是实际存在的路径,则该File对象表示的是目录;如果path是文件名,则该File对象表示的是文件
- File(String path, String name):path是路径名,name是文件名。
- File(File dir, String name):dir是路径对象,name是文件名
public void test(){
// File(String pathname):通过一个路径名创建File对象
File file = new File("D:\\demo\\a.txt");
System.out.println(file.toString());
// File(String parent, String child): 通过父路径和子路径名创建File对象
File file2 = new File("D:\\demo", "a.txt");
System.out.println(file2.toString());
// File(File parent, String child):通过父路径File对象和子路径名创建File对象
File file3 = new File("D:\\demo");
System.out.println(file3.toString());
File file4 = new File(file3, "a.txt");
System.out.println(file4.toString());
}
程序运行结果:
1.2 File类创建功能
方法名 | 说明 |
boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件 如果文件不存在,就创建文件并返回true 如果文件存在,就返回false |
boolean mkdir() | 创建由此抽象路径名命名的目录 如果目录不存在,就创建文件并返回true 如果目录存在,就返回false |
boolean mkdirs() | 创建由此抽象路径名命名的目录,包含任何必须但不存在的父目录 如果目录不存在,就创建文件并返回true 如果目录存在,就返回false |
/*
* 创建功能:
* A:创建文件,如果文件不存在,就创建。存在,就不创建。
* public boolean createNewFile() throws IOException
*
* B:创建文件夹,如果文件夹不存在,就创建。存在,就不创建。
* public boolean mkdir() 单级文件夹
* public boolean mkdirs() 多级文件夹
*
* 到底创建的是文件还是文件夹,取决于你使用的方法。不取决于后缀名。
*/
public void CreatFile() throws IOException{
// 创建File文件
File file = new File("d:\\a.txt"); //File:文件和目录名的抽象表现形式
// public boolean createNewFile() throws IOException
System.out.println("createNewFile:" + file.createNewFile());
// 创建目录
File file2 = new File("d:\\demo");
System.out.println("mkdir:" + file2.mkdir());
// 创建多级目录
File file3 = new File("d:\\ccc\\dddd\\eee\\fff");
System.out.println("mkdirs:" + file3.mkdirs());
}
1.3 File类判断和获取功能
方法名 | 说明 |
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
1.4 File类的删除功能
方法名 | 说明 |
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
二、流
Java将数据的输入输出(I/O)操作当作“流”来处理,“流”是一组有序的数据序列。“流”分为两种形式:输入流和输出流,从数据源中读取数据是输入流,将数据写入到目的地是输出流。
2.1 Java流的设计理念
如下图,数据输入的数据源有多种形式,如文件、网络和键盘等,键盘是默认的标准输入设备。而数据输出的目的地也有多种形式,如文件、网络和控制台,控制台是默认的标准输出设备。
所有的输入形式都抽象为输入流,所有的输出形式都抽象为输出流,它们与设备无关。
2.2 流的分类
以字节为单位的流称为字节流,以字符为单位的流称为字符流。Java SE提供4个顶级抽象类,两个字节流抽象类:InputStream和OutputStream;两个字符流抽象类:Reader和Writer。
字节输入流:
字节输入流根类是InputStream,其子类如下图所示:
InputStream主要方法如下:
- int read():读取一个字节,返回0到255范围内的int字节值。如果已经到达流末尾,而且没有可用的字节,则返回值-1。
- int read(byte b[] ):读取多个字节,数据放到字节数组b中,返回值为实际读取的字节的数
量,如果已经到达流末尾,而且没有可用的字节,则返回值-1。 - int read(byte b[ ], int off, int len):最多读取len个字节,数据放到以下标off开始字节数组b中,将读取的第一个字节存储在元素b[off]中,下一个存储在b[off+1]中,依次类推。返回值为实际读取的字节的数量,如果已经到达流末尾,而且没有可用的字节,则返回值-1。
- void close():流操作完毕后必须关闭。
字节输入流FileInputStream类使用示例:
/*
* 字节输入流读数据步骤:
* A:创建字节输入流对象
* B:调用方法,读取数据
* C:释放资源
*
* 字节输入流读取数据有两种方式:
* A:一次读取一个字节
* B:一次读取一个字节数组
*
* 一次读取一个字节:public int read()
*/
public class InputStreamTest {
public static void main(String[] args) {
InputStreamTest inStream = new InputStreamTest();
try {
inStream.CopyFileTest();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
/**
* 一次读取一个字节,循环读取。当读取到文件末尾时,返回值为-1
* @throws IOException
*/
public void InputTest() throws IOException{
File file = new File("F:\\a.txt");
// 创建字节输入流对象
// FileInputStream fis = new FileInputStream("fis.txt");
FileInputStream fis = new FileInputStream(file);
int by = 0;
//fis.read() 从输入流中读取一个字节返回int型变量,若到达文件末尾,则返回-1
//读取的是字节,为何返回int型数据:
/* 1、方法解释中的-1相当于是数据字典告诉调用者文件已到底,可以结束读取了,这里的-1是Int型
* 2、那么当文件未到底时,我们读取的是字节,若返回byte类型,那么势必造成同一方法返回类型不同的情况,这是不允许的
* 3、我们读取的字节实际是由8位二进制组成,二进制文件不利于直观查看,可以转成常用的十进制进行展示,因此需要把读取的字节从二进制转成十进制整数,故返回int型
* 4、 因此结合以上3点,保证返回类型一致以及直观查看的情况,因此该方法虽然读取的是字节但返回int型
*
* */
while ((by = fis.read()) != -1) { //从输入流中读取一个字节返回int型变量,若到达文件末尾,则返回-1
System.out.print((char) by);
}
fis.close();
}
/*
* 为了提高读取数据的效率,就有了第二种方案。
* 一次读取一个字节数组:public int read(byte[] b):返回的是实际的读取长度,把数据读取到字节数组中
*/
public void InputTest2() throws IOException{
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("C:\\Users\\lenovo\\Pictures\\变形记.txt");
byte[] bys = new byte[1024 * 1024]; // 这里的数据一般是1024或者其整数倍
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.println(new String(bys, 0, len));
System.out.println();
}
fis.close();
}
public void CopyFileTest() throws IOException{
File file = new File("C:\\Users\\lenovo\\Pictures\\变形记.txt");
File file2 = new File("F:\\b.txt");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
//读取数据的存放地址
byte by[] = new byte[1024];
int len=0;
while((len = fis.read(by))!=-1){
System.out.println(new String(by, 0, len));
//写数据
fos.write(by,0,len);
}
//释放资源
fos.close();
fis.close();
}
}
字节输出流
字节输出流根类是OutputStream,其子类如下图所示:
OutputStream主要方法如下:
- void write(int b):将b写入到输出流,b是int类型占有32位,写入过程是写入b 的8个低位,b的24个高位将被忽略。
- void write(byte b[ ]):将b.length个字节从指定字节数组b写入到输出流。
- void write(byte b[ ], int off, int len):把字节数组b中从下标off开始,长度为len的字节写入到输出流。
- void flush():刷空输出流,并输出所有被缓存的字节。由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中
- void close( ):流操作完毕后必须关闭。
字节输出流FileOutputStream类使用示例:
/*
*
* 需求:请把一句话:"helloworld"写入一个文本文件。
* 通过简单的分析后,我们找到了OutputStream。
* 由于OutputStream是一个抽象类,所以我们应该找其子类来学习。
* 学习前,我们先回想一个问题,就是硬盘上的文件,java提供了哪个类表示呢?File
* 再结合现在的字节输出流OutputStream,两个拼接一下就有了一个新的名字:FileOutputStream
*
* FileOutputStream的构造方法:
* FileOutputStream(File file)
* FileOutputStream(String name)
*
* 字节输出流操作步骤:
* A:创建字节输出流对象
* B:调用写数据功能写数据
* C:释放资源
*
* 练习:请把字节的名字写到一个文本文件
*/
public class OutStreamTest {
public void WriteTxt() throws IOException{
//创建字节输出流
/*
* 创建字节输入流对象做了哪些事情呢? A:调用系统功能创建了一个文件a.txt B:创建了一个对象fos
* C:把fos对象指向了a.txt文件
*/
FileOutputStream fos = new FileOutputStream("F:\\a.txt");
// 写数据
// public void write(int b):一次写一个字节的数据
// fos.write(57);
// fos.write(55);
// fos.write(97);
// public void write(byte[] b):一次写一个字节数组的数据
// byte[] bys = {97,98,99,100,101};
// fos.write(bys);
// 曾经我们讲解过,可以通过String得到byte[]
// byte[] bys = "abcde".getBytes();
// fos.write(bys);
fos.write("wyf12".getBytes());
// public void write(byte[] b,int off,int len):一次写一个字节数组的一部分
// fos.write("helloworld".getBytes(), 0, 5);
// fos.write("helloworld".getBytes(), 0, "helloworld".length());
//释放资源
//关闭此文件输出流并释放与此流有关的所有系统资源。
/*
* 两件事情:
* A:关闭流对象(流对象不可以继续在写数据了)
* B:释放与此流相关的资源(通知系统去释放与此流相关的资源)
*/
fos.close();
System.out.println("完成!");
}
/*
* 两个小问题:
* A:实现数据的换行?
* 不同的系统,针对换行符号的识别是不一样的。
* Mac: \r
* linux: \n
* windows: \r\n
* 系统自带的记事本软件,只能识别该系统能够识别的换行。
* 而Eclipse自带的记事本,以及Editplus却可以识别任意的换行符。
*
* B:实现数据的追加写入?
* public FileOutputStream(String name,boolean append)
* 如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
*/
public void WriteEnter() throws IOException{
File file = new File("F:\\a.txt");
FileOutputStream fos = new FileOutputStream(file,true); //写入的方式是向末尾追加数据
for(int i=0;i<10;i++){
fos.write(("hello!"+String.valueOf(i)+" \r\n").getBytes());
}
// 释放资源
fos.close();
}
public static void main(String[] arg){
OutStreamTest test = new OutStreamTest();
try {
test.WriteTxt();
test.WriteEnter();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
由上面的FileInputStream示例和FileOutputStream示例可以看出,以数组的方式读写数据比一次一个字节的读写方式快的多,因此Java本身在设计的时候,就设计出内置数组的缓冲区流!
字节缓冲输入流BufferedInputStream和字节缓冲输出流BufferedOutputStream示例代码如下:
/*
* 由于数组的方式一次比一个字节的方式快很多,所以,java本身在设计的时候,也考虑到了。
* 就设计出了内置数组的缓冲区流。
* 字节缓冲输入流
* BufferedInputStream
* 字节缓冲输出流
* BufferedOutputStream
*
* 通过看构造方法,我们发现,缓冲流不能直接操作文件。
* 是建立在基本的操作流之上的。
* 所以,这种流也被称之为高级流。
*/
public class BufferInOutputTest {
//输入缓冲流。
public void BufferInTest() throws IOException{
// 读数据
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\a.txt"));
int by=0;
while((by=bis.read())!=-1){ //一个字节一个字节读
System.out.print((char)by);
}
System.out.println("---------------------------");
bis = new BufferedInputStream(new FileInputStream("F:\\a.txt"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
bis.close();
}
//输出缓冲流
public void BufferOutTest() throws IOException{
//写数据
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\a.txt",true));
bos.write("WangYunfei\r\n".getBytes());
bos.close(); //释放资源
}
public static void main(String[] arg){
BufferInOutputTest bf = new BufferInOutputTest();
try {
bf.BufferOutTest();
bf.BufferInTest();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
对比四种不同读写数据方式的效率:
/*
* 数据源:
* F:\\哥有老婆.mp4
* 目的地:
* 项目路径下D:\\copy.mp4
*
* 四种方式:
* A:基本字节流一次读写一个字节 37100毫秒
* B:基本字节流一次读写一个字节数组 共耗时:51毫秒
* C:高效字节流一次读写一个字节 共耗时:368毫秒
* D:高效字节流一次读写一个字节数组 共耗时:16毫秒
*/
public class IOReadWrite {
public static void main(String[] arg){
IOReadWrite ioRw = new IOReadWrite();
try {
ioRw.method4();
} catch (IOException e) {
e.printStackTrace();
}
}
//方法1: 基本字节流一次读写一个字节(FileInputStream\FileOutputStream)
public void method1() throws IOException{
File file = new File("F:\\哥有老婆.mp4");
File file2 = new File("D:\\copy1.mp4");
//基本文件输入字节流
FileInputStream fis = new FileInputStream(file);
//基本文件输出字节流
FileOutputStream fos = new FileOutputStream(file2);
int by;
long startTime = System.currentTimeMillis(); //获取开始时间
while((by = fis.read())!=-1){ //一个字节一个字节读取
//将读出的一个字节利用输出流写入文件
fos.write(by);
}
long endTime = System.currentTimeMillis(); //获取结束时间
//释放资源
fis.close();
fos.close();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
//方法2: 基本字节流一次读写一个字节数组(FileInputStream\FileOutputStream)
public void method2() throws IOException{
File file = new File("F:\\哥有老婆.mp4");
File file2 = new File("D:\\copy2.mp4");
//定义基本字节输入输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
byte[] by = new byte[1024];
int len=0;
long startTime = System.currentTimeMillis(); //获取开始时间
while((len = fis.read(by))!=-1){
fos.write(by, 0, len);
}
long endTime = System.currentTimeMillis(); //获取结束时间
fos.close();
fis.close();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
//方法3: 高效字节流一次读写一个字节 (BufferedInputStream\BufferedOutputStream)
public void method3() throws IOException{
File file = new File("F:\\哥有老婆.mp4");
File file2 = new File("D:\\copy3.mp4");
//定义输入输出高效字节流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
int by;
long startTime = System.currentTimeMillis(); //获取开始时间
while((by = bis.read())!=-1){
bos.write(by);
}
long endTime = System.currentTimeMillis(); //获取结束时间
//释放资源
bos.close();
bis.close();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
//方法4: 高效字节流一次读写一个字节数组 (最快的方式)(BufferedInputStream\BufferedOutputStream)
public void method4() throws IOException{
File file = new File("F:\\哥有老婆.mp4");
File file2 = new File("D:\\copy4.mp4");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
byte[] by=new byte[1024];
int len;
long startTime = System.currentTimeMillis();
while((len = bis.read(by))!=-1){
bos.write(by, 0, len);
}
long endTime = System.currentTimeMillis();
//释放资源
bis.close();
bos.close();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
}
字符输入流
字符输入流根类是Reader,这类流以16位的Unicode编码表示的字符为基本处理单位。其子类如下图所示:
Reader主要方法如下:
- int read():读取一个字符,返回值范围在0~65535(0x00~0xffff)之间。如果因为已经到达流末尾,则返回值-1。
- int read(char[] cbuf):将字符读入到数组cbuf中,返回值为实际读取的字符的数量,如果因为已经到达流末尾,则返回值-1。
- int read(char[] cbuf, int off, int len):最多读取len个字符,数据放到以下标off开始字符数组 cbuf中,将读取的第一个字符存储在元素cbuf[off]中,下一个存储在cbuf[off+1]中,依次类推。返回值为实际读取的字符的数量,如果因为已经到达流末尾,则返回值-1。
- void close():流操作完毕后必须关闭。
字符输出流
字符输出流根类是Writer,这类流以16位的Unicode编码表示的字符为基本处理单位,下图为其子类。
Writer主要方法如下:
- void write(int c):将整数值为c的字符写入到输出流,c是int类型占有32位,写入过程是写入c的16个低位,c的16个高位将被忽略。
- void write(char[] cbuf):将字符数组cbuf写入到输出流。
- void write(char[] cbuf, int off, int len):把字符数组cbuf中从下标off开始,长度为len的字符写入到输出流。
- void write(String str):将字符串str中的字符写入输出流。
- void write(String str,int off,int len):将字符串str中从索引off开始处的len个字符写入输出流。
- void flush():刷空输出流,并输出所有被缓存的字符。由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中。
- void close( ):流操作完毕后必须关闭。
InputStreamReader、BufferedReader及OutputStreamWriter、BufferedWriter使用示例:
public class IOReadWriteTest {
public static void main(String[] arg){
IOReadWriteTest ioRw = new IOReadWriteTest();
File file = new File("C:\\Users\\lenovo\\Pictures\\格列佛游记.txt");
//File file = new File("F:\\a.txt");
try {
ioRw.GetTxt(file);
//ioRw.GetDataToArray(file);
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//获取Txt文件
public void GetTxt(File file) throws IOException{
//InputStreamReader(字节流与字符流之间的桥梁,可以指定编码格式)(因此InputStreamReader构造函数中有FileInputStream(字节流))
InputStreamReader isr = new InputStreamReader(new FileInputStream(file),"UTF-8");
//定义高效字符流
BufferedReader br = new BufferedReader(isr);
//方法1:高效字符流一行一行获取
// String line = null;
// while((line = br.readLine())!=null){
// System.out.println(line);
// }
//方法2: 高效字符流一次获取一个数组
// char[] ch = new char[1024];
// int len=0;
// while((len=br.read(ch))!=-1){
// System.out.print(new String(ch,0,len));
// }
//方法3:高效字符流一次获取一个字符
int len = 0;
while((len = br.read())!=-1){
System.out.print((char)len);
}
br.close();
}
/*
* 把ArrayList集合中的字符串数据存储到文本文件
*
* 数据源:
* ArrayList
* 目的地:
* array.txt
*/
public void GetArrayString(File file) throws IOException{
ArrayList<String> arr = new ArrayList<String>();
arr.add("wang");
arr.add("yun");
arr.add("fei");
//创建OutputStreamWriter类,将arr中的字符串按照指定的编码格式写入文本文件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file,true),"UTF-8");
BufferedWriter bw =new BufferedWriter(osw);
for(String s:arr){
bw.write(s);
bw.newLine();
bw.flush();
}
bw.flush();
//释放资源,在释放资源之前要先刷新
bw.close();
}
/*
* 从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合
*
* 数据源:
* array.txt
* 目的地:
* ArrayList
*/
public void GetDataToArray(File file) throws IOException{
//创建InputStreamReader类,读取txt文件中的内容
InputStreamReader isr = new InputStreamReader(new FileInputStream(file),"UTF-8");
BufferedReader br =new BufferedReader(isr);
ArrayList<String> arr = new ArrayList<String>();
String line = null;
while((line = br.readLine())!= null){
arr.add(line);
}
isr.close();
br.close();
//遍历集合
for(String s:arr){
System.out.println(s);
}
}
}
结束:
IO流分类:
A:数据流向
输入流 读数据
输出流 写数据
B:数据类型
字节流
字符流
默认情况下,IO流分类说的是按照数据类型分。
IO流的四个基类:
字节流
字节输入流 InputStream
字节输出流 OutputStream
字符流
字符输入流 Reader字符输出流 Writer