上一篇《Java中的IO流(五)》把流中的打印流PrintStream,PrintWriter,序列流SequenceInputStream以及结合之前所记录的知识点完成了文件的切割与文件的合并功能,接下来我们接着记录有关于Java中流的其它知识点。

  这一系列的知识点虽然取名叫“Java中的IO流”,便实质上有些知识点并非IO包中的内容,只是跟IO流相关联或相类似,所以归纳进来了。

一,能操作对象的流:ObjectOutputStream和ObjectInputStream

  ObjectOutputStream将Java对象的基本数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

  此流只能支持实现了Serializable接口的对象写入流中。每个实现了Serializable对象的类都被编码,编码内容包括类名和类签名,对象的字段值和数组值,以及从初始对象中物其他所有对象的闭包。

  实现了Serializable接口的Pserson类代码如下

 1     public class Person implements Serializable {
 2     private String name;
 3
 4     public String getName() {
 5         return name;
 6     }
 7
 8     public void setName(String name) {
 9         this.name = name;
10     }
11
12     public int getAge() {
13         return age;
14     }
15
16     public void setAge(int age) {
17         this.age = age;
18     }
19
20     private int age;
21 }

  Serializable接口里没有任何的方法,它只是一个标识接口,标识所实现了此接口的类具有被序列化以及反序列化的功能。

  将一个Person对象序列化到obj.object文件中代码如下

1     private static void function_demo1() throws IOException {
2         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));
3         Person person = new Person();
4         person.setName("张三");
5         person.setAge(32);
6         oos.writeObject(person);
7         oos.close();
8     }

  将此被序列化的对象反序列化读出来的代码如下

1     private static void function_demo2() throws IOException, ClassNotFoundException {
2         ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));
3         Person p = (Person) ois.readObject();
4         System.out.println(p.getName());
5         System.out.println(p.getAge());
6         ois.close();
7     }

二,Serializable接口
  鉴于以上序列化与反序列化的例子,我们对Person类稍作更改,将属性name的访问修饰符改为public,保存,然后再运行反序列化的那段代码,将得到以下的结果

Exception in thread "main" java.io.InvalidClassException: com.zw.otherslikestream.Person; local class incompatible: stream classdesc serialVersionUID = 3588443867695946454, local class serialVersionUID = -3836108205857858437
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at com.zw.otherslikestream.OtherLikeStreamExercise.function_demo2(OtherLikeStreamExercise.java:19)
at com.zw.otherslikestream.OtherLikeStreamExercise.main(OtherLikeStreamExercise.java:14)

  从上面的错误信息我们可以看出,它的意思是“ stream classdesc serialVersionUID = 3588443867695946454”和“local class serialVersionUID = -3836108205857858437”不一样,所以才会出错。

  其原理是这样的:当我们让一个类实现了Serializable接口时,编译时编译器会用一个名叫serialVersionUID的版本号与该类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID。

  如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面(类中字段的访问修饰符啊,数据类型啊等等)计算该类的默认 serialVersionUID 值,不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

  了解了以上的原理后我们手动为Person类添加一个serialVersionUID:private static final long serialVersionUID = 1L;然后再序列化此类后修改此类信息再反序列化此类便不会报错。

