✅作者简介:大家好我是@每天都要敲代码,一位材料转码农的选手,希望一起努力,一起进步!
📃个人主页:@每天都要敲代码的个人主页🔥系列专栏:JavaSE从入门到精通
💬推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站进行注册学习
目录
🥅目录拷贝
🥅ObjectInputStream && ObjectOutputStream
1.序列化的实现
2.反序列化的实现
3.序列化 && 反序列化多个对象
4.序列化版本号
🥅IO和Properties联合使用
结束语
🥅目录拷贝
把一个目录拷贝到另一个目录
需要用到:FileInputStream+FileOutputStream+File+递归的思想
package com.bjpowernode.java.io;
import java.io.*;
public class CopyAll {
public static void main(String[] args) {
// 拷贝源
File srcFile = new File("C:\\Java学习\\javaSE学习");
// 拷贝目标
File destFile = new File("D:\\a\\b\\c");
// 调用方法进行拷贝
copyDir(srcFile,destFile);
}
/**
* 拷贝目录
* @param srcFile 拷贝源
* @param destFile 拷贝目标
*/
private static void copyDir(File srcFile, File destFile) {
//4、递归结束的条件,如果是文件就结束(就开始拷贝)
if(srcFile.isFile()){
// 是文件的是要要拷贝文件,一边读一遍写
FileInputStream in = null;
FileOutputStream out = null;
try {
// 读文件
in = new FileInputStream(srcFile);
// 写文件
String path = (destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath() :
destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3);
System.out.println(path);
out = new FileOutputStream(path);
// 一边读一边写
byte[] bytes = new byte[1024*1024];// 1M
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; // srcFile如果是一个文件,递归结束
}
//1、 获取拷贝源下面的子目录
File[] files = srcFile.listFiles(); //列出源文件下的子目录(目录和文件)
// 这个file有可能是目录,也有可能是文件;但都是一个File对象
for(File file:files){
// 获取所有文件的(目录和文件)绝对路径;进行打印测试
//System.out.println(file.getAbsolutePath());
// 3、创建对应的目录(目标文件对应的目录,要提前创建好)
if(srcFile.isDirectory()){ // 如果是一个目录
// 新建对应的目录
String srcDir = file.getAbsolutePath(); // 拿出所有的源目录
// 这里如果只是一个盘符的话,写上"//"会被识别出来;如果后面有其它目录,就识别不了"//"
// srcDir.substring(3)表示从下标为3的地方开始截取
String destDir = (destFile.getAbsolutePath().endsWith("\\")? destFile.getAbsolutePath() :
destFile.getAbsolutePath()+"\\")+srcDir.substring(3); // 目标目录
// 获取路径后,开始创建
File newFile = new File(destDir);
if(!newFile.exists()){ // 如果不存在,就递归创建目录
newFile.mkdirs();
}
}
//2、 递归调用(是目录的话一直往下拿,直到是一个文件就开始拷贝)
copyDir(file,destFile);
}
}
}
🥅ObjectInputStream && ObjectOutputStream
1、首先我们先明白两个概念:序列化(Serialize)和反序列化(DeSerialize)
序列化:将java对象存储到文件中,将java对象的状态保存下来的过程
反序列化:将硬盘上的数据重新恢复到内存当中,恢复成java对象
2、ObjectInputStream 和 ObjectOutputStream的作用
ObjectOutputStream是用来序列化的---》拆分对象
ObjectInputStream是用来反序列化的---》组装对象
3、通过图,来理清楚它们之间的关系
1.序列化的实现
(1)参与序列化和反序列化的对象,必须实现Serializable接口。如果下面的Student类没有实现Serializable接口,会报java.io.NotSerializableException,译为:Student对象不支持序列化!
(2)注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable { }
(3)这个接口当中什么代码都没有;那么它起到一个什么作用呢?
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到Serializable这个接口之后,会为该类自动生成一个序列化版本号。
package com.bjpowernode.java.io;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception {
// 创建Java对象
Student stu = new Student(111,"zhangsan");
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("s文件"));
// 序列化对象
oos.writeObject(stu);
// 刷新
oos.flush();
// 关闭
oos.close();
}
}
// 学生类
class Student<toString> implements Serializable {
private int no;
private String name;
// 构造方法
public Student() {
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
// setter and getter
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;
}
// 重写toString
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
2.反序列化的实现
package com.bjpowernode.java.io;
// 反序列化
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("s文件"));
// 开始反序列化,读
Object obj = ois.readObject();
// 反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
System.out.println(obj);
// 关闭
ois.close();
}
}
3.序列化 && 反序列化多个对象
1、可以一次序列化多个对象呢?
可以将对象放到集合当中,序列化集合!
2、参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。3、补充一个关键字:transient;这个关键字表示游离的,不参与序列化。
序列化
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
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();
}
}
// USer类
class User implements Serializable {
private int num;
private transient String name; // name不参与序列化操作;name打印出来的结果就是null
// 构造方法
public User() {
}
public User(int num, String name) {
this.num = num;
this.name = name;
}
// setter and getter
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;
}
// 重写toStringF方法
public String toString() {
return "User{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
反序列化
package com.bjpowernode.java.io;
import java.util.List;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
// 反序列化
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws Exception {
// 开始反序列化,读
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Users"));
// 反序列化回来一个List集合
//Object obj = ois.readObject();
//System.out.println(obj instanceof List); // true,得到返回的是List集合
List<User> userList = (List<User>)ois.readObject();
for(User u:userList){
System.out.println(u);
}
// 关闭
ois.close();
}
}
4.序列化版本号
(1)java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。(2)序列化版本号有什么用呢?
小鹏编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
胡浪编写了一个类:com.bjpowernode.java.bean.Student implements Serializable所以序列化版本号是用来区分类的!例如:类名相同,我们可以通过不通的序列化版本号来区分它;对于但也有缺点,对于同一个代码,我们更改了,必须重新编译才可以!
例如:对于上述的Student类,我们采用默认的序列化版本号,一年前我们实现了这个功能,有一个序列化版本号;一年后我们优化更改了代码,此时JVM就会认为这已经不是同一个类,必须重新进行编译,才能反序列化
(3)自动生成序列化版本号的好处和缺陷好处:不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。)
缺陷:这种自动生成的序列化版本号缺点是一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(4)总结:从需求来看
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。
这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
建议将序列化版本号手动的写出来。不建议自动生成
private static final long serialVersionUID = 1L;
java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。
(5)设置IDEA自动生成序列版本号
File--->Settings--->Code Style--->Inspections--->把下面这个打上对勾--->Apply--->OK
最终回到我们继承Serializable接口的类名上,alt+Enter即可自动生成序列版本号
🥅IO和Properties联合使用
(1)设计理念:把以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启;就可以拿到动态的信息。(2)类似于以上机制的这种文件被称为配置文件;并且当配置文件中的内容格式是:
key1=value
key2=value
的时候,我们把这种配置文件叫做属性配置文件,在属性配置文件当中#注释,如果key重复的话,value会自动覆盖。(3)java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。
这种以.properties结尾的文件在java中被称为:属性配置文件;其中Properties是专门存放属性配置文件内容的一个类(4)Properties是一个Map集合,key和value都是String类型。
package com.bjpowernode.java.io;
import java.io.FileReader;
import java.util.Properties;
public class IoProperiesTest02 {
public static void main(String[] args) throws Exception {
// 新建一个输入流对象
FileReader reader = new FileReader("temp.txt");
// 新建一个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); //admin
// 如果我们把admin改成root,不需要重新进行编译,就能直接运行得到root
System.out.println(pro.getProperty("password")); // 123
}
}
/*temp.txt文件里的内容
username=admin
password=123
*/
结束语
今天的分享就到这里啦!快快通过下方链接注册加入刷题大军吧!各种大厂面试真题在等你哦!
💬刷题神器,从基础到大厂面试题👉点击跳转刷题网站