为了提高sas sata ssd读写性能能,可以采用什么流

CoreJava测试题(教师用)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
CoreJava测试题(教师用)
上传于|0|0|暂无简介
阅读已结束,如果下载本文需要使用1下载券
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩11页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢403 Forbidden
403 Forbidden万丈高楼起于平地,千里之行始于足下。
李青原()创作于博客园个人博客(),转载请标明出处。
java.io是javaSE提供的基础功能,用以执行阻塞式的数据读取和写入操作。
java.io分为4个模块,字节流的读和写、字节流的读和写,分别对应4个基础抽象类:InputStream、OutputStream、Reader、Writer。
在这4个基础类之上,java使用装饰者模式,提供了面向多种应用场景的具体实现。本文主要是我对这些类个人学习分析的总结。
一.字节流读写:
2.代码分析:
  根据UML类图,我按照4个模块来分析:
  基础抽象类:定义基础API,实现部分默认实现,针对一个字节读写的原子操作方法为抽象方法,由核心功能类实现。
  核心功能实现类:针对内存和文件两种场景,实现针对一个字节的原子操作。
  装饰功能实现类:在核心功能实现类基础上,封装了一些可选的装饰功能。
  特殊实现类:针对特殊场景和特殊数据设计的特殊类,但是最基本的原子操作依然由核心功能类实现。
(1)基础抽象类:
  OutputStream和InputStream分别定义了3个读写方法,查看源代码会发现,3个方法都是以其中那个待实现的抽象方法为中心的,也就是write(int)和read()。
  这个方法就是字节流读写的原子操作,针对一个字节的读写,将由核心功能类来实现。
  区别在于,InputStream还规定了一种新的&标记和重置&功能,能够随时在流的当前读取位置打标记(mark方法),然后使用reset方法可以在读取到流的其他位置时重新回到标记位置,同时提供了markSupported方法供查看流对象是否支持此操作。
  不过InputStream本身只是定义了这些API,并未实现,markSupported方法返回的是false,需要子类自己实现。
  注意事项:
  ①write(int)的参数是4个字节的int类型,实现类在写入时会强转为1个字节的byte类型,这个强转会发生去掉溢出的3个字节,比如write(256),实际写入的是0x00,也就等效于write(0);
  ②read()方法返回的是4字节的int类型,API规定了这个方法会把读取的字节的高字节位置补零3个字节,因此如果读到数据,返回值必然在0到255之间,如果未读到返回-1;
  ③InputStream提供标记和重置功能API,是否支持由子类决定。
(2)核心功能实现类:
  所谓的核心功能实现类,也就是实现了最基础的写入功能&&实现了原子操作write(int)和read()。
  A.内存读写:
  ByteArrayOutputStream实现内存字节流的写入。
  它内部持有一个数组,在构造方法中被初始化,默认是32长度,也可以通过构造方法指定长度。
  如何获得写入结果呢?
  原来,为了保证数据安全,ByteArrayOutputStream内部持有的数组是私有的,但它为我们提供了获得写入结果的方法:toByteArray(),获得内部数组的一个克隆,也可以使用toString方法获得编码后的字符流。
  示例:
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] bytes = "io测试".getBytes("utf-8");
byteStream.write(bytes);
System.out.println(new String(byteStream.toByteArray(),"utf-8"));//io测试
System.out.println(byteStream.toString("utf-8"));//io测试
  ByteArrayInputStream实现内存字节流的读取。
  因此ByteArrayInputStream构造方法必须至少有一个参数,传入数据来源byte数组;此核心类支持&标记和重置&功能。
  示例:
