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\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’→保存。
    Java进阶第八章——I/O流:File、序列化、IO与Properties_第1张图片
    Java进阶第八章——I/O流:File、序列化、IO与Properties_第2张图片

    保存后在没有序列号的对象中,alt+Insert选择Create constant field ‘serialVersionUID’ in ‘XXX’,即可自动生成。
    Java进阶第八章——I/O流:File、序列化、IO与Properties_第3张图片

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零基础教程视频

你可能感兴趣的:(咖啡ice的Java学习记录,java,开发语言)