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类似



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值