前言
Github:https://github.com/yihonglei/jdk-source-code-reading(java-io)
概述
在 Java 应用程序中,文件是一种常用的数据源或者存储数据的目标媒介。Java IO 读写文件的 IO 流分为两大类,字节流和字符流,
基于字节流的读写基类: InputStream和OutputStream
基于字符流的读写基类: Reader和Writer
关于文件读取的类关系图如下:
以下通过实例分析 Java 应用程序如何对文件进行读写操作。
一 读文件
我们可以根据文件类型,选择不同 io 子类来读取文件。
如果是二进制文件,使用 FileInputStream 读取;如果是文本文件,使用 FileReader 读取;
这两个类允许我们从文件开始至文件结尾一个字节或字符的读取文件,或者将读取的文件
写入字节数组或字符数组。如果我们想随机的读取文件内容,可以使用 RandomAccessFile 。
基于字节流读取文件步骤:
1、找到指定文件
2、根据文件创建文件输入流
3、创建字节数组
4、读取内容,放到字节数组里面
5、关闭输入流
读取一个文件,然后将文件内容以字符串形式输出。
package com.jpeony.io.bytestream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 读取文件:
* 1、找到指定的文件
* 2、根据文件创建文件的输入流
* 3、创建字节数组
* 4、读取内容,放到字节数组里面
* 5、关闭输入流
*/
public class FileRead {
public static void main(String[] args) {
// 构建指定文件
File file = new File("C:\\mycode\\hello.txt");
InputStream is = null;
try {
// 根据文件创建文件的输入流
is = new FileInputStream(file);
// 创建字节数组
byte[] data = new byte[1024];
// 读取内容,放到字节数组里面
is.read(data);
System.out.println("文件内容:" + new String(data));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭输入流
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
该例子中关于流的关闭并不完美,只是为了逻辑更清楚,才放在finally中关闭流,
其实关于流的关闭可以优化处理,优化后代码如下:
package com.jpeony.io.bytestream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 读取文件:
* 1、找到指定的文件
* 2、根据文件创建文件的输入流
* 3、创建字节数组
* 4、读取内容,放到字节数组里面
* 5、关闭输入流
*/
public class FileRead {
public static void main(String[] args) {
// 构建指定文件
File file = new File("C:\\mycode\\hello.txt");
// 根据文件创建文件的输入流
try (InputStream is = new FileInputStream(file)) {
// 创建字节数组
byte[] data = new byte[1024];
// 读取内容,放到字节数组里面
is.read(data);
System.out.println("文件内容:" + new String(data));
} catch (Exception e) {
e.printStackTrace();
}
}
}
优化后代码不需要在finally语句块中关闭流,把文件转换为流的处理放在try中自动关闭。
关于文件的读取,我们可以将文件流放在缓冲区中处理,效率会更高。
以上实例是基于字节流读取文件,而基于字符流读取文件其实与基于字节流大同小异。
基于字节流创建字节数组,基于字符流创建基于字符的数组。
基于字符流读取文件步骤:
1、找到指定文件
2、根据文件创建文件输入流
3、创建字符数组
4、读取内容,放到字符数组里面
5、关闭输入流
package com.jpeony.io.charstream;
import java.io.*;
/**
* 读取文件:
* 1、找到指定的文件
* 2、根据文件创建文件的输入流
* 3、创建字符数组
* 4、读取内容,放到字符数组里面
* 5、关闭输入流
*/
public class FileReaderTest {
public static void main(String[] args) {
// 构建指定文件
File file = new File("C:\\mycode\\hello.txt");
// 根据文件创建文件的输入流
try (Reader reader = new FileReader(file)) {
// 创建字符数组
char[] data = new char[1024];
// 读取内容,放到字符数组里面
reader.read(data);
System.out.println("文件内容:" + new String(data));
} catch (Exception e) {
e.printStackTrace();
}
}
}
二 写文件
根据要写入数据是二进制数据或字符型数据选择 FileOutputStream 或 FileWriter。
我们可以一次写入一个字节或一个字符到文件中,也可以直接写入字节数组或字符数组到文件中。
数据按照我们写入的顺序存储在文件中。
基于字节流写入文件步骤:
1、找到指定的文件
2、根据文件创建文件的输出流
3、把内容转换成字节数组
4、向文件写入内容
5、关闭输入流
package com.jpeony.io.bytestream;
import java.io.*;
/**
* 写入文件:
* 1、找到指定的文件
* 2、根据文件创建文件的输出流
* 3、把内容转换成字节数组
* 4、向文件写入内容
* 5、关闭输入流
*/
public class FileWriter {
public static void main(String[] args) {
// 构建指定文件
File file = new File("C:\\mycode\\hello.txt");
// 根据文件创建文件的输出流
try (OutputStream os = new FileOutputStream(file)) {
String str = "我是中国人";
// 把内容转换成字节数组
byte[] data = str.getBytes();
// 向文件写入内容
os.write(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
将字符串转换为字节数组,通过输出流写入到指定文件中。
基于字符流写入文件步骤:
1、找到指定的文件
2、根据文件创建文件的输出流
3、把内容转换成字符数组
4、向文件写入内容
5、关闭输入流
package com.jpeony.io.bytestream;
import java.io.*;
/**
* 写入文件:
* 1、找到指定的文件
* 2、根据文件创建文件的输出流
* 3、把内容转换成字符数组
* 4、向文件写入内容
* 5、关闭输入流
*/
public class FileWriterTest {
public static void main(String[] args) {
// 构建指定文件
File file = new File("C:\\mycode\\hello.txt");
// 根据文件创建文件的输出流
try (Writer writer = new FileWriter(file)) {
String str = "我是中国人-FileWriter";
// 把内容转换成字符数组
char[] data = str.toCharArray();
// 向文件写入内容
writer.write(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
三 随机读写文件
package com.jpeony.io.charstream;
import java.io.File;
import java.io.RandomAccessFile;
public class RandomAccessFileTest {
public static void main(String[] args) {
File file = new File("C:\\mycode\\hello.txt");
/**
* model各个参数详解
* r 代表以只读方式打开指定文件
* rw 以读写方式打开指定文件
* rws 读写方式打开,并对内容或元数据都同步写入底层存储设备
* rwd 读写方式打开,对文件内容的更新同步更新至底层存储设备
*/
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
//获取RandomAccessFile对象文件指针的位置,初始位置是0
System.out.println("RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());
//移动文件指针位置
raf.seek(0);
byte[] buff=new byte[1024];
//用于保存实际读取的字节数
int hasRead=0;
//循环读取
while((hasRead=raf.read(buff))>0){
//打印读取的内容,并将字节转为字符串输入
System.out.println(new String(buff,0,hasRead));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
四 复制文件
拷贝的核心思想: 从源文件读取数据,通过循环写入到目标文件,从而实现文件复制。
package com.jpeony.io.bytestream;
import java.io.*;
/**
* 实现思路:
* 1、构建源文件与目标文件
* 2、源文件创建输入流,目标文件创建输出流
* 3、创建字节数组
* 4、使用循环,源文件读取一部分内容,目标文件写入一部分内容,直到写完所有内容
* 5、关闭源文件输入流,目标文件输出流
*/
public class FileCopyTest {
public static void main(String[] args) {
// 构建源文件
File file = new File("C:\\mycode\\hello.txt");
// 构建目标文件
File fileCopy = new File("C:\\mycode\\hello-copy.txt");
InputStream in = null;
OutputStream out = null;
try {
// 目标文件不存在就创建
if (!(fileCopy.exists())) {
fileCopy.createNewFile();
}
// 源文件创建输入流
in = new FileInputStream(file);
// 目标文件创建输出流
out = new FileOutputStream(fileCopy, true);
// 创建字节数组
byte[] temp = new byte[1024];
int length = 0;
// 源文件读取一部分内容
while ((length = in.read(temp)) != -1) {
// 目标文件写入一部分内容
out.write(temp, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭文件输入输出流
in.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
五 读取文件信息
文件信息包括创建时间,所有者等等信息,读取文件属性,从文件属性获取文件信息。
package com.jpeony.io.file;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.util.Date;
/**
* @author yihonglei
*/
public class FileInfoSimple {
public static void main(String[] args) throws IOException {
Path testPath = Paths.get("C:\\mycode\\hello.txt");
BasicFileAttributeView basicView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = basicView.readAttributes();
System.out.println("创建时间:" + new Date(basicFileAttributes.creationTime().toMillis()));
System.out.println("最后访问时间:" + new Date(basicFileAttributes.lastAccessTime().toMillis()));
System.out.println("最后修改时间:" + new Date(basicFileAttributes.lastModifiedTime().toMillis()));
System.out.println("文件大小:" + basicFileAttributes.size());
FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath, FileOwnerAttributeView.class);
System.out.println("文件所有者:" + ownerView.getOwner());
}
}