在Java程序中,对于数据的输入/输出操作以“流”(Stream)方式进行。
J2SDK提供了各种各样的“流”类,用以获取不同种类的数据:程序中通过标准的方法输入或输出数据。
读入写出
流是用来读取数据的,Java有一个类叫File,它封装的是文件的文件名,只有内存里面的一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各样的数据,我们想读文件里面的数据怎么办呢?
是通过一个流的方式来读,咱们要想从程序读数据,对于计算机来说,无论读什么类型的数据都是以0101011101010这样的形式来读取的。怎么把文件里面的数据读出来呢?
你可以吧文件想象成一个小桶,文件就是一个桶,文件里面的数据相当于是这个桶里面的水,那么我们怎么从这个桶里面取水呢,也就是怎么从这个文件读取数据呢?
常见的取水的办法就是我们用一根管道插到桶上面,然后在管道的另一边打开水龙头,桶里面的水就开始从水龙头里流出来,桶里面的水是通过这根管道流出来的,因此这根管道就叫流,Java里面的流式输入/输出跟水流的原理一模一样,当你要从文件读取数据的时候,一根管道插到文件里面去,然后文件里面的数据就会顺着管道流出来,这时你在管道的另一头就可以读取到从文件流出来的各种各样的数据了。
当你要往文件写入数据时,也是通过一根管道,让要写入的数据通过这根管道流进文件里面去。
除了从文件去取数据以外,还可以通过网络,比如用一根管道把我和你的电脑连接起来,我说一句话,通过这个管道流进你的电脑里面,你马上就可以看到,而你说一句话,通过这个管道流进我的电脑里面,我也马上就能看到。
有时候,一根管道不够用,比方说这根管道流过来的水有一些杂质,我们就可以在这根管道外面在包一层管道,把杂质给过滤掉。
从程序的角度来讲,从计算机读取的原始数据肯定都是01010110这种形式的,一个字节一个字节的往外读,当你这样读的时候你会觉得这样的方法不合适,没关系,你在这根管道的外面在包一层比较强大的管道,这个管道可以吧010101101转换成字符串。这样你使用程序读取数据时读取到的不再试010101110这种形式的数据了,而是一些可以看得懂的字符串。
Java.io包中定义了多个流类型(类或抽象类)来实现输入/输出功能,可以从不同的角度对其进行分类
我们来理解两个概念:
J2SDK所提供的所有流类型位于包java.io内,都分别继承以下四种抽象流类型。
输入流:InputStream(字节流),Reader(字符流)
输出流:OutPutStream(字节流),Writer(字符流)
这四个类都是抽象类,可以把这四个类想象成四根不同的管道。一端接着你的程序,另一端接着数据源,你可以通过输出管道从数据源里面往外读取数据,也可以通过输入管道往数据源里面输入数据,总之,通过这四根管道可以让数据流进来和溜出去。
io包里面定义了所有的流,所以一说流指的就是io包里面的
什么叫输入流?什么叫输出流?
用一根管道的一端插进文件里,一端插进程序里面,然后开始读数据,那么这是输入还是输出呢?
如果站在文件的角度,这叫输出。
如果站在程序的角度上,这叫输入。
记住,以后说输入流和输出流都是站在程序的角度来说。
你要是对原始的流不满意,你可以在这根管道外面在套其他的管道,套在其他的管道之上的流叫做处理流。
为什么需要处理流呢?这就跟水流里面有杂质,你要过滤它,你可以在套一层管道过滤这些杂质一样。
类型 | 字符流 | 字节流 |
File(文件) | FileReader、FileWriter | FileInputStream、FileOutputStream |
Memory Array |
CharArrayReader、CharArrayWriter | ByteArrayInputStream、ByteArrayOutputStream |
Memory String |
StringReader、StringWriter | --------- |
Pipe(管道) | PipedReader、PipedWriter | PipedInputStream、PipedOutputStream |
节点流就是一根管道直接插到数据源上面,直接读数据源里面的数据,或者直接往数据源里面写入数据。
典型的节点流是文件流:文件的字节流(FieInputStream),文件的字节输出流(FileOutputStream),文件的字符输入流(FileReader),文件的字符输出流(FileWriter)
处理类型 | 字符流 | 字节流 |
Buffering | BufferedReader、BufffferedWriter |
BufffferedInputStream、BufffferedOutputStream
|
Filtering
|
FilterReader、FilterWriter
|
FilterInputStream,FilterOutputStream
|
Converting between
bytes and chaacter
|
InputStreamReader、OutputStreamWriter
|
---------------------- |
Object Serialization
|
-------------------------- |
ObjectInputStream、ObjectOutputStream
|
Data conversion
|
-------------------------- |
DataInputStream、DataOutputStream
|
Counting
|
LineNumberReader
|
LineNumberInputStream
|
Peeking ahead
|
PusbackReader
|
PushbackInputStream
|
Printing
|
PrintWriter
|
PrintStream
|
处理流是包在别的流上面的流,相当于包在别的管道上面的管道。
我们看到的具体的某一些管道,凡是以InputStream结尾的管道,都是以字节形式向我们的程序输入数据。
继承自InputStream的流都是用于向程序中输入数据,且数据的单位为字节(8bit)
下图中深色为节点流,浅色为处理流
1 //读取一个字节并以整数的形式返回(0-255) 2 //如果返回-1说明已经到输入流的末尾 3 int read() throws IOException 4 5 //读取一系列字节并存储到一个数组buffer 6 //返回实际读取的字节数,如果读取前已到输入流的末尾,则返回-1 7 int read(byte[] buffer) throws IOException 8 9 //读取length个字节 10 //并存储到一个字节数据buffer,从length位置开始 11 //返回实际读取的字节数,如果读取前以到输入流的末尾返回-1 12 int read(byte[] buffer,int offset,int length) throws IOException 13 14 //关闭流释放内存资源 15 void close() throws IOException 16 17 //跳过n个字节不读,返回实际跳过的字节数 18 long skip(long n) throws IOException
read()方法时一个字节一个字节的往外读,每读取一个字节,就处理一个字节。
read(byte[] buffer)方法读取数据时,先把读取到的数据填满这个byte[]类型的数组buffer(buffer是内存里面的一块缓冲区),然后在处理数组里面的数据,这就跟我们取水一样,先用一个桶去接,等桶接满水后在处理桶里面的水。
如果是没读取一个字节就处理一个字节,这样子读取太累了。
以File(文件)这个类型作为讲解节点流的典型代表
源码查看,分析结构
【演示:使用FileInputStream流来读取FileInputStream.java文件的内容】
1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 import java.io.FileReader; 4 import java.io.IOException; 5 6 public class TestFileInputStream { 7 public static void main(String[] args) { 8 //使用变量b来装调用read()方法时返回的整数 9 int b =0; 10 //使用FileInputStream流来读取有中文的内容时,读出来的是乱码 11 //因为InputStream流里面的read()方法读取内容时是一个字节一个字节的读取 12 //而汉字是占用两个字节的,所以读取出来的汉字无法正确显示 13 //FileInputStream in = null; 14 //使用FileReader流来读取内容时,中英文都可以正确显示,因为Reader流里面的read()是一个字符一个字符的读取的 15 //这样每次读取出来的都是一个完整的汉字,这样就可以正确的显示了 16 FileReader in = null; 17 try { 18 //in = new FileInputStream("E:\\javastudy\\java\\static\\src\\Student.java"); 19 in = new FileReader("E:\\javastudy\\java\\static\\src\\Student.java"); 20 } catch (FileNotFoundException e) { 21 e.printStackTrace(); 22 System.out.println("系统找不到指定文件"); 23 //系统非正常退出 24 System.exit(-1); 25 } 26 //使用变量num来记录读取到的字符数 27 long num = 0; 28 //调用read()方法时会抛异常,所以要捕获异常 29 try { 30 while((b = in.read())!=-1) { 31 //把使用数字表示的汉字和英文字母转换成字符输入 32 System.out.println((char) b); 33 num++; 34 } 35 in.close();//关闭输入流 36 System.out.println(); 37 System.out.println("总共读取了"+num+"个字节的文件"); 38 } catch (IOException e1) { 39 e1.printStackTrace(); 40 System.out.println("文件读取错误"); 41 } 42 } 43 44 }
继承自OutputStream的流是用于程序中输出数据,且数据的单位为字节(8bit)
下面中深色的为节点流,浅色的为处理流
1 //向输出流中写入一个字节数据,该字节数据为参数b的低8位 2 void write(int b) throws IOException 3 4 5 //将一个字节类型的数组中的数据写入输出流 6 void write(byte[] b) throws IOException 7 8 9 //将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流 10 void write(byte[] b,int off,int len) throws IOException 11 12 13 //关闭流释放内存资源 14 void close() throws IOException 15 16 17 //将输出流中缓冲的数据全部写出到目的地 18 void flush() throws IOException
【使用FileOutputStream流往一个文件里面写入数据】
1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 6 public class TestFileOutputStream { 7 public static void main(String[] args) { 8 //read()返回的整数值 9 int b = 0; 10 FileInputStream in = null; 11 FileOutputStream out = null; 12 try { 13 in = new FileInputStream("E:\\javastudy\\java\\static\\src\\Student.java"); 14 //指明要写入数据的文件,如果指定的路径中不存在StudentNew.java这样的文件,则系统会自动创建一个 15 out = new FileOutputStream("E:\\javastudy\\java\\static\\src\\StudentNew.java"); 16 while ((b=in.read())!=-1){ 17 out.write(b); 18 } 19 in.close(); 20 out.close(); 21 } catch (FileNotFoundException e) { 22 System.out.println("文件读取失败"); 23 System.exit(-1); 24 }catch (IOException e1){ 25 System.out.println("文件复制失败"); 26 System.exit(-1); 27 } 28 System.out.println("Student.StudentNew.java里面"); 29 } 30 }
FileInputStream和FileOutputStream这两个流都是字节流,都是以一个字节为单位进行输入和输出的。所以对于占用了两个字节存储空间的字符来说读取出来时就会显示乱码。
Reader:和InputStream一模一样,唯一的区别就在于读的数据单位不同
继承来自Reader的流都是用于向程序中输入数据,且数据的单位为字符(16bit)
16位:一个字符也就是两个字节,使用Reader流读取数据时都是两个字节两个字节的往外读的,为什么还要有这两种两个字符的读取方式呢?因为有些字符是占2个字节的,如我们的中文字符在Java里面就是占两个字节的。如果采用一个字节一个字节往外读的方式,那么读出来的就是半个汉字,这样Java就没有办法正确的显示中文字符的,所以有必要存在这种流,一个字符一个字符的往外读。
1 //读取一个字节并以整数的形式返回(0~255) 2 //如果返回-1就说明已经到了输入流的末尾 3 int read() throws IOException 4 5 6 //读取一系列字节并存储到一个数组buffer 7 //返回实际读取的字节数,如果读取前已到输入流的末尾,则返回-1 8 int read(byte[] buffer) throws IOException 9 10 11 //读取length个字节 12 //并存储到一个字节数组buffer,从length位置开始 13 //返回实际读取的字节数,如果读取前以到输入流的末尾返回-1. 14 int read(byte[] buffer,int offset,int length) throws IOException 15 16 17 //关闭流释放内存资源 18 void close() throws IOException 19 20 21 //跳过n个字节不读,返回实际跳过的字节数 22 long skip(long n) throws IOException
继承自Writer的流都是用于程序中输出数据,且数据的单位为字符(16bit)
1 //向输出流中写入一个字节数据,该字节数据为参数b的低16位 2 void write(int b) throws IOException 3 4 5 //将一个字节类型的数组中的数据写入输出流 6 void write(byte[] b) throws IOException 7 8 9 //将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流 10 void write(byte[] b,int off,int len) throws IOException 11 12 13 //关闭流释放内存资源 14 void close() throws IOException 15 16 17 //将输出流中缓冲的数据全部写出到目的地 18 void flush() throws IOException
是的撒
原文:https://www.cnblogs.com/lxzlovewyq/p/13685689.html