FileInputStream :用于读取原始字节流,入图像数据。如果读取字符流,请考虑使用FileReader
public class FileInputStream extends InputStream
继承了静态类 InputStream
public abstract class InputStream implements Closeable {
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {}
public synchronized void mark(int readlimit) {}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {
return false;
}
}
这也说明了静态类不一定只有静态方法 (笑)
构造方法有
//输入相关的文件名
//如果文件不存在,是一个目录而不是一个正常的文件,亦或是因为某些原因文件不能被打开,就会抛出异常
//return :创建一一个被打开文件的字节流
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
//输入文件
//异常同上
//return :创建一一个被打开文件的字节流
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null); //获取文件的路径
SecurityManager security = System.getSecurityManager();//启动java安全管理器
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.attach(this); //关联FileInputStream实例与FileDescriptor实例
path = name;
open(name); //打开文件进行读取
}
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
* FileDescriptor is being shared by streams.
* Register this stream with FileDescriptor tracker.
*/
fd.attach(this);
}
//读取b.length字节的数据流入一个字节数组 b
//byte b[] 就是一个缓冲区
//return : 每一次读入缓冲区的总字节数,如果到达结尾后,返回-1
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
// off b的起始偏移量
// len 每一次读取多少
// 每一次读入缓冲区的总字节数,如果到达结尾后,返回-1
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
//native 表示调用的方法在外部定义
private native int readBytes(byte b[], int off, int len) throws IOException;
@Test
public void ioTest() throws IOException {
InputStream inputStream = new FileInputStream(new File("C:\\file\\hello.txt"));
byte[] b = new byte[4];
int len = 0;
while((len = inputStream.read(b))!= -1) {
System.out.println(len);
}
}
// 4 4 4 4 2 每一次读取4个
//如果改成这样,则会抛出异常
while((len = inputStream.read(b,1,4))!= -1) {
System.out.println(len);
}
//如果这样的话,每一次读取两个
while((len = inputStream.read(b,1,2))!= -1) {
System.out.println(len);
}
// 2 2 2 2 2 2 2 2 2
//返回可以被此输入流读取的剩余字节数的估计值
public native int available() throws IOException;
//跳过并丢失流中的n节字节数据
public native long skip(long n) throws IOException;
@Test
public void ioTest() throws IOException {
InputStream inputStream = new FileInputStream(new File("C:\\file\\hello.txt"));
System.out.println(inputStream.available());
inputStream.skip(3);
System.out.println(inputStream.available());
}
// 18 15
下面是关于FileOutputStream
是一个将数据写入文件的的输出流
public class FileOutputStream extends OutputStream
下面是它的构造函数
//创建一个写入指定文件的输出流
//如果文件是一个目录,或不存在但不能被创建,或者是因为某些原因不能被打开,则抛出异常
//如果有安全管理,那么需要check文件是否允许被写入
//name : 文件的路径+文件的名字+后缀
//可以清楚的看见,这个构造函数使用了File
public FileOutputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, false);
}
//append: 如果为true,将写入文件的末尾而不是开头
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();
fd.attach(this);
this.append = append;
this.path = name;
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.append = false;
this.path = null;
fd.attach(this);
}
FileDescriptor 是“文件描述符”。
FileDescriptor 可以被用来表示开放文件、开放套接字等。
以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。
//将一个字节写入输出流中
public void write(int b) throws IOException {
write(b, append);
}
//从指定的b中获取b.length个字节写入输出流中
public void write(byte b[]) throws IOException {
writeBytes(b, 0, b.length, append);
}
//从指定的b中偏移量off快开始,获取len个字节写入输出流中
public void write(byte b[], int off, int len) throws IOException {
writeBytes(b, off, len, append);
}
@Test
public void ioTest() throws IOException {
InputStream inputStream = new FileInputStream("C:\\file\\hello.txt");
OutputStream outputStream = new FileOutputStream("C:\\file\\hello1.txt",true);
byte[] b = new byte[3];
int len = 0;
while((len = inputStream.read(b))!= -1) {
outputStream.write(b);
}
outputStream.close();
inputStream.close();
}
但这样有时候会出问题,就是当你每次读取的字节数不能被问文件的总字节数整除的时候,就会多写入一些字节
根据输出的结果,我推测是因为每一次使用read的时候,并不是一次性覆盖b。而是一个一个的覆盖,先覆盖第一个,然后第二个。。。等到文件结尾的时候,就不覆盖了。所以,容易写入上一次没有覆的字节
解决方法之一,还是用这些
byte[] b = new byte[inputStream.available()];