Java IO流(对象序列化)

本文介绍Java中对象的序列化过程及其实现方法,包括ObjectOutputStream与Serializable接口的使用,探讨管道流如何实现线程间通信,以及RandomAccessFile、DataInputStream等流对象的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------- android培训java培训、期待与您交流! ----------

一、对象的序列化

   1、流的特点是操作数据的,现在把数据封装成对象,

而对象本身在堆内存中,用完之后堆内存会释放,对象也就不存在了,接下来用流的方式把对象存储在硬盘中,

对象里的数据也会存下来------- 对象的持久 化或者序列化
被操作的对象需要实现Serializable (标记接口);
2、ObjectOutputStream-----直接操作对象的流,使流和对象相结合
public class ObjectOutputStream
extends  OutputStream
implements  ObjectOutput ObjectStreamConstants
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以 使用 ObjectInputStream  读取(重构)对象。通过在流中使用文件可以实 现对象的持久存储 。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
3、public interface Serializable
类通过实现 java.io.Serializable 接口以启用其序列化功能
  Serializable这个接口没有方法,没有方法的接口称为 标记接口 --  就是给类做个标记,加了个 serialVersionUID 
 
4 静态不能被序列化-----静态在方法区里边
 ObjectStreamDemo +preson
示例:
import java.io.*;
class Person implements Serializable
{
public static final long serialVersionUID = 42L;
 
private String name;
transient int age;  // 被这个关键字修饰,保证 age 值就在堆内存中存在,不在文本文件中。。
static String country = "cn";   // 静态不能被序列化-----静态在方法区里边
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
 
 
/***************************/
 
 
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();
 
System.out.println(p);
ois.close();
}
 
public static void writeObj()throws IOException
{
ObjectOutputStream oos = 
new ObjectOutputStream(new FileOutputStream("obj.txt"));
 
oos.writeObject(new Person("lisi0",399,"kr"));
 
oos.close();
}

}

二、管道流

       管道流主要作用是可以连接两个线程间的通信。管道流也分为字节流(PipedInputStream、PipedOutputStream)与字符流(PipedReader、PipedWriter)两种类型.

一个PipedInputStream 对象必须和一个PipedOutputStream 对象进行连接而产生一个通信管道,PipedOutputStream 可以向管道中写入数据,PipedInputStream 可以从管道中读取 PipedOutputStream 写入的数据。


1 public class PipedOutputStream
extends  OutputStream

可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。


2 public class PipedInputStream
extends  InputStream

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。


3、管道实现线程间通信图解
 
示例:
import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void 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(" 管道读取流失败 ");
}
}
}
 
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out = out;
}
public void run()
{
try
{
System.out.println(" 开始写入数据,等待 6 秒后。 ");
Thread.sleep(6000);
out.write("piped lai la".getBytes());// 是将一个字符串转化为一个字节数组。 String getBytes() 方法是得到一个系统默认的编码格式的字节数组。将一个 string 类型的字符串中包含的字符转换成 byte 类型并且存入一个 byte 数组中
out.close();
}
catch (Exception e)
{
throw new RuntimeException(" 管道输出流失败 ");
}
}
}
 
class  PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
 
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);
 
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}

 

 

三、RandomAccessFile   随机访问文件

1、
public class RandomAccessFile
extends Object
implements DataOutputDataInput Closeable

此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。


2RandomAccessFile工作方式 是,把 DataInputStream DataOutputStream 结合起来,再加上它自己的一些方法,比如定位用的 getFilePointer( ) ,在文件里移动用的 seek( ) ,以及判断文件大小的 length( ) skipBytes() 跳过多少字节数。此外,它的构造函数还要一个表示以只读方式 ("r") ,还是以读写方式 ("rw") 打开文件的参数  ( C fopen( ) 一模一样 ) 。它不支持只写文件。
 
3、该类不是算是IO 体系中子类。
而是直接继承自 Object
但是它是 IO 包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过 getFilePointer 获取指针位置,
同时可以通过 seek 改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读 r ,,读写 rw 等。
 
如果模式为只读  r 。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式 rw 。操作的文件不存在,会自动创建。如果存则不会覆盖。
示例:
import java.io.*;
 
