Java基础——IO流介绍(2)

Java基础——IO流介绍(2)

首页休闲益智程序员进阶之路更新时间:2024-04-29
1 基本概括

2 主要介绍

2.1 read()与read(byte[] b)和read(byte[] b,int off,int len)方法的区别

read(): 从输入流中读取数据的下一个字节,返回0255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

read(byte[] b) : 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在不指定读取的起始点时,默认从流中读取b.length长度的字节值到字节数组中去,

完全覆盖字节数组,当然前提是该流长度大于或者等于字节数组,如果小于字节数组就只写入流长度的字节。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。

如果 b 的长度为 0 ,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b中。

read()方法每次只能读取一个字节,所以也只能读取由ASCII码范围内的一些字符。read(byte[]b) 可以把参数数组b定义为大小为2的数组即可正常读取汉字了。如果此处定义b 的大小为37等奇数,则对于全是汉字的一篇文档则不能全部正常读写了。

write(byte[] b,int off,int len):

1.从读取流读取一定数量的字节,如果比如文件总共是102个字节

2.我们定义的数组长度是10,但是这里我们写read(bytes,0,9)那么每次往里面添加的(将只会是9个长度),就要读12次,最后一次放入3个.

3.所以一般读取流都不用这个而是用上一个方法:read(byte[]);

2.2 write()与write(byte[] b)和write(byte[] b,int off,int len)方法的区别

write():直接往流写入字节形式的(0-255)int值.

write(byte[] bytes):往流里边写入缓冲字节数组中的所有内容,不满整个数组长度的”空余内容”也会加入。

write(byte[] bytes,int off,int len):

1.这个是更严谨的写法,在外部定义len,然后每次len(为的是最后一次的细节长度)都等于流往数组中存放的长度

2.如上述read(bytes),前面每次都放入十个,第十一次放入的是2个,如果用第二种write(bytes),将会写入输出流十一次,每次写入十个长度,造成后面有8个空的,比原来的内容多了

3.所以用write(byte[] bytes,int off,int len);就不会出现多出来的空的情况,因为最后一次len不同

2.3 available( ) 的使用

available( ) :这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。

注意:1 本地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。

2 available()方法是用于创建一个大小刚刚适合的缓冲区,不过如果资源文件过大的话,很容易造成内在溢出的问题。

int count = in.available(); byte[] b = new byte[count]; in.read(b); 如果有网络延迟,可能数据是分批次发送的,所以对方数据没有送达,就会出现问题,需要改为 int count = 0; while (count == 0) { count = in.available(); } byte[] b = new byte[count]; in.read(b);

2.4 flush方法

1 FileOutputStream本身没有重写flush(),是调用的父类(OutputStream)的方法,而OutputStream仅仅是实现了Flushable接口,flush()是个空方法体,什么都没做,所以对于FileOutputStream来说,flush()没有意义的

2 对于例如BufferedOutputStream这样的子类因为重新了flush(),才是有意义,就需要再调用close()前,先调用flush()

2.5 简单总结

1 文件输出流和输入流可以用于读取图像,图片等原始的字节流数据。

2 FileInputStream和FileOutputStream都可以通过文件路径name,文件对象File,文件描述符三种传参方法创建流对象。

3 FileOutputStream可以设置现写入数据是否覆盖原始文件(true表示的是覆盖)

3 应用

3.1 读取文件

