一、流的概念
流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。
流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。
实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。
形象的比喻——水流 ,文件======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出.便于理解,这么定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。
二、流的分类
java中的流,可以从不同的角度进行分类。
1)按照数据流的方向不同可以分为:输入流和输出流。
2)按照处理数据单位不同可以分为:字节流和字符流。
3)按照实现功能不同可以分为:节点流和处理流。
a)输出流:
b)输入流:
因此输入和输出都是从程序的角度来说的。
字节流:一次读入或读出是8位二进制。
字符流:一次读入或读出是16位二进制。
字节流和字符流的原理是相同的,只不过处理的单位不同而已。后缀是Stream是字节流,而后缀是Reader,Writer是字符流。 c)节点流:直接与数据源相连,读入或读出。
Java流类图结构:
Jdk提供的流继承了四大类:InputStream(字节输入流),OutputStream(字节输出流),Reader(字符输入流),Writer(字符输出流)。
InputStream字节输入流:
OutputStream字节输出流:
Reader字符输入流:
Writer字符输出流:
简单介绍其上图:
a)对文件进行操作:FileInputStream(字节输入流),FileOutputStream(字节输出流),FileReader(字符输入流),FileWriter(字符输出流)
b)对管道进行操作:PipedInputStream(字节输入流),PipedOutStream(字节输出流),PipedReader(字符输入流),PipedWriter(字符输出流)PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
c)字节/字符数组:ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter是在内存中开辟了一个字节或字符数组。
d)Buffered缓冲流::BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,是带缓冲区的处理流,缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。
e)转化流:InputStreamReader/OutputStreamWriter,把字节转化成字符。
f)数据流:DataInputStream,DataOutputStream。因为平时若是我们输出一个8个字节的long类型或4个字节的float类型,那怎么办呢?可以一个字节一个字节输出,也可以把转换成字符串输出,但是这样转换费时间,若是直接输出该多好啊,因此这个数据流就解决了我们输出数据类型的困难。数据流可以直接输出float类型或long类型,提高了数据读写的效率。
g)打印流:PRintStream,printWriter,一般是打印到控制台,可以进行控制打印的地方。
h)对象流:ObjectInputStream,ObjectOutputStream,把封装的对象直接输出,而不是一个个在转换成字符串再输出。
j)序列化流:SequenceInputStream。
k)对象序列化:把对象直接转换成二进制,写入介质中。使用对象流需要实现Serializable接口,否则会报错。而若用transient关键字修饰成员变量,不写入该成员变量,若是引用类型的成员变量为null,值类型的成员变量为0。
三、字节流的输入与输出的对应
图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出Java IO 中的字节流是极其对称的。“存在及合理”我们看看这些字节流中不太对称的几个类吧!1.LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。
2.PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
3.StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
4.SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
5.PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!
四、字符流的输入与输出的对应
五、java IO操作
Java的IO类操作主要包括如下几类 1、File类的使用。 2、字节操作流:OutputStream、InputStream 3、字符操作流:Reader、Writer 4、对象序列化:serializable
(A)File类
public class File extends Object implements Serizliable Comparable<File> 从定义看,File类是Object的直接子类,同时它继承了Comparable接口可以进行数组的排序。File类的操作包括文件的创建、删除、重命名、得到路径、创建时间等,以下是文件操作常用的函数。
File类的操作:(1)创建文件,注意File.separator可以解决跨操作系统的问题。 下面的例子是一创建一个文件,如果该文件存在则删除,否则创建该文件。
public class FileDemo1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "test.txt"); if (file.exists()) { file.delete(); } else { try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } (2)文件的类型函数 file.isFile(); //判断是不是文件 file.isDirectory();//判断是不是目录(3)列出目录的内容 pulbic String[] list();//列出所有文件名和目录名 public File[] listFiles();//列出所有文件和目录(B)字节操作流(btyle) (1)字节输出流OutputStream
public class FileDemo1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "test.txt");//指定要操作的文件 OutputStream out=null;//定义字节流输出对象 try { //out= new FileOutputStream(file,true);//是否字节追加函数 out= new FileOutputStream(file);//获取实际的字节流输出对象,内容覆盖 } catch (FileNotFoundException e) { e.printStackTrace(); } String info="hello";//要输入的内容 byte[] b=info.getBytes();//将字符转化为字节数组 try { out.write(b); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } (2)字节输入流InputStream
public class FileDemo1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "test.txt");//指定要操作的文件 InputStream In=null;//定义字节流输入对象 try { //out= new FileOutputStream(file,true);//是否字节追加函数 In= new FileInputStream(file);//获取实际的字节流输入对象 } catch (FileNotFoundException e) { e.printStackTrace(); } int len=0;//输入数组长度 byte[] b=new byte[1024];//开辟空间,读取内容 //byte[] b=new byte[(int)file.length()];//根据文件大小开辟空间 try { len=In.read(b);//读取 } catch (IOException e1) { e1.printStackTrace(); } try { In.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println(new String(b,0,len)); } }(3)字符输出流Write
public class FileDemo1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "test.txt");// 指定要操作的文件 Writer write = null;// 定义字符输出流 try { write = new FileWriter(file); } catch (IOException e) { e.printStackTrace(); } String infor = "hello,heiehiehieh"; try { write.write(infor); } catch (IOException e) { e.printStackTrace(); } try { write.close(); } catch (IOException e) { e.printStackTrace(); } } }(4)字符输入流Reader
public class FileDemo1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "test.txt");// 指定要操作的文件 Reader read = null;// 定义字符输入流 try { read = new FileReader(file); } catch (IOException e) { e.printStackTrace(); } String infor = "hello,heiehiehieh"; char[] b=new char[1024];//设置字符的长度 try { int len=read.read(b); } catch (IOException e) { e.printStackTrace(); } try { read.close(); } catch (IOException e) { e.printStackTrace(); } } } (5)字节流和字符流的区别(重点) 字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。(6)转换流:在io中还存在一类是转换流,将字节流转换为字符流,同时可以将字符流转化为字节流。OutputStreamWriter(OutStream out):j将字节流以字符流输出。InputStreamReader(InputStream in):将字节流以字符流输入。(7)打印流 PrintStream 在操作中要求输出信息时,可以采用PrintStream进行输出,它包括PrintWrite和PrintReader(C)对象序列化 对象序列化是指将一个对象可以转化为二进制的byte流,可以以文件的方式进行保存。 将对象保存在文件的操作叫做对象的序列化操作。 将对象从文件中恢复的操作叫做反序列化操作。一个对象如果要能序列化,它必须继承Serizliable。在实现序列化是则需要ObjectOurputStream完成,而需要反序列化时则采用ObjectInputStream。
transient关键字:变量声明为Transient后,该变量不可序列化。
(D)内存流 在项目的开发过程中,有时希望只产生临时文件,将信息输出的内存中,此时会用到内存流,内存流基本方法如下:
public class FileDemo1 { public static void main(String[] args) { String infor = "hello"; // 所有的内容向内存中输入 InputStream input = new ByteArrayInputStream(infor.getBytes()); // 所有内存的内容由outputStream输出 OutputStream out = new ByteArrayOutputStream(); int temp = 0; try { while ((temp = input.read()) != -1) { char c = Character.toUpperCase((char) temp); out.write(c);//从内存中输出,所有的内容都保存在ByteArrayOutputStream中 } } catch (IOException e) { e.printStackTrace(); } try { input.close(); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println(out.toString()); } } (E)System类对IO的支持
(F)缓存读取
新闻热点
疑难解答