我们已经系统学习了File
类,并且已经知道 File
类的实例用于表示文件或目录的路径
名。
虽然我们可以通过 File
实例来访问文件或目录的元数据,甚至可以创建、删除文件或目
录,但是,我们却不能通过File
实例来访问文件中存储的内容,本节主要研究通过流来
读写数据。
为了能够 读取文件中的内容 或者 向文件中写入内容 ,就需要用到文件输入流或文件输
出流,本节将系统讲解通过流完成对文件内容的读取和写入操作。
但千万不要认为我们只能从文件中读取数据或向文件中写入数据,还有在之前我们从控制
台获取或打印到控制台以及在网络编程部分我们将会学习如何通过 Java
程序从网络
上读取数据和向网络发送数据。
在 Java
语言中,将够 读取数据 或者 写出数据 的对象抽象为 流。
流 类似于生活当中的 水管 , 水 可以在 水管 中 定向移动 ,正如 数据 可以在 流 中定向移
动。
我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,
分为 输入input
和 输出 output
,即流向内存是输入流,流出内存是输出流。
Java
中I/O
操作主要是指使用java.io
包下的内容,进行输入、输出操作。输入也叫做读
取数据,输出也叫做作写出数据。
在 Java
传统的IO
体系中,所有的 流 对应的类型都扩展自四个抽象类:
其中的 InputStream
和 OutputStream
的子类表示字节流, Reader
和Writer
的子类则
表示字符流。
这里所提及的Stream
一词均表示流,请不要与 java.util.stream.Stream
接口混为
一谈。
Java
中传统的I/O
体系可以按照不同的方式对流进行分类:
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都是一个一
个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时
候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节流 是以 字节 为单位读写数据的输入流或输出流。下图是Java
语言中提供的 字节流
的继承体系:
由图可以看到, 字节流 体系中涉及的类太多,我们仅选择具有代表性的几个予以讲解,
并不是全部讲解。
java.io.Closeable
接口,实现此接口的都是可以关闭的数据的源或目的地。都会实现 c lose()
方法。如: Scanner
, InputStream
, OutputStream
public interface Closeable extends AutoCloseable
该类继承了 java.lang.AutoCloseable
, AutoCloseable
接口提供了自动关闭的能力。
InputStream
类是所有表示字节输入流的类的父类,它是个抽象类,因此不能直接被实例
化。
public abstract class InputStream implements Closeable
InputStream
类是所有字节输入流的最顶层父类,掌握该类的使用即可从根本上把握其它
字节输入流的使用。
InputStream 类仅有一个无参构造供子类调用:
public InputStream()
JDK9
提供的方法:
transferTo(OutputStream out)
用于将输入流使用指定的输出流输出出去
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
long transferred = 0;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read;
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
out.write(buffer, 0, read);
transferred += read;
}
return transferred;
}
复制文件:
package file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo07_0912 {
public static void main(String[] args) {
/**
* 将 F:\temp\test.mp4可 复制到 D:\kaifamiao\software\text\test.mp4
* 使用`transferTo()`,将输入流的内容直接写入输出流
*/
try {
FileInputStream fis = new FileInputStream("F:\\test.mp4");
FileOutputStream fos = new FileOutputStream("D:\\kaifamiao\\software\\text\\test.mp4");
//第一种方法
// byte[] bytes = new byte[8019];
// int size;
// while ((size=fis.read(bytes))!=-1){
// fos.write(bytes,0,size);
// }
// //第二种方法
// byte[] bytes = fis.readAllBytes();
// fos.write(bytes);
//第三种方法
fis.transferTo(fos);
fis.close();
fos.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
readAllBytes()
读取所有的字节(最大为 Integer.MAX_VALUE
个字节)
public byte[] readAllBytes() throws IOException {
return readNBytes(Integer.MAX_VALUE);
}
在所有方法中,仅 read()
方法是抽象方法,因此 InputStream
类的所有的子类都需要
实现该方法。
因为 InputStream
类是个抽象类,因此必须借助于其 非抽象子类 来完成实例化。
主要学习 FileInputStream
和 BufferedInputStream
FileInputStream
用于从文件系统中的某个文件中读取内容(以字节为单位)。
FileInputStream
用于读取诸如图像数据之类的原始字节流。
如需读取字符流,可以考虑使用 InputStreamReader
或 FileReader
。
public class FileInputStream extends InputStream
FileInputStream
类中定义了三个公开( public
)的构造方法:
其中较为常用的是前两个构造方法。
package com.itlaobing.demo.stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Test {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream in = new FileInputStream("D:\\temp\\zixiafaerie.jpg");
File file = new File("D:\\temp\\zixiafaerie.jpg");
FileInputStream input = new FileInputStream(file);
}
}
FileInputStream
类重写了 InputStream
类中所有的 public
方法,但除了以下方法未被重写外,其它方法均已被 FileInputStream
类所重写
public void reset() throws IOException
public void mark(int readlimit) throws IOException
public boolean markSupported() throws IOException
重写的方法包括:
public int available() throws IOException
public int read() throws IOException
public int read(byte[] bytes) throws IOException
public int read(byte[] bytes, int offset, int length) throws IOException
public long skip(long n) throws IOException
public void close() throws IOException
这里仅结合 FileInputStream 来讲解 InputStream 类中的核心方法的用法。
首先,我们创建在D:/temp
目录下创建一个名称为file-input.txt
的文件,其中内容如
下:
abcd1234efg
我和我的祖国
A little cold awn first, then gun out such as dragon.
然后我们分别用三种不同的方式读取该文件( D:/temp/file-input.txt
)中的数据。
这里需要注意,直接在 Windows
系统中创建的 文本文档 ,其默认编码为 GBK
(每个汉字
占用两个字节)。
在InputStream
类中定义的抽象方法是所有子类所必须实现的:
public abstract int read() throws IOException
该方法用于从字节输入流中读取单个字节;如果到达流的末尾,则返回 -1 。
而另一个方法 skip 则用于跳过指定的字节数:
public long skip(long n) throws IOException
该方法的参数 n
表示期望跳过的字节数,而返回值则表示实际所跳过的字节数。
同时, InputStream
类提供了获取流中剩余字节数的方法:
public int available() throws IOException
用于获取可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末
尾,则返回 0 。