Java-输入输出机制详解

来源:互联网 发布:君知其难也的其意思 编辑:程序博客网 时间:2024/06/01 09:05

输入输出是所有程序必须的一部分,允许程序读取外部数据(磁盘、光盘等数据)、用户输入数据;允许程序记录允许状态并输出到磁盘、光盘中,这些操作就是I/O。而java的IO也有一个完善的IO机制来提供一系列的操作。

文章结构:1.IO的解释;2.File类;3.Java的IO流;4.对象序列化;5.NIO(New IO);6.编码使用IO时的技巧以及注意点


一、IO的解释:

Java中I/O操作主要是指使用Java进行输入,输出操作。Java把所有设备里的有序数据抽象成流模型,设备抽象成一个“水管”的模型。Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。 java的IO流(均在java.io包下)。


二、File类

定义:代表与平台无关的文件和目录

作用:1.在程序操作文件和目录;2.不能访问文件内容本身,访问文件内容本身需要使用输入输出流

用法:可以使用文件路径字符串来创建File实例(此路径可以是绝对路径也可以是相对路径,windows系统下,如果路径开头是盘符,则说明它是个绝对路径)

注意 File类的一个用法-文件过滤器:File类的list()方法中可以接收一个FilenameFilter参数,FilenameFilter接口里包含一个accept(File dir,String name)方法,可依次对指定File的所有子目录或者文件进行迭代,如果返回true则会列出该子目录或文件。作用:列出符合条件的文件。


三、Java的IO流

(1)流的分类(不同方式)和定义:

1.按流的流向不同(从程序所在内存角度来划分)。分为输入流和输出流。其中FileInputStream和FileReader都是节点流,直接和指定文件关联,FileOutputStream和FileWriter都是节点流,直接和指定文件关联。

这里写图片描述

2.按操作的数据单元不同。分为字节流和字符流。

这里写图片描述

3.按流的角色不同。分为:节点流(低缓流)和处理流(高级流)

这里写图片描述

使用处理流重点!!!)来保证节点流是一种装饰器模式,使用处理流保证不同的节点流优点有:1.可以消除不同节点流的实现差异;2.提供更方便的方法来完成输入/输出功能。

(2)简述抽象类的部分子类:

有的输入输出流实现类都是从InputStream、OutputStream、Reader、Writer抽象基类派生来的,然后如果访问文件则提供了FileInputStream,FileOutputStream,FileReader,FileWriter。访问字符串,提供了StringReader,StringWriter。
下面列出几个常用的实现类说明:

这里写图片描述


四、对象序列化(Serialize)(重点!!):

关于对象序列化需要清楚几个东西:1.序列化机制;2.序列化定义;3.序列化用法;4.反序列化;5.自定义序列化。

(1)序列化机制:允许将实现序列化的Java对象转换成字节序列,这些字节序列可以保存在磁盘上,或者通过网络传输,以备以后重新恢复成原理的对象。序列化机制使得对象脱离程序的运行而独立存在。

序列化算法:一、所有保存到磁盘中的对象都有一个序列化编号;二、当程序视图序列化一个对象时,会先检查对象是否被序列化过;三、如果某个对象已经序列化过,程序就只是直接 输出一个序列化编号,而不是重新序列化对象。

(2)序列化定义:将一个Java对象写入IO流中。而对象的反序列化指从IO流中恢复该Java对象

(3)序列化用法:为了让某个类可序列化,就必须实现两个接口之一:1. Serializable,一个标记接口,即实现该接口无须实现任何方法,表明该类的实例是可序列化的了;2.Externalizable,但这个接口强制自定义序列化机制。(在一会的自定义序列化中介绍)。

    //普通的一个Bean类,只是实现了Serializable接口,该接口标识该类的对象是可序列化的    public class Person implements java.io.Serializable{            private String name;            private int age;            public Person(String name,int age){                    System.out.println("有参数的构造器");                    this.name=name;                    this.age=age;            }            //省略get和set方法      }

(4)反序列化:

定义:对象的反序列化指从IO流中恢复该Java对象
注意:一、反序列号读取的仅仅是java对象的数据,而不是java类;二、反序列化机制无需通过构造器来初始化java对象;三、如果使用序列化机制向文件中写入多个java对象,使用反序列化机制恢复对象时必须按实际写入的顺序读取;四、当一个可序列化类有多个父类,那么这些父类要么有无参数的构造器要么也是可序列化的。
一些对类的修改可能会导致失败的情况:一、如果修改类时,仅修改方法,则反序列化不受影响,类定义无须修改serialVersionUID类变量的值;二、如果修改类时仅修改静态变量或瞬态实例变量,则反序列化不受影响,类定义无须修改serialVersionUID;三、如果修改类时修改了非瞬态的实例变量,则可能导致序列化版本不兼容。

