java IO流和序列化

转载自:https://blog.csdn.net/lulei1217/article/details/50527824

今天再次回顾了一些关于java IO流的知识。虽然以前会点IO的方面的编程,但是还是知其然不知其所以然。

首先上一张IO流的主要家族图吧。

    

    

该图给出了IO流的具体分类和各类之间的继承关系。其中还给出了各个类的使用场景和用途。

一、IO流的主流分类

    IO流主要分为字节流和字符流。它们之间的区别就是:

1、读写的单位不同,字节流是以字节(8bit)为单位的。字符流是以字符为单位的,根据字符码表映射的字符,一次可能读取多个字节。

2、处理的对象不同。字节流处理所有类型的数据,包括图片、音视频等。而字符流只能处理字符型的数据。

3、字节流是直接传输的流通道,在进行流操作时是没有缓冲区的。但是字符流在操作的时候会使用到缓冲区。这就是为什么字符流的类实例都会有flush()方法。

    然后针对字节流和字符流会分成相应的输入输出流。

字节流:InputStream输入字节流、OutputStream输出字节流。

字符流:Reader字符输入流、Writer字符输出流。 

二、文件类的介绍和说明

java中表示文件类的有FileRandomAccessFile类。而IO流的类主要是针对文件类中的文件内容来进行读写的操作的。

    1、File类表示文件的本身,可以直接使用此类来完成文件的各种操作。比如:创建、删除、判断是否为目录等。

    2、RandomAccessFile类可以从指定的位置开始读取信息,主要是因为这个类中定义了文件指针。但是这个类是要求文件中各个数据的保存的长度必须是固定的。

接下来举一个经常会问到的例子:如何遍历一个文件目录中的所用文件??

         

代码如下:

[java] view plain copy
  1. package com.io;  
  2.   
  3. import java.io.*;  
  4.   
  5. public class listFile {  
  6.   
  7.     public static void main(String[] args) {  
  8.         // TODO Auto-generated method stub  
  9.         //1 新建一个File类的实例  
  10.         File file=new File("d:"+File.separator+"Xiaomi");  
  11.         //2 进行文件目录的打印  
  12.         print(file);  
  13.     }  
  14.   
  15.     public static void print(File file) {  
  16.         // TODO Auto-generated method stub  
  17.         if(file!=null){//判断文件不为空  
  18.             if(file.isDirectory()){//判断文件是不是目录  
  19.                File[] fs=file.listFiles();//返回所有的子文件  
  20.                    if(fs!=null){  
  21.                        //开始遍历子文件  
  22.                        for (int i = 0; i < fs.length; i++) {  
  23.                                 print(fs[i]);  
  24.                             }  
  25.                    }  
  26.             }else{//如果不是目录就打印文件的路径  
  27.                 System.out.println(file);  
  28.             }  
  29.         }  
  30.     }  
  31.   
  32. }  

结果显示:


三、输入输出流的介绍

    输入输出流主要分为字节流和字符流。但是在传输中使用字节流的操作会比较的多。InputStream输入字节流、OutputStream输出字节流、Reader字符输入流、Writer字符输出流它们都是抽象类,根据使用的子类不同输入或输出的位置会不同。

比如向对上面的File文件类来进行操作时,我们应该会想到使用FileInputStreamFileOutputStream类。

Java的JDK中也提供了将字节流转换为字符流的类:InputStreamReader(InputStream)OutputStreamWriter(OutputStream)这两个类。

使用ByteArrayInputStreamByteArrayOutputStream类是可以对内存进行输入和输出的。

在线程之间进行输入和输出时主要使用的是PipedOutputStreamPipedInputStream类。

    这里注意一下,在日常的IO输出时最好是使用打印流来进行输出。即PrintWriterPrintStream类。这样可以方便的打印各种类型的数据,说到底就是打印流中的print方法实现了对各种数据打印方法的重载。还有我们经常使用的System.out实际上就是一个PrintStream类。

-----------------------------------------------------*-------------------------------------------------------------------------

    当然对于这些IO输入输出类实际上我们是可以进行装饰的。这就是FilterInputStreamFilterReader等类的作用,它们的子类就是对应的各种各样的装饰类。比如以 FilterOutputStream的子类是:BufferedOutputStream、 DataOutputStreamPrintStream

    BufferedOutputStream是可以直接向缓冲区写入数据。该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。 

    DataOutputStream是数据输出流,提供了与平台无关的数据操作。数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。

    PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

注意这些经过装饰类修饰的IO字节流,有的会带有缓冲区功能。所以也就会有flush()方法。 

    BufferedOutputStream为IO提供了带有缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。从应用程序中把输入放入文件,相当于将一缸水倒入另一个缸中。

1>  FileOutputStream---->write方法相当于一滴一滴的把水转移过去。

2>  DataOutputStream-->writeXXX方法会方便一些,相当于一瓢一瓢的把水转移过去

3>  BufferedOutputStream-->write()方法会更方便一些,相当于一瓢一瓢先放入水桶中,在从桶中倒入缸中,性能提高了。


四、对象的序列化

什么叫对象的序列化。实际上就是把一个对象变为二进制的字节数据流的一种方法。


    如果一个类想实现序列化只需要实现Serializable接口即可。接着我们是如何将这个类进行传输的。这时会有两个字节流:ObjectInputStreamObjectOutputStream(对象输入输出流)。我们要知道类的序列化序列的是属性的内容,不会序列化方法的。如果我们使用transient关键字来修饰某个属性,这就表示这个属性是不会被序列化的。

