写在前面
  你们好,我是小庄。很高兴能和你们一起学习Java。如果您对Java感兴趣的话可关注我的动态.
  写博文是一种习惯,在这过程中能够梳理和巩固知识。

Java文件读写操作

  • 一、File类
  • 二、IO流的介绍
  • 三、文本文件读写(字符流)
  • 四、二进制文本读写(字节流)
  • 五、Zip文件的读写
    • 1、Zip文件的压缩
    • 2、Zip文件的解压缩
  • 六、序列化和反序列化
    • 1、序列化和反序列化的概念
    • 2、序列化实现
    • 3、反序列化实现
    • 4、反序列化中遇到的问题以及解决方法
  • 七、文件锁

在学习文件流读写数据之前,让我们先看一个实用类库工具,它可以帮我们处理文件目录问题

一、File类

这个类主要操作目录和文件
不同的系统有不同的分隔符
Window系统的分隔符是:\
Linux系统的分隔符是:/
为了不把文件路径写死,我们可以通过File.separator获取系统分隔符

//路径写死:"C://abc.txt",只能匹配Window系统
//路径没有写死,什么系统都可以用
String path="C:"+File.separator+"abc.txt";

我们可以使用File类的方法

  1. 创建文件/文件夹
方法 作用
createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
mkdir() 创建由此抽象路径名命名的目录。只创建一级目录
mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。可以创建多级目录
  1. 删除文件/文件夹
方法 作用
delete() 删除由此抽象路径名表示的文件或目录。
  1. 获取文件/文件夹
方法 作用
getName() 返回由此抽象路径名表示的文件或目录的名称。
getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null
getPath() 将此抽象路径名转换为路径名字符串。
  1. 判断文件/文件夹是否存在
方法 作用
exists() 测试此抽象路径名表示的文件或目录是否存在。
isFile() 测试此抽象路径名表示的文件是否为普通文件
isDirectory() 测试此抽象路径名表示的文件是否为目录。
  1. 遍历文件夹
方法 作用
list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
listFiles(FileFilter f) 返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录

listFiles()方法我们单独拿出来讲

  1. 获取文件的大小
方法 作用
length() 返回由此抽象路径名表示的文件的长度。

我们对以上的方法进行测试

