Javaの序列化流
序列化流
概述
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该 对象的数据 、 对象的类型 和 对象中存储的属性 等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。 对象的数据 、 对象的类型和对象中存储的数据 信息,都可以用来在内存中创建对象。
ObjectOutPutStream类
- 将Java对象的原始数据类型写出到文件,实现对象的持久存储。
- 一个对象要想序列化,必须满足两个条件:
- 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException 。
- 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
public class Employee implements java.io.Serializable {
public String name;
public String address;
public transient int age; // transient瞬态修饰成员,不会被序列化
public void addressCheck() {
System.out.println("Address check : " + name + " ‐‐ " + address);
}
}
public class SerializeDemo{
public static void main(String [] args) {
Employee e = new Employee();
e.name = "zhangsan";
e.address = "beiqinglu";
e.age = 20;
try {
// 创建序列化流对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
// 写出对象
out.writeObject(e);
// 释放资源
out.close();
fileOut.close();
System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
} catch(IOException i) {
i.printStackTrace();
}
}
}
- ObjectInputPutStream类
- ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
public static void main(String [] args) {
Employee e = null;
try {
// 创建反序列化流
FileInputStream fileIn = new FileInputStream("employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
// 读取一个对象
e = (Employee) in.readObject();
// 释放资源
in.close();
fileIn.close();
}catch(IOException i) {
// 捕获其他异常
i.printStackTrace();
return;
}catch(ClassNotFoundException c) {
// 捕获类找不到异常
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
// 无异常,直接打印输出
System.out.println("Name: " + e.name); // zhangsan
System.out.println("Address: " + e.address); // beiqinglu
System.out.println("age: " + e.age); // 0
}
}
- 练习
- 将存有多个自定义对象的集合序列化操作,保存到 list.txt 文件中。
- 反序列化 list.txt ,并遍历集合,打印对象信息。
public class Demo3 {
//将存有多个自定义对象的集合序列化操作,保存到list.txt文件中
//反序列化list.txt,并遍历集合,打印对象信息
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s1 = new Student("叶蕴森",18,"上程数据");
Student s2 = new Student("叶叶叶",19,"下程数据");
Student s3 = new Student("蕴蕴蕴",20,"左程数据");
Student s4 = new Student("森森森",21,"右程数据");
Student s5 = new Student("叶蕴蕴",22,"前程数据");
Student s6 = new Student("蕴蕴森",23,"后程数据");
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
list.add(s6);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("studentList.txt"));
out.writeObject(list);
out.close();
FileInputStream fillIn = new FileInputStream("studentList.txt");
ObjectInputStream in = new ObjectInputStream(fillIn);
List<Student> newlist = null;
newlist = (ArrayList<Student>)in.readObject();
in.close();
fillIn.close();
Iterator<Student> iterator = newlist.iterator();
while (iterator.hasNext()){
Student next = iterator.next();
System.out.println(next.toString());
}
}
转化流
- InputStreamReader类
- 转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法- InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
- InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
- 转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "E:\\file_gbk.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != ‐1) {
System.out.print((char)read); // ��Һ�
}
isr.close();
// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != ‐1) {
System.out.print((char)read);// 大家好
}
isr2.close();
}
}
OutputStreamReader类
- 转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
- OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
- OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。
public class OutputDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "E:\\out.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("你好"); // 保存为6个字节
osw.close();
// 定义文件路径
String FileName2 = "E:\\out2.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("你好");// 保存为4个字节
osw2.close();
}
}
- 转换文件编码
- 需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
public class TransDemo {
public static void main(String[] args) {
// 1.定义文件路径
String srcFile = "file_gbk.txt";
String destFile = "file_utf8.txt";
// 2.创建流对象
// 2.1 转换输入流,指定GBK编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");
// 2.2 转换输出流,默认utf8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));
// 3.读写数据
// 3.1 定义数组
char[] cbuf = new char[1024];
// 3.2 定义长度
int len;
// 3.3 循环读取
while ((len = isr.read(cbuf))!=‐1) {
// 循环写出
osw.write(cbuf,0,len);
}
// 4.释放资源
osw.close();
isr.close();
}
}
打印流
- PrintStream类
- 平时我们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于java.io.PrintStream 类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
构造方法: - public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流。
- System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,我们就可以将数据输出到指定文本文件中。
- 平时我们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于java.io.PrintStream 类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
public class PrintDemo {
public static void main(String[] args) throws IOException {
// 调用系统的打印流,控制台直接输出97
System.out.println(97);
// 创建打印流,指定文件的名称
PrintStream ps = new PrintStream("ps.txt");
// 设置系统的打印流流向,输出到ps.txt
System.setOut(ps);
// 调用系统的打印流,ps.txt中输出97
System.out.println(97);
}
}
- PrintWriter类
- 将信息打印到文本。
构造方法:- public PrintWriter(File file) : 使用指定的文件名创建一个打印流。
- 将信息打印到文本。
随机访问流RandomAccessFile
RandomAccessFile 类既可以读取文件内容,也可以向文件输出数据
RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读写文件,支持只访问文件的部分内容,可以向已存在的文件后追加内容
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到 pos 位置
创建 RandomAccessFile 类可以指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r: 以只读方式打开
rw:以读、写方式打开
读取任意位置的数据:将指针移到需要读取数据的前面:
追加数据:将指针移到文件末尾:
追加数据:任意位置插入数据:
RandomAccessFile依然只能追加,不能像文件的指定位置插入内容。如果强制将文件记录指针移动到中间位置后开始输出内容,则新的内容会覆盖文件中原有的内容。如果需要向文件指定的位置插入内容,程序需要先把插入点后面的内容读入缓冲区,等插入完成后,再将缓冲区的内容追加到文件的后面。
案例12-4
要求从文件中读取所有产品并按价格高低排序打印,求所有商品总价,并求各个等级商品的总价
文本内容如下:
西瓜,88,a,99
苹果,20.8,a,200
香蕉,18.9,b,100
栗子,15.8,c,88
梨子,8.9,c,199
荔枝,29.9,b,108
编写产品类:
package demo;
public class Fruit implements Comparable{
private String name;//商品名
private double price;//价格
private String lv;//等级
private int count;//存货
public Fruit() {
}
@Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", price=" + price +
", lv='" + lv + '\'' +
", count=" + count +
'}';
}
public Fruit(String name, double price, String lv, int count) {
this.name = name;
this.price = price;
this.lv = lv;
this.count = count;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getLv() {
return lv;
}
public void setLv(String lv) {
this.lv = lv;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public int compareTo(Object o) {
return -(int)(this.price-((Fruit)o).price);
}
}
编写测试类
package demo;
import java.io.*;
import java.util.*;
public class Test {
//1.从文件中读取产品并打印
//2.按照价格高低排序打印
//3.求所有产品总价
//4.求各个等级产品总价
public static void main(String[] args) {
//创建目标文件对象
File file = new File("d:/123.txt");
try {
//创建目标文件对象的缓冲字符输入流
BufferedReader br = new BufferedReader(new FileReader(file));
String s = "";
//存放商品的集合
Set<Fruit> list = new TreeSet<>();
//存放等级及对应总价
Map<String,Double> map=new TreeMap<>();
//读取目标文件
while ((s = br.readLine()) != null) {
System.out.println(s);
//将目标文件按照‘,’切割,并将数据转型,封装进对象,添加进集合
String[] split = s.split(",");
double v = Double.parseDouble(split[1]);
int i1 = Integer.parseInt(split[3]);
//集合实现了Comparable接口,按照价格高低排序
list.add(new Fruit(split[0], v, split[2], i1));
}
//定义总价
double sum = 0.0;
Iterator<Fruit> iterator = list.iterator();
while (iterator.hasNext()) {
//遍历所有商品,得到总价
Fruit f = iterator.next();
sum = sum + f.getPrice() * f.getCount();
System.out.println(f);
//---------------------------
//获取各个等级的总价
if(map.get(f.getLv())==null){
map.put(f.getLv(),f.getPrice()*f.getCount());
}else{
map.put(f.getLv(),map.get(f.getLv())+f.getPrice()*f.getCount());
}
}
//遍历map,打印等级及其总价
Set<Map.Entry<String, Double>> entries = map.entrySet();
Iterator<Map.Entry<String, Double>> iterator1 = entries.iterator();
while (iterator1.hasNext()){
Map.Entry<String, Double> next = iterator1.next();
System.out.println(next.getKey()+":"+next.getValue());
}
System.out.println("所有商品总价:" + sum);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}