为何要进行序列化呢.不进行序列化,我的程序不跑的好好的吗?你想要什么结果,我也能给解决不是.我想说确实是这样,如果你的程序与网络无关,那很好你已经可以摒弃它了.
    那下面我来简单分析下为何java需要进行序列化呢?
    首先我们要明白,序列化是做什么作用的?java序列化:以特定的方式对类实例的瞬时状态进行编码保存的一种操作。(可能不是很精确,咱不是搞学术的,看懂即可)。从此定义可以看出,序列化作用的对象是类的实例。对实例进行序列化,就是保存实例当前在内存中的状态。包括实例的每一个属性的值和引用等。
    既然后序列化,便会有反序列化。反序列化的作用便是将序列化后的编码解码成类实例的瞬时状态,申请等同的内存保存该实例。
    从上述定义可以发现,序列化就是为了保存java的类对象的状态的。保存这个状态的作用主要用于不同jvm之间进行类实例间的共享。在ORMaping中的缓存机制,进行缓存同步时,便是常见的java序列化的应用之一。在进行远程方法调用,远程过程调用时,采用序列化对象的传输也是一种应用...当你想从一个jvm中调用另一个jvm的对象时,你就可以考虑使用序列化了。

    简而言之:序列化的作用就是为了不同jvm之间共享实例对象的一种解决方案。由java提供此机制,效率之高,是其他解决方案无法比拟的。


五、新的IO(NIO)

  我们知道在文件操作的时候,速度的提高是很重要的。速度的提高是来自于我们使用了更接近于操作系统执行I/O的方式:通道和缓冲器。


     这里唯一直接与通道交互的缓冲器(即缓冲区)就是ByteBuffer。这是新的IO,主要是为了提升IO操作的速度的。建议有时间去看JDK中的描述。

     下面的代码演示三种类型的流的可写、可读的通道:

[java] view plain copy
  1. package com.io;  
  2.   
  3. import java.io.*;  
  4. import java.nio.ByteBuffer;  
  5. import java.nio.channels.FileChannel;  
  6. /*注意该方法只能测试英文字符。如果测试中文就会出现乱码*/  
  7. public class GetChannels {  
  8.     private static final int BSIZE=1024;//用于后面设置字节缓冲区的大小  
  9.     public static void main(String[] args) throws  Exception{  
  10.         // TODO Auto-generated method stub  
  11.         //写一个文件  
  12.         FileChannel fc=new FileOutputStream("date.txt").getChannel();//获取与此文件输出流相关的唯一通道  
  13.         fc.write(ByteBuffer.wrap("my dear,".getBytes()));//注意是先获取字符串的字节数组,然后在将字节数组包装到缓冲区中  
  14.         fc.close();//关闭通道  
  15.           
  16.         //尝试在文件中添加内容  
  17.         fc=new RandomAccessFile("date.txt""rw").getChannel();//重新获取文件的任意操作流的唯一通道  
  18.         fc.position(fc.size());//将文件指针指向最末尾,这样可以防止之前的字符被新的字符给覆盖  
  19.         fc.write(ByteBuffer.wrap("my family".getBytes()));  
  20.         fc.close();  
  21.           
  22.         //读取文件  
  23.         fc=new FileInputStream("date.txt").getChannel();  
  24.         ByteBuffer buff=ByteBuffer.allocate(BSIZE);//分配一个新的字节缓冲区,大小为1k  
  25.         fc.read(buff);//将文件内容读取到字节缓冲区buff中  
  26.         buff.flip();//为读取字节缓冲区做准备  
  27.         while(buff.hasRemaining())//字节缓冲区是否还有字节元素  
  28.             System.out.print((char)buff.get());//获取字节,并转化为字符  
  29.           
  30.           
  31.     }  
  32.   
  33. }  

结果显示:


注意这种使用方法是无法读取中文字符的。会出现乱码的。所以尝试使用编码的方式来解决:

[java] view plain copy
  1. package com.io;  
  2.   
  3. import java.io.*;  
  4. import java.nio.ByteBuffer;  
  5. import java.nio.channels.FileChannel;  
  6. import java.nio.charset.Charset;  
  7.   
  8. public class BufferToText {  
  9.     private static final int BSIZE=1024;  
  10.     public static void main(String[] args) throws Exception {  
  11.         // TODO Auto-generated method stub  
  12.         //写入file中  
  13.         FileChannel fc=new FileOutputStream("date2.txt").getChannel();  
  14.         fc.write(ByteBuffer.wrap("my love我的爱".getBytes()));//将字符转换为字节数组,在放入字节缓冲器中。最后写入到通道中  
  15.         fc.close();  
  16.         fc=new FileInputStream("date2.txt").getChannel();  
  17.         ByteBuffer buffer=ByteBuffer.allocate(BSIZE);//定义一个指定大小的字节缓冲区  
  18.         fc.read(buffer);  
  19.         buffer.flip();  
  20.         //打印读取到的字节  
  21.         //System.out.print(buffer.asCharBuffer());//这种方法没什么用,会出现乱码。  
  22.           
  23.         //我们需要系统的指定默认的字符集  
  24.         buffer.rewind();  
  25.         String encoding =System.getProperty("file.encoding");//获取文件的字节码  
  26.         System.out.println("Decoding using:"+encoding+":"+Charset.forName(encoding).decode(buffer));  
  27.           
  28.         //或者我们也可以使用编码来向文件中写入字符  
  29.         fc=new FileOutputStream("date3.txt").getChannel();  
  30.         fc.write(ByteBuffer.wrap("我是一份大菠菜。".getBytes("UTF-16BE")));  
  31.         fc.close();  
  32.           
  33.         //接下来我们进行读文件  
  34.         fc=new FileInputStream("date3.txt").getChannel();  
  35.         buffer.clear();  
  36.         fc.read(buffer);  
  37.         buffer.flip();  
  38.         System.out.print(buffer.asCharBuffer());  
  39.     }  
  40.   
  41. }  

结果显示:



  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值