文章目录
- 1. IO流,什么是IO?
- 2.IO流的分类?
- 3.IO流都在java.io.*包下
- 4.java IO流有四个家族
- 5.java.io包下需要掌握的流有16个
- 6.FileInputStream的1个字节读入法
- 7.FileInputStream的byte[]读入法
- 8.FileInputStream的其他方法
- 9.FileOutputStream用法
- 10.文件复制
- 11.FileReader用法
- 12.复制普通文本文件(Reader/Writer)
- 13.带有缓冲区的字符输入流
- 14.包装流和节点流(即转换流)
- 15.带有缓冲区的字符输出流
- 16.数据流
- 17.标准输出流
- 18.File类的理解以及常用方法
- 19.目录拷贝
- 20.序列化和反序列化
- 21.序列化多个对象
- 22.transient关键字
- 23.序列化版本号
- 24.IO和Properties的联合使用
1. IO流,什么是IO?
I : Input
O: Output
通过IO可以完成硬盘文件的读和写。
2.IO流的分类?
有多种分类方式:
1)一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入。或者叫做读(Read)。
从内存中出来,叫做输出。或者叫做写(Write)。
2)另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte等同于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括文本文件、图片、声音文件、视频文件等等…
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:1个字节,正好读到’a’
第二次读:1个字节,正好读到’中’字符的一半
第三次都:1个字节,正好读到’中’字符的另外一半
有的流是按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件存在的。这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读: 'a’字符('a’字符在windows系统中占用1个字节)
第二次读: '中’字符('中’字符在windows系统中占用2个字节)
综上所述: 流的分类
输入流、输出流(按照流的方向进行分类)
字节流、字符流(按照读取的方式进行分类)
3.IO流都在java.io.*包下
java中的IO流都已经写好了,我们程序员不需要关心,我们最主要还是掌握,在java中已经提供了哪些流,每个流的特点是什么,每个流对象上的常用方法有哪些?
java中所有的流都是在: java.io.*下。
java中主要还是研究:
怎么new流对象。
调用流对象的哪个方法是读,哪个方法是写。
4.java IO流有四个家族
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
注意: 在java中只要"类名"以Stream结尾的都是字节流。以"Reader/Writer"结尾的都是字符流。
5.java.io包下需要掌握的流有16个
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DateInputStream
java.io.DateOutputStream
标准输出流:
java.io.printWriter
java.io.printStream
对象专属流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
6.FileInputStream的1个字节读入法
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*
java.io.FileInputStream:1.文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。2.字节的方式,完成输入的操作,完成读的操作。(硬盘-->内存)*/
public class FileInputStreamTest01 {public static void main(String[] args) {FileInputStream fis=null;try {//创建字节输入流对象//文件路径:C:\Users\Jm\Desktop\Java\doSome.txt//文件内容为:abcedf//FileInputStream fis=new FileInputStream("C:\\Users\\Jm\\Desktop\\Java\\doSome.txt");//都采用了: 绝对路径//将"\\"写成"/"也是可以的fis=new FileInputStream("C:/Users/Jm/Desktop/Java/doSome.txt");int readDate=fis.read();//这个方法的返回值是读到字节本身System.out.println(readDate);// 97} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {//在finally语句块中确保流一定要关闭if (fis != null) {//关闭流的前提是:流不是空。流是null的时候没必要关闭。try {fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
7.FileInputStream的byte[]读入法
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*
int read(byte[] b)一次最多读取b.length个字节。减少硬盘和内存之间的交互,提高程序的执行效率。往byte[]数组当中读。*/
public class FileInputStreamTest02 {public static void main(String[] args) {FileInputStream fis=null;try {//相对路径的话呢?相对路径一定是从当前所在的位置作为起点开始找!//IDEA默认的当前路径是工程Object的根就是IDEA的默认当前路径。//文件内容:abcdeffis=new FileInputStream("text02.txt");byte[] bytes=new byte[4];//准备一个4个长度的byte数组,一次最多读取4个字节。//这个方法的返回值是,读取到的字节数量。(不是字节本身)//int readCount=fis.read(bytes);//System.out.println(readCount);// 第一次读到4个字节//将字节数组全部转换成字符串//System.out.println(new String(bytes));//不应该全部转换,应该读取了多少个字节,转换多少个。//System.out.println(new String(bytes,0,readCount));// abcd//最终版本,读取文件int readCount=0;while((readCount = fis.read(bytes))!=-1){System.out.print(new String(bytes,0,readCount));}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (fis != null) {try {fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
8.FileInputStream的其他方法
1)int available(): 返回流当中剩余的没有读取到的字节的数量
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;public class FileInputStreamTest03 {public static void main(String[] args) {FileInputStream fis=null;try {fis=new FileInputStream("test03");System.out.println("总字节数量:"+fis.available());// 41//读一个字节//int readDate=fis.read();//还剩下的可读字节为40//System.out.println("还剩下的可读字节的数量:"+fis.available());// 40//这个方法有什么用?byte[] bytes=new byte[fis.available()];//不需要循环了!//直接读一次就行了。int readCount=fis.read(bytes);// 6System.out.println(new String(bytes)); // abcdef} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (fis != null) {try {fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
2)long stip(long n): 跳过几个字节不读
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;public class FileInputStreamTest04 {public static void main(String[] args) {FileInputStream fis=null;try {fis=new FileInputStream("text02.txt");//a b c d e f//97 98 99 100 101 102System.out.println(fis.read());// 97fis.skip(3);//跳过三个字节System.out.println(fis.read());// 101} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (fis != null) {try {fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
9.FileOutputStream用法
package com.jmpower.javase.io;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class FileOutputStreamTest01 {public static void main(String[] args) {FileOutputStream fos=null;try {//file01文件不存在会自动新建!//这种方式慎用,这种方式会先将文件清空,然后重新写入。//fos=new FileOutputStream("file01");//以追加的方式在文件末尾写入。不会清空原文件的内容。fos=new FileOutputStream("text02.txt",true);//开始写byte[] bytes={97,98,99,100};//将byte数组全部写出!fos.write(bytes);// abcd//将bute数组的一部分写出!fos.write(bytes,0,2);// ab//字符串String s="我是中国人!";//将字符串转换成数组byte[] bs=s.getBytes();//写fos.write(bs);//写完之后一定要刷新fos.flush();} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (fos != null) {try {fos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
10.文件复制
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;/*
使用FileInputStream + FileOutputStream完成文件的拷贝。
拷贝的过程一定是一边读,一边写。
使用以上的字节流拷贝文件的时候,文件类型随意,万能的。什么样的文件都可以拷贝。*/
public class Copy01 {public static void main(String[] args) {FileInputStream fis=null;FileOutputStream fos=null;try {//创建一个输入流对象fis=new FileInputStream("C:\\Users\\Jm\\Pictures\\自建\\十三届蓝桥杯省一.jpg");//创建一个输出流对象fos=new FileOutputStream("C:\\Users\\Jm\\Desktop\\Java\\十三届蓝桥杯省一.jpg");//最核心的: 一边读,一边写byte[] bytes=new byte[1024*1024];//1MB(一次最多拷贝1MB)int readCount=0;while((readCount=fis.read(bytes))!=-1){fos.write(bytes,0,readCount);}//刷新,输出流最后要刷新fos.flush();} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {//分开try,不要一起try//一起try的时候,其中一个出现异常,可能会影响到另一个流的关闭if (fis != null) {try {fis.close();} catch (IOException e) {throw new RuntimeException(e);}}if (fos != null) {try {fos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
11.FileReader用法
package com.jmpower.javase.io;import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
FileReader:读取文本内容时,比较方便,快捷。一次读取一个字符。*/
public class FileReaderTest {public static void main(String[] args) {FileReader reader=null;try {//创建文件字符输入流reader=new FileReader("text02.txt");//开始读char[] chars=new char[4];//一次读取4个字符(1个字符2个字节)int readCount=0;while((readCount=reader.read(chars))!=-1){System.out.print(new String(chars,0,readCount));}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (reader != null) {try {reader.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
12.复制普通文本文件(Reader/Writer)
package com.jmpower.javase.io;import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;public class Copy02 {public static void main(String[] args) {FileReader in=null;FileWriter out=null;try {//读in=new FileReader("src/com/jmpower/javase/io/FileInputStreamTest02.java");//写out=new FileWriter("reader");//一边读一边写char[] chars=new char[1024*512];//1MBint readCount=0;while((readCount=in.read(chars))!=-1){out.write(new String(chars,0,readCount));}//刷新out.flush();} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {//关闭流if (in != null) {try {in.close();} catch (IOException e) {throw new RuntimeException(e);}}if (out != null) {try {out.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
13.带有缓冲区的字符输入流
package com.jmpower.javase.io;import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;/*
BufferedReader:带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。*/
public class BufferedReaderTest01 {public static void main(String[] args) {FileReader reader=null;BufferedReader br=null;try {reader=new FileReader("src/com/jmpower/javase/io/Copy02.java");//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做: 节点流。//外部负责包装的这个流,叫做: 包装流,还有一个名字叫做: 处理流。//像当前这个程序来说: FileReader叫做一个节点流。BufferedReader就是包装流/处理流。br=new BufferedReader(reader);/*//第一行String firstLine=br.readLine();System.out.println(firstLine);//第二行String secondLine=br.readLine();System.out.println(secondLine);*///br.readLine()方法读取一个文本行,但不带换行符。String line=null;while((line=br.readLine())!=null){System.out.println(line);}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {//关闭流//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。(可以看源代码)if (br != null) {try {br.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
14.包装流和节点流(即转换流)
package com.jmpower.javase.io;import java.io.*;public class BufferedReaderTest02 {public static void main(String[] args) {BufferedReader br=null;try {//字节流FileInputStream in=new FileInputStream("src/com/jmpower/javase/io/Copy02.java");//通过转换流转换(InputStreamReader将字节流转换成字符流)//in是节点流。reader是包装流。InputStreamReader reader=new InputStreamReader(in);//这个构造方法只能传一个字符流。不能传字节流。//reader是节点流。br是包装流。br=new BufferedReader(reader);String line=null;while((line=br.readLine())!=null){System.out.println(line);}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {//关闭最外层try {br.close();} catch (IOException e) {throw new RuntimeException(e);}}}
}
15.带有缓冲区的字符输出流
package com.jmpower.javase.io;import java.io.*;
/*
BufferedWriter: 带有缓冲的字符输出流
OutputStreamWriter: 转换流*/
public class BufferedWriterTest01 {public static void main(String[] args) {BufferedWriter out=null;try {//带有缓冲的字符输出流//out=new BufferedWriter(new FileWriter("bo"));out=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("doSome"/*,true*/)));//开始写out.write("123");out.write("\n");out.write("456");//刷新(输出流记得刷新)out.flush();} catch (IOException e) {throw new RuntimeException(e);} finally {if (out != null) {try {out.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
16.数据流
1)写
package com.jmpower.javase.io;import java.io.*;/*
java.io.DataOutputStream:数据专属的流
这个流可以将数据连通数据的类型一并写入文件。
注意: 这个文件不是普通文本文档。(这个文件使用记事本打不开)*/
public class DataOutputStreamTest01 {public static void main(String[] args) {DataOutputStream dos=null;try {//创建数据专属的字节输出流dos=new DataOutputStream(new FileOutputStream("data"));//写数据byte b=100;short s=400;int i=200;long l=300;float f=3.0f;double d=3.14;boolean sex=false;char c='0';//写dos.writeByte(b);dos.writeShort(s);dos.writeInt(i);dos.writeLong(l);dos.writeFloat(f);dos.writeDouble(d);dos.writeBoolean(sex);dos.writeChar(c);//刷新dos.flush();} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (dos != null) {try {dos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
2)读
package com.jmpower.javase.io;import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*
java.io.DataInputStream: 数据字节输入流
DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。
读的顺序需要和谐的顺序一致。才可以正常读出数据。*/
public class DataInputStreamTest01 {public static void main(String[] args) {DataInputStream dis=null;try {dis=new DataInputStream(new FileInputStream("data"));//开始读byte b=dis.readByte();short s=dis.readShort();int i=dis.readInt();long l=dis.readLong();float f= dis.readFloat();double d= dis.readDouble();boolean sex= dis.readBoolean();char c= dis.readChar();System.out.println(b);System.out.println(s);System.out.println(i);System.out.println(l);System.out.println(f);System.out.println(d);System.out.println(sex);System.out.println(c);} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (dis != null) {try {dis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
17.标准输出流
package com.jmpower.javase.io;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;/*
java.io.PrintStream: 标准的字节输出流。默认输出到控制台。*/
public class PrintStreamTest {public static void main(String[] args) {//联合起来写System.out.println("hello world!");//分开写java.io.PrintStream ps=System.out;ps.println("hello zhangsan");ps.println("hello lisi");ps.println("hello wangwu");//标准输出流不需要手动colse()关闭。//可以改变标准输出流的输出方向吗?可以try {//标准输出流不再指向控制台,指向“log”文件PrintStream printStream=new PrintStream(new FileOutputStream("log"));//修改输出方向,将输出方向修改到"log"文件System.setOut(printStream);//再输出,输入到了"log"文件中System.out.println("hello lisi");System.out.println("hello zhangsan");System.out.println("hello wangwu");} catch (FileNotFoundException e) {throw new RuntimeException(e);}}
}
18.File类的理解以及常用方法
1)
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
boolean mkdir() 创建由此抽象路径名命名的目录。
boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
File getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
package com.jmpower.javase.io;import java.io.File;
import java.io.IOException;/*
File1.File类和四大家族没有关系,所以File类不能完成文件的读和写2.File对象代表什么?文件和目录路径名的抽象表示方式。C:\Users\Jm\Desktop\Java 这是一个File对象C:\Users\Jm\Desktop\Java\学习笔记(Java) 这也是一个File对象一个File对象有可能对应的目录,也可能是文件File只是一个路径名的抽象表达形式3.需要掌握File类中常用的方法boolean exists() 测试此抽象路径名表示的文件或目录是否存在。boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。boolean mkdir() 创建由此抽象路径名命名的目录。boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。File getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。 */
public class FileTest01 {public static void main(String[] args) {//创建一个File对象File f1=new File("D:\\file");//判断是否存在System.out.println(f1.exists()); // false//如果D:\file不存在,则以文件的形式创建出来/*if(!f1.exists()){try {//以文件的形式创建出来f1.createNewFile();} catch (IOException e) {throw new RuntimeException(e);}}*///如果D:\\file不存在,则以目录的形式创建出来/*if(!f1.exists()){//以目录的形式创建出来f1.mkdir();}*///可以创建多重目录吗?/*File f2=new File("D:\\a\\b\\c\\d");if(!f2.exists()){f2.mkdirs();}*/File f3=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)");//获取文件的父路径String fatherPath=f3.getParent();System.out.println(fatherPath);// C:\Users\Jm\Desktop\JavaFile parentFile=f3.getParentFile();System.out.println("获得绝对路径: "+parentFile.getAbsolutePath());File f4=new File("doSome");System.out.println("绝对路径: "+f4.getAbsolutePath());}
}
2)
String getName() 返回由此抽象路径名表示的文件或目录的名称。
boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。
long lastModified() 返回此抽象路径名表示的文件上次修改的时间。
long length() 返回由此抽象路径名表示的文件的长度。
package com.jmpower.javase.io;import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;/*
File常用方法:String getName() 返回由此抽象路径名表示的文件或目录的名称。boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。long lastModified() 返回此抽象路径名表示的文件上次修改的时间。long length() 返回由此抽象路径名表示的文件的长度。 */
public class FileTest02 {public static void main(String[] args) {File f1=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)\\day01.txt");//获取文件名System.out.println("文件名: "+f1.getName());//判断是否是一个目录System.out.println(f1.isDirectory());// false//判断是否是一个文件System.out.println(f1.isFile());// true//判断文件最后一次修改时时间long haoMiao=f1.lastModified();Date time=new Date(haoMiao);SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");String strTime=sdf.format(time);System.out.println(strTime);//获取文件的大小System.out.println(f1.length());// 2155字节}
}
3)
File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
package com.jmpower.javase.io;import java.io.File;/*
File中的lastFiles方法。File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。 */
public class FileTest03 {public static void main(String[] args) {File f1=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)");File[] files=f1.listFiles();//foreachfor(File file:files){//获取绝对路径//System.out.println(file.getAbsolutePath());//获取名字System.out.println(file.getName());}}
}
19.目录拷贝
package com.jmpower.javase.io;import java.io.*;
//程序BUG:拷贝源目录下的第一个文件无法拷贝
public class CopyAll {public static void main(String[] args) {//拷贝源File srcFile=new File("D:\\666\\c语言");//拷贝目标File destFile=new File("C:\\Users\\Jm\\Desktop\\Java");//调用方法拷贝copyDir(srcFile,destFile);}/*** 拷贝目录* @param srcFile 拷贝源* @param destFile 拷贝目标*/private static void copyDir(File srcFile,File destFile){if(srcFile.isFile()){//srcFile如果是一个文件的话,递归结束//是文件需要拷贝//....一边读一边写FileInputStream in=null;FileOutputStream out=null;try {//读这个文件//D:\666\c语言\算法模板\map函数.txtin=new FileInputStream(srcFile);//写到这个文件中//C:\Users\Jm\Desktop\Java\666\c语言\算法模板\map函数.txtString path=(destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3);out=new FileOutputStream(path);//一边读一边写byte[] bytes=new byte[1024*1024];//1MBint readCount=0;while((readCount=in.read(bytes))!=-1){out.write(bytes,0,readCount);}//刷新out.flush();} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (out != null) {try {out.close();} catch (IOException e) {throw new RuntimeException(e);}}if (in != null) {try {in.close();} catch (IOException e) {throw new RuntimeException(e);}}}return ;}//获取源下面的子目录File[] files=srcFile.listFiles();for(File file:files){if(file.isDirectory()){//新建对应目录//System.out.println(file.getAbsolutePath());//D:\666\c语言\算法模板 源目录//C:\Users\Jm\Desktop\Java\666\c语言\算法模板 目标目录String srcDir=file.getAbsolutePath();String destDir=(destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcDir.substring(3);File newFile=new File(destDir);if(!newFile.exists()){newFile.mkdirs();}}else {//保证拷贝目录下第一个是文件时,不会出现FileNotFound的异常File newFile=new File((destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3));if(!newFile.exists()){newFile.mkdirs();}}//递归调用copyDir(file,destFile);}}
}
20.序列化和反序列化
序列化: Serialize java对象存储到文件中。将java对象的状态保存下来的过程。
反序列化:DeSerialize 将硬盘上的数据重新恢复到内存当中,恢复成java对象。
1)序列化的实现:
package com.jmpower.javase.io;import com.jmpower.javase.bean.Student;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
1.参与序列化和反序列化的对象,必须实现Serializable接口。2.注意: 通过源码发现,Serializable接只是一个标志接口:public interface Serializable{}这个接口当中什么代码都没有。他只是起到一个标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。3.序列化版本号有什么用呢?java.io.InvalidClassException:com.jmpower.javase.bean.Student;local class incompatible:stream classdesc serialVersionUID = -6680413217747085692, (10年前)local class serialVersionUID = -1967551671371379446(10年后)java语言是采用什么机制来区分类的?第一,首先通过类名进行比对,如果类名不一样,肯定不是同一个类。第二,如果类名一样,再根据序列化版本号进行区分。自动生成序列化版本号的好处:java虚拟机会自动区分不同人相同的实现了Serializable接口的类,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。自动生成序列化版本号的坏处:这种自动生成的序列化版本号,一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(不利于以后的修改)*/
public class ObjectOutputStreamTest01 {public static void main(String[] args) {//创建java对象Student s=new Student(1111,"zhansan");ObjectOutputStream oos=null;try {//序列化oos=new ObjectOutputStream(new FileOutputStream("students"));//序列化对象oos.writeObject(s);//刷新oos.flush();} catch (IOException e) {throw new RuntimeException(e);} finally {if (oos != null) {try {oos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
2)反序列化的实现
package com.jmpower.javase.io;import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;/*
反序列化*/
public class ObjectInputStreamTest01 {public static void main(String[] args) {ObjectInputStream ois=null;try {ois=new ObjectInputStream(new FileInputStream("students"));//开始反序列化,读Object obj=ois.readObject();//反序列化回来是一个学生对象,所以会调用学生对象的toString方法。System.out.println(obj);ois.close();} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} finally {if (ois != null) {try {ois.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
3)Student类
package com.jmpower.javase.bean;import java.io.Serializable;public class Student implements Serializable {private int age;private int no;private String name;public Student(){}public Student(int no, String name) {this.no = no;this.name = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"no=" + no +", name='" + name + '\'' +'}';}
}
21.序列化多个对象
1)序列化
package com.jmpower.javase.io;import com.jmpower.javase.bean.User;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
/*
一次序列化多个对象呢?可以,可以将对象放到集合当中,序列化对象。
提示:参与序列化的ArrayList集合以及集合中的元素User都需要实现java.io.Serializable接口。*/
public class ObjectOutputStreamTest02 {public static void main(String[] args) {List<User> list=new ArrayList<>();list.add(new User(1111,"zhangsan"));list.add(new User(2222,"lisi"));list.add(new User(3333,"wangwu"));list.add(new User(4444,"zhaoliu"));ObjectOutputStream oos=null;try {oos=new ObjectOutputStream(new FileOutputStream("users"));//序列化一个集合,这个集合对象中放了很多其他对象。oos.writeObject(list);//刷新oos.flush();} catch (IOException e) {throw new RuntimeException(e);} finally {if (oos != null) {try {oos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
2)反序列化
package com.jmpower.javase.io;import com.jmpower.javase.bean.User;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;public class ObjectInputStreamTest02 {public static void main(String[] args) throws FileNotFoundException {ObjectInputStream ois=null;try {//反序列化ois=new ObjectInputStream(new FileInputStream("users"));List<User> list= (List<User>) ois.readObject();for(User user:list){System.out.println(user);}} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} finally {if (ois != null) {try {ois.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
22.transient关键字
变量被transient关键词修饰,不会参与序列化。
package com.jmpower.javase.bean;import java.io.Serializable;public class User implements Serializable {//java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号//这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。//建议将序列化版本号手动的写出来。不建议自动生成。private static final long serialVersionUID=1L;//java虚拟机识别一个类的时候,先看类名,类名一样再看序列化版本号。private int age;//若不手动写序列化版本号,过了很久,Student这个类源代码改动了。//源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。//并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。private int no;//transient关键字表示游离的,不参与序列化。private transient String name;//name不参与序列化操作!
}
23.序列化版本号
序列化版本号有什么用呢?
java.io.InvalidClassException:
com.jmpower.javase.bean.Student;
local class incompatible:
stream classdesc serialVersionUID = -6680413217747085692,
(10年前)
local class serialVersionUID = -1967551671371379446
(10年后)
java语言是采用什么机制来区分类的?
第一,首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二,如果类名一样,再根据序列化版本号进行区分。
自动生成序列化版本号的好处:
java虚拟机会自动区分不同人相同的实现了Serializable接口的类,都有默认的序列化版本号,
他们的序列化版本号不一样。所以区分开了。
自动生成序列化版本号的坏处:
这种自动生成的序列化版本号,一旦代码确定之后,不能进行后续的修改,
因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候
java虚拟机会认为这是一个全新的类。(不利于以后的修改)
所以我们应该手动生成序列化版本号,以后修改源代码的时候,可以不用重新编译,直接运行,不会使得java虚拟机产生新的类,便于程序的修改。
package com.jmpower.javase.bean;import java.io.Serializable;public class User implements Serializable {//java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号//这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。//建议将序列化版本号手动的写出来。不建议自动生成。private static final long serialVersionUID=1L;//java虚拟机识别一个类的时候,先看类名,类名一样再看序列化版本号。private int age;//若不手动写序列化版本号,过了很久,Student这个类源代码改动了。//源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。//并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。private int no;//transient关键字表示游离的,不参与序列化。private transient String name;//name不参与序列化操作!
}
24.IO和Properties的联合使用
package com.jmpower.javase.io;import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;/*
IO+Properties的联合应用:
非常好的一个设计理念:以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启。就可以拿到动态的信息。类似于以上机制的这种文件被称为配置文件。并且当配置文件中的内容格式是:key1=valuekey2=value的时候,我们把这种配置文件叫做属性配置文件。java规范中要求: 属性配置文件建议以.properties结尾,但这不是必须的。这种以.properties结尾的文件在java中称为: 属性配属文件。其中Properties是专门存放属性配置文件内容的一个类。*/
public class IopropertiesTest01 {public static void main(String[] args) {/*Properties是一个Map集合,key和value都是String类型。想将doSome文件中的数据加载得到Properties的对象当中。*/FileReader reader=null;try {//创建输入流对象reader=new FileReader("doSome1.properties");//创建一个Map集合Properties pro=new Properties();//调用Properties对象的load方法将文件中的数据加载到Map集合中。pro.load(reader);//文件中的数据顺着管道加载到Map集合中,其中=左边做key,右边做value//通过key来的调用valueSystem.out.println(pro.getProperty("age"));//遍历Set<Map.Entry<Object, Object>> s=pro.entrySet();for(Map.Entry<Object,Object> o:s){System.out.println(o.getKey()+"="+o.getValue());}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {if (reader != null) {try {reader.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
属性配置文件的使用:
#在属性配置文件中井号是注释
#建议key和value之间使用=的方式,不建议使用 age:40
#=左边是key,=右边是value
#最好不要有空格
username=zhangsan
age=40