一、
在java程序中,对于数据的输入/输出操作以“流”方式进行:J2SDK提供了各种各样的“流”类,用以获取不同种类的数据;
程序中通过标准的方法输入或输出数据。
流是用来读写数据的,java有一个类叫File,它封装的是文件的文件名,只是内存里面的一个对象,真正的文件是在硬盘上的一块空间,在这个而文件里面存放着各种各样的数据,我们想读文件里面的数据怎么办?是通过一个流的方式来读,咱们想要从程序中读数据,对于计算机而言,对于计算机来说,无论读什么类型的数据都是以0101010100101这样的形式读取的。怎么把文件里面的数据读出来呢,你可以把文件想象成一个小桶,文件就是一个桶,文件里面的数据就相当于这个桶里面的水,那么我们怎么从这个桶里面取水呢,也就是怎么从这个文件读取数据呢。
常见的取水方法是我们用一根管道插在桶上面,然后在管道的另一边打开水龙头,桶里面的水就开始从水龙头里面流出来了,桶里面的水是通过这根管道流出来的,因此这根管道就叫流,java里面的流式输入/输出跟水流的原理一模一样,当你要从文件读取数据的时候,一根管道查到文件里面去,然后文件里面的数据就顺着管道流出来,这时你在管道的另一头就可以读取到从文件流出来的各种各样的数据了。当你要往文件写入数据时,也是通过一根管道,让要写入的数据通过这根管道流进文件里面去。除了ongoing文件中取数据外,还可以通过网络,比如用一根管道把我和你的机子连接起来,我说一句话,通过这个管道流进你的机子里面,你马就可以看得到,而你说一句话,通过这根管道留到我的机子里面,我也马上就可以看到。有的时候,一根管道不够用,比方说这根管道流过来的水有一些杂质,我们就可以在这根管道的外面再包一层管道,把杂质过滤掉。从程序的角度讲,从计算机读取到的原始数据肯定都是010101这种形式,一个字节一个字节地往外读。
二、
java.io包中定义了多个流类型(类或抽象类)来实现输入、输出功能:可以从不同的角度来对其进行分类:
按流的方向不同可以分为输入流和输出流.
按处理数据单位不同可以分为字节流和字符流。
(字符流是一个字符一个字符的往外读取数据。一个字符是两个字节。字节流最原始的一个流,读出来的数据就是010101这种最底层的数据表示形式,只不过它是按照字节来读的,一个字节(byte)是8bit,读的时候不是一位一位来读,而是一个字节一个字节来读)
按照功能分为节点流和处理流
io包里面定义了所有的流,所以一说流指的是io包里的
什么叫输入流?什么叫输出流?用一根管道一端插进文件里程序里面,然后开始读数据,那么这是输入还是输出呢?如果站在文件的角度上,这叫输出,如果站在程序的角度上,这叫输入。
记住:以后说输入流和输出流都是站在程序的角度上来说。
三、节点流和处理流
节点流为可以从一个特定的数据源(节点)读写数据(如:文件,内存)
处理流是"连接"在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
类型 | 字符流 | 字节流 |
File |
FileReader FileWriter |
FileInputStream FileOutputStream |
Memory Array |
CharArrayReader CharArrayWriter |
ByteArrayInputStream ByteArrayOutputStream |
Memory String |
StringReader StringWriter |
-- |
Pipe |
PipedReader PipedWriter |
PipedInputStream PipedOutputStream |
字节流就是一根管道直接插到数据源上面,直接读数据源里面的数据,或者是直接往数据源里面写入数据,典型的节点流就是文件流:文件的字节输入流(FileInputStream),文件的字节输出流(FileOutputStream),文件的字符输入流(FileReader),文件的字符输出流(fileWriter)。
处理类型 | 字符流 | 字节流 |
Buffering |
BufferedReader BufferedWriter |
BufferedInputStream BufferedOutputStream |
Filtering |
FilterReader FilterWriter |
FilterInputStream FilterOutputStream |
Converting between bytes and character |
InputStreamReader OutStreamWriter |
|
Object Serialization |
ObjectInputStream ObjectOutputStream |
|
Counting | LineNumberReader | LineNumberInputStream |
Data conversion |
DataInputstream DataOutputStream |
|
peeking ahead | pushbackReader | PushbackInputStream |
Printing | PrintWriter | PrintStream |
处理流是包在别的流上面的流,相当于包到别的管道上面的管道。
四、InputStream(输入流)
InputStream
继承自InputStream的流都是用于向程序中输入数据,且数据的单位为字节。
凡是以InputStream结尾的管道,都是以字节的形式向我们的程序输入数据。
abstract int |
read()
Reads the next byte of data from the input stream.
|
int |
read(byte[] b)
Reads some number of bytes from the input stream and stores them into the buffer array
b . |
int |
read(byte[] b, int off, int len)
Reads up to
len bytes of data from the input stream into an array of bytes. |
read是一个字节一个字节地往外读,每读取一个字节,就处理一个字节。read(byte[] buffer)方法读取数据时,先把读取到的数据填满这个byte[]类型的数组buffer(buffer是内存里面的一块缓冲区),然后再处理数组里面的数据。
五、OutputStream(输出流)
继承自OutputStrema的流用于程序输入数据,且数据的单位是字节8(bit);
void |
close()
Closes this output stream and releases any system resources associated with this stream.
|
void |
flush()
Flushes this output stream and forces any buffered output bytes to be written out.
|
void |
write(byte[] b)
Writes
b.length bytes from the specified byte array to this output stream. |
void |
write(byte[] b, int off, int len)
Writes
len bytes from the specified byte array starting at offset off to this output stream. |
abstract void |
write(int b)
Writes the specified byte to this output stream.
|
六、Reader流
继承自Reader的流都是用于向程序中输入数据,而且数据单位为字符(16bit)
int |
read()
Reads a single character.
|
int |
read(char[] cbuf)
Reads characters into an array.
|
abstract int |
read(char[] cbuf, int off, int len)
Reads characters into a portion of an array.
|
int |
read(CharBuffer target)
Attempts to read characters into the specified character buffer.
|
七、Writer流
继承自Writer的流都是用于程序中输出数据,而且数据的单位为字符(16bit)
基本方法
abstract void |
flush()
Flushes the stream.
|
void |
write(char[] cbuf)
Writes an array of characters.
|
abstract void |
write(char[] cbuf, int off, int len)
Writes a portion of an array of characters.
|
void |
write(int c)
Writes a single character.
|
void |
write(String str)
Writes a string.
|
void |
write(String str, int off, int len)
Writes a portion of a string.
|
八、处理流
1、缓冲流
缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法。
J2SDK提供了四种缓冲流。
BufferReader
BufferWriter
BufferInputStream
BufferoutpuStream
缓冲输入流支持其父类的mark和reset方法
BufferReader方法提供了readLine方法用于读取一行字符串
BufferWriter提供了newLine用于写入一个行分隔符
对于输出的缓冲流,写出的数据会现在内存中缓冲,使用flush方法将会使内存中的数据立刻写出
flush 将缓冲区的文件取出 放到目标文件中去。
带有缓冲区的,缓冲区(buffer)就是内存里面的一块区域,读写数据时都是先把数据放到缓冲区里面,减少对io的访问次数,保护我们的硬盘。先把数据放置到缓冲区上,等到缓冲区满了以后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样就可以有效的减少对硬盘的访问次数,有力我们的硬盘保护。
2、转换流
1)InputStreamReader 和 OutputStreamWriter用于字节数据到字符数据之间的转换。
2)InputStreamReader需要和InputStream“套接”
3)OutputStreamWriter需要和OutputStream“套接”
转换流在构造时可以指定其编码集合 例如:
InputStream str = new InputStreamReader(System.in,"USI8859_1");
转换流非常的有用,他可以把一个字节流转化成一个字符流,转换流有两种,一种叫InputStreamReader,另一种叫OutputStreamWriter。InputStream是字节流,InputStreamReader就是把InputStream转换成Reader。OutputStream是字节流,Write是字符流,OutputStreamWriter就是把OutputStream转换成Writer。把OutputStream转换成Writer之后就可以一个字符一个字符地通过管道写入数据了,而且还可以写入字符串。我们如果用一个FileOutputStream流往文件里面写东西,得要一个字节一个字节地写进去,但是如果我们在FileOutputStream流上面套上一个字符转换流,那我们就可以一个字符串一个字符串地写进去。
package com.lost.Stream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; public class TestTransform1 { public static void main(String[] args) { // TODO Auto-generated method stub try { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\a.txt")); osw.write("00000000000000000008888"); System.out.println(osw.getEncoding()); osw.close(); osw = new OutputStreamWriter(new FileOutputStream("D:\a.txt",true), "ISO8859_1"); osw.write("222222222222222222229999999999999999999"); System.out.println(osw.getEncoding()); osw.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.lost.Stream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class TestTransform2 { public static void main(String[] args) { // TODO Auto-generated method stub try { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = null; s = br.readLine(); while(s != null) { System.out.println(s.toUpperCase()); s = br.readLine(); if(s.equalsIgnoreCase("exit")) { break; } } } catch (Exception e) { // TODO: handle exception } } }
in本身就是一个InputStream,是一个标准的输入流,所以这里首先是用in这个流作为节点流直接与数据源连接,数据源里面的数据等待着用于在键盘上输入。用户从键盘输入数据后,首先通过in流传递到包在它外面的isr流,这个流是一个转换流,可以把字节流转换为字符流,这样数据在经过isr流以后变成了字符流传递到抱在它外面的br流,使用br流是为了把数据存储到内存里面时可以用br流里面的readLine方法,readLine方法是每次读取一行的数据,这样可以较快地把从输入的数据吸入到内存里面的s标记的那块区域里去,在输入的数据最终是存储在内存里面的s那块区域中。需要看到s里面装的内容时就可以把S里面装的内容打印出来就可以看得到了。
3、数据流
DataInputStream和DataOutputStream分贝继承自InputStream和OutputStream,它是处理流,需要分别套接在InputStream和OutputStream类型的节点流上。
DataInputStream和DataOutputStream提供了可以存取与机器无关的java原始类型数据(如:int,double等)的方法。
package com.lost.Stream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; public class TestDataStream { public static void main(String[] args) { // TODO Auto-generated method stub ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try { double d_num = Math.random(); dos.writeDouble(d_num); dos.writeBoolean(true); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); System.out.println(bais.available()); DataInputStream dis = new DataInputStream(bais); System.out.println(dis.readDouble()); System.out.println(dis.readBoolean()); dos.close(); bais.close(); } catch (Exception e) { // TODO: handle exception } } }
通过bais这个流往外读取数据的时候,是一个字节一个字节的往外读取的,因此读取出来的数据无法判断是字符串还是bool类型的值,因此要在它的外面再套一个流,通过DataInputStream把读取的数据转换就可以了。注意:读取数据的时候是先写进来的就先读出来,因此读ByteArray字节数组的顺序应该是把8个字节的double类型读出来,然后再读哪个只占一个字节的boolean类型的数,因为double类型的数是先写进数组里面的,读的时候也要先读它。这就是所谓的先写的要先读。如果限度Boolean类型的那个数,那么读出来的情况可能就是把double类型的8个字节的一个流读了出来。
4、打印流
PrintWriter和PrintStream都属于输出流,分别针对字符和字节。
PrintWriter和PrintStream提供了重载的print
println方法用于多种数据类型的输出
PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息。
PrintWriter和PrintStream有各自的flush功能
package com.lost.Stream; import java.io.FileOutputStream; import java.io.PrintStream; public class TestPrintStream { public static void main(String[] args) { // TODO Auto-generated method stub PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream("D:\a.txt"); ps = new PrintStream(fos); if(ps != null) { System.setOut(ps); } for(char c=0;c<256;c++) { System.out.print(c+" "); } } catch (Exception e) { // TODO: handle exception } } }
setOut输出重定向。
5、对象流
直接将Object写入或读出
1)transient 关键字
2)serializable接口
3)externalizable接口
package com.lost.Stream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; //************************************************* //凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口 // Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类作标记,只是起到一个标记作用。 //这个标记是给编译器看的,编译器看到这个标记之后就可以知道这个类可以被序列化 如果想把某个类的对象序列化,就必须得实现Serializable接口 class T implements Serializable { //Serializable是可以被序列化的。 int i = 10; int j = 9; double d = 2.3; int k = 15; transient int m = 15; //声明变量时如果加上transient关键字,那么这个变量就会被当做是透明的,即不存在。 } public class TestObjectIo { public static void main(String[] arg) { T t = new T(); t.k = 8; try { FileOutputStream fos = new FileOutputStream("D:\a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(t); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("D:\a.txt"); ObjectInputStream ois = new ObjectInputStream(fis); T t_read = (T)ois.readObject(); System.out.println(t_read.i+" "+t_read.j+" "+t_read.d+" "+t_read.k+" "+t_read.m+" "); ois.close(); fis.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
直接实现Serializable接口的类是JDK自动把这个类的对象序列化,而如果实现public interface Externalizable extends Serializable 的类则可以自己控制对象的序列化。
小结: