java基础知识(IO)

对象引用的序列化:
如果某个类的属性类型不是基本类型或String类型,而是另一个引用类型,那么这个引用类必须是可序列化的,否则拥有该类型属性的类是不可序列化的。
Java序列化机制采用了一种特殊的序列化算法,其算法内容是:
1. 所有保存到磁盘中的对象都有一个序列化编号
2. 当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有当该对象从未(在本次虚拟机中)被序列化过,系统才会将该对象转换成字节序列并输出。
3. 如果某个对象是已经序列化过,程序将直接只是输出一个序列化编号,而不是再次重新序列化该对象
由于Java序列化机制使然:如果多次序列同一个Java对象时,只有第一次序列化时才
会把该Java对象转化成字节序列并输出,这样可能会引起一个潜在的问题:当程序序列化一个可变的对象时,程序只有第一次使用writeObject方法输出时才会将该对象转换成字节序列并输出,即使后面该对象的属性已被改变,当程序再次调用writeObject语句时,程序只是输出前面的序列化编号,所以改变的属性值不会被输出。
如:
public class SerializeMutable {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
//创建一个ObjectOutputStream输入流
oos=new ObjectOutputStream(new FileOutputStream(“mutable.txt”));
Preson per=new Preson(“猴子”, 520);
//系统会per对象转化字节序列并输出
oos.writeObject(per);
//改变per对象的name属性
per.setName(“八戒”);
//系统只是输序列化编号,所以改变后的name不会被序列化
oos.writeObject(per);
//创建一个ObjectInputStream输入流
ois=new ObjectInputStream(new FileInputStream(“mutable.txt”));
Preson p1=(Preson)ois.readObject();
Preson p2=(Preson)ois.readObject();
System.out.println(p1==p2);
System.out.println(p2.getName());
System.out.println(p1.getName());
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {
try {
if(ois!=null)
{
ois.close();
}
if(oos!=null)
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
自定义序列化:
当对某个对象进行序列化时,系统会自动把该对象的所有属性依次进行序列化,如果某个属性引用到另一个对象,则被引用的对象也会被序列化,如果被引用的对象的属性也引用了其他对象,则被引用的对象也会被序列化,这种被称为递归序列化。
通过在属性前面加上transient关键字,可以指定Java序列化时无需理会该属性值。
public class Preson implements java.io.Serializable{
private String name;
private transient int age;
//不提供无参构造器
public Preson(String name, int age) {
System.out.println(“有参构造”);
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;
}
}
Transient关键词只能用于修饰属性,可以修饰Java程序中其他部分。如:
public class SerializeMutable {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
//创建一个ObjectOutputStream输入流
oos=new ObjectOutputStream(new FileOutputStream(“mutable.txt”));
Preson per=new Preson(“猴子”, 520);
//系统只是输序列化编号,所以改变后的name不会被序列化
oos.writeObject(per);
//创建一个ObjectInputStream输入流
ois=new ObjectInputStream(new FileInputStream(“mutable.txt”));
Preson p1=(Preson)ois.readObject();
System.out.println(p1.getAge());
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {

			try {
				if(ois!=null)
				{
					ois.close();
				}
				if(oos!=null)
					oos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}
}

}
使用transient关键字修饰属性虽然简单方便,但被transient修饰的属性将被完全隔离在序列化机制外,这样将导致反序列化机制可以让恢复Java无法取得该属性的值,Java还提供了另外一种自定义序列化机制,通过这种自定义序列化机制可以让程序控制如何序列化各属性,甚至完全不序列化某些属性(与使用transient关键字的效果)
序列化和反序列化过程中需要特殊处理的类应该提供如下特殊签名的方法,这些特殊的方法用以实现自定义序列化。
1. private void writeObject(java.io.ObjectOutputStream out)throws IOException
2. Private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException
3. Private void readObjectNoData()throws ObjectStreamException
writeOject方法负责写入特定的实例的状态,以便相应的readObject方法可以恢复它
默认情况下,该方法会调用out.defaultWriteObject来保存Java对象 的各属性,从而实现序列化Java对象状态的目的。
ReadObject方法负责从流中读取并恢复对象的属性,通过重写该方法,可以完全获得反序列化的控制。默认情况下,该方法会调用in.defaultReadObject来恢复Java对象的非静态和非瞬态属性。通常情况下,readObject方法与writeObject对应,如果writeObject方法中对Java对象的属性进行了一些处理,则应该在readObject方法中对其属性进行相应的反处理。
当序列化不完整的时候,readObjectNoData可以用来正确的初始化反序列对象。如:
public class Preson implements java.io.Serializable{
private String name;
private transient int age;
//不提供无参构造器
public Preson(String name, int age) {
System.out.println(“有参构造”);
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;
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.writeObject(new StringBuffer(name).reverse());
out.writeInt(age);
}
private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException
{
this.name=((StringBuffer)in.readObject()).reverse().toString();
this.age=in.readInt();
}
}
WriteObject方法存储属性的顺序应该和readObject方法中恢复属性的顺序一致,否则将不能正常恢复该Java对象。
还有一种自定义机制,他甚至可以在序列化对象时将该对象替换成其他对象,如果需要实现序列化某对象时替换该对象,应为序列化类提供如下特殊的方法:
ANY-ACCESS-MODIFIER Object writeRepalce()throws ObjectStreamException
此writeReplace方法由序列化机制调用,前提是如果此方法存在。因为该方法可以拥有私有(private),受保护的(protected)和包私有(package-private)等访问权限,所以子类可能获得该方法。如:
public class Preson implements java.io.Serializable{
private String name;
private transient int age;
//不提供无参构造器
public Preson(String name, int age) {
System.out.println(“有参构造”);
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;
}
private Object writeReplace() {
ArrayList list=new ArrayList();
list.add(name);
list.add(age);
return list;
}
}
Java的序列化机制保证在序列化某个对象之前,先调用该对象的writeReplace方法,如果该方法返回是另一个Java对象,则系统转为序列化另一个对象。如下:程序实例化的是Arraylist
public class SerializeMutable {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
//创建一个ObjectOutputStream输入流
oos=new ObjectOutputStream(new FileOutputStream(“replace.txt”));
Preson per=new Preson(“猴子”, 520);
//系统只是输序列化编号,所以改变后的name不会被序列化
oos.writeObject(per);
//创建一个ObjectInputStream输入流
ois=new ObjectInputStream(new FileInputStream(“replace.txt”));
ArrayList list=(ArrayList)ois.readObject();
System.out.println(list);
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {

			try {
				if(ois!=null)
				{
					ois.close();
				}
				if(oos!=null)
					oos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}
}

}
序列化机制库还有一个特殊的方法,它可以实现保护性复制整个对象:
ANY-ACCESS-MODIFIER Object readResolve()throws ObjectStreamException
这个方法会紧跟着readObject之后被调用,该方法将返回原来值将会代替原来反序列化的对象,而原来readObject反序列化的对象将被立即抛弃。
所有的单例类,枚举类在实现序列化时都应该提供readResolve方法,这样才可能保证反序列化对象依然正常。
通常的建议是;对于final类重写readResolve方法不会有任何问题,重写readResolve()方法时尽量用private修饰该方法。
另一种自定义序列化机制:
Java还提供了另一种序列化机制,这种序列化的方法完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口,该接口定义两个方法:
1. void readExternal(ObjectInput in):需要序列化的类实现readExternal方法来实现反序列化。该方法调用DataInput(它是ObjectInput的父接口)的方法来恢复基本类型的属性值,调用ObjectInput的readObject方法来恢复应用类型的属性值。
2. Void writeExternal(ObjectOutput out):需要序列化的类实现writeExternal方法来保存对象的状态。该方法调用Data Input(它是ObjectInput的父接口)的方法来保存基本类型的属性值,调用ObjectOutput的writeObject方法来保存应用类型的属性值。
public class Preson implements java.io.Serializable{
private String name;
private transient int age;
//不提供无参构造器
public Preson(String name, int age) {
System.out.println(“有参构造”);
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;
}
private void writeExternal(java.io.ObjectOutputStream out) throws IOException
{
out.writeObject(new StringBuffer(name).reverse());
out.writeInt(age);
}
private void readExternal(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException
{
this.name=((StringBuffer)in.readObject()).reverse().toString();
this.age=in.readInt();
}
}
在这里插入图片描述
关于对象序列化,注意以下几点:
1. 对象的类名,属性(包括基本类型,数组,对其他对象的引用)都会被序列化;方法,static属性(静态属性),transient属性(也被称为瞬态属性)都不会被序列化
2. 实现Serializable接口的类如果需要想让某个属性不被序列化,可以在该属性前面加transient修饰符,而不是加static关键字。
3. 保证序列化对象的属性的类型也是可序列化的,否则需要使用transient关键字来修饰该属性,要不然,则该类是不可序列化的。
4. 反序列化对象时必须有序列化对象的class文件
5. 当通过文件,网络来读取序列化后对象时,必须按实际写入的顺序读取。
版本:
Java如何保证两个class文件的兼容性?
Java序列化机制允许为序列化类提供一个private static final的serialVersionUID属性值,
该属性值用于标识该Java类的序列化版本,也就是说如果一个类升级后,只要他的serialVersionUID属性值保持不变,序列化机制也会把他们当成同一个序列化版本
可以通过JDK安装路径下的serialver.exe工具来获得该类的serialVersionUID值,如:
Serialver Preson
在这里插入图片描述
那些对类的哪些修改可能导致该类反序列化失败?
在这里插入图片描述
Java新IO
新IO采用内存映射文件的方式来处理输入/输出。
Java中NIO相关包如下:
Java.nio包:主要提供一些和Buffer相关的类
Java.nio.channels包:主要包括Channel和Selector相关的类
Java.nio.Charset包:主要包含和字符集相关的类
Java.nio.channels.spi包:主要包含提供Channel服务的类
Java.niocharset.spi包:主要包含提供字符集服务的相关类
Channel(通道)和Buffer(缓冲)是新IO中的两个核心对象,Channel是对传统的输入输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输。Channel与传统的相比最大区别就是它提供了一个Map方法,通过该map方法可以直接将一块数据映射到内存中
Buffer可以被理解成一个容器,它本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先读到Buffer中,还提供了用于支持非阻塞式输入输出的Selector类。
使用buffer:
Buffer是一个抽象类,其最常用的子类是ByteBuffer,他可以在底层字节数组上进行get/set操作。除了它之外,对应其他基本数据类型都有相应的Buffer类:ByteBuffer,CharBuffer,CharBuffer,ShortBuffer,IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer。这些Buffer都没有构造器,通过使用如下方法来得到一个Buffer对象:
Static XXXBuffer allocate(int capacity):创建一个容量为capacity的XXXBuffer对象
ByteBuffer还有一个子类:MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map方法返回。
在Buffet中有三个重要的概念:容量(capacity),界限(limit)和位置(position)
1. 容量:缓冲区的容量表示该Buffer的最大数据容量,即最多也可以存储多少数据。缓冲区的容量不可为负值,在创建后也不能改变。
2. 界限:第一个不应该被读出或写入的缓冲区位置索引。也就是说,位于limit后的数据既不可被读,也不可被写
3. 位置:用于指明下一个可以被读出的或者写入的缓冲区位置索引(类似于IO流中的记录指针)当使用Buffer从Channel中读取数据时,position的值恰好等于已经读到了多少数据。当刚刚创建一个Buffer对象时,其Position为0,如果从Channel中读取了2个数据到Buffer中,则Position为2,指向Buffer中第三个(索引从0开始)位置。
除此之外,Buffer里还可以支持一个可选标记(mark,类似于创痛IO中的mark),该mark允许程序直接将position定位到该mark处。这些值满足如下关系:
0≤mark≤position≤limit≤capacity
在这里插入图片描述
Buffer的主要作用就是装入数据,然后输出数据
Buffer中包含两个重要的方法flip和clear,flip为从Buffer中读取数据做好准备,而clear则向Buffer中装入数据做好准备。
除此之外,Buffer还包含一个常用的方法:
Int capacity():返回Buffer的capacity大小。
Boolean hasRemaining():判断当前位置(position)和界限(limit)之间是否还有元素可供处理。
Int limit():返回Buffer的界限(limit)的位置
Buffer llimit(int newLt):重新设置界限(limit)的值,并返回一个具有新的limit的缓冲区对象
Buffer mark():设置Buffer的mark位置,它只能在0和位置(position)之间做mark
Int position():返回Buffer中当前位置
Buffer position(int newPs):设置buffer的新位置,并返回一个具有改变position后的Buffer对象。
Int remaining():返回当前位置和界限(limit)之间元素的个数
Buffer reset():将位置转到mark所在的位置
Buffer rewind():将位置设置成0取消设置的mark
Buffer的所有子类还提供了两个重要的方法:put和get方法,用于向Buffer中放入数据和取出数据。Buffer既支持对单个数据的访问,又支持对多个数据的访问。
当使用put 和get来访问呢Buffer中的数据时,分为相对和绝对两种。
相对(Relective):从Buffer的当前位置读取或写入数据,然后将位置的值按处理元素的个数增加
绝对:直接根据索引来向Buffer中读取或写入数据,使用绝对方式来访问Buffer里数据时,并不会影响位置position的值。
public class BufferTest {
public static void main(String[] args) {
//创建Buffer
CharBuffer buff=CharBuffer.allocate(8);
System.out.println(“capacity:”+buff.capacity());
System.out.println(“limit:”+buff.limit());
System.out.println(“position:”+buff.position());
//放入元素
buff.put(‘a’);
buff.put(‘b’);
buff.put(‘c’);
System.out.println(“加入三个元素后,position=”+buff.position());
//调用flip方法
buff.flip();
System.out.println(“执行flip后的:”+buff.limit());
System.out.println(“position =”+buff.position());
//取出第一个元素
System.out.println(“第一个元素:”+buff.get());
System.out.println(“取出一个元素后:”+buff.position());
//调用clear方法
System.out.println(“执行clear()后limit:”+buff.limit());
System.out.println(“执行clear后position:”+buff.position());
System.out.println(“执行clear后缓冲区内容并没有被清楚:”+buff.get(2));
System.out.println(“执行绝对读取后,position:”+buff.position());
}
}
使用Channel:
Channel由两个主要的区别:
1. Channel可以直接将指定文件的部分或全部直接映射成Buffer
2. 程序不能直接访问Channel中的数据,包括读取,写入都不行,Channel只能与Buffer进行交互,也就是说,如果要从Channel中取得数据,必须先用Buffer从Channel中取出一些数据,然后让程序从buffer中取出这些数据;如果要将程序中的数据写入Channel,一样先让程序将数据放入Buffer中,程序在将Buffer里的输入写入到Channel
Channel是一个接口,位于Java.nio.channels包下,系统为该接口提供了DATa gram Channel,FileChannel,Pipe.SinkChannel, Pipe.SourceChannel, ServerSocketChannel, SocketChannel等实现类。
所有的Channel方法都不应该通过构造器来创建,而是通过传统的节点InputStream,OutputStream的getChannel方法来返回对应的Channel,不同的节点流获得的Channel不一样。
Channel中最常用的三类方法map,read和write,其中map方法用于将Channel对应的部分或全部数据映射的ByteBuffer;而read或write方法都有一系列重载形式,这些方法用于从Buffer中读取数据或向Buffer里写入数据。
其map方法签名为:MappedByteBuffer map(FileChannel.MapMode mode,long position,long size):第一个参数执行映射时的模式,分别有只读,读写等模式,而第二个,第三个参数用于控制将Channel的那些数据映射成ByteBuffer
如:public class FileChannelTest {
public static void main(String[] args) {
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
File f=new File(“FileChannelTest.java”);
//创建FileInputStream,以该文件输入流创建FileChannel
inChannel=new FileInputStream(f).getChannel();
//将FileChannel 里的数据映射成ByteBuffer
MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
//使用GBK的字符集创建解码器
Charset charset=Charset.forName(“GBK”);
//以文件输出流创建FileBuffer,用以控制输出
outChannel=new FileOutputStream(“a.txt”).getChannel();
//直接将buffer对象里的数据全部输出
outChannel.write(buffer);
//再次调用buffer的clear方法,复原limit,position的位置
buffer.clear();
//创建解码器
CharsetDecoder decoder=charset.newDecoder();
//使用解码器将ByteBuffer转化成CharBuffer
CharBuffer charBuffer=decoder.decode(buffer);
System.out.println(charBuffer);
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {
try {
if(inChannel!=null)
inChannel.close();
if(outChannel!=null)
outChannel.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
RandomAccessFile也有getChannel方法,由RandomAccessFile返回的FileChannel是只读的还是读写的Channel则取决于RandomAccessFile打开文件的模式。如:
public class RandomFileChannelTest {
public static void main(String[] args) {
FileChannel randomChannel=null;
try {
File f=new File(“a.txt”);
//创建一个RandomAccessFile对象
RandomAccessFile raf=new RandomAccessFile(f, “rw”);
//获取RandomAccessFile对应的Channel
randomChannel=raf.getChannel();
//将Channel中所有数据映射成ByteBuffer
ByteBuffer buffer=randomChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
//把Channel记录指针移到最后
randomChannel.position(f.length());
System.out.println(buffer);
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {
try {
if(randomChannel!=null)
randomChannel.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
编码集和Charset:
通常而言,把明文的字符串序列转换成技术及理解的字节序列称为编码,把字节序列转化成普通人能看懂的明文字符串称为解码。
在这里插入图片描述
Java中的编码采用了UNICODE字符集,但很多操作系统并不使用UNICODE字符集,Java提供了CharSet来处理字节序列和字符序列之间的转换关系,该类包含了用于创建解码器和编码器的方法,还提供了获取Charset所支持字符集的方法,Charset类是不可改变的。
Charset里提供了一个availableCharsets()静态方法来获取当前JDK所支持的字符集,所以程序可以使用如下程序来获取该JDK所支持的全部字符集。如:
public class CharsetTest {
public static void main(String[] args) {
//获取全部字符集
SortedMap<String, Charset> map=Charset.availableCharsets();
for (String alias : map.keySet()) {
//输出
System.out.println(alias+"---------->"+map.get(alias));
}
}
}
可以使用System类的getProperties方法来访问本地系统文件编码格式,文件编码格式的属性名为file.encoding。
程序可以调用Charset的forName()方法来创建对应的Charset对象,forName方法参数就是相应字符集的别名,如:
Charset cs=Charset.forName(“ISO-8859-1”);
Charset csCn=Charset,forName(“GBK”);
public class CharsetTransform {
public static void main(String[] args) throws IOException {
//创建简体中文对应的Charset
Charset cn=Charset.forName(“GBK”);
//获取cn对象对应的编码器和解码器
CharsetEncoder cnEncoder=cn.newEncoder();
CharsetDecoder cnDecoder=cn.newDecoder();
//创建一个CharBuffer
CharBuffer cbuff=CharBuffer.allocate(8);
cbuff.put(‘孙’);
cbuff.put(‘污’);
cbuff.put(‘空’);
cbuff.flip();
//将Charbuffer中的字符序列转化成字节序列
ByteBuffer bbuff=cnEncoder.encode(cbuff);
//循环访问ByteBuffer每个字节
for (int i = 0; i < bbuff.capacity(); i++) {
System.out.print(bbuff.get(i)+" “);
}
//将ByteBuffer的数据解码成字符序列
System.out.println(”\n"+cnDecoder.decode(bbuff));
}
}
实际上Charset里提供了如下三个方法:
1. CharBuffer decode(ByteBuffer bb):将ByteBuffer中字节序列转化成字符序列的便捷方法
2. ByteBuffer encode(CharBuffer cb):将CharBuffer中的字符序列转换成字节序列的
3. ByteBuffer enCode(String str):将string 中的字符串序列转换成字节序列的便捷方式。
在String类里也提供了一个getBytes(String charset)方法,该方法返回byte[] ,该方法也
是使用指定的字符集将字符串转换成字节序列。
文件锁:
使用文件锁可以有效的阻止多条进程并发修改同一份文件,所以现在大部分操作系统都提供了文件锁的功能。
在新IO中,Java提供了FileLock来支持文件锁定功能,在FileChannel中提供的lock()/trylock()
方法来获得文件锁File Lock对象,从而锁定文件,lock和tryLock方法存在区别:当lock试图锁定某个文件时,如果无法得到文件锁,程序将一直阻塞。而tryLock也是尝试锁定文件,他将直接返回而不是阻塞,如果获得了文件锁,该方法返回文件锁,否则将返回null
如果FileChannel只想锁定文件的部分,而不是锁定全部则可以使用如下lock或tryLock方法:
Lock(long position,long size,Boolean shared):对文件从position位置开始,长度为size的内容加锁,该方法时阻塞式的。
TryLock(long position,long size,Boolean shared):不阻塞的加锁方法。
当参数shared为true,则表明该锁是一个共享锁,他将允许多个进程来读取文件,但阻止其他进程获得对该文件的排它锁。当shared为false时,表明该锁是一个排它锁,他将锁住对该文件的读写。程序可以通过调用FileLock的isShared来判断它获得的锁是否为共享锁。
直接使用Lock或tryLock对整个文件获取的文件锁是排它锁。
处理完文件后通过release()方法释放文件锁。
如:
public class FileLockTest {
public static void main(String[] args) throws Exception {
FileChannel channel=null;
try {
//使用FileOutputStream获取FileChannel
channel=new FileOutputStream(“a.txt”).getChannel();
//使用非阻塞方式对指定文件加锁
FileLock lock=channel.tryLock();
//程序暂停五秒
Thread.sleep(5000);
//释放锁
lock.release();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
finally {
try {
if(channel!=null)
channel.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
关于文件锁还需要注意:
1. 在某些平台上,文件锁仅仅是建议性的,并不是强制性的,这意味着即使一个程序不能获得文件锁,它也可以对该文件进行读写
2. 在某些平台上,不能同步的锁定一个文件并把她映射到内存中。
3. 文件锁是由Java虚拟机所持有的,如果两个Java程序使用同一个Java虚拟机运行,则他们不能对同一个文件加锁。
4. 在某些平台上当关闭FileChannel时,会释放Java虚拟机在该文件上的所有锁,因此应该避免对同一个被锁定的我呢见打开多个FileChannel。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值