I/O流:File、序列化、IO与Properties
1.File类
-
File和输出输入流没有关系,不能完成文件的读写操作。
-
File类代表文件和路径名的抽象表示形式。例如:
这是一个file对象:D:\study
这是一个file对象:D:\study\trmp.txt
注:file对象有可能对应目录,也有可能对应文件。
-
File类的常用方法:
判断文件是否存在:exists()
以文件形式创建:createNewFile()
以目录形式创建:mkdir()
创建多重目录:mkdirs()
获取文件父路径:getParent()
获取绝对路径:getAbsolutePath()
删除文件:delete()
public class FileTest { public static void main(String[] args) throws Exception { //创建File对象 File f1 = new File("D:\\study\\file"); //判断这个文件是否存在 System.out.println(f1.exists()); //存在时为true,不存在为false //如果D:\file不存在,则以文件形式创建 if(!f1.exists()){ //以文件形式新建 f1.createNewFile(); } if(!f1.exists()){ //以目录形式新建 f1.mkdir(); } File f2 = new File("D:\\study\\file\\a\\b\\c\\d"); //尝试多重目录是否可以创建 if(!f2.exists()){ f2.mkdirs(); } File f3 = new File("D:\\study\\file\\a"); //获取文件的父路径 System.out.println(f3.getParent()); //D:\study\file File parentFile = f3.getParentFile(); //getAbsolutePath()方法获取绝对路径 System.out.println("获取绝对路径" + parentFile.getAbsolutePath()); //获取绝对路径D:\study\file //boolean delete() 删除文件 } }
获取文件名:getName()
判断是否是一个目录:isDirectory()
判断是否是一个文件:isFile()
返回最后一次修改时间:lastModified()
获取文件大小:length()
public class FileTest02 { public static void main(String[] args) { File f1 = new File("D:\\study\\temp.txt"); //获取文件名 System.out.println("文件名:" + f1.getName() ); //文件名:temp.txt //判断是否是一个目录 System.out.println(f1.isDirectory()); //false //判断是否是一个文件 System.out.println(f1.isFile()); //true //获取文件最后一次修改时间 //f1.lastModified()返回的是long时间。 Date time = new Date(f1.lastModified()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); //2024-01-16 09:54:17 855 String strTime = sdf.format(time); System.out.println(strTime); //获取文件大小 System.out.println(f1.length() + "字节"); //6字节 } }
获取路径下所有文件:listFiles()
public class FileTest03 { public static void main(String[] args) { //File[] listFiles() //获取当前目录下所有子文件 File f = new File("D:\\study"); File[] files = f.listFiles(); for (File file: files) { System.out.println(file.getName()); /* * file * study * study2 * study4 * temp.txt * */ } } }
2.练习:将一个目录下的所有文件全部拷贝到另一个目录下
- 创建测试用的文件
public class CopyTest {
public static void main(String[] args) {
String fname = "C:\\Users\\Administrator\\Desktop\\copytest\\" ;
for (int i = 0; i < 10; i++) {
String f1name = fname + "第" + i + "目录";
File f1 = new File(f1name);
if(!f1.exists()){
f1.mkdir();
}
for (int j = 0; j <10; j++) {
File f2 = new File(f1name +"\\" + "第" + i + "目录—文件" + j +".txt" );
if(!f2.exists()){
try {
f2.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
- 正式拷贝实现
public class CopyTest03 {
public static void main(String[] args) {
//定义要拷贝的文件
File copyFile = new File("C:\\Users\\Administrator\\Desktop\\copytest\\");
//定义目标文件
File toFile = new File("D:\\copytest\\");
//调用方法
copyFile(copyFile,toFile);
}
//定义一个拷贝文件的路径copyFile,以及目标路径toFile
public static void copyFile(File copyFile,File toFile){
//获取目录下所有文件
File[] files = copyFile.listFiles();
if(files!=null){
for (File file:
files) {
//获取文件名备用
String filename = file.getName();
//如果文件是目录,这递归调用
if(file.isDirectory()){
String toFilesonpath = toFile.getAbsolutePath() + "\\" + filename;
File toFileson = new File(toFilesonpath);
if(!toFileson.exists()){
toFileson.mkdir();
}
copyFile(file.getAbsoluteFile(),toFileson);
}
//如果这是个文件,则copy
if(file.isFile()){
//获取copy文件路径
String copyFilePath = copyFile.getAbsolutePath() + "\\"+filename;
//获取目标文件路径
String toFilePath = toFile.getAbsolutePath() + "\\"+filename;
//新建字节输入流
FileInputStream fis = null;
//新建字节输出流
FileOutputStream fos = null;
try {
//创建输入流为copy文件
fis = new FileInputStream(copyFilePath);
//创建输出流的目标文件
fos = new FileOutputStream(toFilePath);
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) {
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);
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
}else {
return;
}
}
}
3.序列化和反序列化
-
Serialize序列化:java对象存储到文件中,将java对象的状态保存下来的过程。
负责序列化:ObjectOutputStream
-
DeSerialize反序列化:将硬盘上的数据重新恢复到内存当中,恢复成java对象。
负责反序列化:ObjectInputStream
-
参与序列化和反序列化的对象,必须实现Serializable接口,如果没有实现此接口,则会报错:java.io.NotSerializableException,即Student对象不支持序列化
注:源代码中,Serializable只是一个标志接口,接口中什么代码都没有。起到标识作用。java虚拟机看到此接口后,会为该类自动生成一个序列化版本号。
-
定义一个Student类,需要实现Serializable接口,才能够进行序列化和反序列化。
public class Student implements Serializable {
/*
* 在没有实现Serializable时,使用student进行序列化会报错
* java.io.NotSerializableException
* Student对象不支持序列化
* */
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 + '\'' +
'}';
}
}
- 序列化Student对象
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception {
// 创建Java对象
Student s = new Student(1111,"icecoffee");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
- 反序列化
//反序列化
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='icecoffee'}
ois.close();
}
}
- 在存储第二个对象时会报错,一次序列化多个对象方式:将对象放到集合当中,序列化集合。
//定义一个User对象
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 String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getNo() { return no; }
public void setNo(int no) { this.no = no; }
@Override
public String toString() {
return "User{" + "no=" + no + ", name='" + name + '\'' + '}';
}
}
//通过一个集合序列化多个对象。
public class ObjectOutputStreamTest {
public static void main(String[] args) throws Exception {
List<User> userList = new ArrayList<>();
userList.add(new User(1,"ice"));
userList.add(new User(2,"coffee"));
userList.add(new User(3,"is"));
userList.add(new User(4,"good"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
//序列化一个集合,集合对象中放了很多其他对象。
oos.writeObject(userList);
oos.flush();
oos.close();
}
}
//反序列化集合
public class ObjectInputStreamTest {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
List<User> userList = (List<User>)ois.readObject();
for (User user:
userList) {
System.out.println(user);
//User{no=1, name='ice'}
//User{no=2, name='coffee'}
//User{no=3, name='is'}
//User{no=4, name='good'}
}
ois.close();
}
}
-
不参与序列化关键字:transient(游离的)
-
例如在User对象加上transient字段后,再经过序列化和反序列化后,得到结果为:
User{no=1, name=‘null’}
User{no=2, name=‘null’}
User{no=3, name=‘null’}
User{no=4, name=‘null’}
//定义一个User对象
public class User implements Serializable {
private int no;
private transient String name; //表示这个字段不参与序列化
public User() { }
public User(int no, String name) {
this.no = no;
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getNo() { return no; }
public void setNo(int no) { this.no = no; }
@Override
public String toString() {
return "User{" + "no=" + no + ", name='" + name + '\'' + '}';
}
}
- 序列化版本号:在没有手动写出序列号时,java虚拟机会默认提供一个序列化版本号。
- 当Student这个类源码改动了,重新编译后生成了全新的字节码文件,class文件再次运行时(也就是没有序列化直接反序列化),java虚拟机生成的序列化版本号会发生相应的改变,导致反序列化不出来。
- 建议手动将序列化版本号写出来,不建议自动生成。
//定义一个User对象
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int no;
private transient String name; //表示这个字段不参与序列化
public User() { }
public User(int no, String name) {
this.no = no;
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getNo() { return no; }
public void setNo(int no) { this.no = no; }
@Override
public String toString() {
return "User{" + "no=" + no + ", name='" + name + '\'' + '}';
}
}
-
IDEA自动生成序列号:File→Settings→Editor→Inspections→搜索serializable→勾中Serializable class without 'serialVersionUID’→保存。
保存后在没有序列号的对象中,alt+Insert选择Create constant field ‘serialVersionUID’ in ‘XXX’,即可自动生成。
4.IO与Properties联合使用
- IO流:文件的读写。
- Properties:是一个Map集合,key和value都是String类型。
- 调用Properties对象的load方法将文件中的数据(输入流)加载到Map集合当中。
//IO+Properties联合应用
public class IoPropertiesTest01 {
public static void main(String[] args) throws Exception {
//是一个Map集合,key和value都是String类型。
//要将userinfo文件中数据加载到Properties对象当中
//新建一个输入流对象
FileReader reader = new FileReader("D:\\study\\study4\\study4\\userinfo");
/*
文件中的数据:
username=damin
password=123
注:这里如果key重复了,value覆盖。
*/
//新建一个Map集合
Properties pro = new Properties();
//调用Properties对象的load方法将文件中的数据加载到Map集合当中。
pro.load(reader); //文件中数据顺着管道加载到Map集合中。等号左边做key右边做value。
//通过key获取value
System.out.println(pro.getProperty("username")); //admin
System.out.println(pro.get("password")); //123
}
}
- 经常需要修改的配置信息,可以使用这个方法。例如数据库的登录账号、密码等。
——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频