Java的IO流与对象的序列化浅谈

来源:互联网 发布:数据库系统分析与实现 编辑:程序博客网 时间:2024/06/06 00:50

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

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

    

    

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

一、IO流的主流分类

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

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

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

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

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

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

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

二、文件类的介绍和说明

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

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

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

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

         

代码如下:

package com.io;import java.io.*;public class listFile {public static void main(String[] args) {// TODO Auto-generated method stub//1 新建一个File类的实例File file=new File("d:"+File.separator+"Xiaomi");//2 进行文件目录的打印print(file);}public static void print(File file) {// TODO Auto-generated method stubif(file!=null){//判断文件不为空if(file.isDirectory()){//判断文件是不是目录   File[] fs=file.listFiles();//返回所有的子文件       if(fs!=null){       //开始遍历子文件       for (int i = 0; i < fs.length; i++) {       print(fs[i]);       }       }}else{//如果不是目录就打印文件的路径System.out.println(file);}}}}

结果显示:


三、输入输出流的介绍

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

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

JavaJDK中也提供了将字节流转换为字符流的类: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中的描述。

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

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

结果显示:


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

package com.io;import java.io.*;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.charset.Charset;public class BufferToText {private static final int BSIZE=1024;public static void main(String[] args) throws Exception {// TODO Auto-generated method stub//写入file中FileChannel fc=new FileOutputStream("date2.txt").getChannel();fc.write(ByteBuffer.wrap("my love我的爱".getBytes()));//将字符转换为字节数组,在放入字节缓冲器中。最后写入到通道中fc.close();fc=new FileInputStream("date2.txt").getChannel();ByteBuffer buffer=ByteBuffer.allocate(BSIZE);//定义一个指定大小的字节缓冲区fc.read(buffer);buffer.flip();//打印读取到的字节//System.out.print(buffer.asCharBuffer());//这种方法没什么用,会出现乱码。//我们需要系统的指定默认的字符集buffer.rewind();String encoding =System.getProperty("file.encoding");//获取文件的字节码System.out.println("Decoding using:"+encoding+":"+Charset.forName(encoding).decode(buffer));//或者我们也可以使用编码来向文件中写入字符fc=new FileOutputStream("date3.txt").getChannel();fc.write(ByteBuffer.wrap("我是一份大菠菜。".getBytes("UTF-16BE")));fc.close();//接下来我们进行读文件fc=new FileInputStream("date3.txt").getChannel();buffer.clear();fc.read(buffer);buffer.flip();System.out.print(buffer.asCharBuffer());}}

结果显示:



2 0
原创粉丝点击