三,transient关键字

  我们再次修改Person类

 1 public class Person implements Serializable {
 2     private static final long serialVersionUID = 1L;
 3     private String name;
 4     private static int age;
 5
 6     public String getName() {
 7         return name;
 8     }
 9
10     public void setName(String name) {
11         this.name = name;
12     }
13
14     public int getAge() {
15         return age;
16     }
17
18     public void setAge(int age) {
19         this.age = age;
20     }
21
22 }

  即将字段age改为静态的。再次运行序列化,然后再反序列化此类,发现age变为0了;原理是这样的,静态的字段是被放在JVM的静态区域的,属于共享数据,序列化Person类的时候不可能拿到静态区域里的东西即Serializable接口不可以序列化静态字段或方法;

  若类中的某一个字段不能作为共享数据即不需要被static所修饰,又不想被序列化,则此字段可以被transient关键安来修饰,如下

 1 public class Person implements Serializable {
 2     private static final long serialVersionUID = 1L;
 3     private transient String name;
 4     private static int age;
 5
 6     public String getName() {
 7         return name;
 8     }
 9
10     public void setName(String name) {
11         this.name = name;
12     }
13
14     public int getAge() {
15         return age;
16     }
17
18     public void setAge(int age) {
19         this.age = age;
20     }
21
22 }

  此时Person类的对象被序列化,再被反序列化时,此字段的值为null

 四,RandomAccessFile随机读取与写入类
  RandomAccessFile类直接继承自Objec类,此类支持随机的读取与写入文件(仅限于文件);
  此类内部维护着一个大型的byte类型数组,存在指向该数组的隐含光标或指针被称为文件指针;
  输入操作从文件指针开始读取字节并随着对字节的读取而前移指针;
  文件指针可以通过getFilePointer来获取位置,并可通过seek来设置指针位置;
  该类有两个构造函数
    RandomAccessFile(File file,String mode)创建从中读取和向其中写入(可选)的随机访问文件流,该文件由File参数指定。
    RandomAccessFile(String name,String mode)创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
  由此构造函数也可以看出是此类主要是用来操作文件的;构造函数的第二个参数mode的枚举值包括以下几种 
    r      以只读方式打开。调用结果对象的合作write方法都将导致抛出IOException。
    rw      打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
      rws     打开以便读取和写入,对于“rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备
      rwd       打开以便读取和写入,对于“rw”,还要求对文件内容的每个更新都同步写入到底层存储设备
 
  以下代码演示了通过RandomAccessFile类实现写入操作

1     private static void function_demo3() throws IOException {
2         RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");//以读取写入方法创建文件(若文件不存在)
3         raf.write("sb".getBytes());//写入两个字节
4         raf.writeInt(97);//写入一个Int数据,此方法按四个字节写入文件,先写高位
5         raf.write("小龙".getBytes());//由于是utf8编码形式,所以两个汉字占用了六个字节
6         raf.writeInt(109);//写入四个字节
7         raf.close();8     }

  RandomAccessFile类的读取操作

 1     private static void function_demo4() throws IOException {
 2         RandomAccessFile raf = new RandomAccessFile("raf.txt", "r");
 3         byte[] bt = new byte[2];//因为存储时是两个字节,所以这里声明字节数组长度为2
 4         raf.read(bt);//从RandomAccessFile所维护的byte数组中读取前两个字节的内容
 5         System.out.println(new String(bt));//转为字符输出
 6         System.out.println(raf.readInt());//用readInt方法读取写入的int数据97
 7         byte[] be = new byte[6];//因为存储时两个汉字共6个字节,所以声明长度为6
 8         raf.read(be);//读取6个字节到be
 9         System.out.println(new String(be));//转成字符输出
10         System.out.println(raf.readInt());//读取109数据
11         raf.close();
12     }

  RandomAccessFile类的随机读取与写入操作只是针对于此类所维护的那个byte数组的指针的位置,以下是示例代码

 1     private static void function_demo5() throws IOException {
 2         RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
 3         raf.seek(10);// 将RandomAccessFile维护的数组指针移动到下标为10的位置
 4         raf.write("李四".getBytes());// 写入六个字节
 5         System.out.println(raf.getFilePointer());// 获取当前指针的位置16
 6         raf.seek(10);// 将指针移动到10的位置,可直接读取到刚才写放的内容
 7         byte[] bt = new byte[3];
 8         int len;
 9         while ((len = raf.read(bt)) != -1) {
10             System.out.print(new String(bt) + "   ");
11         }
12         raf.close();
13     }

 五,管道流PipedInputStream和PipedOutputStream
  管道输入流与管道输出流是为了操作线程间的通信而设立的;管道输入流提供管道输出流要写入的所有字节数据;通常情况下,数据由某个线程从管道输入流对象读取数据,并由其它线程将数据写入到与之相对应的输出流对象;
  管道输入流与管道输出流对象关联可通过两种方法:
    1,通过构造函数,即创建管道流的时候传入相对应的流对象,如下:
      PipedOutputStream output=new PipedOutputStream();
      PipedInputStream input=new PipedInputStream(output);
      或
      PipedInputStream input=new PipedInputStream();
      PipedOutputStream output=new PipedOutputStream(input);
    2,通过connect方法传入相应的流对象
      PipedOutputStream output=new PipedOutputStream();
      PipedInputStream input=new PipedInputStream();
      output.connect(input);
      或
      PipedInputStream input=new PipedInputStream();
      PipedOutputStream output=new PipedOutputStream();
      input.connect(output);
  管道流的具体操作如下
 1 public class Input implements Runnable {
 2     private PipedInputStream input;
 3
 4     public Input(PipedInputStream input) {
 5         this.input = input;
 6     }
 7
 8     @Override
 9     public void run() {
10         try {
11             int len;
12             byte[] bt = new byte[1024];
13             while ((len = input.read(bt)) != -1) {
14                 System.out.println(new String(bt));
15             }
16             input.close();
17         } catch (IOException e) {
18             e.printStackTrace();
19         }
20     }
21
22 }

 1 public class Output implements Runnable {
 2     private PipedOutputStream output;
 3
 4     public Output(PipedOutputStream output) {
 5         this.output = output;
 6     }
 7
 8     @Override
 9     public void run() {
10         try {
11             output.write("测试管道流".getBytes());
12             output.close();
13         } catch (IOException e) {
14             e.printStackTrace();
15         }
16     }
17
18 }

