IO流:InputStream/OutputStream字节流
一、InputStream/OutputStream图例
二、InputStream中的读取数据的方法如下
int read()
功能:读取一个字节的数据,并且返回读到得数据,如果返回-1,则表示读到输入流的末尾。int read(byte[] b)
功能:从输入流中读取一定量的字节,并将其存储在字节数组b中,返回实际读取的字节数,如果返回-1,则表示读到输入流的末尾。int read(byte[] b, int off, int len)
功能:将数据读入一个字节数组,同时返回读取的实际字节数,如果返回-1,则表示读到输入流的末尾。off指定在数组b中存放数据的起始偏移位置,len指定读取的最大字节数。available()
功能:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取或跳过的估计字节数。close()
功能:关闭输入流,释放这个流的相关资源。
三、OutputStream中写入数据的方法如下:
int write(int b)
功能:将b的最低的一个字节写入此输入流,其他三个字节丢弃。int write(byte[] b)
功能:将指定的字节数组b写入此输入流。int write(byte[] b, int off, int len)
功能:将指定byte数组中从偏移量off开始的len个字节写入输入流。flush()
功能:刷新此输入流并强制写出所有缓冲的输出字节数。close()
功能:关闭输出流,释放这个流的相关资源。
四、FileInputStream/FileOutputStream
定义:
- FileInputStream具体实现了在文件上读取数据,FileOutputStream实现了在文件上写byte数据的方法
FileInputStream构造函数:
- File对象创建:public FileInputStream(File file);
- 文件地址字符串创建:public FileInputStream(String name);
FileInputStream一般方法:
public int read()
不接收任何对象,每次写入一个字节8bit数据。public int read(byte b[])
接受一个数组对象,每次从文件中读取相应byte[]数组大小的内容。public int read(byte b[], int off, int len)
每次从文件中读取len长度的内容到byte数组中,并且从byte数组的off处开始存储。close()
写完后close(),关闭输入流;- 注意:read方法如果没有读到文件内容结尾,每返回的是每次的读入的(byte型)内容,如果读到到文件内容结尾,返回-1
FileOutputStream构造函数:
- 文件地址字符串:public FileOutputStream(String name)
- 文件地址字符串/是否追加文件内容:public FileOutputStream(String name, boolean append)
- File对象:public FileOutputStream(File file)
- File对象/是否追加文件内容:public FileOutputStream(File file, boolean append)
FileOutputStream一般方法:
public void write(byte b[])
接受一个数组对象,将数组中对象中的内容输出到文件中public void write(byte b[], int off, int len)
将数组中的off角标开始后的len个字节写入public void write()
不接收任何对象,每次写入一个字节8byte数据close()
写完后需要close()关闭输出流注意:write方法如果没有写完相应内容,每返回的是每次的写入(byte型)内容;如果写到内容结尾,返回-1;
copy文件示例:
public static void piliang_copy(String path, String fileName){
long startTimes = System.currentTimeMillis();
File file = new File(path, fileName);
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(new File(path,"14.rar"));
byte[] buff = new byte[1024];
int by;
while ((by = fileInputStream.read(buff,0,buff.length)) != -1){
fileOutputStream.write(buff,0,by);
fileOutputStream.flush();
}
fileOutputStream.close();
fileInputStream.close();
long endTimes = System.currentTimeMillis();
System.out.println("FileInputStream 批量读取:" + (endTimes - startTimes));
} catch (IOException e) {
e.printStackTrace();
}
}
五、PipedInputStream/PipedOutputStream
PipedInputStream定义:
- 管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。 通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。
注意:
- 不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏
PipedInputStream构造方法:
- 创建尚未连接的 PipedInputStream:PipedInputStream();
- 创建一个尚未连接的 PipedInputStream,并对管道缓冲区使用指定的管道大小:PipedInputStream(int pipeSize);
- 创建 PipedInputStream,使其连接到管道输出流 src:PipedInputStream(PipedOutputStream src);
- 创建一个 PipedInputStream,使其连接到管道输出流 src,并对管道缓冲区使用指定的管道大小:PipedInputStream(PipedOutputStream src, int pipeSize);
PipedInputStream一般方法:
int available()
返回可以不受阻塞地从此输入流中读取的字节数void connect(PipedOutputStream src)
使此管道输入流连接到管道输出流 srcint read()
读取此管道输入流中的下一个数据字节int read(byte[] b, int off, int len)
将最多 len 个数据字节从此管道输入流读入 byte 数组
PipedOutputStream定义:
- 可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。
注意:
不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。
PipedOutputStream构造函数:
- PipedOutputStream()
创建尚未连接到管道输入流的管道输出流。 - PipedOutputStream(PipedInputStream snk)
创建连接到指定管道输入流的管道输出流。
PipedOutputStream一般方法:
void connect(PipedInputStream snk)
将此管道输出流连接到接收者。void flush()
刷新此输出流并强制写出所有缓冲的输出字节。void write(byte[] b, int off, int len)
将 len 字节从初始偏移量为 off 的指定 byte 数组写入该管道输出流。void write(int b)
将指定 byte 写入传送的输出流。
示例:
public class PipedStream {
public static void main(String[] args) throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
// 使输入管道流与输出管道流联通
input.connect(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
}
class Input implements Runnable {
private PipedInputStream in;
Input(PipedInputStream in) {
this.in = in;
}
public void run() {
try {
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1) { //read 读取从管道输出流写入的数据,没有读到数据则阻塞
String s = new String(buf, 0, len);
System.out.println("s=" + s);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Output implements Runnable {
private PipedOutputStream out;
Output(PipedOutputStream out) {
this.out = out;
}
public void run() {
try {
while (true) { // 模拟数据写入
Thread.sleep(3000);
out.write("Output管道写入的数据!".getBytes());
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
六、ByteArrayInputStream/ByteArrayOutputStream
ByteArrayInputStream定义:
- ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
注意:
- 关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
ByteArrayInputStream构造函数:
- 创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组:ByteArrayInputStream(byte[] buf)
- 创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组:ByteArrayInputStream(byte[] buf, int offset, int length)
ByteArrayInputStream示例:
private static void test1() {
byte[] buf=new byte[]{'h','e','l','l','o'};
// 用一个字节数组来构造一个ByteArrayInputStream
ByteArrayInputStream bais=new ByteArrayInputStream(buf,0,3);
byte[] buff=new byte[bais.available()-1];
try {
bais.read(buff);
System.out.println(new String(buff));
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bais.close(); // 关闭 ByteArrayInputStream 无效,是一个空实现
} catch (IOException e) {
e.printStackTrace();
}
}
// 仍然可以继续读
int read = bais.read();
System.out.println((char)read);
}
ByteArrayOutputStream定义:
- 此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。 可使用 toByteArray() 和 toString() 获取数据。
注意:
- 关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
ByteArrayOutputStream构造函数:
- ByteArrayOutputStream()
创建一个新的 byte 数组输出流。 - ByteArrayOutputStream(int size)
创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)
ByteArrayOutputStream一般方法:
int size()
返回缓冲区的当前大小。byte[] toByteArray()
创建一个新分配的 byte 数组。String toString()
使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。String toString(String charsetName)
使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串。void writeTo(OutputStream out)
将此 byte 数组输出流的全部内容写入到指定的输出流参数中,这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
ByteArrayOutputStream示例:
private static void test() throws IOException {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
byte[] buf=new byte[]{'h','e','l','l','o','w','o','r','l','d'};
baos.write(buf, 0, buf.length-5);
System.out.println(baos.toString());
baos.close(); // 空实现
// 继续往流中写入
baos.write(buf, 5, 5);
System.out.println(baos.toString());
}
七、SequenceInputStream
定义:
- 序列流,用于将多个输入流串联起来,形成一个更大的流,以方便对多个文件的同时操作。 从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
构造函数:
- SequenceInputStream(Enumeration< ? extends InputStream> e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。 - SequenceInputStream(InputStream s1, InputStream s2)
通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。
一般方法:
- int available()
返回不受阻塞地从当前底层输入流读取(或跳过)的字节数的估计值,方法是通过下一次调用当前底层输入流的方法。 void close()
关闭此输入流并释放与此流关联的所有系统资源。int read()
从此输入流中读取下一个数据字节。int read(byte[] b, int off, int len)
将最多 len 个数据字节从此输入流读入 byte 数组。
示例一:
- 两个流的情况
需求:把a.java和b.java的内容复制到copy.java中。
使用 SequenceInputStream(InputStream s1, InputStream s2)构造实现
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
InputStream s1 = new FileInputStream("a.java");
InputStream s2 = new FileInputStream("b.java");
// SequenceInputStream(InputStream s1, InputStream s2)
SequenceInputStream sis = new SequenceInputStream(s1, s2);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("copy.java"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = sis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
示例二:
- 多个流的情况
需求:把三个文件a.java,b.java,c.java的内容复制到copy.java中
使用SequenceInputStream(Enumeration e)构造方法来实现多个流的串联
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建Vector用于存储输入流
Vector<InputStream> v = new Vector<InputStream>();
InputStream s1 = new FileInputStream("a.java");
InputStream s2 = new FileInputStream("b.java");
InputStream s3 = new FileInputStream("c.java");
v.add(s1);
v.add(s2);
v.add(s3);
// Enumeration是Vector的elements()方法的返回值类型,Enumeration<E> elements()
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("copy.java"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = sis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
八、ObjectInputStream/ObjectOutputStream
ObjectInputStream定义:
- 对象输入流,对 ObjectOutputStream 写入的基本数据和对象进行反序列化。
注意:
- 对象要能从流中读取,它必须是可序列化的
- 读取的顺序应该与写入时的顺序是一致的
ObjectInputStream构造函数:
- 创建从指定 InputStream 读取的 ObjectInputStream:public ObjectInputStream(InputStream in);
ObjectInputStream一般方法 :
int readInt()
读取一个 32 位的 int 值Object readObject()
从 ObjectInputStream 读取对象String readUTF()
读取 UTF-8 修改版格式的 Stringint read()
读取数据字节int read(byte[] buf, int off, int len)
读入 byte 数组
ObjectOutputStream定义:
- 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。
通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
注意:
- 要写入流中的对应必须支持序列化,并且最好指定该对象的序列化UID
- 反序列化的顺序与写入的顺序要一致
- 使用transient关键字声明不需要序列化的成员变量
ObjectOutputStream构造函数:
- 创建写入指定 OutputStream 的 ObjectOutputStream:ObjectOutputStream(OutputStream out) ;
ObjectOutputStream一般方法:
void writeBytes(String str)
以字节序列形式写入一个 String。void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream。void writeUTF(String str)
以 UTF-8 修改版格式写入此 String 的基本数据。
示例:
public static void d7(){
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
/*objectOutputStream = new ObjectOutputStream(
new FileOutputStream("c:\\test\\java\\dd3.txt")
);
StuTest stuTest = new StuTest(1, "省道", 17);
objectOutputStream.writeObject(stuTest);
objectOutputStream.flush();*/
objectInputStream= new ObjectInputStream(
new FileInputStream("c:\\test\\java\\dd3.txt")
);
try {
StuTest test = (StuTest) objectInputStream.readObject();
System.out.print(test);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
objectInputStream.close();
//objectOutputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
九、PrintStream
定义:
- 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。
注意:
- 可以创建一个自动刷新PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (‘\n’)来完成。
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
构造函数:
PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。PrintStream(File file, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。PrintStream(OutputStream out)
创建新的打印流。PrintStream(OutputStream out, boolean autoFlush)
创建新的打印流。PrintStream(OutputStream out, boolean autoFlush, String encoding)
创建新的打印流。PrintStream(String fileName)
创建具有指定文件名称且不带自动行刷新的新打印流。PrintStream(String fileName, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
示例(我们常见的System.out返回值其实就是PrintStream):
public class PrintStreamDemo{
public static void main(String[] args) throws IOException {
// 获取标准输入流
PrintStream ps = System.out;
// // OutputStream os = ps;
// OutputStream os = System.out; // 多态
// ps.println("hello");
// ps.print(false);
//BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 等价于下面两句
// OutputStreamWriter osw = new OutputStreamWriter(os);
// BufferedWriter bw = new BufferedWriter(osw);
OutputStream out=new FileOutputStream("a.txt");
PrintStream stream=new PrintStream(out, true); // 开启自动刷新
stream.print(0);
stream.print("hello");
stream.println("world");
stream.close();
stream.append((char) 0);
stream.println("world"); // 关闭流打印,仍不会抛出异常。
}
}
十、BufferedInputStream/BufferedOutputStream
BufferedInputStream定义:
- 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。缓冲区用以减少频繁的IO操作,提高程序的性能。
注意:
- 在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。
BufferedInputStream构造函数:
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream一般方法:
int read()
功能:读取一个字节的数据,并且返回读到得数据,如果返回-1,则表示读到输入流的末尾。int read(byte[] b)
功能:从输入流中读取一定量的字节,并将其存储在字节数组b中,返回实际读取的字节数,如果返回-1,则表示读到输入流的末尾。int read(byte[] b, int off, int len)
功能:将数据读入一个字节数组,同时返回读取的实际字节数,如果返回-1,则表示读到输入流的末尾。off指定在数组b中存放数据的起始偏移位置,len指定读取的最大字节数。available()
功能:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取或跳过的估计字节数。close()
功能:关闭输入流,释放这个流的相关资源。
BufferedInputStream性能测试示例:
/**
* @author zhl
*
* 效率测试
*
* FileInputStream
* ①read() 35950毫秒
* ②read(buff) 62毫秒
*
* BufferedInputStream
* ③read() 295毫秒
* ④read(buff) 18毫秒
*
*/
public class EfficiencyTest {
public static void main(String[] args) throws IOException {
//test1();
//test2();
//test3();
test4();
}
// BufferedInputStream ④read(buff) 18毫秒
private static void test4() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"cpp.wmv"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("cpp_copy4.wmv"));
byte[] buff = new byte[1024];
int len = 1;
while ((len = bis.read(buff)) != -1) {
bos.write(buff, 0, len);
}
bos.close();
bis.close();
System.out.println(System.currentTimeMillis() - start + "毫秒");
}
// BufferedInputStream ③read() 295毫秒
private static void test3() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"cpp.wmv"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("cpp_copy3.wmv"));
int b = 1;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bos.close();
bis.close();
System.out.println(System.currentTimeMillis() - start + "毫秒");
}
// FileInputStream ②read(buff) 62毫秒
private static void test2() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("cpp.wmv");
FileOutputStream fos = new FileOutputStream("cpp_copy2.wmv");
int len;
byte[] buff = new byte[1024];
while ((len = fis.read(buff)) != -1) {
fos.write(buff, 0, len);
}
fis.close();
fos.close();
System.out.println(System.currentTimeMillis() - start + "毫秒");
}
// FileInputStream ①read() 35950毫秒
private static void test1() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("cpp.wmv");
FileOutputStream fos = new FileOutputStream("cpp_copy1.wmv");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
System.out.println(System.currentTimeMillis() - start + "毫秒");
}
}
BufferedOutputStream定义:
- 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。缓冲区减少IO操作以提高性能。
注意:
- 在读取或跳过流中的字节时, 可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。
BufferedOutputStream构造函数:
- BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 - BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedOutputStream一般方法:
int write(int b)
功能:将b的最低的一个字节写入此输入流,其他三个字节丢弃。int write(byte[] b)
功能:将指定的字节数组b写入此输入流。int write(byte[] b, int off, int len)
功能:将指定byte数组中从偏移量off开始的len个字节写入输入流。flush()
功能:刷新此输入流并强制写出所有缓冲的输出字节数。close()
功能:关闭输出流,释放这个流的相关资源。
BufferedOutputStream示例:
private static void test1() throws IOException {
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("bos.txt"));
// 等价于下面两句
//FileOutputStream fos=new FileOutputStream(new File("bos.txt"));
//BufferedOutputStream bos=new BufferedOutputStream(fos);
bos.write(66); // B的ASCII为66
bos.write(new byte[]{'h','e','l','l','0','~'});
bos.close();
// 写入文件内容
// Bhell0~
}