序列化:指把堆内存中的Java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他的网络节点(在网络上传输)我们把这个过程称之为序列化。
反序列化:把磁盘文件中的对象数据或者把网络节点的对象数据,恢复成Java对象的过程。
为什么需要序列化:
1、在分布式系统中,需要共享数据的JavaBean对象,都得做序列化,此时下需要把对象在网络上传输,此时就得把对象数据转换为二进制形式。
以后存储在HttpSession中的对象,都应该实现序列化接口(只有实现序列化接口的类,才能做序列化操作)。
2、服务钝化:如果服务发现某些对象好久都没有活动了,此时服务器就会把这些内存中的对象,持久化在本地磁盘文件中(Java对象-->二进制文件)。
如果某些对象需要活动的时候,先在内存中去寻找,找到就使用。找不到再去磁盘文件中,反序列化我们的对象数据,恢复成Java对象。
需要做序列化的对象的类,必须实现序列化接口:java.io.Serializable接口(标志接口[没有抽象方法])
底层会做判断,如果当前对象是Serializable的实例,才允许做序列化:boolean ret = java对象 instanceof Serializable;
在Java中大多数类都已经实现Serializable接口。
使用对象流来完成序列化和反序列化操作:
ObjectOutputStream:通过writeObject方法做序列化操作的;
ObjectInputStream:通过readObject方法做反序列化操作的。
准备一个Person类:
import java.io.Serializable;
/**
* Created by Layne_Yao on 2017-7-29 下午4:49:43.
* CSDN:http://blog.csdn.net/Jsagacity
*/
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String sex;
private transient String password;//无需序列化
private int age;
public Person(String name, String sex, String password, int age) {
this.name = name;
this.sex = sex;
this.password = password;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", password="
+ password + ", age=" + age + "]";
}
}
对象流序列化操作:
/**
* Created by Layne_Yao on 2017-7-29 下午4:49:25.
* CSDN:http://blog.csdn.net/Jsagacity
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
File file = new File("obj.txt");
writeObject(file);
readObject(file);
}
// 序列化操作
private static void writeObject(File file) throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
file));
out.writeObject(new Person("Layne", "man","123456", 20));
out.close();
}
//反序列化操作
private static void readObject(File file) throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
file));
Person person = (Person) in.readObject();
System.out.println(person);
in.close();
}
}
运行结果:
序列化的细节,序列化的版本:
1、如果某些数据不需要做序列化,比如密码,该怎么做?
理论上说,静态的字段和瞬态的字段是不能做序列化操作的。
解决方案:给该字段加上一个修饰符:transient
2、序列化的版本问题:
反序列化Java对象时必须提供该对象的class文件,现在问题是,随着项目的升级,系统的class文件也会升级(增加一个字段,或删除一个字段),
这是如何保证两个class文件的兼容性?
Java通过serialVersionUID(序列化版本号)来判断字节码是否发生改变。如果不显示定义serialVersionUID类变量,该类变量的值由JVM根据类相关信息计算,而修改后的类计算方式和之前往往不同。
解决方案:在类中提供一个固定的serialVersionUID。
private static final long serialVersionUID = 1L;
打印流:打印数据的,打印流只能是输出流
PrintStream:字节打印流
PrintWriter:字符打印流
对于PrintWriter来说,当启用字段刷新之后,
PrintWriter pw = new PrintWriter(new FileOututStream(new File("file/out")),true);
调用println、或者printf、或者format方法,便会立马刷新操作
如果没有开启自动刷新,则需要手动刷新或者当缓冲区满的时候,再自动刷新
使用打印流作为输出流,此时的输出操作会特别简单因为在打印流中:
提供了print方法:打印不换行
提供了println方法:打印再换行
print和println方法可以支持打印输出各种数据类型的数据,记住void println(Object x)即可。
字节打印流:
public class PrintStreamDemo {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream(new File("file/out.txt"));
ps.write("ABC".getBytes());
ps.print(false);
ps.print(18);
ps.print("layne");
//其实可以不用刷新
ps.close();
}
}
字符打印流:
public class PrintWriterDemo {
public static void main(String[] args) throws Exception {
PrintWriter ps = new PrintWriter(new File("file/out.txt"));
ps.write("ABC");
ps.print(false);
ps.println(18);
ps.print("layne");
//其实可以不用刷新
ps.close();
}
}
打印流中的格式化输出(printf方法):
System.out.println();其实等价于PrintStream ps = System.out; ps.println();
//Java的格式化输出
public class printfDemo {
public static void main(String[] args) {
//打印一句话,效果:姓名:layne,年龄:18
String name = "layne";
int age = 18;
//传统的打印风格
String str = "姓名:"+name+",年龄:"+age;
System.out.println(str);
//格式化输出
String format = "姓名:%s,年龄:%d";
Object[] data = {name,age};
System.out.printf(format,data);
System.out.println();
//简化
System.out.printf("姓名:%s,年龄:%d",name,age);
}
}
运行结果:
标准的IO:
标准的输入:通过键盘录入数据给程序
标准的输出:在屏幕上显示程序的数据
在System类中有两个常量:
InputStream in = System.in;
PrintStream out = System.out;
标准流的重定向操作:
标准的输入:通过键盘录入数据给程序;
重新指定输入的源不再是键盘,而是一个文件。
static void setIn(InputStream in);重新分配“标准”输入流。
此后,System.in数据的来源就是通过setIn指定的源。
标准的输出:在屏幕上显示程序的数据
重新指定输出的目标不再是屏幕,而是一个文件。
static void setOut(PrintStream out);重新分配“标准”输出流。
public class SystemIODemo {
public static void main(String[] args) throws Exception {
// 重定向标准输入流
System.setIn(new FileInputStream("file/123_copy.txt"));
//重定向标准输出流
System.setOut(new PrintStream("file/print.txt"));
System.out.println("...开始...");
int data = System.in.read(); //读取文件123_copy.txt中的第一个字节
System.out.println(data);
System.out.println("...结束...");
}
}
Scanner:扫描类,在java.util包中,表示输入的操作
存在的方法:xxx表示数据类型,如byte,int,boolean等;
boolean hasNextXxx();//判断是否有下一种类型的数据
Xxx nextXxx();//获取下一个该类型的数据。
public class ScannerDemo {
public static void main(String[] args) throws Exception {
//扫描文件中的数据
//Scanner sc = new Scanner(new File("file/ch.txt"),"GBK");
//扫描键盘输入的数据
//Scanner sc = new Scanner(System.in);
//扫描字符串的数据
Scanner sc = new Scanner("这是扫描类,我是即将被扫描的数据!!!");
while(sc.hasNextLine()){
String line = sc.nextLine();
System.out.println(line);
}
}
}