虽然看了一些code,但是对于Java IO流一直没有系统学习,今天在此做一个总结。
将数据冲外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。
在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了。
主要的类如下:
1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。
Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。
4. Reader(文件格式操作):抽象类,基于字符的输入操作。
5. Writer(文件格式操作):抽象类,基于字符的输出操作。
java.io包中包含了流式I/O所需要的所有类。在java.io包中有四个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流:
基本数据流的I/O
输入/输出 |
字节流 |
字符流 |
输入流 |
Inputstream |
Reader |
输出流 |
OutputStream |
Writer |
1. 字节流InputStream/OutputStream
1. InputStream抽象类
InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit);
(1) public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
(2) public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的
(3) public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。
1)FileInputStream 把一个文件作为一个InputStream,实现对文件的读取操作
2)ByteArrayInputStream:把内存中的一个缓冲区作为一个InputStream.
3)StringBufferInputStream把一个String对象作为一个InputStream.
2.OutputStream抽象类
输出流OutputStream类是字节输入流的抽象类,此抽象类表示输出字节流的所有类的超类。
1)FileOutputStream把一个文件作为一个OutputStream,实现对文件写操作
2)ByteArrayOutputStream把信息存入内存中的一个缓存区。
1. public void write(byte b[ ]):将参数b中的字节写到输出流。
2. public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。
4. public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。
5. public void close( ) : 关闭输出流并释放与流相关的系统资源。
3. 文件输入流: FileInputStream类
FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节。在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承接数据的中间角色。
作用:以文件作为数据输入源的数据流。或者说是打开文件,从文件读数据到内存的类。
使用方法(1) File fin=new File("d:/abc.txt"); FileInputStream in=new FileInputStream( fin);
使用方法(2) FileInputStream in=new FileInputStream(“d: /abc.txt”); //传这个参数进去还是会重新new一个file
4.文件输出流:FileOutputStream类
作用:用来处理以文件作为数据输出目的数据流;或者说是从内存区读数据入文件
方式1:
File f=new File (“d:/myjava/write.txt ");
FileOutputStream out= new FileOutputStream (f);
方式2:
FileOutputStream out=new FileOutputStream(“d:/myjava/write.txt ");
举一个例子,将file1.txt的内容读出来存到file2.txt中。
1 package lesson1212; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 9 public class FileInputStreamTest { 10 11 public static void main(String[] args) throws IOException { 12 File file = new File("C:/Workspace/file1.txt"); 13 try { 14 FileInputStream fis = new FileInputStream(file); 15 FileOutputStream fos = new FileOutputStream("C:/Workspace/file2.txt"); 16 //byte [] bytes = new byte[50]; 17 //调用read(byte b[], int off, int len)其实这个地方是有问题的,取决于定义bytes数组长度, 18 //bytes被反复覆盖重写,最后可能会把一些多余的信息存到file2.txt中。 19 /* while(fis.read(bytes, 0, bytes.length) != -1){ 20 21 fos.write(bytes, 0, bytes.length); 22 }*/ 23 int num; 24 while((num = fis.read())!=-1){ //这种才是正解 25 fos.write(num); 26 } 27 fis.close(); 28 fos.close(); 29 } catch (FileNotFoundException e) { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 } 33 } 34 }
上面code中说的有个问题,其实多定义个size就可以:
/*int size;
//调用read(byte b[], int off, int len)其实这个地方是有问题的,取决于定义bytes数组长度,
//bytes被反复覆盖重写,最后可能会把一些多余的信息存到file2.txt中。
while((size = bis.read(bytes, 0, bytes.length)) != -1){
bos.write(bytes, 0, size);
}*/
5. 缓冲输入输出流 BufferedInputStream/ BufferedOutputStream
理解紫色箭头部分,是一个缓冲流。
计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。
BufferedInputStream:当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给内存。
BufferedOutputStream :当缓冲流输出数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从内存读取数据到缓冲区。
1)将文件读入内存:
将BufferedInputStream与FileInputStream相接
FileInputStream in=new FileInputStream( “file1.txt ” );
BufferedInputStream bin=new BufferedInputStream( in);
BufferedInputStream(InputStream in) //使用默认buf大小、底层字节输入流构建bis ,默认buffersize是8192
BufferedInputStream(InputStream in, int size) //使用指定buf大小、底层字节输入流构建bis
2)将内存写入文件:
将BufferedOutputStream与 FileOutputStream相接
FileOutputStreamout=new FileOutputStream(“file1.txt”);
BufferedOutputStream bin=new BufferedInputStream(out);
BufferedOutputStream(OutputStream out); //使用默认大小、底层字节输出流构造bos。默认缓冲大小是 8192 字节( 8KB )
BufferedOutputStream(OutputStream out, int size); //使用指定大小、底层字节输出流构造bos
将上面的例子加入缓冲流:
1 package lesson1212; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileNotFoundException; 8 import java.io.FileOutputStream; 9 import java.io.IOException; 10 11 public class FileInputStreamTest { 12 13 public static void main(String[] args) throws IOException { 14 File file = new File("C:/Workspace/file1.txt"); 15 try { 16 FileInputStream fis = new FileInputStream(file); 17 BufferedInputStream bis = new BufferedInputStream(fis); 18 FileOutputStream fos = new FileOutputStream("C:/Workspace/file2.txt"); 19 BufferedOutputStream bos = new BufferedOutputStream(fos); 20 21 //byte [] bytes = new byte[50]; 22 //调用read(byte b[], int off, int len)其实这个地方是有问题的,取决于定义bytes数组长度, 23 //bytes被反复覆盖重写,最后可能会把一些多余的信息存到file2.txt中。 24 /* while(bis.read(bytes, 0, bytes.length) != -1){ 25 26 bos.write(bytes, 0, bytes.length); 27 }*/ 28 int num; 29 while((num = bis.read())!=-1){ //这种才是正解 30 bos.write(num); 31 } 32 bis.close(); 33 bos.close(); 34 } catch (FileNotFoundException e) { 35 // TODO Auto-generated catch block 36 e.printStackTrace(); 37 } 38 } 39 }
这个地方有个疑问:什么时候需要加入BufferedInputStream和BufferedOutputStream?????
BufferedInputStream
和BufferedOutputStream
类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快
从构造方法中我们可以知道BufferedInputStream没有无参构造方法,它必须传入一个InputStream(一般是FileInputStream),来一起使用,以提高读写效率。
那么什么时候flush()才有效呢?
答案是:当OutputStream是BufferedOutputStream时。
6. ByteArrayInputStream/ByteArrayOutputStream
输出不一定非要输出到文件中,也可以输出到内存中,如:ByteArrayOutputStream。输入流的read方法是从流里面读出来数据,
输出流的write方法时将内存中的数据写入到流中,方便输出出去。
ByteArrayInputStream
可以将字节数组转化为输入流 。
ByteArrayOutputStream
可以捕获内存缓冲区的数据,转换成字节数组。
ByteArrayOutputStream
其中封装了一个byte类型的数组buf作为数据的缓存区,当进行数据写人的时候,数据会不断地写入buf缓存区中,该缓存区默认大小为32字节。每一次写人数据后,都要执行ensureCapacity方法来确定缓存区容量十分能够容纳写入数据,如果不够的时候会执行grow方法,将容量翻倍以适应写入的数据。当写入完成后,我们可以通过toByteArray方法,来获得缓存区数组的一个副本。因为这个特性,我们可以使用ByteArrayOutputStream进行一次性的数据写入。
7. 对象操作流(ObjectInputStream,ObjectOutputStream )
ObjectInputStream 称为 反序列化流,利用输入流从文件中读取对象; ObjectOutputStream 称为 序列化流,利用输出流向文件中写入对象
特点:用于操作对象。可以将对象写入到文件中,也可以从文件中读取对象。主要用于序列化和反序列化。
添加一个例子:
1 package lesson1212; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.ByteArrayInputStream; 6 import java.io.ByteArrayOutputStream; 7 import java.io.FileNotFoundException; 8 import java.io.IOException; 9 import java.io.ObjectInputStream; 10 import java.io.ObjectOutputStream; 11 import java.io.Serializable; 12 13 public class ObjectInputStreamDemo { 14 15 public static void main(String[] args) throws ClassNotFoundException { 16 17 Person person1 = new Person("zhangsan",30); 18 Person person2 = new Person("lisi",40); 19 20 try { 21 /*用FileInputStream作为输入流来操作 22 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Workspace/object.txt")); 23 oos.writeObject(person1); 24 oos.writeObject(person2); 25 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:/Workspace/object.txt"));*/ 26 27 //用ByteArrayInputStream作为输入流来操作 28 ByteArrayOutputStream bo = new ByteArrayOutputStream(); 29 ObjectOutputStream oos = new ObjectOutputStream(bo); 30 oos.writeObject(person1); 31 oos.writeObject(person2); 32 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bo.toByteArray())); 33 34 //下面这个地方运行报异常 35 /*ByteArrayOutputStream bao = new ByteArrayOutputStream(); 36 BufferedOutputStream bo = new BufferedOutputStream(bao); 37 ObjectOutputStream oos = new ObjectOutputStream(bo); 38 oos.writeObject(person1); 39 oos.writeObject(person2); 40 ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(bao.toByteArray()))); 41 */ 42 Person person1clone = (Person)ois.readObject(); 43 Person person2clone = (Person)ois.readObject(); 44 System.out.println(person1clone); 45 System.out.println(person2clone); 46 47 oos.close(); 48 ois.close(); 49 } catch (FileNotFoundException e) { 50 // TODO Auto-generated catch block 51 e.printStackTrace(); 52 } catch (IOException e) { 53 // TODO Auto-generated catch block 54 e.printStackTrace(); 55 } 56 57 } 58 59 } 60 61 class Person implements Serializable{ 62 63 /** 64 * 65 */ 66 private static final long serialVersionUID = 1L; 67 private String name; 68 private int age; 69 70 public Person(String name, int age) { 71 this.name = name; 72 this.age = age; 73 } 74 /** 75 * @return the name 76 */ 77 public String getName() { 78 return name; 79 } 80 /** 81 * @param name the name to set 82 */ 83 public void setName(String name) { 84 this.name = name; 85 } 86 /** 87 * @return the age 88 */ 89 public int getAge() { 90 return age; 91 } 92 /** 93 * @param age the age to set 94 */ 95 public void setAge(int age) { 96 this.age = age; 97 } 98 /* (non-Javadoc) 99 * @see java.lang.Object#toString() 100 */ 101 @Override 102 public String toString() { 103 return "Person name " + this.name + ", age " + this.age; 104 } 105 106 107 }
注意:
1:ByteArrayInputStream, FileInputStream ,StringBufferInputStream是三种基本的介质流,分别从Byte数组,文件,StringBuffer中读取数据。
2:ObjectInputStream和BufferedInputStream都是装饰流。 例如BufferedInputStream使其他流具有缓冲功能。
3:ByteArrayOutputStream, FileOutputStream, 是两种基本的介质流,分别向Byte数组,文件中写入数据。
4:ObjectOutputStream和BufferedOutputStream都是装饰流。
在上面第7小节,code里面想多用一次BufferedInputStream,可是有错误,下面code解决了。不过一般应该是不会这样做的。
1 package lesson1212; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.ByteArrayInputStream; 6 import java.io.ByteArrayOutputStream; 7 import java.io.FileInputStream; 8 import java.io.FileNotFoundException; 9 import java.io.FileOutputStream; 10 import java.io.IOException; 11 import java.io.ObjectInputStream; 12 import java.io.ObjectOutputStream; 13 import java.io.Serializable; 14 15 public class ObjectInputStreamDemo { 16 17 public static void main(String[] args) throws ClassNotFoundException { 18 19 Person person1 = new Person("zhangsan",30); 20 Person person2 = new Person("lisi",40); 21 22 try { 23 /*用FileInputStream作为输入流来操作 24 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Workspace/object.txt")); 25 oos.writeObject(person1); 26 oos.writeObject(person2); 27 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:/Workspace/object.txt"));*/ 28 29 //用ByteArrayInputStream作为输入流来操作 30 /* ByteArrayOutputStream bos = new ByteArrayOutputStream(); 31 ObjectOutputStream oos = new ObjectOutputStream(bos); 32 oos.writeObject(person1); 33 oos.writeObject(person2); 34 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 35 ObjectInputStream ois = new ObjectInputStream(bis);*/ 36 37 //下面这个地方运行报异常 38 //FileOutputStream fos = new FileOutputStream("C:/Workspace/object.txt");//对应line46 39 ByteArrayOutputStream bao = new ByteArrayOutputStream(); 40 BufferedOutputStream bos = new BufferedOutputStream(bao); 41 ObjectOutputStream oos = new ObjectOutputStream(bos); 42 oos.writeObject(person1); 43 oos.writeObject(person2); 44 bos.flush(); 45 46 //FileInputStream fis = new FileInputStream("C:/Workspace/object.txt"); //对应于line38 47 ByteArrayInputStream bais = new ByteArrayInputStream(bao.toByteArray()); 48 BufferedInputStream bis = new BufferedInputStream(bais); 49 ObjectInputStream ois = new ObjectInputStream(bis); 50 51 Person person1clone = (Person)ois.readObject(); 52 Person person2clone = (Person)ois.readObject(); 53 System.out.println(person1clone); 54 System.out.println(person2clone); 55 56 oos.close(); 57 ois.close(); 58 } catch (FileNotFoundException e) { 59 // TODO Auto-generated catch block 60 e.printStackTrace(); 61 } catch (IOException e) { 62 // TODO Auto-generated catch block 63 e.printStackTrace(); 64 } 65 66 } 67 68 } 69 70 class Person implements Serializable{ 71 72 /** 73 * 74 */ 75 private static final long serialVersionUID = 1L; 76 private String name; 77 private int age; 78 79 public Person(String name, int age) { 80 this.name = name; 81 this.age = age; 82 } 83 /** 84 * @return the name 85 */ 86 public String getName() { 87 return name; 88 } 89 /** 90 * @param name the name to set 91 */ 92 public void setName(String name) { 93 this.name = name; 94 } 95 /** 96 * @return the age 97 */ 98 public int getAge() { 99 return age; 100 } 101 /** 102 * @param age the age to set 103 */ 104 public void setAge(int age) { 105 this.age = age; 106 } 107 /* (non-Javadoc) 108 * @see java.lang.Object#toString() 109 */ 110 @Override 111 public String toString() { 112 return "Person name " + this.name + ", age " + this.age; 113 } 114 115 116 }
可能没分清这几个Stream具体的使用场景。所以才出现上面那么怪的引用BufferedInputStream。希望哪位大侠指出这么用的不好。
对于字符流,没有研究,后面继续加上。还有一些其他知识加进去。
下面是参考的博客:
https://blog.csdn.net/moonfish0607/article/details/77161138
https://www.cnblogs.com/dolphin0520/p/3791327.html
https://www.cnblogs.com/Jtianlin/p/4188945.html
https://www.cnblogs.com/java1024/p/8906567.html