1.序列化
序列化和文件的输入输出
对象的序列化的两种方式
1.如果对象是给Java程序使用可以使对象序列化 实现Serializatable 将序列化对象写到文件中
需要对象时再展开
2.如果对象是给非Java程序使用 写文本文件 将变量保存在文本文件中
将序列化对象写入文件
//创建出FileOutputStream 将字节写入文件
FileOutputStream fileStream = new FileOutputStream ( "Save.bat" );
//创建ObjectOutputStream 将两个流连接起来
ObjectOutputStream os = new ObjectOutputStream( fileStream );
//将对象写入 Save.bat文件
os.writeObject( Object1 );
os.writeObject( Object2 );
//写完之后记得关闭流
os.close(); //会自动关闭 fileStream
将串流连接起来代表来源和目的地之间的连接(文件或者网络端口)
序列化:
序列化的对象保存了实例变量的值 如果对象上还有其他对象的引用变量,当对象被序列化时引用对象会自动的被序列化
如果让引用的对象也被序列化,则引用对象也实现序列化(如果有两个引用都指向同一个对象则序列化只会保存对象一次)
public class Pond implements Serializable{
//运行时报错, 保存Duck对象时 由于Duck对象没有实现Serializable会导致出错
private Duck duck = new Duck(); //实例变量的引用
public static void main( String [] args ){
Pond p = new Pond();
try {
FileOutputStream fs = new FileOutputStream( "Pond.bat" );//如果不存在就会被创建
ObjectOutputStream os = new ObjectOutputStream( fs );
os.writeObject( p );
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Duck {
//duck 代码
}
如果不想实例变量被序列化 可以把它标记成transient(瞬时)的
public class Duck implements Serializable {
transient private int size; //序列化程序会把它跳过
//duck 代码
}
关于父类和子类的序列化
父类实现序列化子类会自动实现序列化
解序列化:
FileInputStream fileStream = new FileInputStream( "Save.bat" );
ObjectInputStream os = new ObjectInputStream ( fileStream );
//每次调用readObject()都会从stream中读取下一个对象
Object1 = os.readObject();
Object2 = os.readObject();
Duck1 = ( Duck )Object1;
Duck2 = ( Duck )Object2;
//记得关闭流
os.close();
解序列化时,Java虚拟机会在堆上尝试创建新的对象让他维持被序列化时相同的状态 transient恢复成null 或则primitive的默认值
解序列化:
1.对象从stream中读出来
2.Java虚拟机判断出对象的class类
3.Java虚拟机寻找和加载对象的类(如果无法加载则抛出异常)
4.新的对象会被配置在堆上,但构造函数不会被执行(执行会将对象变成全新的)
5.如果对象在继承树上有不可序列化的祖先类则该不可序列化的类和它之上的类(不管有没有序列化)的构造函数都会执行
也就是说从第一个不可序列化的类开始其上的类都会重新回到初始状态
6.对象的实例变量会被还原成序列化时点的状态值
7.如果类中有静态变量,静态变量会维持类中原来的样子,而不是存储时的样子
2.将字符串写入文本文件
写入文本数据
写入字符串
import java.in.*;
public class WriteAFile {
public static void main( String [] args ){
try {
FileWriter fw = new FileWriter( "test.txt" );
fw.write( "Hello file!" );
fw.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
File 类
File代表磁盘上的文件(可以理解为文件路径) 在构造函数中传入文件路径的字符串也可以传入File对象
操作
1.创建File
File f = new File( "MyCode.txt" );
2.创建新的目录
File dir = new File( "Project1" );
dir.mkdir();
3.列出目录下的内容
if( dir.isDirectory ){
String [] dirContents = dir.list();
for( int i=0; i<dirContents.length; i++ ){
System.out.println( dirContents[i] );
}
}
4.取得文件的绝对路径
dir.getAbsolutePath();
5.删除文件或目录(成功返回true)
boolean isDeleted = f.delete();
缓冲区
缓冲区可以提高读写效率(暂时先保存在内存中,然后满的时候再写入磁盘) 因为磁盘的每趟操作要比内存花更多的时间
也可以刷新缓存 writer.flush();
BufferedWriter writer = new BufferedWriter( new FileWriter(file) );
利用缓冲区读文件:( Reader Writer 只能用在文字上)
import java.io.*;
class ReadFile{
public static void main( String[] args ){
try {
File myFile = new File( "MyTest.txt" );
BufferedReader reader = new BufferedReader( new FileReader(myFile) );
String line = null;
while ( (line = reader.readLine()) != null ) {
System.out.println( line );
}
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
3.解析字符串
String的 split()可以将字符串拆成String数组
String toTest = "What is your name?/zhangsan";
String[] result = toTest.split( "/" );
for( String token : result ){
System.out.println( token );
}
4.Version ID和serialVersionUID
Version ID
会损害序列化的修改
删除实例变量 改变实例变量的类型 将非瞬时变量改成瞬时的 改变类的继承层次 将对象从序列化变成非序列化
将实例变量改成静态的
不会损害的修改
加入新的实例变量 在继承层次中加入新的类 从继承层次中删除类 将实例变量从瞬时改为非瞬时(会使用默认值)
serialVersionUID 相当于类的版本识别ID
如果认为类有可能演化,就把版本识别ID放在类中
1.使用serialver工具来取得版本ID
% serialver Dog //取得UID
2.设定类的UID为输出的UID
public class Dog{
static final long serialVersionUID = ..... ;
//方法代码
}
3.在修改类后要确定修改后的Dog能处理旧的Dog解序列化后新加入变量的默认值