(5)自定义序列化:

原因:一些是敏感信息的实例变量,不希望系统将实例变量值进行序列化。当对某个对象进行序列化时,系统会自动把该对象的所有实例变量依次序列化。如果某个实例变量引用到另一个对象,则被引用对象也会被序列化(这就叫递归序列化)。
有两大做法。

做法一:重写序列化方法:

transient关键字(针对于变量 ),用此关键字修饰的实例变量可无须序列化,那个实例变量会被完全隔离在序列化机制外。但是!!要注意:这样导致反序列化机制恢复Java对象时,无法取得那个被transient修饰的值。
针对于方法 :重写签名方法来自定义序列化。两个方法:

writeObject()负责写入特定类的实例状态和readObject()负责从流中读取并恢复对象实例变量。writeObject方法存储实例变量的顺序应和readObject方法中恢复实例变量的顺序一致

做法二:Externalizable接口!!!!

也是刚刚所说的序列化方法之一。意义作用:完全由程序员决定存储和恢复对象数据。使用:如果需要实现此接口的对象,一样调用ObjectOutputStream的writeObject方法输出该对象;反序列化该对象,则调用ObjectInputStream的readObject方法。

对比两种序列化机制:

这里写图片描述

对象序列化注意点:

1.对象的类名、实例变量(包括基本类型、数组、对其他对象的引用)都会被序列化;方法、、类变类(即static修饰的成员变量)、transient实例变量(也称瞬态实例变量)都不会被序列化。

2.实现Serializable接口的类如果需要让某个实例变量不被序列化,则可在该实例变量前加transient修饰符,而不是加static。虽然static可达到这效果,但不能这样用。

3.保证序列化对象的实例变量类型也是可序列化的,否则需要用transient关键字修饰该变量,否则该类是不可序列化的

4.反序列化对象时必须有有序列化对象的class文件

5.当通过文件、网络来读取序列化后的对象时,必须按实际写入的顺序读取。


五、NIO(New IO)

概述:采用内存映射文件的方式来处理输入/输出

在NIO之前,以前介绍的输入流、输出流都是阻塞式的输入/输出,如:BufferedReader读取输入流中的数据时,如果没读到有效数据,程序将在此阻塞该线程;如:使用InputStream的read()方法从流中读取数据,如果数据源没数据,也会阻塞该线程。

核心:Channel(通道)和Buffer(缓冲)。

(一)、Channel(通道)

定义:对传统的输入/输出系统的模拟,在新IO系统中所有数据都需要通过通道传输

与传统的流对象区别:1.Channel可直接将指定文件的部分或全部直接映射成Buffer;2.程序不能直接访问Channel中的而数据(包括读取、写入),Channel只能与Buffer进行交互。

补充:新IO的channel是按功能来划分的,如:支持线程之间通信的管道:SourceChannel。

三类方法:write、read、map。其中:write和read用于从Buffer中读取数据或向Buffer中写入数据,而map用于将Channel对应部分或全部数据映射成ByteBuffer。

(二)Buffer(缓冲)

定义:可被理解为一个容器,本质是一个数组,发送到Channel中的所有对象都要放到Buffer中。

三大重要概念:容量(capacity)–表示该Buffer的最大数据容量;界限(limit)–位于limit后的数据既不可被读,也不可被写;位置(position)–用于知名下一个可被读出的或者写入的缓冲区位置索引。

两个重要方法:flip()–为从Buffer中取出数据做好准备,把没有数据的存储空间封闭起来,不让读,避免读出null;clear()–为再次向Buffer中装入数据做好准备,调用clear方法将Buffer的position设置为0。

补充:allocate()方法创建的Buffer对象是普通Buffer;allocateDirect()方法创建的是直接Buffer,只适用于长生存期的Buffer。而且只有ByteBuffer提供,其他类型Buffer想使用,需要转型。


(六)编码使用IO时的技巧以及注意点:

1.技巧一:-升级的兼容性问题-项目升级,class文件也会升级,要保证两个class文件的兼容性。为了确保序列化版本的兼容性。不显示指定serialVersionUID类变量的值还不利于不同的JVM之间移植。优化做法:在每个要序列化的类加入private static final long serialVersionUID这个类变量。

2.技巧二:程序创建的每个JavaBean类都实现Serializable。具体请注意上文的序列化机制对比。


好了,Java-输入输出机制详解以及开发技巧点。欢迎在下面指出错误,共同学习!

转载请注明:【JackFrost的博客】

更多内容,可以访问JackFrost的博客

本博主最近会一直更新Java知识、数据结构与算法、设计模式的文章(因为最近在学习,哈哈哈),欢迎关注。

0 0