23(5) IO文件切割合并-对象的序列化
导语:
再接再厉
- 实现了Serializable接口。详细描述了该接口的作用和序列号的作用
- 读取配置文件信息的原始方法,也是Properties load方法的原理
- 切割文件,并用Properties存储碎片文件信息
- 很恐怖的合并程序
- 对恐怖程序进行优化,使用了集合
- 使用序列流SequenceInputStream,并使用了解析配置文件的信息
- 应用程序计数器,记录试用次数
- Properties集合的基本使用,从流中加载数据,并数据持久化
- 对象序列化和反序列化
实现了Serializable接口。详细描述了该接口的作用和序列号的作用
package cn.itcast.domain;
import java.io.Serializable;
/*
* 序列化接口的作用:没有方法,不需要覆盖,是一个标记接口为了启动一个序列化功能。
* 唯一作用,给每一个需要序列化的类都分配一个序列版本号。
* 这个版本号和该类相关联。
* 这个版本号有什么用呢?
* 在序列化时,会将这个序列号也异同保存到文件中。
* 在反序列化会读取这个序列化和本类的序列化进行匹配,如果不匹配会抛出异常。java.io.InvalidClassException
* 哦,原来是用于验证的。
*
* 那我们在序列化时,显示定义吗?
*
*
*/
public class Person implements Serializable /*标记接口,用于启动类的序列化功能*/{
private static final long serialVersionUID = 1234567L;
private static String name;// 静态数据是不会被序列化。
private transient/*瞬态*/ int age;//对于一个非静态的数据也不想序列化咋办?需要一个关键字来修饰。transient
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
读取配置文件信息的原始方法,也是Properties load方法的原理
package cn.itcast.io.a.splitfile;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class ReaderPartConfigDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//解析partConfig文件中的信息。
File configFile = new File("E:\\PartFiles\\7.partconfig");
readPathConfig(configFile);
}
public static void readPathConfig(File configFile) throws IOException {
/*
* 配置文件规律,只要读取一行文本,按照 = 对文本进行切割即可。
*/
BufferedReader bufr = new BufferedReader(new FileReader(configFile));
String line = null;
while((line=bufr.readLine())!=null){
String[] arr = line.split("=");
System.out.println(arr[0]+":::::"+arr[1]);
//map.put(arr[0],arr[1]);
}
/*
* 发现配置文件信息很多,需要进行存储。
* 用哪个容器呢?个数不确定,就使用集合。
* 发现信息中存在对应关系,使用Map集合。
* 发现一点配置文件中的信息都是字符串,这些信息不在内存中而是在硬盘上。
* map中和io技术集合的集合对象: Properties,它里面存储的键值都是字符串,通常这个集合就用于配置文件的操作。
*
*/
bufr.close();
}
}
切割文件,并用Properties存储碎片文件信息
package cn.itcast.io.a.splitfile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class SplitFileTest {
private static final int BUFFER_SIZE = 1048576;//1024*1024
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 练习:将一个媒体文件切割成多个碎片。
/*
* 思路:
* 1,读取源文件,将源文件的数据分别复制到多个文件中。
* 2,切割方式有两种:按照碎片个数切,要么按照指定大小切。
* 3,一个输入流对应多个输出流。
* 4,每一个碎片都需要编号,顺序不要错。
*
*/
File srcFile = new File("E:\\1.mp3");
File partsDir = new File("E:\\PartFiles");
splitFile(srcFile,partsDir);
}
/**
* 切割文件。
* @param srcFile
* @param partsDir
* @throws IOException
*/
public static void splitFile(File srcFile, File partsDir) throws IOException {
//健壮性的判断。
if(!(srcFile.exists() && srcFile.isFile())){
throw new RuntimeException("源文件不是正确的文件或者不存在");
}
if(!partsDir.exists()){
partsDir.mkdirs();
}
//1,使用字节流读取流和源文件关联。
FileInputStream fis = new FileInputStream(srcFile);
//2,明确目的。目的输出流有多个,只创建引用。
FileOutputStream fos = null;
//3,定义缓冲区。1M.
byte[] buf = new byte[BUFFER_SIZE];//1M
//4,频繁读写操作。
int len = 0;
int count = 1;//碎片文件的编号。
while((len=fis.read(buf))!=-1){
//创建输出流对象。只要满足了缓冲区大小,碎片数据确定,直接往碎片文件中写数据 。
//碎片文件存储到partsDir中,名称为编号+part扩展名。
fos = new FileOutputStream(new File(partsDir,(count++)+".part"));
//将缓冲区中的数据写入到碎片文件中。
fos.write(buf,0,len);
//直接关闭输出流。
fos.close();
}
/*
* 将源文件以及切割的一些信息也保存起来随着碎片文件一起发送。
* 信息;
* 1,源文件的名称(文件类型)
* 2,切割的碎片的个数。
* 将这些信息单独封装到一个文件中。
* 还要一个输出流完成此动作。
*/
String filename = srcFile.getName();
int partCount = count;
//创建一个输出流。
fos = new FileOutputStream(new File(partsDir,count+".properties"));
//创建一个属性集。
Properties prop = new Properties();
//将配置信息存储到属性集中。
prop.setProperty("filename", srcFile.getName());
prop.setProperty("partcount", Integer.toString(partCount));
//将属性集中的信息持久化。
prop.store(fos, "part file info");
// fos.write(("filename="+filename+LINE_SEPARATOR).getBytes());
// fos.write(("partcount="+Integer.toString(partCount)).getBytes());
fos.close();
fis.close();
}
}
很恐怖的合并程序
package cn.itcast.io.b.mergefile;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class MergeFileTest {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//合并碎片文件。
/*
* 思路:
* 1,碎片文件有很多,每一个碎片都需要和读取流关联。
* 2,每一个读取流读取到的数据都需要通过一个输出流写入到一个文件中。
* 3,原理:多个源--->一个目的地。
*
*
* 如下代码的问题:
* 碎片过多,会产生很多的输入流对象,这是正常的,不正常在于,面对每一个输入流对象去操作。
* 当流对象过多时,必须先存储起来。面的流对象的容器操作更容易。
* 1,需要容器。
* 2,将流对象和碎片文件关联后存储到容器中。
* 3,遍历容器获取其中的流对象,在进行频繁的读写操作。
* 4,即使关流也是遍历容器对每一个流对象进行close的调用。
* 参阅MergeFileTest2.java
*/
FileInputStream fis1 = new FileInputStream("E:\\PartFiles\\1.part");
FileInputStream fis2 = new FileInputStream("E:\\PartFiles\\2.part");
FileInputStream fis3 = new FileInputStream("E:\\PartFiles\\3.part");
FileInputStream fis4 = new FileInputStream("E:\\PartFiles\\4.part");
FileInputStream fis5 = new FileInputStream("E:\\PartFiles\\5.part");
FileInputStream fis6 = new FileInputStream("E:\\PartFiles\\6.part");
FileOutputStream fos = new FileOutputStream("E:\\PartFiles\\0.mp3");
byte[] buf = new byte[1024*1024];
int len1 = fis1.read(buf);
fos.write(buf,0,len1);
int len2 = fis2.read(buf);
fos.write(buf,0,len2);
int len3 = fis3.read(buf);
fos.write(buf,0,len3);
int len4 = fis4.read(buf);
fos.write(buf,0,len4);
int len5 = fis5.read(buf);
fos.write(buf,0,len5);
int len6 = fis6.read(buf);
fos.write(buf,0,len6);
fos.close();
fis1.close();
fis2.close();
fis3.close();
fis4.close();
fis5.close();
fis6.close();
}
}
对恐怖程序进行优化,使用了集合
package cn.itcast.io.b.mergefile;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MergeFileTest2 {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 合并碎片文件。
/*
* 思路: 1,碎片文件有很多,每一个碎片都需要和读取流关联。 2,每一个读取流读取到的数据都需要通过一个输出流写入到一个文件中。
* 3,原理:多个源--->一个目的地。
*
*
* 如下代码的问题: 碎片过多,会产生很多的输入流对象,这是正常的,不正常在于,面对每一个输入流对象去操作。
* 当流对象过多时,必须先存储起来。面的流对象的容器操作更容易。 1,需要容器。 2,将流对象和碎片文件关联后存储到容器中。
* 3,遍历容器获取其中的流对象,在进行频繁的读写操作。 4,即使关流也是遍历容器对每一个流对象进行close的调用。
* 参阅MergeFileTest2.java
*/
List<FileInputStream> list = new ArrayList<FileInputStream>();
for (int i = 1; i < 7; i++) {
list.add(new FileInputStream("E:\\PartFiles\\" + i + ".part"));
}
FileOutputStream fos = new FileOutputStream("E:\\PartFiles\\00.mp3");
byte[] buf = new byte[1024 * 1024];
// 遍历集合,获取流对象。
for (FileInputStream fis : list) {
int len = fis.read(buf);
fos.write(buf, 0, len);
}
fos.close();
// 关闭所有流对象。
for (FileInputStream fis : list) {
fis.close();
}
}
}
/*
class MyMergeStream{
MyMergeStream(List<? extends InputStream> list){
}
public void close(){
for (InputStream fis : list) {
fis.close();
}
}
}
*/
使用序列流SequenceInputStream,并使用了解析配置文件的信息
package cn.itcast.io.b.mergefile;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
public class MergerFileTest3 {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
File partsDir = new File("E:\\PartFiles");
mergerFile(partsDir);
}
public static void mergerFile(File partsDir) throws IOException {
/*
* 虽然合并成功,问题如下:
* 1,如何明确碎片的个数,来确定循环的次数,以明确要有多少个输入流对象。
* 2,如何知道合并的文件的类型。
* 解决方案:应该先读取配置文件。
*/
//1,获取配置文件。
File configFile = getConfigFile(partsDir);
//2,获取配置文件信息容器。获取配置信息的属性集。
Properties prop = getProperties(configFile);
//3,将属性集对象传递合并方法中。
merge(partsDir,prop);
}
//根据配置文件获取配置信息属性集。
private static Properties getProperties(File configFile) throws IOException {
FileInputStream fis = null;
Properties prop = new Properties();
try{
//读取流和配置文件相关联。
fis = new FileInputStream(configFile);
//将流中的数据加载的集合中。
prop.load(fis);
}finally{
if(fis!=null){
try{
fis.close();
}catch(IOException e){
//写日志,记录异常信息。便于维护。
}
}
}
return prop;
}
//根据碎片目录获取配置文件对象。
private static File getConfigFile(File partsDir) {
if(!(partsDir.exists() &&partsDir.isDirectory())){
throw new RuntimeException(partsDir.toString()+",不是有效目录");
}
//1,判断碎片文件目录中是否存在properties文件。使用过滤器完成。
File[] files = partsDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".properties");
}
});
if(files.length!=1){
throw new RuntimeException("properties扩展名的文件不存在,或不唯一");
}
File configFile = files[0];
return configFile;
}
private static void merge(File partsDir,Properties prop) throws FileNotFoundException,
IOException {
//获取属性集中的信息。
String filename = prop.getProperty("filename");
int partCount = Integer.parseInt(prop.getProperty("partcount"));
//使用io包中的SequenceInputStream,对碎片文件进行合并,将多个读取流合并成一个读取流。
List<FileInputStream> list = new ArrayList<FileInputStream>();
for (int i = 1; i < partCount; i++) {
list.add(new FileInputStream(new File(partsDir, i + ".part")));
}
//怎么获取枚举对象呢?List自身是无法获取枚举Enumeration对象的,考虑到Collections中去找。
Enumeration<FileInputStream> en = Collections.enumeration(list);
//源。
SequenceInputStream sis = new SequenceInputStream(en);
//目的。
FileOutputStream fos = new FileOutputStream(new File(partsDir,filename));
//不断的读写。
byte[] buf = new byte[4096];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
应用程序计数器,记录试用次数
package cn.itcast.io.c.properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class AppCountTest {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
/*
* 练习:定义功能记录程序运行次数,满足试用次数后,给出提示:试用次数已到,请注册。
*
* 思路:
* 1,需要计数器。这个软件使用一次计数一次。每使用一次,就进行计数累计。
* 2,计数器是程序中的一个变量,程序启动计数器计数,可是程序结束这个计数器就消失了。
* 下次启动会重新进行计数,原来计数的值没有保留下来。咋办?
* 3,让这个计数器持久化。存储到文件中,为了标识数据可读性,数据起个名字。出现键值对。
* 而且还是一个持久化的键值对,Properties集合正好符合这个要求。
*
*/
if(isStop()){
System.out.println("试用次数已到,请注册");
return;
}
runcode();
}
private static boolean isStop() throws IOException {
//1,配置文件。
File configFile = new File("tempfile\\app.properties");
if(!configFile.exists()){//如果配置文件不存在,就创建。
configFile.createNewFile();
}
//2,创建属性集。
Properties prop = new Properties();
//3,定义读取流和配置文件关联。
FileInputStream fis = new FileInputStream(configFile);
//4,将流关联的数据读取到属性集中。
prop.load(fis);
//5,通过属性集的指定键count,获取具体的次数。
String value = prop.getProperty("count");
int count = 0;
//6, 对value进行判断,如果存在就对其自增。
if(value!=null){
count = Integer.parseInt(value);
if(count>=5){
return true;
}
}
count++;//对其值进行自增。
//7,将自增后的值和指定的键重新存储到属性集中,键相同,值覆盖。
prop.setProperty("count", Integer.toString(count));
//8,将属性集存储到配置文件中。对配置文件中的信息进行更新。
FileOutputStream fos = new FileOutputStream(configFile);
prop.store(fos, "app run count");
//9,关闭资源。
fos.close();
fis.close();
return false;
}
//程序主体。
public static void runcode(){
System.out.println("程序运行....play");
}
}
Properties集合的基本使用,从流中加载数据,并数据持久化
package cn.itcast.io.c.properties;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// Properties集合的使用。
// methodDemo_1();
// methodDemo_2();
methodDemo_3();
}
/*
* 保存到流中的方法(持久化)
*/
public static void methodDemo_3() throws IOException {
Properties prop = new Properties();
// 添加数据。
prop.setProperty("zhangsan", "39");
prop.setProperty("lisi", "29");
//想要把数据保存到文件中,需要输出流。
FileWriter fw = new FileWriter("info.properties");
//使用store方法。
prop.store(fw, "info");
fw.close();
}
/*
* 演示从流中加载,。
*/
public static void methodDemo_2() throws IOException {
File configFile = new File("E:\\PartFiles\\7.partconfig");
FileReader fr = new FileReader(configFile);
Properties prop = new Properties();
// 使用Properties集合的load方法,就可以将流中的数据加载集合中。原理;ReaderPartConfigDemo.java
// 中的readPathConfig();
prop.load(fr);
System.out.println(prop);
fr.close();
}
// 1,基本使用,存和取。
public static void methodDemo_1() {
// 创建一个Properites集合。
Properties prop = new Properties();
// 添加数据。
prop.setProperty("zhangsan", "39");
prop.setProperty("lisi", "29");
// 获取数据。一个。
// String value = prop.getProperty("lisi");
// System.out.println("value="+value);
// 全部取出。map--set--iterator
Set<String> set = prop.stringPropertyNames();
// System.out.println("-- listing properties --");
for (String name : set) {
String value = prop.getProperty(name);
System.out.println(name + ":" + value);
}
// prop.list(System.out);
}
}
对象序列化和反序列化
package cn.itcast.io.d.objectstream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import cn.itcast.domain.Person;
public class ObjectStreamDemo {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//需求:想要将封装了数据的对象进行持久化。当写入的对象很多对象会按照顺序排列,也称之为对象的序列化。
//1,应该先有对象。Person name age。
//2,往硬盘写数据,进行持久化,需要io技术。输出流。FileOutputStream。
//3,在字节输出流中按照名称规律在api找到一个子类 ObjectOutputStream
//4,在基础流对象上使用额外功能。
// writeObj();
//需求:读取已有的对象文件,并获取对象中的数据。
//通过阅读ObjectOutputStream对象的文档,发现有一个对应的对象ObjectInputStream可以用于读区存储对象的文件
//对象的反序列化。
readObj();
}
public static void readObj() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("tempfile\\obj.object");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
System.out.println(obj.toString());
}
public static void writeObj() throws IOException {
Person p = new Person("lisi",20);
FileOutputStream fos = new FileOutputStream("tempfile\\obj.object");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p);
oos.close();
}
}