1     private static void function_demo6() throws IOException {
2         PipedInputStream input = new PipedInputStream();
3         PipedOutputStream output = new PipedOutputStream();
4         input.connect(output);
5         new Thread(new Input(input)).start();
6         new Thread(new Output(output)).start();
7     }

六,操作基本数据类型的流对象DataInputStream和DataOutputStream

  IO流中提供了两个操作基本数据类型的流对象,可对基本数据类型进行流的读取与写入操作,这两个流提供了对各种基本数据类型的读与写的操作,其中有两个特殊的读写方法readUTF()和writeUTF(),这两个方法分别使用UTF-8修改版编码将一个字符串写入或读出流中,演示如下

1     private static void function_demo7() throws IOException {
2         DataOutputStream outputStream = new DataOutputStream(new FileOutputStream("output.txt"));
3         outputStream.writeUTF("张三");
4         outputStream.close();
5     }

1     private static void function_demo8() throws IOException {
2         DataInputStream inputStream = new DataInputStream(new FileInputStream("output.txt"));
3         System.out.println(inputStream.readUTF());
4         inputStream.close();
5     }

七,操作数组的流ByteArrayInputStream和ByteArrayOutputStream,操作字符的流CharArrayReader和CharArrayWriter,操作字符串的流StringReader和StringWriter

  由于这几个流的操作方式基本相同,所以下面演示一下操作数组的流,其它两个流就不再演示了

1     private static void function_demo9() throws IOException {
2         ByteArrayInputStream in = new ByteArrayInputStream("李四".getBytes());
3         ByteArrayOutputStream ba = new ByteArrayOutputStream();
4         int len;
5         while ((len = in.read()) != -1) {
6             ba.write(len);
7         }
8         System.out.println(ba.toString());
9     }

 

转载于:https://www.cnblogs.com/zw971084570/p/10063994.html

