初始IO流
I/O流(Input/Output)流,通常用于处理设备间的数据传输,如读写文件,网路通讯等,如下图演示文件读写:
简单来说就是Java程序将磁盘上的文件通过流读到内存层面称为Input流(输入流);Java程序将内存层面的数据通过流持久化到磁盘文件称为Output流(输出流)
流的分类
按操作数据单位
:字节流、字符流按数据的流向
:输入流、输出流按流的角色
:节点流、处理流
流的体系结构
节点流以及处理流都继承抽象基类,只需实例化节点流或处理流即可对文件进行操作,除开抽象基类,节点流和处理流使用步骤基本一致。而节点流也是最基础的流处理类,处理流则是包裹在节点流外层,提升读写的效率。所以在开发中,多使用缓冲流(处理流)
文件操作
- 文件读入
/**
* 文件读入
* @throws Exception
*/
@Test
public void myTest() throws Exception {
File file=new File("test.txt");
//字符读入流
FileReader reader=new FileReader(file);
char[]data=new char[5];
//返回每次读到char[]中的字符个数,如果读到文件末尾,则返回-1
int len ;
while ((len=reader.read(data))!=-1){
String str=new String(data,0,len);//这里的len是指读到char[]中的字符个数,而不是char[]的长度
System.out.println(str);
}
}
- 文件写出
/**
* 文件写出
* @throws IOException
*/
@Test
public void testWriter()throws IOException{
//如果文件不存在,在流写入时会自动创建
File file=new File("my.txt");
//写出流:append:默认为false,对原有文件进行覆盖;若为true,对原有文件进行追加
FileWriter writer=new FileWriter(file,false);
writer.write("helloworld\n");
writer.write("helloxf");
writer.close();
}
- 文件数据读写
/**
* 文件数据读写
*/
@Test
public void testWriteAndRead(){
File srcFile=new File("test.txt");
File destFile=new File("my.txt");
FileReader reader=null;
FileWriter writer=null;
try {
//读入流
reader=new FileReader(srcFile);
//写出流
writer=new FileWriter(destFile,true);
//数据的读入和输出
char[]data=new char[5]; //记录每次读入到数组的字符个数
int len;
while ((len=reader.read(data))!=-1){
writer.write(data,0,len); //输出len个数据
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer!=null)
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (reader!=null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 图片复制
@Test
public void transferImg(){
File srcImg =new File("xf.png");
File destImg =new File("xf2.png");
FileInputStream inputStream=null;
FileOutputStream outputStream=null;
try {
inputStream=new FileInputStream(srcImg);
outputStream=new FileOutputStream(destImg);
byte[]data=new byte[100];
int len;
while ((len=inputStream.read(data))!=-1){
outputStream.write(data,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对于文本文件(.txt,.c++,.js),用字符流处理(FileReader/FileWriter);对于非文本文件(.jpg,.jar),用字节流处理(FileInputStream/FileOutputStream)
缓冲流的使用
/**
* 缓冲流的使用:提升读写的效率
* BufferInputStream
*/
public class BufferTest {
/**
* 缓冲流处理
* @param file1
* @param file2
*/
public void copyBufferedStream(String file1,String file2){
FileInputStream inputStream=null;
FileOutputStream outputStream=null;
BufferedInputStream bufferedInputStream=null;
BufferedOutputStream bufferedOutputStream=null;
try {
File srcFile=new File(file1);
File destFile=new File(file2);
inputStream=new FileInputStream(srcFile);
outputStream=new FileOutputStream(destFile);
//包装缓冲流
bufferedInputStream=new BufferedInputStream(inputStream);
bufferedOutputStream=new BufferedOutputStream(outputStream);
byte[]data=new byte[1024];
int len;
while ((len=bufferedInputStream.read(data))!=-1){
bufferedOutputStream.write(data,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedInputStream!=null){
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedOutputStream!=null){
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 字节流处理
* @param file1
* @param file2
*/
public void copyStream(String file1,String file2){
FileInputStream inputStream=null;
FileOutputStream outputStream=null;
try {
File srcFile=new File(file1);
File destFile=new File(file2);
inputStream=new FileInputStream(srcFile);
outputStream=new FileOutputStream(destFile);
byte[]data=new byte[1024];
int len;
while ((len=inputStream.read(data))!=-1){
outputStream.write(data,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//字节流测试
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testStream(){
long startTime=System.currentTimeMillis();
String str1="E:\\xf\\resQuality.jar";
String str2="E:\\xf\\resQuality1.jar";
copyBufferedStream(str1,str2);
//copyStream(str1,str2);
long endTime=System.currentTimeMillis();
System.out.println("复制文件花费:"+(endTime-startTime)+"ms");
}
}
经过测试发现,加上缓冲流文件操作的效率提升近3倍,所以在开发中,一般多选择缓冲流(处理流的一种)居多
处理流
前面提到缓冲流,也是处理流的一种,除此之外,处理流也还有其他
- 转换流 属于字符流,提供字符与字节的转换
InputStreamReader
:将一个字节的输入流转为字符的输入流OutputStreamWriter
:将一个字符的输出路转为字节的输出流 接着我们尝试实现用转换流实现文件的读写
@Test
public void convertFileContent()throws IOException{
//1.造流,造文件
File srcFile=new File("utf-8.txt");
File destFile=new File("gbk.txt");
FileInputStream inputStream=new FileInputStream(srcFile);
FileOutputStream outputStream=new FileOutputStream(destFile);
InputStreamReader inputStreamReader=new InputStreamReader(inputStream,"utf-8");
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(outputStream,"gbk");
//2.数据读写
char[]data=new char[20];
int len;
while ((len=inputStreamReader.read(data))!=-1){
outputStreamWriter.write(data,0,len);
}
//3.资源关闭
inputStreamReader.close();
outputStreamWriter.close();
}
实现将utf-8文件转为gbk格式文件后,我们用IDEA打开,会出现乱码,这就实现了上述需求
对象流
ObjectInputStream和ObjectOutputStream;用于存储和读取基本数据类型或对象的处理流,并且实现对象序列化和反序列化的过程。序列化
:将Java对象转化为与平台无关的二进制流保存到磁盘或通过网络进行传输,使用ObjectOutputStream实现反序列化
:通过读取文件或网络获取对象二进制流并还原为Java对象的过程;使用ObjectInputStrea实现
注:要能实现对象序列化和反序列化的前提就是该对象支持序列化机制,具体做法就是使该Java对象实现Serializable接口,并提供一个全局序列化版本号,类的各个属性也支持序列化机制(默认情况下,基本数据类型支持序列化机制)。例如下面的Java对象:
/**
* User类支持序列化机制
*/
public class User implements Serializable {
public static final long serialVersionUID = 21321312312L; //序列版本号
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
第三方Jar包支持文件操作
上面我们都是自己通过流来对文件进行操作,步骤都代替都基本一致:获取文件->创建流->数据的读写->关闭资源。但实际开发中,我们可以直接借用第三方提供的Jar包实现对文件的操作,底层封装了文件的操作的以上步骤,我们只需调用相应的方法即可。
- 导入依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
- 文件复制
/**
* FileUtils操作文件
* @param args
*/
public static void main(String[] args) {
//通过工具类获取File对象
File file1 = FileUtils.getFile("my.txt");
File file2 = FileUtils.getFile("my2.txt");
//文件复制
try {
FileUtils.copyFile(file1,file2);
} catch (IOException e) {
e.printStackTrace();
}
}
- copyFile源码
private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
if (destFile.exists() && destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' exists but is a directory");
} else {
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel input = null;
FileChannel output = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
input = fis.getChannel();
output = fos.getChannel();
long size = input.size();
long pos = 0L;
for(long count = 0L; pos < size; pos += output.transferFrom(input, pos, count)) {
count = size - pos > 31457280L ? 31457280L : size - pos;
}
} finally {
IOUtils.closeQuietly(output);
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(fis);
}
if (srcFile.length() != destFile.length()) {
throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'");
} else {
if (preserveFileDate) {
destFile.setLastModified(srcFile.lastModified());
}
}
}
}
通过源码我们看到,底层封装了前面的文件操作的步骤,并在该基础上,提供了更多新的特性,封装许多方法,第三方提供的API更强大,推荐在项目中使用。
总结
了解流的概念,熟练使用jdk提供的API来操作文件的读写。熟悉流的分类,并通过导入第三方工具包,实现对文件的操作