文件读写
2018-07-06 本文已影响0人
luoqiang108
- 计算机的硬盘在保存数据时都是byte by byte的字节挨着字节。
RandomAccessFile
- 作用:可以方便的读写文件内容。
- 打开文件模式:
rw:打开文件后可进行读写操作
r:打开文件后只读 - write(int data):写一个字节,写这个int值的低八位
- write(byte[] data):将一组字节写入
- read():读一个字节,若已经读取到文件末尾,返回-1
- int read(byte[] buf):尝试读取buf.length个字节。并将读取的字节存入buf数组。返回值:实际读取的字节数。
RandomAccessFile是基于指针进行读写操作的,指针在哪里就从哪里读/写 - writeInt(111):将int值111转换为字节并写入磁盘
- 基本类型序列化:将基本类型数据转换为字节数组(序列)的过程
- 持久化:将数据写入磁盘的过程
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* RandomAccessFile读写文件。
*/
public class RandomAccessFileTest {
public static void main(String[] args) {
try {
/**
* 向文件.\data.dat中写数据
* 步骤:
* 1、创建一个File对象用于描述该文件
* 2、不存在则创建该文件
* 3、创建RandomAccessFile,并将File传入。
* 使RandomAccessFile对File表示的文件进行
* 读写操作
* 4、向文件中写入内容
*/
//1
File file = new File("data.dat");
//2、不存在则创建该文件
if (!file.exists()) {
file.createNewFile();
}
//3、对file文件可以进行读写操作
RandomAccessFile raf =new RandomAccessFile(file,"rw");
//4、写一个int最大值
/**
* 2位16进制代表一个字节(8位2进制)
* 1位16进制代表4位2进制 f==1111
* 4字节代表32位2进制
*
* write(int)写一个字节
* 7f ff ff ff
* 01111111 11111111 11111111 11111111
*
* 00000000 00000000 00000000 01111111 00 00 00 7f
* 00000000 00000000 01111111 11111111 00 00 7f ff
* 00000000 01111111 11111111 11111111 00 7f ff ff
* 01111111 11111111 11111111 11111111 7f ff ff ff
*/
int i = 0x7fffffff;//int最大值
//写int值最高的8位
raf.write(i>>>24);//00 00 00 7f
raf.write(i>>>16);//00 00 7f ff
raf.write(i>>>8);//00 7f ff ff
raf.write(i); //7f ff ff ff
//定义一个10字节数组
byte[] data = new byte[]{0,1,2,3,4,5,6,7,8,9};
//将这10个字节全部写入文件
raf.write(data);
//写到这里,当前文件应该有14(前面int的4字节加后面数字的10字节)个字节了
/**
* 写字节数组的重载方法
*write(byte[] data,int offset,int length)
* 从data数组的offset位置开始写,连续写length个字节到文件中。
*/
raf.write(data,2,5);//{2,3,4,5,6}
System.out.println("当前指针位置:"+raf.getFilePointer());//19
raf.seek(0);//将指针移动到文件开始的位置
System.out.println("当前指针位置:"+raf.getFilePointer());//0
/**
* num:00000000 00000000 00000000 00000000
*/
int num = 0;//准备读取的int值
int b = raf.read();//读取第一个字节 7f
System.out.println("当前指针位置:"+raf.getFilePointer());//1
num = num|(b<<24);//01111111 00000000 00000000 00000000
b = raf.read();//读取第二个字节 ff
num = num|(b<<16);//01111111 11111111 00000000 00000000
b = raf.read();//读取第三个字节 ff
num = num|(b<<8);//01111111 11111111 11111111 00000000
b = raf.read();//读取第四个字节 ff
num = num|b;//01111111 11111111 11111111 11111111
System.out.println("int最大值"+num);//int最大值2147483647
raf.seek(0);
byte[] buf = new byte[1024];//1k的容量
int sum = raf.read(buf);//尝试读取1k的数据
System.out.println("总共读取了"+sum+"个字节");
System.out.println(Arrays.toString(buf));
/*RandomAccessFile的其他读写*/
raf.seek(0);
//写int最大值
raf.writeInt(Integer.MAX_VALUE);//一次写4个字节,写int值
//写long最大值
raf.writeLong(Long.MAX_VALUE);
//写字符串
raf.writeUTF("hello!");
raf.seek(0);
int intMax = raf.readInt();//连续读取4字节返回该int值
long longMax = raf.readLong();
String string = raf.readUTF();
raf.close();//写完了不再写了就关了
}catch(IOException e){
e.printStackTrace();
}
}
}
java I/O 输入/输出
- 流:根据方向流分为输入流和输出流,方向的定论是基于我们的程序。流向我们的程序的流叫做输入流,从程序向外流的叫做输出流。我们可以把流想象为管道。管道里流动的是水,而java中的流,流动的是字节。输入流是用于获取数据的。输出流是用于向外输出数据的。输入流是用于读取的,输出流是用于写出的。
- java中
InputStream:该接口定义了输入流的特性,
OutputStream:该接口定义了输出流的特征。 - 流根据源头分为:
基本流(低级流,节点流):有来源。
处理流(高级流,过滤流):没有数据来源。不能独立存在。它的存在是用于处理基本流的。 - 流根据处理的数据单位不同划分为:字节流(Stream结尾)和字符流(Reader/Writer结尾)
- 用于读写文件的字节流FIS/FOS
FileInputStream:文件输入流
FileOutputStream:文件输出流 - FileOutputStream用于向文件写入数据。构造其实例可以使用FileOutputStream(File file):向file文件写入数据。若该文件不存在会自动创建该文件。
- BufferedInputStream:缓冲字节输入流
- BufferedOutputStream:缓冲字节输出流
缓冲字节流内部维护着一个缓冲区(字节数组)。可以提高读写效率。缓冲字节流是处理流(高级流,过滤流)。 - 辨别高级流的简单方法:
看构造方法,若构造方法要求传入另一个流,那么这个流就是高级流。 - DataInputStream:可以直接读取基本类型数据的流。
简化了对基本类型数据的读写操作。
DIS:
int readInt():连续读取4个字节,返回该int值;
double readDouble():连续读取8个字节,返回double值;
String readUTF():读取字符串 ,返回String值; - DataOutputStream:可以直接写基本数据的流。
DIS:
int writeInt(int i);
double writeLong(long l);
String writeUTF(String s);
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class FISFOSTest {
public static void main(String[] args) throws IOException {
File file = new File("data.dat");
/**
* 根据文件创建用于读取其数据的文件输入流
*/
FileInputStream fis = new FileInputStream(file);
int b = -1;
while ((b=fis.read())!=-1){
//输出这个字节的16进制形式
System.out.print(Integer.toHexString(b));
}
fis.close();//切记,流用完了要关闭!
/*
* 文件输入流FileOutputStream
* write(int d):写int值的低八位
* write(byte[] d):将d数组中所以字节写出
* write(byte[] d,int offset,int length):将d数组中offset位置开始
* 写length个字节
* */
File outFile =new File("out.txt");
if (!outFile.exists()){
file.createNewFile();
}
//FileOutputStream fos = new FileOutputStream(outFile);
/*
* FileOutputStream(File file,boolean append)
* append为true:会在当前文件末尾进行写操作
* FileOutputStream(String filePath,boolean append)
* 传文件路径进行写入
* */
FileOutputStream fos = new FileOutputStream(outFile,true);
String info = "你好!输出流!";
byte[] outData = info.getBytes("UTF-8");
fos.write(outData);//将字符串转换的字节写出
fos.close();
//测试IO工具类方法
byte[] data =IOUtils.loadBytes(new File("data.dat"));
System.out.print("\n"+Arrays.toString(data));
File des = new File("abc.dat");//该文件在磁盘上不存在
IOUtils.saveBytes(des,data);
File srcCopy = new File("Eclipse.zip");
File desCopy = new File("EclipseCopy.zip");
//IOUtils.copyFile(srcCopy,desCopy);
//IOUtils.copyFile2(srcCopy,desCopy);
//测试DataOutputStream
FileOutputStream out = new FileOutputStream("o.txt");
IOUtils.saveString("大家好才是真的好!",out);
out.close();
FileInputStream in = new FileInputStream("o.txt");
System.out.println("\n"+IOUtils.loadString(in));
}
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* IO工具类
*/
public class IOUtils {
/**
* 读取给定的文件,并将数据以字节数组的形式返回
* @param src
* @return
*/
public static byte[] loadBytes(File src) throws IOException{
if (src==null){
throw new IllegalArgumentException("文件为空!");
}
if (!src.exists()){
throw new IllegalArgumentException(src+"不存在!");
}
FileInputStream fis = null;//创建文件输入流
try{
fis = new FileInputStream(src);
/*FileInputStream的available()方法:
* 返回当前字节输入流可读取的总字节数
* */
byte[] data = new byte[fis.available()];
fis.read(data);
return data;
}catch (IOException e){
throw e;//读取出现错误将异常抛给调用者解决
}finally {
if (fis!=null){
fis.close();
}
}
}
/**
* 数据源 DataSource
* 将给定数组写入给定文件中
* @param src
* @param data
* @throws IOException
*/
public static void saveBytes(File src ,byte[] data) throws IOException{
if (src==null){
throw new IllegalArgumentException("文件为空!");
}
//FileOutputStream(File file),若file文件不存在FileOutputStream(File file)构造方法会自动创建该文件
/*if (!src.exists()){
src.createNewFile();
}*/
FileOutputStream fos = null;
try {
fos = new FileOutputStream(src);
fos.write(data);
}catch (IOException e){
throw e;
}finally {
if (fos!=null){
fos.close();
}
}
}
/**
* 将文件复制保存到des文件
* @param src
* @param des
* @throws IOException
*/
public static void copyFile(File src,File des) throws IOException{
if (src==null){
throw new IllegalArgumentException("源文件为空!");
}
if (!src.exists()){
throw new IllegalArgumentException("源文件不存在!");
}
if (des==null){
throw new IllegalArgumentException("目标文件为空!");
}
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(des);
int data = -1;
while ((data=fis.read())!=-1){
fos.write(data);
}
/*byte[] srcData = new byte[fis.available()];
fis.read(srcData);
fos.write(srcData);*/
}catch (IOException e){
throw e;
}finally {
if (fis!=null){
fis.close();
}
if (fos!=null){
fos.close();
}
}
}
/**
* 使用缓冲流进行文件拷贝
* @param src
* @param des
*/
public static void copyFile2(File src,File des) throws IOException{
if (src==null){
throw new IllegalArgumentException("源文件为空");
}
if (!src.exists()){
throw new IllegalArgumentException("源文件不存在");
}
//创建缓冲流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(des));
int data = -1;
while ((data=bis.read())!=-1){
bos.write(data);
}
/*flush的作用是将缓冲区中未写出的数据一次性写出*/
bos.flush();//数据都写完后应该flush
}catch (IOException e){
throw e;
}finally {
if (bis!=null){
/*通常情况下,我们只需要关闭最外层的流,里层的流会自动关闭*/
bis.close();
}
if (bos!=null){
bos.close();
}
}
}
/**
* 向给定的输出流中写字符串
* @param info
* @param out
* @throws IOException
*/
public static void saveString(String info, OutputStream out)throws IOException{
/*
* 步骤:
* 1:创建DataOutputStream并处理参数out这个输出流
* 2:通过DOS直接将一个字符串写出
* 3:清空DOS的缓冲区,确保所有字符都写出。
* */
//1
DataOutputStream dos = new DataOutputStream(out);
//2
dos.writeUTF(info);//将字符串写出
//3
dos.flush();//将缓冲区中的数据写出
//这里应该将dos关掉么?
/*不需要关,因为关闭会连带自动的把传递进来的out低级流关闭,
而这个低级流传参处很可能还需要用,这里dos在方法里用完后,
其它地方没有对它的应用,GC会自动把它回收*/
}
/**
* 从输入流中读取字符串
* @param in
* @return
* @throws IOException
*/
public static String loadString(InputStream in)throws IOException{
DataInputStream dis = new DataInputStream(in);
String data = dis.readUTF();
return data;
}
}
字符流:
以字符为单位读写数据,一次处理一个字符(unicode)。字符流底层还是基于字节形式读写的。所有字符流都是高级流。
- InputStreamReader:字符输入流
- OutputStreamWriter:字符输出流
- InputStreamReader与OutputStreamWriter是字符输入流和字符输出流,它们是高级流。作用是可以以字节为单位读写数据。
- BufferedReader(缓冲字符输入流)和BufferedWriter(缓冲字符输出流),特点是以行为单位读写数据。
- 打印流,PrintStream(打印字节流),PrintWriter(打印字符流)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Created by 罗强 on 2018/7/9.
*/
public class CharacterStreamTest {
public static void main(String[] args) throws IOException {
/*字符输出流
* Writer:字符输出流
* OutputStreamWriter:高级流,可以以字符为单位写数据
* */
/*向文件中写入字符
* 步骤
* 1:创建文件输出流(字节流)
* 2:创建字符输出流(高级流),处理文件输出流
* 目的是我们可以以字节为单位写数据
* 3:写入字符
* 4写完后关闭流*/
OutputStreamWriter writer = null;
try {
//1
FileOutputStream fos =new FileOutputStream("writer.txt");
//2
//writer = new OutputStreamWriter(fos);//使用系统默认编码集输出字符串
//使用UTF-8作为编码集输出字符串
writer = new OutputStreamWriter(fos,"UTF-8");
//3
writer.write("你好!");
writer.flush();//将缓冲区数据一次性写出
}catch (IOException e){
throw e;
}finally {
//4
if (writer!=null){
writer.close();
}
}
/**
* 字符输入流
*/
InputStreamReader reader = null;
try {
//创建用于读取文件的字节输入流
FileInputStream fis = new FileInputStream("writer.txt");
//创建用于以字符为单位的读取数据的高级流
reader = new InputStreamReader(fis);
//读取数据
int c = -1;
while ((c=reader.read())!=-1){
System.out.println((char)c);
}
}catch (IOException e){
throw e;
}finally {
if (reader!=null){
reader.close();
}
}
/*缓冲字符输出流
* 高级流
* 可以以行为单位写字符
* */
//创建用于写文件的输出流
FileOutputStream fos = new FileOutputStream("buffered.txt");
//创建一个字符输出流
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
/*
BufferedWriter的构造方法中不支持给定一个字节输出流
只能给定一个字符输出流Writer的子类
Writer:字符输出流的父类
*/
BufferedWriter bw = new BufferedWriter(osw);
bw.write("你好呀!");
bw.newLine();//输出一个换行
bw.write("我是第二行");
bw.newLine();
bw.write("我是第三行");
//输出流关闭后,不能再通过其写数据
bw.close();
/*
* 缓冲字符输入流
* 读取src/day02/DemoBufferedReader.java
* readLine()
* */
FileInputStream fis = new FileInputStream("src"+ File.separator+
"day02"+File.separator+"DemoBufferedReader.java");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String str =null;
/**
* readLine()读取一行字符并以字符串形式返回
* 返回值中不带换行符。
* 若返回null,说明读取到了文件末尾
* EOF:end of file 文件末尾
*/
while ((str=br.readLine())!=null){
System.out.println(str);
}
reader.close();
//打印字节流
System.out.println("你好!");
PrintStream out = System.out;
PrintStream fileOut =
new PrintStream(new FileOutputStream("SystemOUT.TXT"));
//将我们指定的输出流赋值到System.out上
System.setOut(fileOut);
System.out.println("你好!我是输出到控制台的!");
System.setOut(out);
System.out.println("我是输出到控制台的!");
fileOut.close();
}
}
- FileReader与FileWriter用于读写文件的字符输入流和输出流。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* Created by 罗强 on 2018/7/11.
*/
public class FileWriterAndFileReaderTest {
public static void main(String[] args) throws IOException{
//文件字符输出流
/*常用的两个构造方法
* FileWriter(File file)
* FileWriter(String filePath)
* 意思和FileOutputStream的两个同类型参数的构造方法一致
*
* FileWriter的效果等同于
* FileOuputStream + OutputStreamWriter
* */
FileWriter writer = new FileWriter("filewriter.txt");
//File file = new File("filewriter.txt");
//writer = new FileWriter(file);
writer.write("hello!FileWriter!");
writer.close();
//文件字符输入流
FileReader reader = new FileReader("filewriter.txt");
int c = -1;
//以字符为单位读取文件
while ((c=reader.read())!=-1){
System.out.print((char)c);
}
// 文件字符输入流转换为缓冲字符输入流便可以以行为单位读取
BufferedReader br = new BufferedReader(reader);
String info = null;
while ((info=br.readLine())!=null){
System.out.println(info);
}
reader.close();
}
}
- PrintWriter
另一种缓冲字符输出流。
Servlet:运行在服务器端的小程序。给客户端发送相应的输出流就是PrintWriter。 - 写方法:println(String data):带换行符输出一个字符串。
构造方式:PrintWriter(File file):以行为单位向文件写数据。
PrintWriter(OutputStream out):以行为单位向字节输出流写数据。
PrintWriter(Writer writer):以行为单位向字符输出流写数据。
PrintWriter(String fileName):以行为单位向指定路径的文件写数据。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class PrintWriterTest {
public static void main(String[] args) throws IOException{
PrintWriter writer =new PrintWriter("printwriter.txt");
//向文件写入一个字符串
writer.println("你好!PrintWriter");//自动加换行符;
/*
* 我们要在确定做写操作的时候调用flush()方法
* 否则数据可能会在输出流的缓冲区中,没有做真实的写操作!
* */
writer.flush();
writer.close();
/*使用缓冲字符输入流读取键盘输入信息*/
/*
* 将键盘的输入流转换为字符输入流在转换为缓冲字符输入流
* */
//1、将键盘的字节输入流转换为字符输入流
InputStreamReader isr = new InputStreamReader(System.in);
//2、将字符输入流转换为缓冲字符输入流。按行读取信息
BufferedReader br = new BufferedReader(isr);
//循环读取用户输入信息并输出到控制台
while (true){
String info = br.readLine();
if ("exit".equals(info)){
break;
}
/*
* 若我们不是输出到控制台,而是通过对文件的输出流
* 进行写操作,可以将控制台的信息写到文本文件中了。
* */
System.out.print(info);
}
br.close();
}
}
断点续传
import java.io.Serializable;
/**
* 保存一个下载任务所需要的信息
* VO:Value Object 值对象
* 作用:保存一组数据。
* 若这组数据表示的是数据库中的一条数据的话
* 那么这个对象就叫做:Entity实体
*/
public class DownloadInfo implements Serializable {
private String url;//下载文件的地址
private long pos;//已经下载的字节数
private long fileSize;//文件总大小
private String fileName;//保存在本地的文件名
public DownloadInfo(String url, String fileName) {
this.url = url;
this.fileName = fileName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public long getPos() {
return pos;
}
public void setPos(long pos) {
this.pos = pos;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 断点续传测试
*/
public class TestHttpDownload {
public static void main(String[] args){
/*
* 1:创建下载任务DownLoadInfo实例
* 2:根据任务中的下载地址打开网络连接,获取对应输入流
* 3:创建任务中保存在本地的文件的输出流
* 4:进行读写操作(下载)
* 5:关闭流
* */
try {
//1
DownloadInfo info = new DownloadInfo("http://www.baidu.com/download/java_1_7.zip",
"java_1_7.zip");
//2
URL url = new URL(info.getUrl());//给定网络地址
HttpURLConnection conn = (HttpURLConnection) url.openConnection();//通过地址打开网络连接
//获取要下载的文件的大小
info.setFileSize(Long.parseLong(conn.getHeaderField("CONTENT-LENGTH")));
conn.disconnect(); //与服务器断开,准备下一次请求(一次请求,一次响应)
//再次连接、请求
conn = (HttpURLConnection) url.openConnection();
/*要想实现续传。我们必须告诉服务器我们当次读取
*文件的开始位置。相当于我们本地调用seek()
*因为我们不可能直接调用服务器的对象的方法
*所以我们只能通过某种方式告诉服务器我们要干什么。
*让它自行调用自己流对象的seek()到我们想读取的位置。
*
* bytes=0- 的意思是告诉服务器从第一个字节开始读
* seek(0)
* bytes=128- 的意思是告诉服务器从第129个字节开始读
* seek(128)
* */
String prop = "bytes="+info.getPos()+"-";
//通过连接设置参数,通知服务器从什么地方开始读
conn.setRequestProperty("RANGE",prop);
//打开输入流开始读取文件
InputStream in = conn.getInputStream();
//将任务中记录的本地文件作为写出的目标文件
RandomAccessFile raf = new RandomAccessFile(info.getFileName(),"rw");
//服务器seek的位置就是我们写的位置
raf.seek(info.getPos());
//创建一个缓冲区
byte[] buffer = new byte[1024*10];//10K缓冲区
int sum = -1;//记录读取的字节数
/*
* 进行下载操作
* 从输入流(来自网络的文件)读取数据
* 通过输出流(下载的文件)写数据
* */
while ((sum=in.read(buffer))>0){
raf.write(buffer,0,sum);
//记录当前下载总字节数
info.setPos(info.getFileSize()+sum);
}
raf.close();
in.close();
conn.disconnect();//关闭和服务器的连接
}catch (Exception e){
}
}
}
对象序列化
- 将一个对象转换为字节形式的过程就叫做序列化。
对象的序列化使用的类ObjectOutputStream:
writeObject(Object obj):将给定对象序列化后写出 - 对象的反序列化使用的类ObjectInputStream:
Object readObject():将读取的字节序列还原为对象 - 集合类没有实现Serializable接口,不能用于序列化和反序列化
- 被transient关键字修饰的属性不会被序列化
- 序列化还有个叫法叫串行化。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* Created by 罗强 on 2018/7/12.
*/
public class SerializeObject {
public static void main(String[] args){
try {
//DownloadInfo需实现Serializable接口,否则无法写入文件
DownloadInfo info = new DownloadInfo(
"http://www.baidu.com/download.xxx.zip",
"xxx.zip");
info.setPos(12587);
info.setFileSize(5566987);
//将info对象序列化后写入到文件中
File file = new File("obj.tmp");
FileOutputStream fos = new FileOutputStream(file);
//通过oos可以将对象序列化后写入obj.tmp文件中
ObjectOutputStream oos = new ObjectOutputStream(fos);
//将info序列化后写出
oos.writeObject(info);
oos.close();
/*反序列化操作*/
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
//反序列化
DownloadInfo obj = (DownloadInfo) ois.readObject();
System.out.println(obj.getUrl());
System.out.println(obj.getFileName());
System.out.println(obj.getFileSize());
System.out.println(obj.getPos());//false
System.out.println(info==obj);
ois.close();
}catch (Exception e){
e.printStackTrace();
}
}
}