Java中的IO流(六)相关推荐

  1. java io流分为,Java中的IO流按照传输数据不同,可分为和

    Java中的IO流按照传输数据不同,可分为和 答:字节流 字符流 克里斯蒂安 · 麦茨指出:想象的能指就是电影的能指,作为象征的科学,在第三视野范围内的解读,它是( ) 答:建立在共同的永久的背景之中 ...

  2. 猿创征文|Java中的IO流大家族 (两万字详解)

    目录 IO流 概述 分类 四大家族 需要掌握的16个流 文件专属 FileInputstream(读 字节) FileInputStream类的其他常用方法 FileOutputStream (写 字 ...

  3. java中的IO流(超全)(超详解)结合实例轻松掌握

    java进阶之IO流 IO流的概念(大纲): 1.InputStream和OutputStream的继承关系图 2.Reader和Writer的继承关系图 3.文件专属流(加※为重点掌握) ※File ...

  4. java中关于IO流的知识总结(重点介绍文件流的使用)

    今天做软件构造实验一的时候,用到了java文件流的使用,因为之前学的不是很踏实,于是决定今天好好总结一下, 也方便以后的回顾. 首先,要分清IO流中的一些基础划分: 按照数据流的方向不同可以分为:输入 ...

  5. io流技术java_技术文章-java中的IO流

    1.File类 Java中对文件有操作时,可以实例化一个File对象,将文件路径利用这样的形式赋给File对象. File f = new File(filePath); File类的基本操作包括: ...

  6. Java中的IO流与Properties

    IO流 1 File 1.1 File类概述和构造方法 File:它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一 ...

  7. Java中的IO流修改

    Java.IO Java的核心库java.io提供了全面的IO接口.包括:文件读写.标准设备输出等.Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入. Java. ...

  8. Java中的IO流(最详细解说)

    IO流 一.File类的使用 1.常用构造器 2.路径分隔符 3.常用方法 二.IO流原理 Java IO原理 三.流的分类 1.输入流 (1)InputStream & Reader (2) ...

  9. java中的IO流之序列化与反序列化(对象数据和文件的读写交互)

    前言必读 读者手册(必读)_云边的快乐猫的博客-CSDN博客 一.知识点介绍 1.什么是序列化与反序列化? 对象与磁盘的交互读写. 2.为什么要学? 2.1在之前的IO流中,都是内存与磁盘进行交互的读 ...

最新文章

  1. 英特尔发布智慧社区解决方案,全栈技术支撑,涵盖五大战略方向
  2. ​Java 中的内存溢出和内存泄露是什么?我给你举个有味道的例子​
  3. 升级鸿蒙实例,华为鸿蒙 HarmonyOS 最新动作:上线 40+ 个 Sample 示例应用
  4. Bag of Features (BOF)图像检索算法
  5. 反激式开关电源中PC817与TL431的配合电路探讨
  6. Java Web -【分页功能】详解
  7. 【业务知识】企业数字档案馆总体架构图
  8. LiveVideoStack线上分享第五季(三):新一代直播传输协议SRT
  9. VS2008都出來了﹐看來我們升級VS2005的計划要改了。
  10. css设计引言,HTML5与CSS3设计模式 引言(3)
  11. 一个init.php(网站启始)的一般信息
  12. 【老生谈算法】matlab实现傅里叶变换算法源码——傅里叶变换
  13. 充电器input与output_input和output的区别
  14. 网络延迟及故障分析与排查实战
  15. 使用C++实现“生命游戏“
  16. Edge浏览器如何关闭金山毒霸安全主页.
  17. python的一个基础性问题,求平行四边形的面积
  18. 关于silvaco安装不成功问题解决
  19. 基础30讲 第九讲 一元函数积分学的几何应用
  20. office2016安装后右键新建没有word、excel、ppt等解决方法

热门文章

  1. ubuntu grub 操作
  2. jquery.roundabout.js图片叠加3D旋转
  3. hdu5486 Difference of Clustering 暴力
  4. 无法序列化会话状态。请注意,当会话状态模式为“StateServer”或“SQLServer”时,不允许使用无法序列化的对象或 MarshalByRef 对象。...
  5. 如何修复XML内存“泄漏”
  6. ios 内存管理的理解(二)ARC概念及原理
  7. CoordinatorLayout 使用综述系列(二)与AppBarLayout结合上下联动效果
  8. Android对Bitmap的内存优化
  9. 用.net core实现反向代理中间件
  10. layui 表格点击图片放大