public static void test1() throws IOException{ File file = new File("./test.txt"); if(!file.exists()){ try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileInputStream fis = null; try { fis = new FileInputStream(file); //从输入流中读取数据 // int tmp; // while((tmp = fis.read()) != -1){ // System.out.println(tmp); // } byte[] bytes = new byte[fis.available()];//返回当前文件字节数 fis.read(bytes); System.out.println(Arrays.toString(bytes)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }

3.2 把一张图片复制到指定文件夹

public void t2() throws IOException{ FileInputStream fis=null; FileOutputStream fos=null; //创建输入流输出流 fis=new FileInputStream("C:\\Users\\1\\Desktop\\2018-06-04-V3.png"); fos=new FileOutputStream("C:\\Users\\1\\Desktop\\2018-06-04-V4.png"); //先定义一个字节缓冲区,减少I/O次数,提高读写效率 byte[] n = new byte[1024]; int len; //这里是1024个字节的读取,所以在read()里面放的是每次读取的字节数 while((len = fis.read(n)) != -1){ fos.write(n, 0, len); } fos.close(); fis.close(); }4 源码分析

4.1 FileInputStream源码分析

/** * FileInputStream 从文件系统的文件中获取输入字节流。文件取决于主机系统。 * 比如读取图片等的原始字节流。如果读取字符流,考虑使用 FiLeReader。 */ public class SFileInputStream extends InputStream { /* 文件描述符类---此处用于打开文件的句柄 */ private final FileDescriptor fd; /* 引用文件的路径 */ private final String path; /* 文件通道,NIO部分 */ private FileChannel channel = null; private final Object closeLock = new Object(); private volatile boolean closed = false; private static final ThreadLocal<Boolean> runningFinalize = new ThreadLocal<>(); private static boolean isRunningFinalize() { Boolean val; if ((val = runningFinalize.get()) != null) return val.booleanValue(); return false; } /* 通过文件路径名来创建FileInputStream */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } /* 通过文件来创建FileInputStream */ public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } fd = new FileDescriptor(); fd.incrementAndGetUseCount(); this.path = name; open(name); } /* 通过文件描述符类来创建FileInputStream */ public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; path = null; fd.incrementAndGetUseCount(); } /* 打开文件,为了下一步读取文件内容。native方法 */ private native void open(String name) throws FileNotFoundException; /* 从此输入流中读取一个数据字节 */ public int read() throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int b = 0; try { b = read0(); } finally { IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1); } return b; } /* 从此输入流中读取一个数据字节。native方法 */ private native int read0() throws IOException; /* 从此输入流中读取多个字节到byte数组中。native方法 */ private native int readBytes(byte b[], int off, int len) throws IOException; /* 从此输入流中读取多个字节到byte数组中。 */ public int read(byte b[]) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, 0, b.length); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } /* 从此输入流中读取最多len个字节到byte数组中。 */ public int read(byte b[], int off, int len) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, off, len); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } public native long skip(long n) throws IOException; /* 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 */ public native int available() throws IOException; /* 关闭此文件输入流并释放与此流有关的所有系统资源。 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { fd.decrementAndGetUseCount(); channel.close(); } int useCount = fd.decrementAndGetUseCount(); if ((useCount <= 0) || !isRunningFinalize()) { close0(); } } public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } /* 获取此文件输入流的唯一FileChannel对象 */ public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, path, true, false, this); fd.incrementAndGetUseCount(); } return channel; } } private static native void initIDs(); private native void close0() throws IOException; static { initIDs(); } protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { runningFinalize.set(Boolean.TRUE); try { close(); } finally { runningFinalize.set(Boolean.FALSE); } } } }

4.2 FileOutputStream 源码分析

/** * 文件输入流是用于将数据写入文件或者文件描述符类 * 比如写入图片等的原始字节流。如果写入字符流,考虑使用 FiLeWriter。 */ public class SFileOutputStream extends OutputStream { /* 文件描述符类---此处用于打开文件的句柄 */ private final FileDescriptor fd; /* 引用文件的路径 */ private final String path; /* 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 */ private final boolean append; /* 关联的FileChannel类,懒加载 */ private FileChannel channel; private final Object closeLock = new Object(); private volatile boolean closed = false; private static final ThreadLocal<Boolean> runningFinalize = new ThreadLocal<>(); private static boolean isRunningFinalize() { Boolean val; if ((val = runningFinalize.get()) != null) return val.booleanValue(); return false; } /* 通过文件名创建文件输入流 */ public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); } /* 通过文件名创建文件输入流,并确定文件写入起始处模式 */ public FileOutputStream(String name, boolean append) throws FileNotFoundException { this(name != null ? new File(name) : null, append); } /* 通过文件创建文件输入流,默认写入文件的开始处 */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); } /* 通过文件创建文件输入流,并确定文件写入起始处 */ public FileOutputStream(File file, boolean append) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } this.fd = new FileDescriptor(); this.append = append; this.path = name; fd.incrementAndGetUseCount(); open(name, append); } /* 通过文件描述符类创建文件输入流 */ public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } this.fd = fdObj; this.path = null; this.append = false; fd.incrementAndGetUseCount(); } /* 打开文件,并确定文件写入起始处模式 */ private native void open(String name, boolean append) throws FileNotFoundException; /* 将指定的字节b写入到该文件输入流,并指定文件写入起始处模式 */ private native void write(int b, boolean append) throws IOException; /* 将指定的字节b写入到该文件输入流 */ public void write(int b) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { write(b, append); bytesWritten = 1; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 将指定的字节数组写入该文件输入流,并指定文件写入起始处模式 */ private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; /* 将指定的字节数组b写入该文件输入流 */ public void write(byte b[]) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { writeBytes(b, 0, b.length, append); bytesWritten = b.length; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 将指定len长度的字节数组b写入该文件输入流 */ public void write(byte b[], int off, int len) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { writeBytes(b, off, len, append); bytesWritten = len; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 关闭此文件输出流并释放与此流有关的所有系统资源 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { fd.decrementAndGetUseCount(); channel.close(); } int useCount = fd.decrementAndGetUseCount(); if ((useCount <= 0) || !isRunningFinalize()) { close0(); } } public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, path, false, true, append, this); fd.incrementAndGetUseCount(); } return channel; } } protected void finalize() throws IOException { if (fd != null) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { runningFinalize.set(Boolean.TRUE); try { close(); } finally { runningFinalize.set(Boolean.FALSE); } } } } private native void close0() throws IOException; private static native void initIDs(); static { initIDs(); } }

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved