关于 new FileOutputStream(FileDescriptor)的疑问
2018-01-01 22:12
966 查看
案例一:
两个数据流关联同一个文件,都没关闭,由于数据流默认采取 append = false 的方法写入文件,故后一个数据流总是会覆盖前一个数据流写入的数据。public class Test { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream(new File("data.txt")); FileOutputStream fos2 = new FileOutputStream(new File("data.txt")); fos.write("lehua".getBytes()); fos2.write("wendan".getBytes()); fos.close(); fos2.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果:
案例二:
前一个数据流关闭后,后一个数据流依然能后正常写入数据public class Test { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream(new File("data.txt")); FileOutputStream fos2 = new FileOutputStream(new File("data.txt")); fos.write("lehua".getBytes()); fos.close(); fos2.write("wendan".getBytes()); fos2.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果:
案例三:
一个数据流按照正常方式关联着文件,另一个数据流由前一个数据流的FileDescriptor产生。public class Test { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream(new File("data.txt")); FileDescriptor fd = fos.getFD(); FileOutputStream fos2 = new FileOutputStream(fd); fos.write("lehua".getBytes()); fos2.write("wendan".getBytes()); fos.close(); fos2.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果如下:
案例三和案例一很相似,都是两个输出流没有关闭就写入了数据,但是为什么这里写入的是”lehuawendan”,而案例一写入的只是“wendan”呢?因为fos2引用了fos1的fd,个人猜测,只有new File()才真正和一个文件建立了关联,fos2是通过fos1建立的,fos2写入数据的时候,可能使用的也是fos1建立起来的关联,故虽然同样是写入,案例三没有发生数据重写,而案例一发生了。
案例四:
前一个数据流关闭后,后一个数据流无法写入数据public class Test { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream(new File("data.txt")); FileDescriptor fd = fos.getFD(); FileOutputStream fos2 = new FileOutputStream(fd); fos.write("lehua".getBytes()); fos.close(); fos2.write("wendan".getBytes()); fos2.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果如下:
疑问1
案例三中为什么 fos2 能够写入数据?查看 FileOutputStream 的源码
关联源文件的唯一属性就是path,也就是说,要想写入数据到文件,必须知道path,那fos2知道path是什么吗?
fos2 是通过new FileOutputStream(FileDescriptor)产生的,源码如下:
public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } this.fd = fdObj; this.append = false; this.path = null; fd.attach(this); }
根据源码我们知道,FileOutputStream fos2 并不直接知道要写入数据的路径(因为其this.path = null),fos2只是从fos1得到了一个 FileDescriptor对象,难道fos1的fd中有path信息?
查看FileDescriptor源码如下:
FileDescriptor 的成员属性中并不包含path信息,而且fos1在赋值 fd时,传入的是一个新值,肯定没有path信息,那唯一可以传入path信息的就是fd.attach()
查看 FileDescriptor的attach()源码如下:
而FileOutputStream 本身 implements Closeable,也就是说,fos1将自己赋值给了fd的属性parent,那fd当然能够掌握fos1的全部信息,也就包括path信息,fos2引用了fd,故也就找到了写入数据的路径。
测试如下:
public class Test { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream(new File("data.txt")); System.out.println("源FileOutputStream 地址:" + fos); FileDescriptor fd = fos.getFD(); FileOutputStream fos2 = new FileOutputStream(fd); fos.write("lehua".getBytes()); fos2.write("wendan".getBytes()); fos.close(); fos2.close(); Class clazz = fd.getClass(); Field field = clazz.getDeclaredField("parent"); field.setAccessible(true); FileOutputStream source = (FileOutputStream) field.get(fd); System.out.println("fd 的parent 地址 : "+ source); Class c = source.getClass(); Field field2 = c.getDeclaredField("path"); field2.setAccessible(true); String path = (String) field2.get(source); System.out.println("fos2 可以访问路径: "+ path); } catch (Exception e) { e.printStackTrace(); } } }
运行结果如下:
大胆猜测:
FileOutputStream 的write()方法是native方法,源码如下:
private native void write(int b, boolean append) throws IOException;
此方法在写入数据的时候,首先直接判断path是否为null,如果为null,则通过其属性FileDescriptor,通过上面的反射方法获取path,总之,一定要找到写入数据的路径。
疑问2:
同样是两个不同的FileOutputStream对象,为什么案例2,前者fos关闭,后者正常写入,而案例四,前者关闭了,而后者就崩溃了呢?
fos.write("lehua".getBytes()); fos.close(); fos2.write("wendan".getBytes()); fos2.close();
查看FileOutputStream的close()源码如下:
关键之处就是 fd.closeAll(),查看源码如下:
此方法关闭了此FileDescriptor对象的所有parent,fos1对象建立时调用一次fd.attach(),则fd.parent = fos1,源码如下:
public FileOutputStream(File file, boolean append) throws FileNotFoundException { …… this.fd = new FileDescriptor(); fd.attach(this); …… }
fos2对象建立时,再调用一次fd.attach(),则fos1,fos2都成为了 fd对象的otherParent,
源码如下:
public FileOutputStream(FileDescriptor fdObj) { …… this.fd = fdObj; this.append = false; this.path = null; fd.attach(this); }
所以 fos1调用close(),关闭了其fd的所有otherParent,也就把fos2关闭了,所以fos2再写入时,就会报错。
注意:
fos2是通过fos1产生的,fos2调用close(),fos1也会照样关闭,因为他们作用的,是连接它们的FileDescriptor对象,其执行closeAll(),六亲不认,所有parent,统统关闭。相关文章推荐
- 关于Android file.createNewFile() 失败的问题
- 关于C++中常用的对DEBUG_NEW、THIS_FILE重定义的说明
- new FileOutputStream(fileName)时出现 FileNotFoundException
- 关于GCC的warning: no newline at end of file警告的原因
- 关于Android file.createNewFile() 方法出现的问题总结
- new FileOutputStream("a.bmp") 报错java.io.FileNotFoundException: /a.bmp (Read-only file system)
- 关于OnFileNew()的定制
- java如何对文件追加写入【new FileOutputStream(file, true)】
- 关于no newline at end of file
- 关于Android file.createNewFile() 方法出现的问题总结
- 程序使用ObjectOutputStream(new FileOutputStream(fileName,true))向文件尾写入多个对象,多次运行,最后进行读取的时候抛出StreamCorruptedException
- [Java 12 IO] PrintStream 打印流 new PrintStream(new FileOutputStream(new File("/home。。
- [Java 12 IO] PrintStream 打印流 new PrintStream(new FileOutputStream(new File("/home。。
- 关于MFC中如何调用CWinApp::OnFileNew()和OnFileClose()
- 关于FileDialog的路径问题
- 关于new和delete,new[] 和delete[]
- 【spring】关于location路径的设置(classpath、file、file+system property)
- 关于递归实现字符串反转,没想到字符随机写入操作,不new就不行?
- 关于web.xml中的<welcome-file-list>中的默认首页文件
- keil:warning: #1-D: last line of file ends without a newline