-
什么是IO?
I : Input
O : Output
通过IO可以完成硬盘文件的读和写。 -
IO的分类
(1)一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入(Input)。或者叫做读(Read)。
从内存中出来,叫做输出(Output)。或者叫做写(Write)。(2)另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件 都可以读取。包括:文本文件,图片,声音文件,视频文件等…
假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到’a’
第二次读:一个字节,正好读到’中’字符的一半。
第三次读:一个字节,正好读到’中’字符的另外一半。
有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取
普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a’字符('a’字符在windows系统中占用1个字节。)
第二次读:'中’字符('中’字符在windows系统中占用2个字节。) 综上所述:流的分类
输入流、输出流
字节流、字符流 -
java IO流的四大家族:
四大家族的首领(四大家族的首领都是抽象类。(abstract class)):
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流 java.io.Reader 字符输入流
java.io.Writer 字符输出流所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流 一定要关闭。所有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据
强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()可能会导致丢失数据。**注意:**在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
-
需要掌握的流
文件专属:
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.DataInputStream
java.io.DataOutputStream标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
二、文件专属流以及转换流
- FileInputStream
①文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
②字节的方式,完成输入的操作,完成读的操作(硬盘—> 内存)
文件路径:D:\Java\007-API文档 (IDEA会自动把编程\,因为java中表示转义) 写成这个/也是可以的。都是采用了:绝对路径的方式。
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream fis = null;
//创建文件字节输入流对象
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
//开始读
int readDate = fis.read();//这个方法的返回值是:读取到的“字节”本身。
System.out.println( readDate);//97
System.out.println( fis.read());//98
System.out.println( fis.read());//99
System.out.println( fis.read());//100
System.out.println( fis.read());//101
System.out.println( fis.read());//102
System.out.println( fis.read());//-1 已经读到文件的末尾了,再读的时候读取不到任何数据,返回-1.
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
以上程序的缺点是 一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面了。
将以上程序进行改进。循环
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
while(true) {
int readData = fis.read();
if(readData == -1) {
break;
}
System.out.println(readData);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
再改进
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
int readData = 0;
while((readData = fis.read()) != -1){
System.out.println(readData);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
③IDEA默认的当前路劲是工程Project的根
④采用byte数组开始读,一次读取多个字节,最多读取“数组.length”个字节。
package 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("D:\Java\007-API文档\111");
// 开始读,采用byte数组,一次读取多个字节。最多读取“数组.length”个字节。
byte[] bytes = new byte[4]; // 准备一个4个长度的byte数组,一次最多读取4个字节。
// 这个方法的返回值是:读取到的字节数量。(不是字节本身)
int readCount = fis.read(bytes);
System.out.println(readCount); // 第一次读到了4个字节。
//System.out.println(new String(bytes));//将字节数组全部转换成字符串 // abcd
// 不应该全部都转换,应该是读取了多少个字节,转换多少个。
System.out.println(new String(bytes,0, readCount));
readCount = fis.read(bytes); // 第二次只能读取到2个字节。
System.out.println(readCount); // 2
System.out.println(new String(bytes));//将字节数组全部转换成字符串 // abcd
System.out.println(new String(bytes,0, readCount));
readCount = fis.read(bytes); // 1个字节都没有读取到返回-1
System.out.println(readCount); // -1
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
改进,最终版
package 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("D:\Java\007-API文档\111");
byte[] bytes = new byte[4];
int readCount = 0;
while((readCount = fis.read(bytes)) != -1){
System.out.print(new String(bytes,0,readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
⑤FileInputStream其他常用方法
int available():返回流当中剩余的没有读到的字节数量
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest05 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
//读一个字节
int readByte = fis.read();
//还剩下可以读的字节的数量是:
System.out.println("还剩下可以读的字节的数量是:" + fis.available());
//该方法的用处:可以知道字节数量,直接读就可以,不需要再循环,但是byte数组不能太大,不适合太大的文件
byte[] bytes = new byte[fis.available()];
int readCount = fis.read(bytes);
System.out.println(new String(bytes));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
long skip(long n):跳过几个字节不读。
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest06 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
fis.skip(3);
System.out.println(fis.read());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- FileOutputStream
① 文件字节输出流,负责写。从内存到硬盘。
package 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 {
//myfile文件不存在时,会自动新建
// 这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入。
//fos = new FileOutputStream("myfile");
fos = new FileOutputStream("myfile",true);//以追加的方式在文件末尾写入。不会清空原文件内容。
//开始写
byte[] bytes = {39,22,30,25,29};
//将byte数组全部写出
fos.write(bytes);
//将byte数组的一部分写出,再写两个
fos.write(bytes,0,2);
String s = "我是中国人";
byte[] bytes2 = s.getBytes();
fos.write(bytes2);
//写完之后,最后一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 文件复制
使用FileInputStream + FileOutputStream完成文件的拷贝。拷贝的过程应该是一边读,一边写。
使用以上的字节流拷贝文件的时候,文件类型随意,什么样的文件都能拷贝。
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Copy01 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:\Java\007-API文档\111");
fos = new FileOutputStream("C:\Java\IO\请粘贴到此处\111");
//一边读 一边写
byte[] bytes = new byte[1024 * 1024];//1MB
int readCount = 0;
while((readCount = fis.read(bytes)) != -1){
fos.write(bytes,0,readCount);
}
//刷新,输出流最后要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
//一起try catch的话 其中一个出现异常 可能会影响另一个流的关闭
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
-
FileReader的使用
文件字符输入流,只能读取普通文本。读取文本内容时,比较方便,快捷。
package IO; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest01 { public static void main(String[] args) { FileReader reader = null; try { // 创建文件字符输入流 reader = new FileReader("D:\Java\007-API文档\111"); //准备一个char数组 char[] chars = new char[4]; // 往char数组中读 reader.read(chars); // 按照字符的方式读取:第一次e,第二次f,第三次 风.... for(char c : chars) { System.out.println(c); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } } -
FileWriter的使用
FileWriter:
文件字符输出流。写。只能输出普通文本。package IO; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest01 { public static void main(String[] args) { FileWriter out = null; try { // 创建文件字符输出流对象 //out = new FileWriter("file"); out = new FileWriter("file", true); // 开始写。 char[] chars = {'我','是','中','国','人'}; out.write(chars); out.write(chars, 2, 3); out.write("我是一名java软件工程师!"); // 写出一个换行符。 out.write("n"); out.write("hello world!"); // 刷新 out.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } -
复制普通文本文件
使用FileReader FileWriter进行拷贝的话,只能拷贝“普通文本”文件。
package IO; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Copy2 { public static void main(String[] args) { FileReader in = null; FileWriter out = null; try { // 读 in = new FileReader("Day2/src/IO/Copy2.java"); // 写 out = new FileWriter("Copy2.java"); // 一边读一边写: char[] chars = new char[1024 * 512]; // 1MB int readCount = 0; while((readCount = in.read(chars)) != -1){ out.write(chars, 0, readCount); } // 刷新 out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
三、缓冲流
-
BufferedReader
带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。
package IO;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest01 {
public static void main(String[] args) {
// 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
// 外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
// 像当前这个程序来说:FileReader就是一个节点流。BufferedReader就是包装流/处理流。
FileReader read = null;
BufferedReader br = null;
try {
read = new FileReader("file");
br = new BufferedReader(read);
// br.readLine()方法读取一个文本行,但不带换行符。
String s = null;
while((s = br.readLine()) != null){
System.out.println(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- InputStreamReader 转换流
package IO;
import java.io.*;
public class BufferedReaderTest02 {
public static void main(String[] args) {
FileInputStream fis = null;
BufferedReader br = null;
InputStreamReader reader = null;
try {
//合并
br = new BufferedReader(new InputStreamReader(new FileInputStream("file")));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
-
BufferedWriter
带有缓冲的字符输出流。
package IO; import java.io.*; public class BufferedWriterTest01 { public static void main(String[] args) { BufferedWriter bw = null; try { //创建带有缓冲区的字符输出流 //bw = new BufferedWriter(new FileWriter("file",true)); bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("file",true))); //开始写 bw.write("我"); bw.write("是"); bw.write("n"); bw.write("程序员"); //刷新 bw.flush(); } catch (IOException e) { e.printStackTrace(); }finally{ if (bw != null) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
四、数据流
-
java.io.DataOutputStream:数据专属的流。
这个流可以将数据连同数据的类型一并写入文件。
注意:这个文件不是普通文本文档。(这个文件使用记事本打不开。)package IO; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class DataOutputStreamTest01 { public static void main(String[] args) { DataOutputStream dos = null; try { // 创建数据专属的字节输出流 dos = new DataOutputStream(new FileOutputStream("data")); byte b = 100; short s = 200; long l = 300L; int i = 400; double d = 3.14; float f = 1.5F; boolean sex = false; char c = 'a'; // 写数据 dos.writeByte(b); dos.writeShort(s); dos.writeLong(l); dos.writeInt(i); dos.writeDouble(d); dos.writeFloat(f); dos.writeBoolean(sex); dos.writeChar(c); // 刷新 dos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ // 关闭最外层 if (dos != null) { try { dos.close(); } catch (IOException e) { e.printStackTrace(); } } } } } -
DataInputStream:数据字节输入流。
DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。
package IO; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class DataInputStreamTest01 { public static void main(String[] args) { DataInputStream dis =null; try { dis = new DataInputStream(new FileInputStream("data")); System.out.println(dis.readByte()); System.out.println(dis.readShort()); System.out.println(dis.readLong()); System.out.println(dis.readInt()); System.out.println(dis.readDouble()); System.out.println(dis.readFloat()); System.out.println(dis.readBoolean()); System.out.println(dis.readChar()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if (dis != null) { try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
五、标准输出流
-
java.io.PrintStream:
标准的字节输出流。默认输出到控制台。
标准输出流不需要手动close()关闭。
package IO; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; public class PrintStreamTest01 { public static void main(String[] args) { // 联合起来写 System.out.println("hello world!"); // 分开写 PrintStream ps = System.out; ps.println("hello zhangsan"); ps.println("hello lisi"); ps.println("hello wangwu"); // 标准输出流不再指向控制台,指向“log”文件。 PrintStream printStream = null; try { printStream = new PrintStream(new FileOutputStream("log")); } catch (FileNotFoundException e) { e.printStackTrace(); } // 修改输出方向,将输出方向修改到"log"文件。 System.setOut(printStream); // 再输出 System.out.println("hello world"); System.out.println("hello kitty"); System.out.println("hello zhangsan"); } } -
应用:日志工具
package IO; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date; public class Logger { //记录日志的方法 public static void log(String msg){ try { PrintStream ps = new PrintStream(new FileOutputStream("log.txt",true)); //改变输出方向 System.setOut(ps); Date nowTime = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); String time = sdf.format(nowTime); System.out.println(time + ": " + msg); } catch (FileNotFoundException e) { e.printStackTrace(); } } }package IO; public class LoggerTest { public static void main(String[] args) { //测试日志工具 Logger.log("调用了System类的gc()方法,建议启动垃圾回收"); Logger.log("调用了UserService的doSome()方法"); Logger.log("用户尝试进行登录,验证失败"); Logger.log("用户下线"); } }
六、File类
-
File类和四大家族没有关系,所以File类不能完成文件的读和写。
-
File对象代表什么?
文件和目录路径名的抽象表示形式。
C:Drivers 这是一个File对象
C:DriversLanRealtekReadme.txt 也是File对象。
一个File对象有可能对应的是目录,也可能是文件。
File只是一个路径名的抽象表示形式。 -
File类的常用方法
package IO; import java.io.File; import java.io.IOException; public class FileTest01 { public static void main(String[] args) { File f1 = new File("D:\file"); //判断是否存在该文件 System.out.println(f1.exists()); //如果该文件不存在,则以文件的形式创建 if(!f1.exists()){ try { f1.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } //创建多重目录 File f3 = new File("D:\mj"); String parentPath = f3.getParent(); System.out.println(parentPath); File parentFile = f3.getParentFile(); System.out.println("获取绝对路径" + parentFile.getAbsolutePath()); } }package IO; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class FileTest02 { public static void main(String[] args) { File f1 = new File("D:\Java\007-API文档"); // 获取文件名 System.out.println("文件名:" + f1.getName()); // 判断是否是一个目录 System.out.println(f1.isDirectory()); // false // 判断是否是一个文件 System.out.println(f1.isFile()); // true // 获取文件最后一次修改时间 long haoMiao = f1.lastModified(); // 这个毫秒是从1970年到现在的总毫秒数。 // 将总毫秒数转换成日期 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()); //216064字节。 } }package IO; import java.io.File; public class FileTest03 { public static void main(String[] args) { File f = new File("D:\Java"); File[] files = f.listFiles(); // foreach for(File file : files){ //System.out.println(file.getAbsolutePath()); System.out.println(file.getName()); } } }
七、对象流
-
对象的序列化与反序列化
(1)序列化 Serialize :java对象存储到文件中,将java对象的状态保存下来的过程。 过程中需要ObjectOutputStream.
反序列化 DeSerialize :将硬盘上的数据重新恢复到内存当中,恢复成java对象。过程中需要ObjecInputStream.
(2)参与序列化和反序列化的对象,必须实现Serializable接口。
以下程序中Student没有实现Serializable接口,会出现java.io.NotSerializableException: Student对象不支持序列化!
package IO; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class ObjectOutputStreamTest01 { public static void main(String[] args) { //创建java对象 Student s = new Student(1111,"zhangsan"); ObjectOutputStream oos = null; //序列化 try { oos = new ObjectOutputStream(new FileOutputStream("stu")); } catch (IOException e) { e.printStackTrace(); } // 序列化对象 try { oos.writeObject(s); } catch (IOException e) { e.printStackTrace(); } //刷新 try { oos.flush(); } catch (IOException e) { e.printStackTrace(); }finally{ //关闭 if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }package IO; import java.io.Serializable; public class Student implements Serializable { private int num; private String name; public Student() { } public Student(int num, String name) { this.num = num; this.name = name; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "num=" + num + ", name='" + name + ''' + '}'; } }(3)**注意:**通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable {
}
这个接口当中什么代码都没有。 起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行 特殊待遇。Serializable这个标志接口是给java虚拟机参考的,会为该类自动生成一个序列化版本号。(4)反序列化
package 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("stu")); System.out.println(ois.readObject()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally{ if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } }(4)序列化多个对象
可以将对象放到集合当中,序列化集合。参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。
package IO; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; public class ObjectOutputStreamTest02 { public static void main(String[] args) { Listlist = new ArrayList<>(); list.add(new Student(1111,"zhangsan")); list.add(new Student(2222,"lisi")); list.add(new Student(3333,"dingwu")); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("students")); oos.writeObject(list); oos.flush(); } catch (IOException e) { e.printStackTrace(); }finally{ if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } } package IO; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.List; public class ObjectInputStreamTest02 { public static void main(String[] args) { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("students")); ListstuList = (List )ois.readObject(); for(Student student : stuList){ System.out.println(student); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } } (5)transient关键字
表示游离的,不参与序列化。
(6)序列化版本号
java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。 序列版本号的优点:
不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用。对于java虚拟机来说,java虚 拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不 一样。所以区分开了。(这是自动生成序列化版本号的好处)
自动序列化版本号的缺点:
一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java
虚拟机会认为这是一个全新的类。 最终结论:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版 本号不变,java虚拟机会认为是同一个类。 private static final long serialVersionUID = 1L;
八、测试
拷贝目录:
将D:course拷贝到C盘根目录下。
需要使用到:
FileInputStream
FileOutputStream
File
可能需要使用到递归。
package IO.Homework;
import java.io.*;
public class CopyAll {
public static void main(String[] args) {
//拷贝源
File srcFile = new File("D:\course");
//拷贝目标
File destFile = new File("C:\");
//调用方法拷贝
copyDir(srcFile,destFile);
}
private static void copyDir(File srcFile, File destFile) {
if(srcFile.isFile()) {
// srcFile如果是一个文件的话,递归结束。
// 是文件的时候需要拷贝。
// ....一边读一边写。
FileInputStream in = null;
FileOutputStream out = null;
try {
// 读这个文件
in = new FileInputStream(srcFile);
// 写到这个文件中
String path = (destFile.getAbsolutePath().endsWith("\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\") + srcFile.getAbsolutePath().substring(3);
out = new FileOutputStream(path);
// 一边读一边写
byte[] bytes = new byte[1024 * 1024]; // 一次复制1MB
int readCount = 0;
while((readCount = in.read(bytes)) != -1){
out.write(bytes, 0, readCount);
}
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
//获取源下的子目录
File[] files = srcFile.listFiles();
for(File file : files){
if(srcFile.isDirectory()){
//新建对应的目录
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();
}
}
//递归调用
copyDir(file,destFile);
}
}
}
九、IO和Properties联用
(1)IO流:文件的读和写。
Properties:是一个Map集合,key和value都是String类型。
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class IoPropertiesTest01 {
public static void main(String[] args) {
// 新建一个输入流对象
FileInputStream fis = null;
try {
fis = new FileInputStream("myfile");
// 新建一个Map集合
Properties pro = new Properties();
// 调用Properties对象的load方法将文件中的数据加载到Map集合中。
pro.load(fis);
// 通过key来获取value呢?
System.out.println(pro.getProperty("username"));
System.out.println(pro.getProperty("password"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(2)IO+Properties的联合应用。
非常好的一个设计理念:
以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,java代码不需要改 动,不需要重新编译,服务器也不需要重启。就可以拿到动态的信息。
(3)类似于以上机制的这种文件被称为配置文件,并且当配置文件中的内容格式是:
key1=value
key2=value
的时候,我们把这种配置文件叫做属性配置文件。
(4)java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。这种以.properties结尾的文件在java中被称为:属性 配置文件。其中Properties是专门存放属性配置文件内容的一个类。



