文章目录
- 一. I/O流概述
- 二. I/O流的分类
- 三. 流应该怎么学习
- 四. IO流四大家族
- 五. 需要掌握的流
- 1. java.io.FileInputStream常用方法
- 2. java.io.FileOutputStream常用方法
- 3. 文件的复制原理
- 4. java.io.FileReader
- 5. java.io.FileWriter
- 6. java.io.BufferedReader
- 7. java.io.BufferedWriter
- 8. java.io.DataOutputStream
- 9. java.io.DataInputStream
- 10. java.io.PrintStream
- 11. java.io.ObjectOutputStream
- 1. 对象的序列化和反序列化
- 2. transient关键字
- 3. 关于序列化版本号
- 六. java.io.File类
- 1. File类的常用方法
- 2. 拷贝目录
- 七. IO和Properties联合使用
一. I/O流概述
通过IO可以完成硬盘的读和写
二. I/O流的分类
按照流的方向进行分类:以内存为参照物
- 往内存中去,叫做输入。或者叫读
- 从内存中出来,叫做输出,或者叫写
按照读取数据方式不同进行分类
- 有的流是按照字节方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取。包括:文本、声音、视频、图片…
假设文件file.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到'a'
第二次读:一个字节,正好读到'中'字符的一半
- 有的流是按照字符的方式读取数据,一次读取1个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取
假设文件file.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a'字符('a'字符在windows系统中占用1个字节)
第二次读:'中'字符('中'字符在Windows系统中占用2个字节)
三. 流应该怎么学习
Java中IO流都已经写好了,程序员不需关心,主要掌握在Java中提供了哪些流,每个流的特点,常用方法
- java流都是在java.io.*下
四. IO流四大家族
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类(abstract class)
- 所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法,流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费很多资源
- 注意:在java中只要’'类名"以Stream结尾的都是字节流,以"Reader/Writer"结尾的都是字符流
- 所有的输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法,养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下,这个刷新表示将管道当中剩余未输出的数据强行输出完(清空管道)刷新的作用就是清空管道,没有flush可能会导致丢失数据
五. 需要掌握的流
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.DataInputStream
- java.io.DataOutputStream
标准输出流:
- java.io.PrintWriter
- java.io.PrintStream
对象专属流:
- java.io.ObjectInputStream
- java.io.ObjectOutputStream
1. java.io.FileInputStream常用方法
- 文件字节输入流,万能的,任何类型的文件都可以采用这个流来读
- 字节的方式,完成输入的操作,完成读的操作(硬盘—>内存)
package com.pudding;
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:\\IDEA\\temp");
//开始读
int readData = fis.read(); //这个方法的返回值是:读取到的"字节"本身
System.out.println(readData); //97
readData = fis.read();
System.out.println(readData); //98
readData = fis.read();
System.out.println(readData); //99
readData = fis.read();
System.out.println(readData); //100
readData = fis.read();
System.out.println(readData); //101
readData = fis.read();
System.out.println(readData); //102
//已经读到文件末尾了,再读的时候读取不到任何数据,返回去-1
readData = fis.read();
System.out.println(readData); //-1
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//finally语句块当中确保流一定关闭
//关闭流的前提是:流不为空。流是空的时候没必要关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.pudding;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
对第一个程序进行改进,循环方式
分析这个程序的缺点:
一次读写一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面
能不能一次读写多个字节呢
*/
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\\IDEA\\temp");
/*while (true) {
int readData = fis.read();
if (readData == -1) {
break;
}
System.out.println(readData);
}*/
//改造while循环
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();
}
}
}
}
}
97
98
99
100
101
102
进行改进,使用如下方法:
- int read(byte[] b)
一次最多读取b.length个字节
减少硬盘和内存的交互,提高程序的执行效率
往byte[]数组中读
package com.pudding;
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 {
//相对路径一定是从当前位置作为起点开始找的
//IDEA默认当前的路径是:工程Project的根就是IDEA的默认当前路径
//fis = new FileInputStream("tempfile");
fis = new FileInputStream("IO流/src/tempfile2");
//开始读,采用byte数组,一次读取多个字节,最多读取"数组.length"个字节
byte[] bytes = new byte[4]; //准备一个长度为4的byte数组,一次最多读取4个字节
//这个方法的返回值是:读取到的字节数量,不是字节本身
int readCount = fis.read(bytes);//第一次读到4个字节
System.out.println(readCount); //4
//将byte数组全部转换为字符串
//System.out.println(new String(bytes));//abcd
//不应该全部转换,应该读取多少个字节,转换多少个
System.out.println(new String(bytes,0,readCount)); //abcd
readCount = fis.read(bytes); //第二次只能读到2个字节
System.out.println(readCount);//2
//将byte数组全部转换为字符串
System.out.println(new String(bytes));//efcd
//不应该全部转换,应该读取多少个字节,转换多少个
System.out.println(new String(bytes,0,readCount)); //ef
readCount = fis.read(bytes); //一个字节都没有读到返回-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 com.pudding;
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("IO流/src/tempfile2");
//准备一个byte数组
byte[] bytes = new byte[4];
/*while (true) {
int readCount = fis.read(bytes);
if (readCount == -1) {
break;
}
//把byte数组转换为字符串
System.out.print(new String(bytes,0,readCount)); //abcdef
}*/
int readCount = 0;
while ((readCount = fis.read(bytes)) != -1){
System.out.println(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():返回流当中剩余没有读到的字节数量
- long skip(long n):跳过几个字节不读
package com.pudding;
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("IO流/src/tempfile2");
System.out.println("总字节数量:"+fis.available()); //6
/*
//读一个字节
int readByte = fis.read();
//还剩下可以读的字节数量:5
System.out.println("剩下多少字节没有读:"+fis.available());
//这个方法的用处?
byte[] bytes = new byte[fis.available()]; //不适合大文件,因为byte[]不能太大
//不需要循环了。读一次即可
int readCount = fis.read(bytes); //6
System.out.println(new String(bytes)); //abcdef
*/
//skip跳过去几个字节不读取,以后会用
fis.skip(3);
System.out.println(fis.read()); //100
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2. java.io.FileOutputStream常用方法
文件字节输出流,负责写
从内存到硬盘
package com.pudding;
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 {
/*
//这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入
fos = new FileOutputStream("IO流/src/myfile");
*/
//以追加的方式在文件末尾写入,不会清空原文件
fos = new FileOutputStream("IO流/src/myfile",true);
//开始写
byte[] bytes = {97,98,99,100};
//将byte数组全部写出
fos.write(bytes); //abcd
//将byte数组的一部分写出
fos.write(bytes,0,2); //在写ab
//字符串
String s = "hello world";
//将字符串转换为byte数组
byte[] bs = s.getBytes();
fos.write(bs);
//写完之后,最后一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3. 文件的复制原理
- 使用FileInputStream + FileOutputStream完成文件的拷贝
- 拷贝的过程应该是一边读,一边写
- 使用以上的字节流拷贝文件的时候,文件类型随意,万能的,什么样的文件都能拷贝
package com.pudding;
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("IO流/src/myfile");
//创建一个输出流对象
fos = new FileOutputStream("IO流/src/com/pudding/myfile");
//最核心的,一边读,一边写
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,一起try的时候,其中一个出现异常,可能会影响到另一个流的关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4. java.io.FileReader
- 文件字符输入流,只能读取普通文本
- 读取文本内容时,比较方便
package com.pudding;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader = null;
try {
//创建文件字符输入流
reader = new FileReader("IO流/src/tempfile2");
//开始读
char[] chars = new char[4]; //一次读取4个字符
int readCount = 0;
while ((readCount = reader.read(chars)) != -1) {
System.out.print(new String(chars,0,readCount)); //abcdef
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
使用FileReader、FileWriter进行拷贝的话,只能拷贝普通文本文件
package com.pudding;
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("IO流/src/com/pudding/Copy02.java");
//负责写
out = new FileWriter("Copy02.java");
//一边读一边写
char[] chars = new char[1024*512];
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();
}
}
}
}
}
5. java.io.FileWriter
- 文件字符输出流,写
- 只能输出普通文本
package com.pudding;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter out = null;
try {
//创建文件字符输出流对象
out = new FileWriter("file");
//开始写
char[] chars = {'我','是','中','国','人'};
out.write(chars);
out.write(chars,2,3);
out.write("我是一名Java软件工程师");
out.write("\n");
out.write("hello,world");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6. java.io.BufferedReader
- 带有缓冲区的字符输出流
- 使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓存
package com.pudding;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class BufferedReaderTest01 {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("Copy02.java");
//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
//外部负责包装的这个流,叫做:包装流,还有一个名字:处理流
//像当前的这个程序来说:FileReader就是一个节点流。BufferedReader就是包装流/处理流
BufferedReader br = new BufferedReader(reader);
//读一行
/*String firstLine = br.readLine();
System.out.println(firstLine);
String secondLine = br.readLine();
System.out.println(secondLine);*/
//br.readLine()读取一整行,但是不带换行符
String s = null;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
//关闭流
//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭
br.close();
}
}
转换流:InputStreamReader
package com.pudding;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
public class BufferedReaderTest02 {
public static void main(String[] args) throws Exception {
/*
//字节流
FileInputStream in = new FileInputStream("Copy02.java");
//通过转换流转换(将字节流转换成字符流)
//in是节点流,reader是包装流
InputStreamReader reader = new InputStreamReader(in);
//这个构造方法只能传一个字符流,不能传字节流
//reader是节点流,br是包装流
BufferedReader br = new BufferedReader(reader);
*/
//合并
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Copy02.java")));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//关闭
br.close();
}
}
7. java.io.BufferedWriter
- 带有缓冲的字符输出流
- 转换流:OutputStreamWriter
package com.pudding;
import java.io.*;
public class BufferedWriterTest {
public static void main(String[] args) throws Exception {
//带有缓冲区的字符输出流
//BufferedWriter out = new BufferedWriter(new FileWriter("copy"));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("copy2.java",true)));
//开始写
out.write("hello world");
out.write("\n");
out.write("pudding");
//刷新
out.flush();
//关闭
out.close();
}
}
8. java.io.DataOutputStream
- java.io.DataOutputStream是数据专属流
- 这个流可以将数据连同数据的类型一并写入文件
- 注意:这个文件不是普通文本文档(记事本打不开)
package com.pudding;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataOutputStreamTest {
public static void main(String[] args) throws Exception {
//创建数据专属的字节输出流
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
//写数据
byte b = 100;
short s = 200;
int i = 300;
long l = 400L;
float f = 3.0F;
double d = 3.14;
boolean sex = false;
char c = 'a';
//写
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();;
//关闭最外层
dos.close();
}
}
9. java.io.DataInputStream
- java.io.DataInputStream是数据字节输入流
- DataInputStream写的文件,只能使用DataInputStream去读,并且读的时候你需要提前知道写的顺序
- 读的顺序需要和写的顺序一致,才可以正常取出数据
package com.pudding;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class DataInputStreamTest01 {
public static void main(String[] args) throws Exception {
DataInputStream 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);
dis.close();
}
}
10. java.io.PrintStream
- 标准的字节输出流,默认输出到控制台
package com.pudding;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws Exception {
//联合起来写
System.out.println("hello world");
//分开写
PrintStream ps = System.out;
ps.println("pudding");
ps.println("zhangsan");
//标准输出流不需要手动关闭
//可以改变标准输出流的输出方向
/*
之前System类使用过的方法和属性
System.gc();
System.arraycopy();
System.currentTimeMillis();
System.exit(0);
PrintStream ps2 = System.out;
*/
//标准输出流不再指向控制台,指向"log"文件
PrintStream printStream = new PrintStream(new FileOutputStream("log"));
//修改输出方向,将输出方向修改到log文件
System.setOut(printStream);
//再输出
System.out.println("hello world");
}
}
package com.pudding;
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) {
PrintStream out = null;
try {
//指向一个日志文件
out = new PrintStream(new FileOutputStream("log.txt"));
//改变输出方向
System.setOut(out);
//日期当前时间
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(nowTime);
System.out.println(strTime + ":" +msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
package com.pudding;
public class LogTest {
public static void main(String[] args) {
//测试工具类是否好用
Logger.log("调用了System类的gc()方法,建议启动垃圾回收");
Logger.log("用户尝试登录");
}
}
11. java.io.ObjectOutputStream
1. 对象的序列化和反序列化
- 序列化:Java对象存储到文件中。将java对象的状态保存下来的过程
- 反序列化:将硬盘上的数据重新恢复到内存当中,恢复成java对象
参与序列化和反序列化的对象,必须实现Serializable接口
注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable {
}
这个接口当中什么代码也没有
起到一个标识的作用,Java虚拟机看到这个类实现的接口,可能会对这个类进行特殊处理,Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号
package bean;
import java.io.Serializable;
public class Student implements Serializable {
//Java虚拟机看到Serializable接口会自动生成一个序列化版本号
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;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
package com.pudding;
import bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/*
java.io.NotSerializableException
Student对象不支持序列化
*/
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception {
//创建Java对象
Student s = new Student(1111,"zhangsan");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
package com.pudding;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/*
反序列化
*/
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
//开始反序列化,读
Object obj = ois.readObject();
//反序列化回来是异构学生对象,所以会调用学生对象的toString方法
System.out.println(obj);//Student{no=1111, name='zhangsan'}
ois.close();
}
}
序列化多个对象
注:一次可以序列化多个对象,将多个对象放到集合当中,序列化集合
- 参与序列化的ArrayList集合以及集合中的元素User都需要实现java.io.Serializable接口
package bean;
import java.io.Serializable;
public class User implements Serializable {
private int no;
private String name;
public User() {
}
public User(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;
}
@Override
public String toString() {
return "User{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
序列化集合
package com.pudding;
import bean.User;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception {
List<User> userList = new ArrayList<>();
userList.add(new User(1,"zhangsan"));
userList.add(new User(2,"lisi"));
userList.add(new User(3,"wangwu"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
//一次序列化一个集合,这个集合对象中放了很多其他的对象
oos.writeObject(userList);
oos.flush();
oos.close();
}
}
反序列化集合
package com.pudding;
import bean.User;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.ObjectInputStream;
import java.util.List;
/*
反序列化集合
*/
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
//Object obj = ois.readObject();
//System.out.println(obj instanceof List); //true
List<User> userList = (List<User>) ois.readObject();
for (User user : userList){
System.out.println(user);
}
ois.close();
}
}
2. transient关键字
package bean;
import java.io.Serializable;
public class User implements Serializable {
private int no;
//transient关键字,表示游离的,不参见序列化操作
private transient String name;
public User() {
}
public User(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;
}
@Override
public String toString() {
return "User{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
3. 关于序列化版本号
- 第一:首先通过类名进行比对,如果类名不一样,肯定不是一个类
- 第二:如果类名一样,再通过序列化版本号进行区分
- 不同人编写同一个类,但这两个类确实不是同一个类,这个时候序列化版本就起上作用了,对于java虚拟机来说是可以区分的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样,所以区分开了(好处)
小球:com.pudding.java.bean.Student implement Serializable
小强:com.pudding.java.bean.Student implement Serializable
- 不足:自动生成的序列化版本号缺点:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这个是一个全新的类
- 最终结论:凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号,这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类
package bean;
import sun.dc.pr.PRError;
import java.io.Serializable;
public class Student implements Serializable {
//如果没有手动写出序列化版本号,Java虚拟机看到Serializable接口会自动生成一个序列化版本号
//建议将序列化手动写出来,不建议自动生成
private static final long serialVersionUID = 1L;
private int no;
private String name;
private String email;
//过来很久,Student这个类源代码改动了
//源代码改动之后,需要重新编译,编译之后生成全新的字节码文件
//并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变
private int age;
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;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
六. java.io.File类
File类和四大家族没有关系,所
以File类不能完成文件的读和写
File对象代表什么?
- 文件和目录路径名的抽象表示形式
- C:\Driver 这是一个File对象
- C:\Driver\Readme.txt 也是一个File对象
- 一个File对象有可能对应的是目录,也可能是文件
- File只是一个路径名的抽象表示形式
1. File类的常用方法
package com.pudding;
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:\\学习.ppt");
//获取文件名
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()); //获取文件大小
}
}
package com.pudding;
import java.io.File;
public class FileTest03 {
public static void main(String[] args) {
//File[] listFiles();
//获取当前目录下所有的子文件
File f = new File("D:\\IDEA");
File[] files = f.listFiles();
//foreach
for (File file : files) {
System.out.println(file.getAbsoluteFile());
}
}
}
2. 拷贝目录
package com.pudding;
import java.io.*;
public class CopyAll {
public static void main(String[] args) {
//拷贝源
File srcFile = new File("D:\\IDEA\\code\\JDBC");
//拷贝目标
File destFile = new File("E:\\");
//调用方法拷贝
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 {
//读这个文件
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()) != -1){
out.write(bytes,0,readCount);
}
} 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){
//获取所有文件的(包括目录和文件)绝对路径
//System.out.println(file.getAbsoluteFile());
if (file.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联合使用
- IO流:文件的读和写
- Properties:是一个Map集合,key和value都是String类型
非常好的设计理念:
- 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取
- 将来只需要修改这个文件内容,java代表不需要改动
- 编译,服务器也不需要重启,就可以拿到动态信息
类似以上机制的这种文件被称为配置文件,并且配置文件中内容格式是:
- key1=value
- key2=value
- 的时候,我们把这种配置文件叫做属性配置文件
java规范中要求,属性配置文件建议以.properties结尾,但这不是必须的
- 其中properties是专门存放属性配置文件内容的一个类
package com.pudding;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Properties;
public class IoPropertiesTest {
public static void main(String[] args) throws Exception {
/*
Properties是一个Map集合,key和value都是String类型
想将userinfo文件中的数据加载到Properties对象当中
*/
//新建一个输入流对象
FileReader reader = new FileReader("IO流/userinfo.properties");
//新建一个map集合
Properties pro = new Properties();
//调用Properties对象的load方法将文件中的数据加载到Map集合中
pro.load(reader); //文件中的数据顺着管道加载到Map集合当中,其中等号=左边做key,右边为value
//通过key来获取value
String username = pro.getProperty("username");
System.out.println(username);
String password = pro.getProperty("password");
System.out.println(password);
}
}