Java文件读写操作(c站最全!一篇就够了)
写在前面
你们好,我是小庄。很高兴能和你们一起学习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类的方法
- 创建文件/文件夹
方法 | 作用 |
---|---|
createNewFile()
|
当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。 |
mkdir()
|
创建由此抽象路径名命名的目录。只创建一级目录 |
mkdirs()
|
创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。可以创建多级目录 |
- 删除文件/文件夹
方法 | 作用 |
---|---|
delete()
|
删除由此抽象路径名表示的文件或目录。 |
- 获取文件/文件夹
方法 | 作用 |
---|---|
getName()
|
返回由此抽象路径名表示的文件或目录的名称。 |
getParent()
|
返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null |
getPath()
|
将此抽象路径名转换为路径名字符串。 |
- 判断文件/文件夹是否存在
方法 | 作用 |
---|---|
exists()
|
测试此抽象路径名表示的文件或目录是否存在。 |
isFile()
|
测试此抽象路径名表示的文件是否为普通文件 |
isDirectory()
|
测试此抽象路径名表示的文件是否为目录。 |
- 遍历文件夹
方法 | 作用 |
---|---|
list()
|
返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。 |
listFiles(FileFilter f)
|
返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录 |
listFiles()方法我们单独拿出来讲
- 获取文件的大小
方法 | 作用 |
---|---|
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类的lock
或tryLock
方法
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)
我们对上面代码进行分析:
- 前面两个参数是文件加锁的长度(容量)
- 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站最全!一篇就够了)相关推荐
- java文件读写操作类
借鉴了项目以前的文件写入功能,实现了对文件读写操作的封装 仅仅需要在读写方法传入路径即可(可以是绝对或相对路径) 以后使用时,可以在此基础上改进,比如: 写操作: 1,对java GUI中文本框中的内 ...
- Java文件读写操作指定编码方式防乱码
读文件:BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而提供字符.数组和行的高效读取. 可以指定缓冲区的大小,或者可使用默认的大小.大多数情况下,默认值就足够大了. 通常,Re ...
- Java文件读写操作
Java中I/O流对文件的读写有很多种方法,在这里我主要介绍三种方式,供大家参考. 第一种方式:使用FileWriter和FileReader,对文件内容按字符读取,代码如下 String dir = ...
- java文件读写操作大全
转自http://blog.sina.com.cn/s/blog_4a9f789a0100ik3p.html 一.获得控制台用户输入的信息 public String getInputMessage( ...
- java文件读写操作指定编码格式[转]
读文件: BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而提供字符.数组和行的高效读取. 可以指定缓冲区的大小,或者可使用默认的大小.大多数情况下,默认值就足够大了. 通常,R ...
- java大文件读写操作
转载自:http://blog.csdn.net/akon_vm/article/details/7429245 RandomAccessFile RandomAccessFile是用来访问那些保存数 ...
- IO流进行文件读写操作
IO流进行文件读写操作 一.Java程序读excel文件 excel文件分为xls,xlsx和csv文件. 1.xls和xlsx的主要区别是版本不同: xls是excel2003及以前版本所生成的文件 ...
- C++ builder 的文件读写操作总结
C++ builder 的文件读写操作总结 在编程的过程中,文件的操作是一个经常用到的问题,在C++Builder中,可以使用多种方法对文件操作,下面我就按以下几个部分对此作详细介绍,就是: 1.基于 ...
- 【转】Android - 文件读写操作 总结
Android - 文件读写操作 总结 原文出处:http://blog.csdn.net/ztp800201/article/details/7322110 在android中的文件放在不同位置,它 ...
最新文章
- Windows中配置java变量环境
- 参数签名ascii码排序的坑
- 【Spring】使用Spring和AMQP发送接收消息(下)
- nagios监控之(监控配置)
- Spring-cloud Config Server 3种配置方式
- 学习BIOS与CMOS区别
- 功放前级的左右_TDG Audio达芬奇:什么是前级,后极?
- C++ 类的静态成员详细讲解(转)
- Eucalyptus学习汇总
- 李一男2003年在港湾给开发人员培训时的语录
- 电影天堂爬去示例基础2
- 2021-08-02彻底解决Typora+PicGo-Core+SMMS图床的问题
- 【SAP打印】SMARTFORMS标签无法调整横向打印
- 【Cocos2dx】飘字特效与碰撞检测
- 全民小程序社交分销时代已经来临,掌握小程序分销系统即掌握商机!
- 微信怎么更改绑定的游戏服务器,注意啦!微信号可以改了!这里还有一个新功能...
- 浅谈表面反射——波动光学篇
- 口语100的flash驱动
- vue设置img大小的属性_vue-quill-editor图片大小修改
- ubantu16.04下配置使用DeepDive