import java.io.File;
import java.io.IOException;public class FileTest {//为了方便查看,这里直接抛出异常,不对异常进行处理public static void main(String[] args) throws IOException{//获取文件分隔符String separator=File.separator;//声明目录,在Window系统中C://testFile d=new File("c:"+separator+"test");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!d.exists()) {/*创建新的目录(文件夹),这里只有一级目录,所以可以使用mkdir()创建如果是C://test//a,test目录也不存在,必须使用mkdirs()创建,否则报错*/d.mkdirs();}//判断是否为目录,返回true/falseSystem.out.println("这是目录? "+d.isDirectory());//声明文件File f=new File("c:"+separator+"test"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {//创建新的文件f.createNewFile();}//获取文件名System.out.println(f.getName());//获取上一级目录System.out.println(f.getParent());//获取全局路径System.out.println(f.getPath());//判断是不是文件System.out.println(f.isFile());//获取长度System.out.println(f.length());//获取目录下的文件/文件夹String[] fe=d.list();//遍历目录下的所有文件for(String s:fe) {System.out.println(s);}//删除文件f.delete();//删除目录d.delete();}}

通过上面的代码,相信你对File更加的熟悉了
我们接着讲FileFilter过滤器

我们有个需求是:只遍历C:\test目录下的图片,实现FileFilter接口和 listFiles(FileFilter f)方法我们可以很快速的实现

FileFilter接口如下

public interface FileFilter{public boolean accept(File filename);
}

FileFilter接口说明
FileFilter接口只有一个boolean 类型的accept方法,当返回为true时,会把传递过去的File对象保存到File数组中,否则不会保存到File数组中。filename是listFiles方法传递过去的File对象。
我们先在C盘的test目录(没有的话新建)放些内容

下面我们自定义一个FileFilter过滤器MyFileFilter

import java.io.File;
import java.io.FileFilter;
//MyFileFilter类实现FileFilter
public class MyFileFilter implements FileFilter{@Overridepublic boolean accept(File filename) {//获取文件名,转换为字符串,不区分大小写,截取后缀Boolean jpg=filename.getName().toString().toLowerCase().endsWith(".jpg");Boolean jpeg=filename.getName().toString().toLowerCase().endsWith(".jpeg");Boolean png=filename.getName().toString().toLowerCase().endsWith(".png");//jpg值为true时执行if(jpg) {return true;}//png值为true时执行下面else if(png){return true;}else if(jpeg) {return true;}else {return false;}}}

然后我们在新建一个类进行测试

import java.io.File;
import java.io.IOException;public class Test {//为了方便查看,这里直接抛出异常,不对异常进行处理public static void main(String[] args) throws IOException{//获取文件目录File d=new File("c:\\test");//对文件目录进行过滤遍历File[] files=d.listFiles(new MyFileFilter());//防止空指针异常,对数组进行判断,不为空时输出if(files!=null) {for(File f:files) {System.out.println(f);}}}
}

下面我们接着讲IO流

二、IO流的介绍

什么是IO流?
  流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以方式进行输入输出

简而言之,我们是以流的形式对文件读写操作。就好比我们平时拿吸管喝水,数据的传输也是和”水流一样“;
那么IO流的分类是怎样的呢?

按照数据的流向分为

  • 输入流:其他设备中的数据读取内存中的流
  • 输出流:内存中的数据写入到其他设备中的流

按数据类型

  • 字节流: 以二进制的存储方式的流
  • 字符流: 以字符的存储方式的流,通常处理中文

讲完了这些分类,我们来谈谈java.io包中的类
java.io包中分为

  • 结点类: 直接对文件的读写操作
  • 包装类
    转化类:字节/字符数据类型的转化
    装饰类:装饰结点类

结点类有哪些呢?

字节流:OutputStream(输出)、InputStream(输入)
  – 子类:FileOutputStream,FileInputStream

字符流:Writer(输出)、Reader(输入)
  – 子类FileWriter,FileReader

这些类都是能直接对文件的读写操作

那么什么是转化类呢?
我们读取文件的时候会常常遇到乱码问题,而转化类可以帮助我们转换为对应的字符编码格式,比如:UTF-8,GBK等等
OutputStreamWriter(输出流使用),InputStreamReader(输入流使用)

那么什么是装饰类呢?
在流的传输过程中,我们使用了缓存进行了”装饰“。
字节缓存流:BufferedOutputStream(输出缓存)、BufferedInputStream(输入缓存)
字符缓存流:BufferedWriter(输出缓存)、BufferedReader(输入缓存)

讲了那么多概念,我们来用代码验证

三、文本文件读写(字符流)

因为一个中文占3个字节空间
所以,一个字节空间无法存放一个中文,这里就会用到字符流。

方法 作用
write() 写入数据到流中
read() 从流中读取数据
newLine() 换行操作
flush() 刷新流
close() 关闭流

字符输出流:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;//以字符输出流写入数据
public class WriterTest {public static void main(String[] args){//声明字节输入流FileOutputStream os=null;//声明转化类OutputStreamWriter ow=null;//声明装饰类BufferedWriter bo=null;try {//获取文件分隔符String separator=File.separator;//声明文件:D://ab.txtFile f=new File("D:"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {f.createNewFile();}//把文件作为参数传进去os=new FileOutputStream(f);//对输入流进行转化,以UTF-8的形式ow=new OutputStreamWriter(os,"UTF-8");//使用缓存装饰转化流bo=new BufferedWriter(ow);//写入字符串bo.write("加油啊");//换行bo.newLine();//写入字符bo.write('a');//换行bo.newLine();//以缓存的方式写入,65表示Abo.write(65);//刷新流bo.flush();System.out.println("存储成功");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {try {//关闭os.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

字符输入流:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.io.IOException;//以字符串输入流读取数据
public class Readerest {public static void main(String[] args){//声明字节输入流FileInputStream is=null;//声明转化类InputStreamReader ir=null;//声明装饰类BufferedReader bi=null;try {//获取文件分隔符String separator=File.separator;//声明文件:D://ab.txtFile f=new File("D:"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {f.createNewFile();}//把文件作为参数传进去is=new FileInputStream(f);//对输入流进行转化,以UTF-8的形式ir=new InputStreamReader(is,"UTF-8");//使用缓存装饰转化流bi=new BufferedReader(ir);//方式一:一行读取到控制台,返回字符串
//          System.out.println(bi.readLine());
//          System.out.println(bi.readLine());System.out.println("---方式二,遍历方式---");//末尾以-1结束String s;while((s=bi.readLine())!=null) {System.out.println(s);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {try {//只需要关闭最外层bi.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

我们使用更加简洁的方式
使用1.7版本的新特性处理异常:在try后面加()
字节输出流:
重点是要加入flush()方法刷新流,不然没有写进内容

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;//以字符输出流写入数据
public class WriterTest {public static void main(String[] args) throws IOException{//获取文件分隔符String separator=File.separator;//声明文件:D://ab.txtFile f=new File("D:"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {//创建新文件f.createNewFile();}try(BufferedWriter bo=new BufferedWriter(new OutputStreamWriter(newFileOutputStream(f),"UTF-8" ))){ //写入字符串bo.write("加油啊");//换行bo.newLine();//写入字符bo.write('a');//换行bo.newLine();//以缓存的方式写入,65表示Abo.write(65);}catch(IOException e) {e.printStackTrace();}}
}

字符输入流:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;//以字符输入流读取数据
public class ReaderTest {public static void main(String[] args) throws IOException{//获取文件分隔符String separator=File.separator;//声明文件:D://ab.txtFile f=new File("D:"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {f.createNewFile();}try(BufferedReader br=new BufferedReader(new InputStreamReader(newFileInputStream(f),"UTF-8" ))){                  //方式一:一行读取到控制台,返回字符串
//              System.out.println(br.readLine());
//              System.out.println(br.readLine());
//              System.out.println("---方式二,遍历方式---");//不为空时结束String s;while((s=br.readLine())!=null) {System.out.println(s);}}catch(IOException e) {e.printStackTrace();}}
}

四、二进制文本读写(字节流)

字节流的操作和字符流的操作一样,只不过使用的类不一样。

方法 作用
write() 写入数据到流中
read() 从流中读取数据
newLine() 换行操作
flush() 刷新流
close() 关闭流

我把输入流和输出流放同一个文件

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;//以字节输入流写入数据
public class Test {public static void main(String[] args) throws IOException{//获取文件分隔符String separator=File.separator;//声明文件:D://ab.txtFile f=new File("D:"+separator+"ab.txt");//判断文件/文件夹是否存在,!表示flase时执行下面语句if(!f.exists()) {f.createNewFile();}try(DataOutputStream ds=new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)))){ds.writeUTF("a");ds.writeInt(100);ds.write(65);System.out.println("---输出流结束--");}catch(IOException e) {e.printStackTrace();}System.out.println("---输入流开始--");//以下内容为输入流File f2=new File("D:"+separator+"ab.txt");try(DataInputStream ds=new DataInputStream(new BufferedInputStream(new FileInputStream(f2)))){System.out.println(ds.readUTF());System.out.println(ds.readInt());System.out.println(ds.read());}catch(IOException e) {e.printStackTrace();}        }
}

以上关于字节流和字符流的代码中,我们对文件的写入操作是覆盖原有的内容,但有时候我们需要追加内容时,应该怎么做呢?

FileOutputStream类中有构造方法FileOutputStream(File file, boolean append)
File file:表示写入数据的目的地
boolean append:表示追加是否开启 ,当我们设置为true时,说明开启追加

我们的原代码:new FileOutputStream(f)

File f=new File("D:"+separator+"ab.txt");
FileOutputStream os=new FileOutputStream(f)

开启追加模式的代码:

File f=new File("D:"+separator+"ab.txt");
//这里是变化的地方,后面的参数
FileOutputStream os=new FileOutputStream(f,true)

上面介绍的都是文件或者文件夹,但是我们突发异想,怎么把文件压缩和解压呢?下面我们对此进行展开讲解

五、Zip文件的读写

使用到的类:

  • ZipOutputStream
  • ZipInputStream
  • ZipEntry
  • FileOutputStream
  • FileInputStream
  • File

1、Zip文件的压缩

单个/多个文件压缩步骤
  1、设置原文件和目标文件(压缩文件名)
  2、打开输出zip文件,指向压缩文件
  3、添加一个ZipEntry,使用putNextEntry方法
  4、打开一个输入文件,读数据,向ZipEntry写数据,关闭输入文件
  5、重复以上两个步骤,写入多个文件到zip文件中
  6、关闭zip文件

首先,我们先介绍单个文件的压缩

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;public class ZipOutOneTest {public static void main(String[] args){//要压缩的原文件File f=new File("d:\\ab.txt");//目标文件File zipfile=new File("d:\\ab.zip");try {//步骤二、打开zip文件,指向压缩的目标文件  ZipOutputStream zipout=new ZipOutputStream(new FileOutputStream(zipfile));//步骤三、添加ZipEntry,往Zip放文件名zipout.putNextEntry(new ZipEntry(f.getName()));//设置注释,可以不设置zipout.setComment("txt.file zip");//步骤四、打开输入文件,读取数据InputStream in=new FileInputStream(f);//压缩过程,把读取到的文件数据遍历写入到压缩文件中int temp=0;while((temp=in.read())!=-1) {//把读取到的文件数据遍历写入到压缩文件中zipout.write(temp);}//步骤五、关闭输入文件流in.close();//步骤六、关闭压缩文件zipout.close();} catch (IOException e) {e.printStackTrace();}System.out.println("压缩成功");}
}

下面我们来看看多个文件压缩,重复步骤3、4

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;//以字节输入流写入数据
public class MathTest {public static void main(String[] args){//要压缩的原文件目录,这个要和电脑的文件路径一致File f=new File("D:\\学习\\博客");//目标文件,起名字File zipfile=new File("d:\\个人博客.zip");try {//步骤二、打开zip文件,指向压缩的目标文件   ZipOutputStream zipout=new ZipOutputStream(new FileOutputStream(zipfile));int temp=0;if(f.isDirectory()) {//获取所有的文件/文件名File[] files= f.listFiles();//遍历filesfor(File fs:files) {//步骤三、添加ZipEntry,往Zip放文件名zipout.putNextEntry(new ZipEntry(fs.getName()));//设置注释,可以不设置zipout.setComment("小庄的个人博客");//步骤四、打开输入文件,读取数据InputStream in=new FileInputStream(fs);//压缩过程,把读取到的文件数据遍历写入到压缩文件中while((temp=in.read())!=-1) {//把读取到的文件数据遍历写入到压缩文件中zipout.write(temp);}//步骤五、关闭输入文件流in.close();}}//步骤六、关闭压缩文件zipout.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("压缩成功");}
}

这里压缩没有使用到缓存,可以自己练习一下。
压缩讲完了,那么讲讲解压缩吧

2、Zip文件的解压缩

解压缩是压缩的反操作。

单个/多个文件解压缩步骤
  1、打开输入zip文件
  2、获取下一个ZipEntry,使用getNextEntry方法
  3、新建一个目标文件,从ZipEntry读取数据,向目标文件写数据
  4、关闭目标文件
  5、重复以上两个步骤,从zip包中读取数据到多个目标文件
  6、关闭zip文件

单个或多个文件均可用

import java.io.OutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;//以字节输入流写入数据
public class MathTest {public static void main(String[] args){try {//zip文件位置File file=new File("d:\\毕业论文.zip");//实例化ZipFileZipFile zipfile=new ZipFile(file);//步骤一、打开输入Zip文件ZipInputStream zipIn=new ZipInputStream(new FileInputStream(file));//步骤二,获取下一个ZipEntry,使用getNextEntry方法ZipEntry entry=null;//如果ZipEntry不为空,则继续遍历while((entry=zipIn.getNextEntry())!=null) {//步骤三、新建目标文件,,根据entry获取压缩包中的文件/目录名字File f=new File("d:\\测试\\"+entry.getName());//判断这个文件路径是否已经存在if(!f.getParentFile().exists()) {//不存在则创建目录f.getParentFile().mkdirs();}//判断文件/文件夹是否存在if(!f.exists()) {//不存在,则判断是不是文件夹if(f.isDirectory()) {//是文件夹就新建文件夹f.mkdirs();}else {//不是文件夹就新建文件f.createNewFile();}}//如果这个不是目录,只是一个文件的话,那么就进行读写操作if(!f.isDirectory()) {//输出流,输出到指定的文件OutputStream out=new FileOutputStream(f);//使用装饰类BufferedOutputStream bo=new BufferedOutputStream(out);//获取每个ZipEntry的输入流InputStream in=zipfile.getInputStream(entry);//步骤四、对每一个ZipEntry进行遍历读取并写入到目标文件中int temp=0;while((temp=in.read())!=-1) {//写入目标文件bo.write(temp);}//关闭写入流in.close();//关闭输出流out.close();}}//步骤六、关闭压缩文件zipIn.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("解压缩成功");}
}

上面代码我们使用了BufferedOutputStream 缓存,大大提高了效率。

我们接着讲序列化和反序列化

六、序列化和反序列化

1、序列化和反序列化的概念

把对象以流的方式写入到文件中保存 -->序列化

反过来,把文件中保存的对象以流的方式进行读取出来 -->反系列化

使用到的类和接口

  • ObjectOutputStream
  • FileOutputStream
  • 实体类(对象)
  • Serializable(接口)

2、序列化实现

步骤:
  1、建立一个实体类User,并设置属性,get和set方法
  2、User类实现Serializable(接口)
  3、建立一个操作类,将User对象的内容以流的方式写入到指定的文件中

新建User类实现Serializable

import java.io.Serializable;public class User implements Serializable{private String name;private int age;//无参构造public User(){}//有参构造public User(String name,int age) {this.name=name;this.age=age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}
}

建立一个操作类

import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;public class FileTest {public static void main(String[] args) throws IOException{//打开输出文件String s=File.separator;File file=new File("d:\\"+s+"User.txt");//判断文件是否存在,若不存在就新建if(!file.exists()) {//新建文件file.createNewFile();}//使用对象序列,实例化ObjectOutputStream op=new ObjectOutputStream(new FileOutputStream(file));//将对象写入op.writeObject(new User("张三",18));//关闭流op.close();}
}

注意:User类必须要实现Serializable,否则会报错!

Serializable接口

源码中这个接口什么内容都没有

public interface Serializable{}

源码介绍:类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。

这说明Serializable是个标记型接口,做记号的作用。

3、反序列化实现

上面我们已经了解到:反序列化意思是把文件中保存的对象进行读取
使用到的类

  • ObjectInputStream
  • FileInputStream
  • User(系列化自定义的类)

我们通过代码进行观察

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.FileInputStream;public class FileTest {public static void main(String[] args) throws IOException, ClassNotFoundException{//打开输出文件String s=File.separator;File file=new File("d:\\"+s+"User.txt");//判断文件是否存在,若不存在就新建if(!file.exists()) {//新建文件file.createNewFile();}//使用对象序列,实例化ObjectInputStream oi=new ObjectInputStream(new FileInputStream(file));User user=(User)oi.readObject();System.out.println(user.toString());//关闭流oi.close();}
}

4、反序列化中遇到的问题以及解决方法

先看看序列化和反序列化的流程:

问题:每次修改类的定义,都会给class文件生成新的序列号

解决方法:无论怎么修改类的定义,都不生成新的序列号,给类绑定一个序列号。
使用:

private static final long serialVersionUID = 42L;

全部代码如下:

import java.io.Serializable;public class User implements Serializable{private static final long serialVersionUID = 42L;private String name;private int age;//无参构造public User(){}//有参构造public User(String name,int age) {this.name=name;this.age=age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}
}

被关键字修饰的变量不能序列化:
  1、transient 瞬态关键字
  2、static 静态关键字

七、文件锁

这个模块的知识涉及到多线程的内容,多线程我们后面会讲。
这里我们对文件锁做简单的介绍。

官方的定义:锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读锁。

从官方的定义我们了解到,锁在多线程访问中发挥着”分配资源“的作用,我们可以根据具体情况设置不同锁的功能,比如,一次只能有一个线程可以获取锁或者运行并发访问共享资源。

文件锁分为

  • 1、独占锁 -->写锁
  • 2、共享锁 -->读锁

在多线程环境下,写锁的过程中该锁一次只能被一个线程所持有,只能被一个线性写
在多线程环境下,读锁的过程中指该锁可被多个线程所持有,多个线程都可以读

文件锁是怎么实现的呢?

要锁定一个文件,我们可以调用FileChannel类的locktryLock方法
lock():在整个文件上获得一个独占锁,这个方法将阻塞直到获得锁,除非调用lock()线程中断或调用lock()的通道关闭。

//使用FileChannel的open方法打开要加锁的文件路径path
FileChannel channel=FileChannel.open(path);
//对文件使用lock加锁
FileLock lock=channel.lock();

tryLock():在整个文件上获得一个独占锁,或者无法获得锁的情况下返回null,这是非阻塞的。

//这里的channel对象使用上面的,只是使用方法是tryLock
FileLock lock=channel.tryLock();

在IO流中,我们可以通过getChannel()方法获取FileChannel对象

我们还可以锁定文件的一部分:

FileLock lock(long start,long size,boolean shared)
或
FileLock tryLock(long start,long size,boolean shared)

我们对上面代码进行分析:

  1. 前面两个参数是文件加锁的长度(容量)
  2. shared表示共享锁是否开启,默认为false

我们通过代码来学习一下
写锁

import java.nio.channels.FileLock;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class FileLockTest {public static void main(String[] args) throws IOException, InterruptedException{FileOutputStream os=null;try {File f=new File("d:"+File.separator+"ab.txt");//如果文件不存在if(!f.exists()) {//创建新的文件f.createNewFile();}//打开输出流os=new FileOutputStream(f);//对文件写入操作os.write(65);//使用lock//FileLock lock=os.getChannel().lock();//FileLock lock=os.getChannel().lock(0,Long.MAX_VALUE,false);//设置独占锁,指定所有内容//使用tryLockFileLock lock2=os.getChannel().tryLock();if(lock2!=null) {System.out.println("Locked File");
//              Thread.sleep(100);//等待100毫秒
//              lock2.release();//释放锁}//关闭文件锁//lock.close();lock2.close();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {//关闭流os.close();}}
}

读锁:我们对上面写锁的文件进行读锁操作

import java.nio.channels.FileLock;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;public class MathTest {public static void main(String[] args) throws IOException{FileInputStream is=null;try {File f=new File("d:"+File.separator+"abd.txt");//如果文件不存在if(!f.exists()) {//提示文件不存在System.out.println("sorry! File not found");}//打开输入流is=new FileInputStream(f);//必须设置为共享锁(true)才能读取,否则会报异常FileLock lock=is.getChannel().lock(0,Long.MAX_VALUE,true);//FileLock lock=is.getChannel().lock();错误用法//以字节为单位返回锁定区域的大小。System.out.println(lock.size());//释放锁lock.release();//关闭锁lock.close();}finally {//关闭流is.close();}}
}

如果上面代码的lock()方法没有设置为共享锁,则会出现异常
异常:NonWritableChannelException


异常介绍:尝试写入最初未打开的通道进行写入时抛出未检查的异常。
出现异常原因:原因是这个文件是独占锁,不可读操作

本篇的内容就到这里结束了,有什么想说的欢迎留言

上一篇:Java集合框架+工具类

下一篇:正在更新中…

Java文件读写操作(c站最全!一篇就够了)相关推荐

  1. java文件读写操作类

    借鉴了项目以前的文件写入功能,实现了对文件读写操作的封装 仅仅需要在读写方法传入路径即可(可以是绝对或相对路径) 以后使用时,可以在此基础上改进,比如: 写操作: 1,对java GUI中文本框中的内 ...

  2. Java文件读写操作指定编码方式防乱码

    读文件:BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而提供字符.数组和行的高效读取. 可以指定缓冲区的大小,或者可使用默认的大小.大多数情况下,默认值就足够大了. 通常,Re ...

  3. Java文件读写操作

    Java中I/O流对文件的读写有很多种方法,在这里我主要介绍三种方式,供大家参考. 第一种方式:使用FileWriter和FileReader,对文件内容按字符读取,代码如下 String dir = ...

  4. java文件读写操作大全

    转自http://blog.sina.com.cn/s/blog_4a9f789a0100ik3p.html 一.获得控制台用户输入的信息 public String getInputMessage( ...

  5. java文件读写操作指定编码格式[转]

    读文件: BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而提供字符.数组和行的高效读取. 可以指定缓冲区的大小,或者可使用默认的大小.大多数情况下,默认值就足够大了. 通常,R ...

  6. java大文件读写操作

    转载自:http://blog.csdn.net/akon_vm/article/details/7429245 RandomAccessFile RandomAccessFile是用来访问那些保存数 ...

  7. IO流进行文件读写操作

    IO流进行文件读写操作 一.Java程序读excel文件 excel文件分为xls,xlsx和csv文件. 1.xls和xlsx的主要区别是版本不同: xls是excel2003及以前版本所生成的文件 ...

  8. C++ builder 的文件读写操作总结

    C++ builder 的文件读写操作总结 在编程的过程中,文件的操作是一个经常用到的问题,在C++Builder中,可以使用多种方法对文件操作,下面我就按以下几个部分对此作详细介绍,就是: 1.基于 ...

  9. 【转】Android - 文件读写操作 总结

    Android - 文件读写操作 总结 原文出处:http://blog.csdn.net/ztp800201/article/details/7322110 在android中的文件放在不同位置,它 ...

最新文章

  1. Windows中配置java变量环境
  2. 参数签名ascii码排序的坑
  3. 【Spring】使用Spring和AMQP发送接收消息(下)
  4. nagios监控之(监控配置)
  5. Spring-cloud Config Server 3种配置方式
  6. 学习BIOS与CMOS区别
  7. 功放前级的左右_TDG Audio达芬奇:什么是前级,后极?
  8. C++ 类的静态成员详细讲解(转)
  9. Eucalyptus学习汇总
  10. 李一男2003年在港湾给开发人员培训时的语录
  11. 电影天堂爬去示例基础2
  12. 2021-08-02彻底解决Typora+PicGo-Core+SMMS图床的问题
  13. 【SAP打印】SMARTFORMS标签无法调整横向打印
  14. 【Cocos2dx】飘字特效与碰撞检测
  15. 全民小程序社交分销时代已经来临,掌握小程序分销系统即掌握商机!
  16. 微信怎么更改绑定的游戏服务器,注意啦!微信号可以改了!这里还有一个新功能...
  17. 浅谈表面反射——波动光学篇
  18. 口语100的flash驱动
  19. vue设置img大小的属性_vue-quill-editor图片大小修改
  20. ubantu16.04下配置使用DeepDive

热门文章

  1. 访黏度计算公式_提升网站访客粘度的三大技巧经验分享
  2. 处理谷歌浏览器导出书签为json格式
  3. 从流水线工人到谷歌上班的程序媛。。。
  4. 如何求矩阵的最小多项式
  5. Linux用户与用户组关系
  6. 逻辑思维,阅读付费平台“得到APP”功能分析
  7. 23.Vue绑定style样式
  8. 微信小程序-毕业设计项目
  9. 卿本佳人,奈何做运营
  10. 关于“前言中不允许有内容”的XML错误