class RandomAccessFileDemo 
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
 
//System.out.println(Integer.toBinaryString(258));
 
}
 
public static void readFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
// 调整对象中指针。
//raf.seek(8*1);
 
// 跳过指定的字节数
raf.skipBytes(8);
 
byte[] buf = new byte[4];
 
raf.read(buf);
 
String name = new String(buf);
 
int age = raf.readInt();
 
 
System.out.println("name="+name);
System.out.println("age="+age);
 
raf.close();
 
 
}
 
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write(" 周期 ".getBytes());
raf.writeInt(103);
 
raf.close();
}
 
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
 
raf.write(" 李四 ".getBytes());
raf.writeInt(97);
raf.write(" 王五 ".getBytes());
raf.writeInt(99);
 
raf.close();
}

}

 

四、操作基本数据类型 --------->可以用于操作基本数据类型的数据的流对象

DataInputStreamDataOutputStream

1
public class DataInputStream
extends  FilterInputStream
implements  DataInput

数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。


2 public class DataOutputStream
extends  FilterOutputStream
implements  DataOutput
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
 
示例
import java.io.*;
class DataStreamDemo 
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
 
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
// osw.write(" 你好 ");
// osw.close();
 
// readUTFDemo();
 
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
 
String s = dis.readUTF();
 
System.out.println(s);
dis.close();
}
 
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
 
dos.writeUTF(" 你好 ");
 
dos.close();
}
 
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
 
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
 
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
 
dis.close();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
 
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
 
dos.close();
 
ObjectOutputStream oos = null;
oos.writeObject(new O());
}

}

 

 

五、操作字节数组   操作字符数组

用于操作字节数组的流对象。
 
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
 
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
 
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行 close 关闭。
 
在流操作规律讲解时:
 
源设备,
键盘  System.in ,硬盘  FileStream ,内存  ArrayStream
目的设备:
控制台  System.out ,硬盘 FileStream ,内存  ArrayStream
 
 
用流的读写思想来操作数据。
 
示例:
import java.io.*;
class ByteArrayStream 
{
public static void main(String[] args) 
{
// 数据源。
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
 
// 数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
int by = 0;
 
while((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
 
// bos.writeTo(new FileOutputStream("a.txt"));
 
}
}

 

 

六、字符编码

1
字符流的出现为了方便操作字符,更重要是的加入了编码转换。
通过子类转换流来完成。
InputStreamReader
OutputStreamWriter

在两个对象进行构造的时候可以加入字符集。


2 、常见的编码表
ASCII :美国标准信息交换码。

用一个字节的 7 位可以表示。

ISO8859-1 :拉丁码表。欧洲码表

用一个字节的 8 位表示。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

Unicode:国际标准码,融合了多种文字。

所有文字都用两个字节来表示 ,Java 语言使用的就是 unicode

UTF-8 :最多用三个字节来表示一个字符
 
示例 1
 
import java.io.*;
 
class EncodeStream 
{
public static void main(String[] args) throws IOException 
{
//writeText();
readText();
}
 
public static void readText()throws IOException 
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
 
char[] buf = new char[10];
int len = isr.read(buf);
 
String str = new String(buf,0,len);
 
System.out.println(str);
 
isr.close();
}
public static void writeText()throws IOException 
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
 
osw.write(" 你好 ");
 
osw.close();
}
 
}
 
 
编码:字符串变成字节数组。
 
解码:字节数组变成字符串。
String-->byte[];  str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);
 
示例 2
import java.util.*;
class  EncodeDemo
{
public static void main(String[] args)throws Exception 
{
String s = " 哈哈 ";
 
byte[] b1 = s.getBytes("GBK");
 
System.out.println(Arrays.toString(b1)); // 数组变成字符串
String s1 = new String(b1,"utf-8");
System.out.println("s1="+s1);
 
// s1 进行 iso8859-1 编码。
byte[] b2 = s1.getBytes("utf-8");
System.out.println(Arrays.toString(b2));    
 
String s2 = new String(b2,"gbk");
 
System.out.println("s2="+s2);
}
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值