按照流的方向:
输入流(InputStream);
输出流(OutputStream)。
按照实现功能,是否直接与特定的地方(如磁盘、内存、设备等)相连:
节点流:可以从或向一个特定的地方(节点)读写数据,如FileReader;
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
按照处理数据的单位:
字节流:字节流继承于InputStream和OutputStream;
字符流:字符流继承于Reader和Writer。
两种方式:
实现Cloneable接口并重写Object 类中的clone()方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁地操作一个资源(如文件或数据库)则性能会很低,所以为了提升性能就可以将一部分数据暂时读写到缓存区,以后直接从此区域中读写数据即可,这样就可以显著的提升性能。
对于Java字符流的操作都是在缓冲区操作的,所以如果我们想在字符流操作中主动将缓冲区刷新到文件则需要使用flush()方法实现。
在IO包中主要由4个可用的Filter流,分别为:
FilterInputStream:是InputStream直接子类,包含其他一些字节输入流,它将这些流用作其基本数据源,本身只是简单地重写那些将所有请求传递给所包含输入流的InputStream的所有方法。
FilterOutputStream:是OutputStream直接子类,包含其他一些字节输出流,它将这些流用作其基本数据源,类本身只是简单地重写那些将所有请求传递给所包含输入流的OutputStream的所有方法。
FilterReader:是Reader直接子类,包含其他一些字符输入流,它将这些流用作其基本数据源,本身只是简单地重写那些将所有请求传递给所包含输入流的Readder的所有方法。
FilterWriter:是Writer直接子类,包含其他一些字符输出流,它将这些流用作其基本数据源,本身只是简单地重写那些将所有请求传递给所包含输入流的Writer的所有方法。
Filter流都是抽象类,不能被实例化的。
流是一种抽象概念,它代表了数据的无结构化传递。
用来进行输入输出操作的流就称为IO流,换句话说,IO流就是以流的方式进行输入输出。
从文件中读取数据存储到程序的进程中叫做输入流。
从程序的进程中读取数据然后写入到目标文件中叫做输出流。
字节流的操作不经过缓冲区(内存),直接操作文件本身;
字符流的操作会先经过缓冲区(内存)然后通过缓冲区再操作文件;
字节流按照8位传输,以字节为单位输入输出数据;
字符流按照16位传输,以字符为单位输入输出数据。
序列化就是一种用来处理对象流的机制,将对象的内容进行流化,对流化后的对象进行读写操作。
序列化的实现:
将需要被序列化的类实现Serializable接口,没有需要实现的方法,此接口只是为了标注对象可被序列化的;
然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream对象,再使用该对象的write(Object obj)方法,将参数obj的对象输出。
IO操作包括:对硬盘的读写、对socket(网络)的读写以及外设的读写。
阻塞IO:当用户线程发起一个IO读请求操作,首先查看要读取的数据是否就绪,如果数据没有就绪,则会一直在那等待,直到数据就绪;
非阻塞IO:当用户线程发起一个IO读请求操作,首先查看要读取的数据是否就绪,如果数据没有就绪,则会返回一个标志信息告知用户线程当前要读的数据没有就绪。当数据就绪之后,便将数据拷贝到用户线程,这样才完成了一个完整的IO读请求操作,也就是说一个完整的IO读请求操作包括两个阶段:
查看数据是否就绪;
进行数据拷贝(内核将数据拷贝到用户线程)。
Java中传统的IO都是阻塞IO,比如通过socket来读数据,调用read()方法之后,如果数据没有就绪,当前线程就会一直阻塞在read方法调用那里,直到有数据才返回;而如果是非阻塞IO的话,当数据没有就绪,read()方法应该返回一个标志信息,告知当前线程数据没有就绪,而不是一直在那里等待。
输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。
IO 流主要分为字符流和字节流。
字节流中有抽象类InputStream和OutputStream,常见子类有FileInputStream、FileOutputStream、BufferedOutputStream等。
字符流中有抽象类Reader和Writer,常见的子类有FilterReader、FilterWriter等。
常见的接口:
Closeable:关闭资源
Flushable:刷新缓冲区
Appendable:追加
Java中的阻塞式方法是指在程序调用该方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句,比如read()和readLine()方法。
Java NIO是非阻塞式IO,由以下几个核心部分组成:
Channels
Buffers
Selectors
Channel有点象流,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。Channel和Buffer有好几种类型,下面是JAVA NIO中的一些主要Channel的实现:
FileChannel(文件)
DatagramChannel(UDP)
SocketChannel(TCP)
ServerSocketChannel(网络)
以下是Java NIO里关键的Buffer实现:
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
Selector:允许单线程处理多个Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便,一个单线程中使用一个Selector处理3个Channel的图示:
传统IO一般是一个线程等待连接,连接过来之后分配给processor线程,processor线程与通道连接后如果通道没有数据过来就会阻塞(线程被动挂起)不能做别的事情。
NIO则不同,首先,在selector线程轮询的过程中就已经过滤掉了不感兴趣的事件,其次,在processor处理事件的read和write都是非阻塞操作即直接返回的,线程没有被挂起。
传统的IO中,每个连接创建一个线程,NIO可以用一个含有限数量线程的线程池,甚至一个线程为多个连接服务。
传统IO的通道是单向的,NIO的通道是双向的。
BIO:同步并阻塞,服务器的实现模式是一个连接一个线程,这样的模式很明显的一个缺陷是:由于客户端连接数与服务器线程数成正比关系,可能造成不必要的线程开销,严重的还将导致服务器内存溢出。当然,这种情况可以通过线程池机制改善,但并不能从本质上消除这个弊端。
NIO:在JDK1.4以前,Java的IO模型一直是BIO,但从JDK1.4开始,JDK引入的新的IO模型NIO,它是同步非阻塞的。而服务器的实现模式是多个请求一个线程,即请求会注册到多路复用器Selector上,多路复用器轮询到连接有IO请求时才启动一个线程处理。
AIO:JDK1.7发布了NIO2.0,这就是真正意义上的异步非阻塞,服务器的实现模式为多个有效请求一个线程,客户端的IO请求都是由OS先完成再通知服务器应用去启动线程处理(回调)。
应用场景:并发连接数不多时采用BIO,因为它编程和调试都非常简单,但如果涉及到高并发的情况,应选择NIO或AIO,更好的建议是采用成熟的网络通信框架Netty。
注(不用回答,问道再答):
同步:Java自己去处理IO。
异步:Java将IO交给操作系统去处理,告诉缓存区大小,处理完成回调。
阻塞:使用阻塞IO时,Java调用会一直阻塞到读写完成才返回。
非阻塞:使用非阻塞IO时,如果不能立马读写,Java调用会马上返回,当IO事件分发器通知可读写时在进行读写,不断循环直到读写完成。
PrintStream:
PrintStream是字节流,并且是打印流;
PrintStream不会抛出IOException,可以通过checkError()来判断是否发生异常;
PrintStream构造的参数可以是File、String、OutputStream;
PrintStream构造的参数可以设置字符集;
PrintStream构造方法可指定参数,实现自动刷新缓存autoflush;
PrintStream写入速度较慢;
PrintStream提供了print、println方法实现打印。
PrintWriter:
PrintWriter是字符流,并且是打印流;
PrintWriter不会抛出IOException,可以通过checkError()来判断是否发生异常;
PrintStream构造的参数可以是File、OutputStream、String、Writer;
PrintWriter 构造方法可指定参数,实现自动刷新缓存autoflush;
PrintWriter带有缓冲区,多次写入数据速度有一定优势;
PrintStream提供了print、println方法实现打印。
BufferedWriter:
BufferedWriter是字符流;
BufferedWriter构造的参数只能是Writer;
BufferedWriter带有缓冲区,并且可以设置缓冲区大小,多次写入数据速度有一定优势。
BufferedWriter提供了newLine方法创建新行。