public static void main(String[] args){
byte[] sourceData = {1,2,3,4,5};
ByteArrayInputStream inputStream = new ByteArrayInputStream(sourceData);
byte[] targetData = new byte[5];
inputStream.read(targetData, 0, 3);
//从第一位读3个,分别是 1 2 3
inputStream.mark(-1);
//打标记,mark方法的参数对ByteArrayInputStream无意义,可以随便传
targetData[3] = (byte)inputStream.read();
//继续读第四个数,4
inputStream.reset();
//重置回标记位置,也就是3处
targetData[4] = (byte)inputStream.read();
//从标记出再开始读,依然是第四个数,4
System.out.println(Arrays.toString(targetData));//显示1,2,3,4,4
  B.文件读写:
  FileOutputStream实现文件写入。
  写入模式分为两种:覆盖或者追加,模式被构造方法的参数指定,默认是覆盖写入。
  示例:&
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
FileOutputStream fileStream = new FileOutputStream("d:/123.txt");//此时文件已经清空
fileStream.write(256);//256超出了byte类型的范围,发生溢出,实际写入的是0x00
fileStream.close();//释放系统文件资源和关闭FileChannel对象
  FileInputStream实现文件读取。
  因此构造方法必须至少有一个参数,指定数据来源文件,可以是文件对象,也可以是文件路径,还可以是FileDescriptor对象;此核心类不支持&标记和重置&功能。
  示例:
public static void main(String[] args) throws IOException{
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{1,2,3,4,5});
FileInputStream inputStream = new FileInputStream("d:/123.txt");
byte[] targetData = new byte[4];
inputStream.read(targetData);//填满targetData,4个字节
System.out.println(inputStream.available());//写了4个字节,文件剩余可写数据为1
inputStream.close();//关闭文件资源
System.out.println(Arrays.toString(targetData));//1,2,3,4
  注意事项:
  ①内存读写已经保证了多线程安全,但是文件读写没有保证;
  ②内存读写不需要调用close方法;而文件读写使用文件系统,必须调用close方法释放文件资源;
  ③字节流核心写入类都不带缓存功能,因此直接使用时无需调用flush方法;
  ④FileOutputStream对象在创建时,就会执行一个native的open操作,如果是覆盖模式,此操作会立刻清除原文件内容,因此创建覆盖模式的FileOutputStream对象要非常谨慎,就算我们没做任何写入操作,文件内容也会被覆盖。
  ⑤ByteArrayInputStream支持标记和重置功能,FileInputStream不支持;
  ⑥ByteArrayInputStream的mark(int)方法中,参数无意义,随便传。
(3)装饰功能类:
  装饰类本身并实现单字节写入的原子操作,而是通过持有2个核心类之一的对象来获得这种能力,并同时提供额外的附加功能。
  A.装饰类父类:
  作为装饰父类,FilterOutputStream和FilterInputStream最重要的作用就是把所有InputStream规定的API转到内部持有的InputStream对象上,本身只是一个很简单的封装层。  
  注意事项:
  FilterOutputStream在close方法中默认调用flush方法,这样在使用字节流写入的装饰子类时,无需在关闭前调用flush方法了。
  B.装饰功能&&缓存读写:
  BufferedOutputStream实现带缓存功能的字节流写入。
  BufferedOutputStream通过内部持有一个字节数组来缓存外部请求写入的数据,以下三种情况,它都会清空数组并真正的把数据写入目标:
    缓存数组已满;
    缓存数组剩余空间不足以缓存新的请求写入的数据;
    flush或者close方法被调用时。
  BufferedInputStream实现带缓存功能的字节流读取。
  BufferedInputStream的缓存功能实现要复杂很多,主要是以下几个方面:
    BufferedInputStream构造方法中指定的缓存大小,只是初始大小;
    BufferedInputStream的缓存数组有一套&可伸缩&机制(参见源码fill方法);
    BufferedInputStream在以上基础上实现了基于缓存数组的标记和重置功能。
  考虑到内存读取和IO读取的消耗区别,BufferedInputStream通常是用来提升FileInputStream的读取性能的,而FileInputStream并未在JVM层面实现标记和重置功能,BufferedInputStream则在代码层面完善了这一功能。
  注意事项:
  ①缓存读写的装饰类都额外保证了读写操作的多线程安全性;
  ②缓存读写的缓存大小默认都是8KB,可以通过构造方法指定,但读取操作的缓存是可变的,使用中大小可能发生变化;
  ③缓存读取还附带实现了标记和重置功能,不同于ByteArrayInputStream的标记重置功能,BufferedInputStream读取提供的mark(int)的参数有意义,指定了标记在多少个字节内是起效的。
  示例:
public static void main(String[] args) throws IOException{
* 缓存写入示范
BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream("d:/123.txt"));
bufferOutput.write("IO测试".getBytes("utf-8"));
bufferOutput.close();//装饰类的close方法会先调用flush方法,所以无需再调用flush()
* 缓存读取示范
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{1,2,3,4,5,6});
FileInputStream inputStream = new FileInputStream("d:/123.txt");
BufferedInputStream bufferInput = new BufferedInputStream(inputStream);
byte[] targetArray = new byte[6];
bufferInput.mark(targetArray.length);//在最开始位置标记
bufferInput.read(targetArray,0,3);//读取三个字节 123
bufferInput.reset();//重置,回到标记位置
bufferInput.read(targetArray,3,3);//再读取三个字节 还是123
bufferInput.close();
for(byte b : targetArray){
System.out.print(b);//循环输出为123123
  C.装饰功能&&JAVA基本类型读写:
  DataOutputStream:提供JAVA基本类型的写入API,可以实现把JAVA基本类型的二进制数据写入目标,同时提供获得已写入数据字节大小的API。  
  DataInputStream:提供JAVA基础类型的读取API,会把读取的字节转为对应类型的数据,根据类型的长度,会选择连续操作几次原子操作。
  注意事项:
  ①JAVA基本类型读写的API都没有保证多线程安全,需要外部调用者自己保证;
  ②DataOutputStream的基本写入(3个write方法)额外保证了多线程安全,而DataInputStream的基本读取(3个read方法)则没有额外保证,完全取决于内部持有的核心实现类;
  ③DataOutputStream的size()方法最大只能提供2的31次方减1,超过这个依然只显示这个数值。
  示例:
public static void main(String[] args) throws IOException {
* JAVA基本类型写入示范
DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream("d:/123.txt"));
dataOutput.writeInt(-1);//文件中写入的16进制数据是 FF FF FF FF,也就是32个1(-1的补码)
dataOutput.writeBoolean(true);//文件中写入的16进制数据是 01
System.out.println(dataOutput.size());;//已写字节,4+1 已写5字节,最大只能记录Integer.MAX_VALUE
dataOutput.close();
* JAVA基本类型读取示范
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{0x00,0x31});//写入 0x0031,也就是49
FileInputStream inputStream = new FileInputStream("d:/123.txt");
DataInputStream dataInput = new DataInputStream(inputStream);
System.out.println(dataInput.readChar());//显示1 DataInputStream会连续读取2个字节,0x0031转为字符也就是'1'
dataInput.close();
  D.装饰功能&&字符输出打印:
  PrintStream是一个非常特殊的类,它虽然和Writer的子类一样能打印字符流,但是它的底层却是以字节为原子操作的,效率上不如PrintWriter,但是它依然能操作字节写入,这点上又更灵活。
  作为打印类,PrintStream提供换行功能,除了调用flush和println类方法外,它还能自动识别换行字符'\n'(这点PrintWriter做不到);同时PrintStream也不会抛出IO异常,而是通过checkError方法获得是否存在错误。
  注意事项:
  ①PrintStream能够识别换行字符'\n',PrintWriter不能;
  ②System.out就是一个PrintStream对象;
  ③PrintStream额外保证了所有API的多线程安全。
  示例:
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
PrintStream printStream = new PrintStream
(new BufferedOutputStream(new FileOutputStream("d:/123.txt")));//封装了缓存功能的打印流
printStream.print("IO测试\n");//包含换行符,此行已经输入文件
printStream.print("IO测试");//此行还未输入
System.out.println(printStream.checkError());//无错误,显示false
printStream.close();//关闭时默认调用了flush方法,第二行也被输入
  E.装饰功能&&字节流读取时的回退功能:
  PushbackInputStream:提供回退功能。
  PushbackInputStream通过内部一个回退区的缓存数组,可以让我们把已经读出来的数据回写到流中,以供其他使用者或者下次读取使用。
  注意事项:
  ①PushbackInputStream可以设置回填区的大小(默认1),如果回填数据长度超过设置值,会抛出IO异常;
  ②PushbackInputStream没有提供额外多线程安全保证,完全取决于内部持有的核心实现类。
  示例:
