Java中的IO流(六)
上一篇《Java中的IO流(五)》把流中的打印流PrintStream,PrintWriter,序列流SequenceInputStream以及结合之前所记录的知识点完成了文件的切割与文件的合并功能,接下来我们接着记录有关于Java中流的其它知识点。
这一系列的知识点虽然取名叫“Java中的IO流”,便实质上有些知识点并非IO包中的内容,只是跟IO流相关联或相类似,所以归纳进来了。
一,能操作对象的流:ObjectOutputStream和ObjectInputStream
ObjectOutputStream将Java对象的基本数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
此流只能支持实现了Serializable接口的对象写入流中。每个实现了Serializable对象的类都被编码,编码内容包括类名和类签名,对象的字段值和数组值,以及从初始对象中物其他所有对象的闭包。
实现了Serializable接口的Pserson类代码如下
public class Person implements Serializable {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age;
}
Serializable接口里没有任何的方法,它只是一个标识接口,标识所实现了此接口的类具有被序列化以及反序列化的功能。
将一个Person对象序列化到obj.object文件中代码如下
private static void function_demo1() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));
Person person = new Person();
person.setName("张三");
person.setAge(32);
oos.writeObject(person);
oos.close();
}
将此被序列化的对象反序列化读出来的代码如下
private static void function_demo2() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));
Person p = (Person) ois.readObject();
System.out.println(p.getName());
System.out.println(p.getAge());
ois.close();
}
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类
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private static int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} }
即将字段age改为静态的。再次运行序列化,然后再反序列化此类,发现age变为0了;原理是这样的,静态的字段是被放在JVM的静态区域的,属于共享数据,序列化Person类的时候不可能拿到静态区域里的东西即Serializable接口不可以序列化静态字段或方法;
若类中的某一个字段不能作为共享数据即不需要被static所修饰,又不想被序列化,则此字段可以被transient关键安来修饰,如下
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private transient String name;
private static int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} }
此时Person类的对象被序列化,再被反序列化时,此字段的值为null
private static void function_demo3() throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");//以读取写入方法创建文件(若文件不存在)
raf.write("sb".getBytes());//写入两个字节
raf.writeInt(97);//写入一个Int数据,此方法按四个字节写入文件,先写高位
raf.write("小龙".getBytes());//由于是utf8编码形式,所以两个汉字占用了六个字节
raf.writeInt(109);//写入四个字节
raf.close(); }
RandomAccessFile类的读取操作
private static void function_demo4() throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.txt", "r");
byte[] bt = new byte[2];//因为存储时是两个字节,所以这里声明字节数组长度为2
raf.read(bt);//从RandomAccessFile所维护的byte数组中读取前两个字节的内容
System.out.println(new String(bt));//转为字符输出
System.out.println(raf.readInt());//用readInt方法读取写入的int数据97
byte[] be = new byte[6];//因为存储时两个汉字共6个字节,所以声明长度为6
raf.read(be);//读取6个字节到be
System.out.println(new String(be));//转成字符输出
System.out.println(raf.readInt());//读取109数据
raf.close();
}
RandomAccessFile类的随机读取与写入操作只是针对于此类所维护的那个byte数组的指针的位置,以下是示例代码
private static void function_demo5() throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
raf.seek(10);// 将RandomAccessFile维护的数组指针移动到下标为10的位置
raf.write("李四".getBytes());// 写入六个字节
System.out.println(raf.getFilePointer());// 获取当前指针的位置16
raf.seek(10);// 将指针移动到10的位置,可直接读取到刚才写放的内容
byte[] bt = new byte[3];
int len;
while ((len = raf.read(bt)) != -1) {
System.out.print(new String(bt) + " ");
}
raf.close();
}
public class Input implements Runnable {
private PipedInputStream input; public Input(PipedInputStream input) {
this.input = input;
} @Override
public void run() {
try {
int len;
byte[] bt = new byte[1024];
while ((len = input.read(bt)) != -1) {
System.out.println(new String(bt));
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
public class Output implements Runnable {
private PipedOutputStream output; public Output(PipedOutputStream output) {
this.output = output;
} @Override
public void run() {
try {
output.write("测试管道流".getBytes());
output.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
private static void function_demo6() throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
input.connect(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
六,操作基本数据类型的流对象DataInputStream和DataOutputStream
IO流中提供了两个操作基本数据类型的流对象,可对基本数据类型进行流的读取与写入操作,这两个流提供了对各种基本数据类型的读与写的操作,其中有两个特殊的读写方法readUTF()和writeUTF(),这两个方法分别使用UTF-8修改版编码将一个字符串写入或读出流中,演示如下
private static void function_demo7() throws IOException {
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream("output.txt"));
outputStream.writeUTF("张三");
outputStream.close();
}
private static void function_demo8() throws IOException {
DataInputStream inputStream = new DataInputStream(new FileInputStream("output.txt"));
System.out.println(inputStream.readUTF());
inputStream.close();
}
七,操作数组的流ByteArrayInputStream和ByteArrayOutputStream,操作字符的流CharArrayReader和CharArrayWriter,操作字符串的流StringReader和StringWriter
由于这几个流的操作方式基本相同,所以下面演示一下操作数组的流,其它两个流就不再演示了
private static void function_demo9() throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream("李四".getBytes());
ByteArrayOutputStream ba = new ByteArrayOutputStream();
int len;
while ((len = in.read()) != -1) {
ba.write(len);
}
System.out.println(ba.toString());
}
Java中的IO流(六)的更多相关文章
- Java中的IO流(四)
上一篇<Java中的IO流(三)>把IO流中的文件及目录操作的对象File类记录了一下,本篇把本不属性IO流但又和IO流有关系的一个对象作一下记录,此对象本属于集合框架里的一个子集,即Pr ...
- Java中的IO流(三)
上一篇<Java中的IO流(二)>把学习Java的字符流以及转换流作了一下记录,从本篇开始将把IO流中对文件或文件夹操作的对象File类的学习进行一下记录. 一,File类的构造函数及字段 ...
- Java中的IO流(二)
上一篇<Java中的IO流(一)>把学习IO流的字符流作了一下记录,本篇把字节流记录一下. 一,Java中的字节流 Java中的字节流的操作方式与字符流的操作方式大致相同,连方法名都是类似 ...
- java中的IO流
Java中的IO流 在之前的时候我已经接触过C#中的IO流,也就是说集中数据固化的方式之一,那么我们今天来说一下java中的IO流. 首先,我们学习IO流就是要对文件或目录进行一系列的操作,那么怎样操 ...
- Java中的IO流总结
Java中的IO流总结 1. 流的继承关系,以及字节流和字符流. 2. 节点流FileOutputStream和FileInputStream和处理流BufferedInputStream和Buffe ...
- Java中的IO流大体介绍
由于Java中的IO流是在是知识点繁多,所以我大约花了1周的时间将其整理起来.但是整理起来后并不是将完事了,我还是要分字节流和字符流来讲述.然后字节流和字符流中还有是否带有缓冲流. 讲述完IO流后我将 ...
- Java中的IO流,Input和Output的用法,字节流和字符流的区别
Java中的IO流:就是内存与设备之间的输入和输出操作就成为IO操作,也就是IO流.内存中的数据持久化到设备上-------->输出(Output).把 硬盘上的数据读取到内存中,这种操作 成为 ...
- Java中的IO流(五)
上一篇<Java中的IO流(四)>记录了一下Properties类,此类不属于IO流,它属于集合框架.接下来说一下IO流中的其它流 一,打印流PrintStream PrintStream ...
- JAVA 中的IO流
Java中的IO流是用来处理设备与设备之前的数据传输,在java中以流的形式传输.流分为两类:字节流和字符流. 字节流:InputStream,OutPutSteam.(计算机内的数据都是以字节存储的 ...
随机推荐
- BZOJ3529: [Sdoi2014]数表(莫比乌斯反演 树状数组)
题意 题目链接 Sol 首先不考虑\(a\)的限制 我们要求的是 \[\sum_{i = 1}^n \sum_{j = 1}^m \sigma(gcd(i, j))\] 用常规的套路可以化到这个形式 ...
- oracle删除归档日志
查看归档模式: SQL> archive log list; Database log mode Archive Mode Automatic archival Enabled Archive ...
- Oracle 通过出生日期计算年龄
方法一: SELECT TRUNC(months_between(sysdate, birth)/12) AS age from mytable 方法二: select TRUNC((to_char( ...
- Java 开源博客 Solo 1.5.0 发布 - 新皮肤
Solo 1.5.0 正式发布了!这个版本主要是加入了 一款新皮肤 next,感谢一直以来关注和支持我们的朋友! 只需一个命令即可启动(不需要安装数据库.部署容器):也可以通过 war 方式部署容器, ...
- mybatis 关联表心得
1,例如订单表与用户表的关联,一个订单对应一个用户,这是一对一关联: 用代码实现是这样: A(用resultType去实现的话,如下) 1,使用到继承, OrderUser extend Order{ ...
- UIResponder NSSet UITouch UIEvent
UIResponder: UIView的超类,用来响应handle(触屏.motion.响应者等)事件. NSSet:一系列的类集合(类似数组). UITouch:一个点击类.负责:点击的view,w ...
- Oracle 数据库实现数据更新:update、merge
工作中遇到的数据更新,学习记录. 1.使用update进行数据更新 1)最简单的更新 update tablea a set a.price=1.00 2)带条件的数据更新 update tablea ...
- Java学习---多线程的学习
基础知识 每个正在系统上运行的程序都是一个进程(process).每个进程包含一到多个线程(thread).进程也可能是整个程序或者是部分程序的动态执行. 线程是一组指令的集合,或者是程序的特殊段,它 ...
- 沉淀再出发:用python画各种图表
沉淀再出发:用python画各种图表 一.前言 最近需要用python来做一些统计和画图,因此做一些笔记. 二.python画各种图表 2.1.使用turtle来画图 import turtle as ...
- [COGS 2066]七十和十七
2066. 七十和十七 ★★★ 输入文件:xvii.in 输出文件:xvii.out 简单对比时间限制:1 s 内存限制:256 MB [题目描述] 七十君最近爱上了排序算法,于是Ta ...