2.1 read()与read(byte[] b)和read(byte[] b,int off,int len)方法的区别
read(): 从输入流中读取数据的下一个字节,返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
read(byte[] b) : 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在不指定读取的起始点时,默认从流中读取b.length长度的字节值到字节数组中去,
完全覆盖字节数组,当然前提是该流长度大于或者等于字节数组,如果小于字节数组就只写入流长度的字节。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
如果 b 的长度为 0 ,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b中。
read()方法每次只能读取一个字节,所以也只能读取由ASCII码范围内的一些字符。read(byte[]b) 可以把参数数组b定义为大小为2的数组即可正常读取汉字了。如果此处定义b 的大小为3或7等奇数,则对于全是汉字的一篇文档则不能全部正常读写了。
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