public static void main(String[] args) throws IOException{
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{1,2,3,4,5,6});
FileInputStream inputStream = new FileInputStream("d:/123.txt");
PushbackInputStream pushbackStream = new PushbackInputStream(inputStream);
System.out.print(pushbackStream.read());
System.out.print(pushbackStream.read());
System.out.print(pushbackStream.read());
System.out.print(pushbackStream.read());
System.out.print(pushbackStream.read());
System.out.print(pushbackStream.read());//读完六次 分别是123456
System.out.print(pushbackStream.read());//无数据,显示-1
pushbackStream.unread(0);//回填一个0
System.out.print(pushbackStream.read());//读出0
pushbackStream.close();
  F.装饰功能&&字节流读取时的行数计数功能:
  LineNumberInputStream:一个过时的提供行数计算的装饰类,这个类是JAVA SE中罕见的存在错误的类,主要是把0x0A这个字节错误的和'\n'换行符等同处理了,'\r'也存在类似问题。
  注意事项:
  LineNumberInputStream在某些情况下存在错误,应当使用LineNumberReader替代。
  错误示例:
@SuppressWarnings("deprecation")
public static void main(String[] args) throws IOException{
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{0x0a});
FileInputStream inputStream = new FileInputStream("d:/123.txt");
LineNumberInputStream lineStream = new LineNumberInputStream(inputStream);
System.out.println(lineStream.getLineNumber());//0
lineStream.read();
System.out.println(lineStream.getLineNumber());//1 很明显错误,0x0a并不一定是换行符,换行符应该是0x000a才对
lineStream.close();
&(4)特殊实现:
  A.序列化对象二进制流的读写:
  ObjectOutputStream和ObjectInputStream依然通过核心类来实现字节写入的原子操作,但是不再直接参与装饰者模式,而是通过内部类来持有一个JAVA基本类型读写装饰类。
  由于目标不再是普通字节流,而是特殊的序列化对象的二进制字节流,所以它们的API也不一样,提供了针对各种类型的读取API。
  注意事项:
  ①ObjectOutputStream为了提高序列化效率,在序列化对象时,会通过内存地址来判断是否为同一个对象,重复对象的序列化会用引用来代替;
  ②规则①对于对象内部的对象一样适用;
  ③使用writeUnshared方法可以强制要求不使用引用方式序列化,但是对于对象内部的对象此方法无效;
  ④ObjectInputStream在读取时,同一类型数据会依次读取,比如连续2次调用readObject()会依次获得第一次写入和第二次写入的Object对象。
  示例:
