IO流__【对象的序列化】【管道流】【RandomAccessFile】【DataStream】【ByteArrayStream等】
对象的序列化(持久化)
就是为了保存在内存中的各种对象的状态,并且可以把保存的对象状态再读出来。
虽然你可以用你自己的各种各样的方法来保存Object States,但是Java给我们提供了一种应该比自己更好的保存对象状态的机制,那就是序列化。
简单说:把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
操作对象
ObjectInputStream与ObjectOutputStream(两个类要一起使用)
被操作的对象需要实现Serializable (标记接口)以启用其序列化功能
该接口没有方法;没有方法的接口通常称为标记接口
writeObject();、readObject();操作对象
示例:通过序列化操作一个Person类
import java.io.*; class ObjectStreamDemo { public static void main(String[] args) throws Exception { // writeObj(); readObj(); } public static void readObj() throws Exception //反序列化 { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt")); Person p = (Person)ois.readObject();//类型强转,引用了另一个。java文件的Person类,所以会抛异常ClassNotFoundException System.out.println(p); ois.close(); } public static void writeObj() throws IOException //序列化 { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt")); oos.writeObject(new Person("xiaoqiang",22,"America")); oos.close(); } }Person类
被操作的对象
import java.io.*; class Person implements Serializable //标记接口 { public static final long serialVersionUID = 42L;//1,自定义UID private String name;//1,私有化的成员 transient int age;//3,修饰符,不会被序列化 static String country ="cn";//2,静态成员 Person(String name, int age, String country){ this.name = name; this.age = age; } public String toString(){ //伪代码,没写set,get return name+" : "+age+" : "+counrty; } }
1,将Person的成员私有化后就不能再被序列化,说明序列号是根据成员获取的
可以设置UID固定标识:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
设置后Person的成员即使被私有化也能被序列化
2,静态的成员不能被私有化,例如静态的成员country序列化修改后依然是cn
因为序列化操作的是堆内存中的对象,而静态成员随着类加载,存在于栈中的方法区,所以执行不到
3,如果想让非静态成员不被序列化,就在该成员前面加上transient修饰
管道流
PipedInputStream和PipedOutputStream
一般流的输入输出没有太大关联,而管道流输入输出可以直接进行连接,必须相互连接后创建通信管道;通过结合多线程使用,单线程可能会死锁。
示例:
import java.io.*; class Read implements Runnable //管道输入流线程 { private PipedInputStream in; Read(PipedInputStream in){ //构造函数 this.in = in; } public void run(){ //实现run方法 try{ byte[] buf = new byte[1024];//缓冲数组 System.out.println("阻塞...数据读取中..."); int len = in.read(buf); //字节输入流读取方法 System.out.println("读到数据,阻塞结束"); String s = new String(buf,0,len);//转成字符串 System.out.println(s); in.close(); } catch (IOException e){ throw new RuntimeException("管道流read读取失败!!!"); } } } class Write implements Runnable //管道输出流线程 { private PipedOutputStream out; Write(PipedOutputStream out){ //构造函数 this.out = out; } public void run(){ //覆盖run方法 try{ System.out.println("开始写入...等待中。。。"); Thread.sleep(3000); out.write("hello Piped !".getBytes());//字节读取流write方法,需要将字符转为字节 out.close(); } catch (Exception e){ throw new RuntimeException("管道流write输出失败"); } } } class PipedStreamDemo { public static void main(String[] args) throws IOException { PipedInputStream in = new PipedInputStream(); PipedOutputStream out=new PipedOutputStream(in); // in.connect(out);//connect()创建通信管道,也可直接将in传入out构造参数列表 new Thread(new Read(in)).start(); new Thread(new Write(out)).start(); } }
运行结果:
由运行结果可以看出:管道流输入输出两个线程在执行的过程中,输入流线程如果没有读到输出流的数据,就会一直处于阻塞状态,直到输出流write完毕输入流才开始read。
如果是单线程,极有可能会线程死锁,所以要和多线程配合使用。
RandomAccessFile
随机读写文件该类不算是IO体系的子类,直接继承自Object;
但RandomAccessFile是IO包中成员,具备文件读写功能,能够读写是其内部封装了字节输入流和输出流
能够随机是因为在内部封装了一个byte[] 数组,通过指针对数组的元素进行操作,同时可以通过getFilePointer获取指针位置,通过seek改变指针位置。seek相当强大
通过其构造函数可以看出:该类只能操作文件
而且还有操作文件的模式:只读r,读写rw 等
如果模式为r,不会创建文件,而是读取一个已存在的文件,若该文件不存在会抛出异常
如果模式为rw,操作的文件不存在,会自动创建,如果存在则不会覆盖,而是直接在该文件上操作
示例:
import java.io.*; class RandomAccessFileDemo { public static void main(String[] args) throws IOException { // writeFile(1); // readFile(0); writeFile_2(1); } public static void readFile(int x) throws IOException //随机获取 { RandomAccessFile raf = new RandomAccessFile("raf.txt","r");//"r"只读权限 // raf.seek(8*x); //调整对象中指针,一个人的信息为8个字节,可以通过8的倍数来决定取哪个人的资料 raf.skipBytes(8*x);//跳过指定的字节数,只能往后跳 byte[] buf = new byte[4]; raf.read(buf); //一次读取四个字节,因为设置的名字是由2个字符(4个字节)组成 String name = new String(buf);//将该字节数组转换成字符串 int age = raf.readInt();//直接一次读取四个字节,并转成int型 sop("name: "+name); sop("age: "+age); raf.close(); } public static void writeFile_2(int x)throws IOException //随机写入 { RandomAccessFile raf = new RandomAccessFile("raf.txt","rw"); raf.seek(8*x); //调整指针,直接将信息插入到第五个信息栏,也可对指定位置信息修改(覆盖原有信息) raf.write("王五".getBytes()); raf.writeInt(110); raf.close(); } public static void writeFile() throws IOException { RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");//"rw"权限,读写 raf.write("小强".getBytes());//将字符转成字节 raf.writeInt(98); //write只写int型的最低八位(一个字节),该类提供了writeInt()方法,写入4个字节 raf.write("小明".getBytes()); raf.writeInt(99); raf.close(); } public static void sop(Object obj){ System.out.println(obj); } }
RandomAccessFile类具备强大的随机读写功能,可以对数据进行分段录入,如果引入多线程技术,可以大大提高效率,实际应用:P2P多线程下载
该类要做重点掌握!
一、专门操作基本数据类型的流对象
DataInputStream与DataOutputStream
import java.io.*; class DataStreamDemo { public static void main(String[] args) throws IOException { // writeData(); // readData(); // writeUTFDemo(); readUTFDemo(); } public static void readUTFDemo() throws IOException{ DataInputStream dis = new DataInputStream(new FileInputStream("utfData.txt")); String s = dis.readUTF(); sop(s); dis.close(); } public static void writeUTFDemo() throws IOException{ //以 UTF-8 修改版格式写入此 String 的基本数据。 DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfData.txt")); dos.writeUTF("小强"); dos.close(); } public static void readData() throws IOException{ //读取基本数据类型 DataInputStream dis = new DataInputStream(new FileInputStream("Data.txt")); int n = dis.readInt(); boolean b = dis.readBoolean(); double d = dis.readDouble(); sop("n= "+n); sop("b= "+b); sop("d= "+d); dis.close(); } public static void writeData() throws IOException{ //写入基本数据类型 DataOutputStream dos = new DataOutputStream(new FileOutputStream("Data.txt")); dos.writeInt(22); //4个字节 dos.writeBoolean(true); //1个字节 dos.writeDouble(3.1415926535);//8个字节 dos.close(); } public static void sop(Object obj){ System.out.println(obj); } }
二、用于操作字节数组的流对象
用流的读写思想操作数组ByteArrayInputStream:在构造时需要接受数据源,数据源是一个字节数组
ByteArrayOutputStream:在构造时不用定义数据目的,因为该对象内部封装了可变长度的字节数组,该数组就是数据目的地。
因为这两个流对象都操作数组,并没有操作系统资源,所以不用close()关闭
流操作规律
源: 键盘System.in、硬盘FileStream、内存ArrayStream
目的: 控制台System.our、硬盘FileStream、内存ArrayStream
writeTo(OutputStream out);
示例:
import java.io.*; class ByteArrayStream { public static void main(String[] args) //操作的是数组,一般不用抛异常 { ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFG".getBytes());//数据源 ByteArrayOutputStream baos =new ByteArrayOutputStream(); //数据目的 int by = 0; while ((by=bais.read()) !=-1){ baos.write(by); } System.out.println(baos.size()); //返回缓冲区大小length长度 System.out.println(baos.toString()); //将缓冲区字节 // baos.writeTo(new FileOutputStream("a.txt")); //该方法调用了底层资源,会抛异常 }
三、操作字符数组
CharArrayReader与CharArrayWrite
四、操作字符串
StringReader 与 StringWriter
这两个类与ByteArrayStream类似