一、ObjectOutputStream和ObjectInputStream(重点)
(1)创建对象:序列化
ObjectOutputStream有两种构造方法
知道了使用构造方法创建对象,那么怎么写入对象呢?
用下面的方法实现,将对象存储起来,实现持久化(程序运行时才存在对象----位于堆内存中,JVM退出时对象被销毁)
要序列化的某些对象不实现java.io.Serializable接口会产生NotSerializableException异常,因此必须要实现Serializable接口。一个类只有实现了Serializable接口才能实现序列化。
(2)读取对象:反序列化-----依据所属类的字节码xxx.class文件
知道了怎么写入对象,下面来说说怎么读取对象
首先创建根据构造方法对象
用下面的方法读取对象-----反序列化
应当注意的是Object readObject() 要抛出两个异常
(3)序列化和反序列化代码实现
import java.io.*;
class Person implements Serializable //实现了该接口的类的对象才能持久化 NotSerializableException: Person
{
public static final long serialVersionUID = 42L;
public transient String name; //transient:瞬态的成员不参与序列化 //3832552154946523251
private int age;
Person(){}
Person(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class Demo1
{
public static void main(String[] args) throws IOException,ClassNotFoundException
{
// ObjectOutputStream ObjectInputStream
writeObj();
readObj();
}
//读取对象:反序列化---依据所属类的字节码Person.class
public static void readObj()throws IOException,ClassNotFoundException
{
ObjectInputStream objin=new ObjectInputStream(new FileInputStream("object.txt"));
//Object readObject()
Person ren =(Person)objin.readObject();
System.out.println(ren.getName()+","+ren.getAge());
objin.close();
}
//对象实现存储:持久化(序列化)
public static void writeObj()throws IOException
{
ObjectOutputStream objout=new ObjectOutputStream(new FileOutputStream("object.txt"));//向文件中写入对象
//void writeObject(Object obj)
objout.writeObject(new Person("李四",20));//Person.class 6353954888121467304-->Person.class
objout.close();
}
}
若将该该文件的类Person类修改再重新编译,readObject()则会找不到Person.class文件,抛出如下异常:
原因在java.io.Serializable有详细说明:大概意思是每个类都会产生一个UID,而修改了类则该类的UID也会发生改变
根据API说明,我们也可以自己指定UID
static final long serialVersionUID = 42L //Long类型,后面数字可以自己指定
自己指定了UID后再修改类后,同样readObject()仍然可以找到该类,充分说明它是根据类的UID找到的
transient:瞬态 transient修饰的成员不参与序列化
问题:为什么药序列化?
实现对象的持久化或网络传输
二、RandomAccessFile
RandomAccessFile是随机访问流类,只能访问文件,内部既有字节输入流,也有字节输出流。该类内有一个数组,使用指针实现随机访问(下标从0开始)。
准备知识:
(1)一个中文占2个字节,一个int型变量占4个字节
(2)RandomAccessFile的三个read()方法和InputStream()的三个read()方法完全一样
(3)RandomAccessFile的三个write()方法和InputStream()的三个write()方法完全一样
此外RandomAccessFile还包含了一系列的readXXX()和writeXXX()方法来完成输入输出
(1)先来看看构造方法
mode可以是"r",“rw”,“rws”,或 “rwd” (read、write缩写)
默认指针是指向了文件开头,那怎么获取文件的指针指到哪里了呢?
查看API有下面的方法 ---- getFilePointer()
那要修改指针的位置怎么办?------ seek()
注意应当使用String的构造方法对byte进行解码
(2)使用RandomAccessFile的示例代码
import java.io.*;
class Demo2
{
public static void main(String[] args)throws IOException,FileNotFoundException
{
writeData();
readData();
}
public static void readData()throws IOException,FileNotFoundException
{
RandomAccessFile random=new RandomAccessFile("random.txt","r");
//默认从文件开头开始读数据
byte[] arr=new byte[4];
int num = random.read(arr);
int age=random.readInt();
System.out.println(new String(arr,0,num)+","+age);
random.seek(16);
num = random.read(arr);
age=random.readInt();
System.out.println(new String(arr,0,num)+","+age);
}
public static void writeData()throws IOException,FileNotFoundException
{
RandomAccessFile random=new RandomAccessFile("random.txt","rw");
//默认指针指向文件开头
random.write("刘能".getBytes());
random.writeInt(58);
//long getFilePointer()
long point = random.getFilePointer();
System.out.println(point);//8
random.seek(16);//设置指针的位置
random.write("赵四".getBytes());
random.writeInt(50);
point = random.getFilePointer();
System.out.println(point);//
}
}
三、内存流
ByteArrayInputStream ----- 向字节数组中写数据(字节数组在内存中)
ByteArrayOutputStream ----- 从字节数组中读数据(字节数组在内存中)
(1)ByteArrayInputStream和ByteArrayOutputStream的构造方法
ByteArrayOutputStream的构造方法里面是空参的,说明ByteArrayOutputStream内部有一个字节数组,那么怎么得到ByteArrayOutputStream内部数组中的数据呢? ----- byte[] toByteArray()
(2)ByteArrayInputStream和ByteArrayOutputStream示例代码
import java.io.*;
class Demo3
{
public static void main(String[] args)throws IOException
{
//ByteArrayInputStream(byte[] buf)
ByteArrayInputStream bis = new ByteArrayInputStream("wiksdhfkjsjhdfksjdhf".getBytes());
ByteArrayOutputStream bos=new ByteArrayOutputStream();//其内部有一个字节数组
byte[] arr=new byte[1024];
int num=0;
while((num=bis.read(arr))!=-1)
{
bos.write(arr,0,num);
}
//得到ByteArrayOutputStream内部数组中的数据 byte[] toByteArray()
byte[] b = bos.toByteArray();
System.out.println(new String(b));
}
}
四、数据流
DataOutputStream
DataInputStream
(1)DataOutputStream构造方法
只有一个
(2)DataInputStream 构造方法
只有一个
(3)使用方法(查看API)
DataOutputStream和DataInputStream的方法是对应的,读的顺序必须和写入的顺序保持一致
(4)示例代码
import java.io.*;
class Demo4
{
public static void main(String[] args)throws IOException
{
write();
read();
}
public static void read()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
//读的顺序必须和写入的顺序一致
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readLong());
dis.close();
}
public static void write()throws IOException
{
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeDouble(88);
dos.writeBoolean(false);
dos.writeLong(66l);
dos.close();
}
}
五、转换流与解码
OutputStreamWriter
InputStreamReader
(1)OutputStreamWriter构造方法
因此使用转换流可以设置编码
可以自己指定编码,若不指定则编码默认为gbk
(2)InputStreamReader构造方法
注意:写入的时候指定的是什么编码,则读取的时候也应当使用相同的编码,否则会产生乱码。
(3)使用OutputStreamWriter、InputStreamReader示例代码
import java.io.*;
class Demo5
{
public static void main(String[] args)throws IOException
{
//System.out.println("Hello World!");
//write();
read();
}
public static void read()throws IOException
{
InputStreamReader isr=new InputStreamReader(new FileInputStream("gbk.txt"),"UTF-8");
char[] arr=new char[10];
int num = isr.read(arr);
System.out.println(new String(arr,0,num));
}
//使用转换流设置编码
public static void write()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
}