@SuppressWarnings("deprecation")
public static void main(String[] args)
throws UnsupportedEncodingException, IOException, ClassNotFoundException {
ObjectOutputStream objectOutput = new ObjectOutputStream(new FileOutputStream("d:/123.txt"));
Date a = new Date("");
objectOutput.writeObject(a);//第一次写入对象本身
a.setHours(12);
objectOutput.writeObject(a);//第二次写入,是重复对象,无视修改,依然写入引用
objectOutput.writeUnshared(a);//第三次写入,不管是否重复都写入对象
objectOutput.close();
ObjectInputStream objectInput = new ObjectInputStream(new FileInputStream("d:/123.txt"));
System.out.println(objectInput.readObject());//Tue Jan 01 00:00:00 CST 2013
System.out.println(objectInput.readObject());//Tue Jan 01 00:00:00 CST 2013 第二次写入修改未起效,因为是引用
System.out.println(objectInput.readObject());//Tue Jan 01 12:00:00 CST 2013 第三次写入修改起效
  B.多来源读取:
  SequenceInputStream用来从多个数据来源,读取字节流,当一个来源读取到末尾,会自动切换到下个数据来源。
  示例:
public static void main(String[] args) throws IOException{
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{1,2,3});//第一个文件写入123
FileOutputStream outputStream2 = new FileOutputStream("d:/456.txt");
outputStream2.write(new byte[]{4,5,6});//第二个文件写入456
FileInputStream inputStream = new FileInputStream("d:/123.txt");
FileInputStream inputStream2 = new FileInputStream("d:/456.txt");
InputStream[] array = new InputStream[]{inputStream,inputStream2};
ArrayEnumeration inputEnum = new ArrayEnumeration(array);
SequenceInputStream sequenceStream = new SequenceInputStream(inputEnum);
byte[] target = new byte[6];
for(int i = 0; i & target. i++){
System.out.println(sequenceStream.read());//循环输出123456
sequenceStream.close();
  注意事项:SequenceInputStream存在一个BUG,如果调用多字节读取的方法,自动切换功能会失效。
  错误示例:
public static void main(String[] args) throws IOException{
FileOutputStream outputStream = new FileOutputStream("d:/123.txt");
outputStream.write(new byte[]{1,2,3});//第一个文件写入123
FileOutputStream outputStream2 = new FileOutputStream("d:/456.txt");
outputStream2.write(new byte[]{4,5,6});//第二个文件写入456
FileInputStream inputStream = new FileInputStream("d:/123.txt");
FileInputStream inputStream2 = new FileInputStream("d:/456.txt");
InputStream[] array = new InputStream[]{inputStream,inputStream2};
ArrayEnumeration inputEnum = new ArrayEnumeration(array);
SequenceInputStream sequenceStream = new SequenceInputStream(inputEnum);
byte[] target = new byte[6];
sequenceStream.read(target);
for(byte b:target){
System.out.println(b);//错误显示123000 而不是123456
  查看源码,发现SequenceInputStream内部的错误:
  D.字符串读取操作:
  StringBufferInputStream也是一个过时的类,由于不恰当的混淆了双字节的字符类型和单字节的byte类型,读取的数据并不能反映真正的字符二进制数据。
  注意事项:
  应当使用StringReader替代StringBufferInputStream。
  E.管道读写操作:
  PipedOutputStream必须和PipedInputStream一一对应的使用,主要是用作线程之间的数据转移,具体的使用将在IO篇的后续第三篇文章中详细介绍。
  (1)字节流的读写在JAVA的最初版本就已经提供,但读取操作中,个别读取类不恰当的处理了字符和字节的关系,导致存在BUG,应当使用1.1版本以后提供的字符流替代类来操作。
  (2)由于使用装饰者模式,很多时候我们可以嵌套核心类和装饰类来使用,这样构建的对象应该同时小心核心类和装饰类的注意事项。
  (3)多线程环境下,内存字节流读写本身已经保证了多线程安全;
    而文件字节流读写则没有保证,但如果使用缓存读写装饰类封装,则能够通过缓存读写装饰类来保证多线程安全,前提是一个文件读写类只被一个缓存读写装饰类使用;
    装饰类提供的JAVA基本数据类型的各种读取API,都没有保证线程安全性,需要调用者自己保证。
  (4)IO功能中的字节流读写都是阻塞式的,会一直等到操作成功或者抛出IO异常,才会返回。
阅读(...) 评论()商品名称:
评价得分:
其他谈论话题
多品类齐全,轻松购物
快多仓直发,极速配送
好正品行货,精致服务
省天天低价,畅选无忧}

我要回帖

更多关于 快乐读写练中提高 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信