Java进阶第八章——I/O流:File、序列化、IO与Properties
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\fileFile 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 855String 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,以及目标路径toFilepublic 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);}//如果这是个文件,则copyif(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; }@Overridepublic 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; }@Overridepublic 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; }@Overridepublic 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; }@Overridepublic 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=daminpassword=123注:这里如果key重复了,value覆盖。*///新建一个Map集合Properties pro = new Properties();//调用Properties对象的load方法将文件中的数据加载到Map集合当中。pro.load(reader); //文件中数据顺着管道加载到Map集合中。等号左边做key右边做value。//通过key获取valueSystem.out.println(pro.getProperty("username")); //adminSystem.out.println(pro.get("password")); //123}
}
- 经常需要修改的配置信息,可以使用这个方法。例如数据库的登录账号、密码等。
——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频