简述JAVAIO流以及IO流中的适配器模式、装饰模式
摘要:读完本章节,您对java 的IO流有更清晰深刻的认识,对适配器模式、装饰模式也有初步的了解。
⼀、关于流引⽤百度百科上的解释:
流是⼀种抽象概念,它代表了数据的⽆结构化传递。按照流的⽅式进⾏输⼊输出,数据被当成⽆结构的字节序或字符序列。从流中取得数据的操作称为提取操作,亦称读操作;⽽向流中添加数据的操作称为插⼊操作,亦称写操作。⽤来进⾏输⼊输出操作的流就称为IO流。换句话说说,IO流就是以流的⽅式进⾏输⼊输出。应⽤场景如Java Web上的⽂件上传与下载、磁盘⽂件的读操作写操作,这些都需要⽤到流。
⼆、流的分类:
按流的流向分为输⼊流、输出流,InputStream、Reader及其所有⼦类都是输⼊流,OutputStream、Writer及其所有⼦类都是输出流,从流中读数据⽤输⼊流,写数据到流中⽤输出流。
按流的数据单位分为字节流、字符流,InputStream、OutputStream及其所有⼦类都是字节流,Reader、Writer及其所有⼦类都是字符流,当操作流中的数据含有中⽂的时候,请务必使⽤字符流进⾏读写。如果流中含有中⽂⽽使⽤字节流进⾏读写有可能出现中⽂乱码、数据失真现象,因为数据如果是
GBK编码⼀个中⽂汉字含有两个字节,如果是UTF-8编码⼀个中⽂汉字含有三个字节,读写的时候如若处理不当,很容易产⽣中⽂乱码。
按流的功能分为节点流、处理流(过滤流),节点流是指从特定地⽅读写的流类如磁盘或者⼀块内存区域内存,⽐如ByteArrayInputStream、FileInputStream等,处理流是指通过已经存在的输⼊输出流构造的流,⽐如BufferedInputStream、DataInputStream等
三、输⼊流详解
a、字节输⼊流InputStream及其⼦类,
InputStream主要⽅法:
1、public abstract int read() throws IOException;
从输⼊流中读取数据的下⼀个字节。返回0到255范围内的int值。如果因为已经到达流末尾⽽没有可⽤的字节, 则返回-1。在输⼊数据可⽤、检测到流末尾或者抛出异常前,此⽅法⼀直阻塞。⼦类必须提供此⽅法的实现。
2、public int read(byte b[]) throws IOException {...};
从输⼊流中读取⼀定数量的字节,并将其存储在缓冲区数组b中。以整数形式返回实际读取的字节个数。在输⼊数据可⽤、检测到⽂件末尾或者抛出异常前,此⽅法⼀直阻塞。如果数组b的长度为0,则不读取任何字节并返回0;否则,尝试读取⾄少⼀个字节。如果因为流位于⽂件末尾⽽没有可⽤的字节,则返回值-1;否则,⾄少读取⼀个字节并将其存储在b中。将读取的第⼀个字节存储在元素b[0]中,下⼀个存储在b[1]中,以此类推。读取的字节数最多等于b的长度。设k为实际读取的字节数;这些字节将存储在b[0]到b[k-1]中,不影响b[k]到b[b.length-1]的元素。类InputStream的read(b)⽅法的效果等同于read(b,0,b.length)。
3、public int read(byte b[], int off, int len) throws IOException {...};
将输⼊流中最多 len 个数据字节读⼊ byte 数组。尝试读取 len 个字节,但读取的字节也可能⼩于该值。以整数形式返回实际读取的字节数。在输⼊数据可⽤、检测到流末尾或者抛出异常前,此⽅法⼀直阻塞。如果 len 为 0,则不读取任何字节并返回 0;否则,尝试读取⾄少⼀个字节。如果因为流位于⽂件末尾⽽没有可⽤的字节,则返回值 -1;否则,⾄少读取⼀个字节并将其存储在 b 中。将读取的第⼀个字节存储在元素 b[off] 中,下⼀个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off] 到 b[off+k-1] 的元素中,不影响 b[off+k] 到 b[off+len-1] 的元素。在任何情况下,b[0] 到 b[off] 的元素以及 b[off+len] 到
b[b.length-1] 的元素都不会受到影响。类 InputStream 的 read(b,off,len) ⽅法重复调⽤⽅法 read()。如果第⼀次这样的调⽤导致IOException,则从对 read(b,off,len) ⽅法的调⽤中返回该异常。如果对 read() 的任何后续调⽤导致 IOException,则捕获该异常并将其视为到达⽂件末尾;到达该点时读取的字节存储在 b 中,并返回发⽣异常之前读取的字节数。在已读取输⼊数据 len 的请求数量、检测到⽂件结束标记、抛出异常前,此⽅法的默认实现将⼀直阻塞。建议⼦类提供此⽅法更为有效的实现。
4、public long skip(long n) throws IOException {...};
跳过和丢弃此输⼊流中数据的 n 个字节。出于各种原因,skip ⽅法结束时跳过的字节数可能⼩于该数,也可能为 0。导致这种情况的原因很多,跳过 n 个字节之前已到达⽂件末尾只是其中⼀种可能。返回跳过的实际字节数。如果 n 为负,则不跳过任何字节。此类的 skip ⽅法创建⼀个 byte 数组,然后重复将字节读⼊其中,直到读够 n 个字节或已到达流末尾为⽌。建议⼦类提供此⽅法更为有效的实现。例如,可依赖搜索能⼒的实现。
5、public int available() throws IOException {...};
返回此输⼊流下⼀个⽅法调⽤可以不受阻塞地从此输⼊流读取(或跳过)的估计字节数,即返回此输⼊流未读取字节数。下⼀个调⽤可能是同⼀个线程,也可能是另⼀个线程。⼀次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能⼩于该数。注意,有些InputStream 的实现将返回流
中的字节总数,但也有很多实现不会这样做。试图使⽤此⽅法的返回值分配缓冲区,以保存此流所有数据的做法是不正确的。如果已经调⽤ ⽅法关闭了此输⼊流,那么此⽅法的⼦类实现可以选择抛出 。类 InputStream 的 available ⽅法总是返回 0。此⽅法应该由⼦类重写。
6、public void close() throws IOException {};
关闭此输⼊流并释放与该流关联的所有系统资源。InputStream 的 close ⽅法不执⾏任何操作。
7、public synchronized void mark(int readlimit) {};
在此输⼊流中标记当前的位置。对 reset ⽅法的后续调⽤会在最后标记的位置重新定位此流,以便后续读取重新读取相同的字
节。readlimit 参数告知此输⼊流在标记位置失效之前允许读取的字节数。mark 的常规协定是:如果⽅法 markSupported 返回 true,那么输⼊流总是在调⽤ mark 之后记录所有读取的字节,并时刻准备在调⽤⽅法 reset 时(⽆论何时),再次提供这些相同的字节。但是,如果在调⽤ reset 之前可以从流中读取多于 readlimit 的字节,则不需要该流记录任何数据。标记已关闭的流对其⽆效。InputStream 的 mark ⽅法不执⾏任何操作。
8、public synchronized void reset() throws IOException {...};
将此流重新定位到最后⼀次对此输⼊流调⽤ mark ⽅法时的位置。除了抛出 IOException 之外,类 InputStream 的⽅法 reset 不执⾏任何操作。
9、public boolean markSupported() {...};
测试此输⼊流是否⽀持 mark 和 reset ⽅法。是否⽀持 mark 和 reset 是特定输⼊流实例的不变属性。InputStream 的 markSupported ⽅法返回 false。如果此输⼊流实例⽀持 mark 和 reset ⽅法,则返回 true;否则返回 false。
InputStream⼦类:
InputStream是⼀个抽象类,表⽰字节输⼊流的所有类的超类,其提供的⼤部分⽅法需要⼦类去实现,⼦类的所有功能也围绕这⼏个⽅法进⾏扩展实现。InputStream的直接⼦类有7个,分别是, , , , , ,
1、ByteArrayInputStream
包含⼀个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read ⽅法要提供的下⼀个字节。关闭ByteArrayInputStream ⽆效。此类中的⽅法在关闭此流后仍可被调⽤,⽽不会产⽣任何 IOException。
字段以及构造⽅法描述,其他⽅法功能描述与⽗类InputStream⼀致,不再说明。截图⾃JDK API1.6⽂档(下同):
java技术介绍百度百科
2、FileInputStream
从⽂件系统中的某个⽂件中获得输⼊字节。哪些⽂件可⽤取决于主机环境。⽤于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使⽤ FileReader。
3、FilterInputStream
包含其他⼀些输⼊流,它将这些流⽤作其基本数据源,它可以直接传输数据或提供⼀些额外的功能。FilterInputStream 类本⾝只是简单地重写那些将所有请求传递给所包含输⼊流的 InputStream 的所有
⽅法。FilterInputStream 的⼦类可进⼀步重写这些⽅法中的⼀些⽅法,并且还可以提供⼀些额外的⽅法和字段。FilterInputStream⼦类有BufferedInputStream、DataInputStream、PustbackInputStream。
3.1、BufferedInputStream
为另⼀个输⼊流添加⼀些功能,即缓冲输⼊以及⽀持 mark 和 reset ⽅法的能⼒。在创建 BufferedInputStream 时,会创建⼀个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输⼊流再次填充该内部缓冲区,⼀次填充多个字节。mark 操作记录输⼊流中的某个点,reset 操作使得在从包含的输⼊流中获取新字节之前,再次读取⾃最后⼀次 mark 操作后读取的所有字节。
3.2、DataInputStream
数据输⼊流允许应⽤程序以与机器⽆关⽅式从底层输⼊流中读取基本 Java 数据类型。应⽤程序可以使⽤数据输出流写⼊稍后由数据输⼊流读取的数据。DataInputStream 对于多线程访问不⼀定是安全的。
3.3、PustbackInputStream
为另⼀个输⼊流添加性能,即“推回 (push back)”或“取消读取 (unread)”⼀个字节的能⼒。在代码⽚段可以很⽅便地读取由特定字节值分隔的不定数量的数据字节时,这很有⽤;在读取终⽌字节后,代码⽚段可以“取消读取”该字节,这样,输⼊流上的下⼀个读取操作将会重新读取被推回的字节。例如,表⽰构成标识符字符的字节可能由表⽰操作符字符的字节终⽌;⽤于读取⼀个标识符的⽅法可以读取到遇到操作符为⽌,然后将该操作符推回以进⾏重读。
, , , 由于篇幅原因⽽且并不常⽤,这⾥不再阐述,后续如有遇到会逐个介绍。
b、字符流Reader及其⼦类
字符流与字节流的⽅法⼤体⼀致,唯⼀不同的就是,读取的时候字符流是⼀个⼀个字符的读或者读字符数组,⽽字节流是⼀个⼀个字节的读或者读字节数组,故关于字符流的类不再详细阐述。
c、两个demo
字节流demo:
// 字节流读取⽂件内容
// ⽂件内容是英⽂数字
File file = new File("D:/work/");
FileInputStream is = new FileInputStream(file);
int i;
StringBuffer sb = new StringBuffer();
// ⼀个字节⼀个字节的读,如有字节返回该字节的int值,读到⽂件末尾返回-1
// while ((i = is.read()) != -1) {
// sb.append((byte) i);
// }
byte[] b = new byte[1024];
// 将数据读到缓存数组中,每次最多读b.length个字节,返回实际读到的字节数,如到⽂件末尾返回-1
while ((i = is.read(b)) != -1) {
sb.append(new String(b, 0, i));
}
is.close();
字符流demo:
// 字符流读取⽂件内容
// ⽂件包含中⽂
File file = new File("D:/work/");
// 可指定编码创建字符流对象
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "GBK");
StringBuffer sb = new StringBuffer();
int i;
// ⼀个字符⼀个字符的读,如有字符返回该字节的int值,读到⽂件末尾返回-1
// while ((i = ad()) != -1) {
// sb.append((char) i);
// }
char[] c = new char[1024];
// 将数据读到缓存字符数组中,每次最多读b.length个字节,返回实际读到的字符数,如到⽂件末尾返回-1
while ((i = ad(c)) != -1) {
sb.append(c, 0, i);
}
reader.close();
四、IO流设计模式之适配器模式
1、适配器模式概念
所谓适配器模式就是将某个类的接⼝转换为客户端期望的另⼀个接⼝表⽰,其主要⽬的是兼容性,让原本不相⼲的两个类可以协同⼀起⼯作。
2、IO流中适配器模式分析
IO流中多处使⽤了适配器模式,⽐较典型的就是字节流转字符流,⽬标接⼝字符流Reader与需要被适配的类InputStream原本是两个不相⼲的类,为了满⾜可以读字符功能,有了适配器者InputStreamReader。InputStreamReader的作⽤就是将字节输⼊流转换为能读取字符的字符输⼊流。
由InputStreamReader构造⽅法可知,接受⼀个字节流InputStream,通过InputSream⽣成⼀个StreamDecoder对象,后续字符流读写都是靠这个StreamDecoder对象,间接利⽤了InputSream的功能。
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
五、IO流中设计模式之装饰模式
1、装饰模式概念
装饰模式指的是在不必改变原类⽂件和使⽤继承的情况下,动态的扩展⼀个对象的功能。它是通过创建⼀个包装对象,也就是装饰来包裹真实对象。装饰模式⼜叫包装(Wrapper)模式。
2、装饰模式各⾓⾊
(1)抽象构件(component)⾓⾊:给出⼀个抽象接⼝,以规范准备接收附加责任的对象。
(2)具体构件(Concrete Component)⾓⾊:定义⼀个将要接收附加责任的类。
(3)装饰(Decorator)⾓⾊:持有⼀个构件(Component)对象的实例,并实现⼀个与抽象构件接⼝⼀致的接⼝。
(4)具体装饰(Concrete Decorator)⾓⾊:负责给构件对象添加上附加的责任。
注:责任可理解为功能,附加责任即为扩展的功能
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论