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.(计算机内的数据都是以字节存储的 ...
随机推荐
- PHP在foreach中对$value赋值无效,应该用 ‘键’ 或者 &$value的形式
首先我们看下这段代码: foreach ($data as$value) { $value['name'] = 'Hehe'; } $data中原始的数据为: array(1) { [0] => ...
- Nodejs + express + ejs
特性 <% %> 用于控制流 <%= %> 用于转义的输出 (会对数据字符进行转义) // 数据源 // app.js var tem={ title:"我是中间部分 ...
- change grayscale to pseudo colouring using colormap in Matlab
change grayscale to pseudo colouring using colormap in Matlab In matlab you can view a grayscale ima ...
- HDFS原理解析
一.HDFS简介 HDFS为了做到可靠性(reliability)创建了多分数据块(data blocks)的复制(replicas),并将它们放置在服务器群的计算节点中(computer nodes ...
- IOS xcode安装
xcode软件下载地址: 可以通过虚拟机共享文件夹将xcode传递给虚拟机上的os系统: 第一个程序创建:
- XML与web开发-01- 在页面显示和 XML DOM 解析
前言: 关于 xml 特点和基础知识,可以菜鸟教程进行学习:http://www.runoob.com/xml/xml-tutorial.html 本系列笔记,主要介绍 xml 在 web 开发时需要 ...
- 安卓app开发-01-开发工具及环境配置
安卓app开发-01-开发工具及环境配置 请大家根据推荐指数和自己的意愿选择 使用 Android Studio 1.可以使用 Android Studio 下载地址:http://www.andro ...
- matplotlib.pyplot 导引
matplotlib.pyplot 是采用 python 语言和使用数值数学库 numpy 数组数据的绘图库.其主要目标是用于数据的可视化显示. 输出图形组成 matplotlib.pyplot 模块 ...
- 关于Java Microbenchmark的一点记录
大家知道单元测试对代码质量的保障作用已经没什么可说的了.Microbenchmark(微基准测试)也是保证代码质量的重要手段,也是容易忽略的,它用来衡量一些小的代码片段的性能指标,完善的Microbe ...
- js变量作用域--变量提升
1.JS作用域 在ES5中,js只有两种形式的作用域:全局作用域和函数作用域,在ES6中,新增了一个块级作用域(最近的大括号涵盖的范围),但是仅限于let方式申明的变量. 2.变量声明 var x; ...