• Java学习之文件io流篇


    Java学习之文件io流篇

    0x00 前言

    在平时的一些开发中,普遍都会让脚本运行的时候,去存储一些脚本运行结果的数据,例如开发一个爬虫,这时候我们爬取下来的内容,就需要存储到本地,那么这时候就会用到一些操作文件的类。

    0x01 File 类

    File类主要用于文件和目录创建、查找、删除等操作的。

    先来查看他的构造方法

    public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
    public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。
    public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例。
    

    常用方法:

    public String getAbsolutePath() :返回此File的绝对路径名字符串。
    public String getPath() :将此File转换为路径名字符串。
    public String getName() :返回由此File表示的文件或目录的名称。
    public long length() :返回由此File表示的文件的长度。
    

    代码实例:

    public static void main(String[] args) {
            String Pathname = "a.txt";
            File abc = new File(Pathname);
            System.out.println(abc.getAbsolutePath());
            String Pathname1 = "a\a.txt";
            File abc1 = new File(Pathname1);
            System.out.println(abc1.getAbsolutePath());
            System.out.println(abc1.getPath());
            System.out.println(abc1.length());
        }
    
    

    判断方法:

    public boolean exists() :此File表示的文件或目录是否实际存在。
    public boolean isDirectory() :此File表示的是否为目录。
    public boolean isFile() :此File表示的是否为文件。
    
        public static void main(String[] args) {
            String Pathname = "a.txt";
            File abc = new File(Pathname);
            boolean a = abc.exists();
            System.out.println(a);
            System.out.println(abc.isFile());
            System.out.println(abc.isDirectory());
        }
    

    增删功能方法

    public boolean createNewFile() :当前仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    public boolean delete() :删除由此File表示的文件或目录。
    public boolean mkdir() :创建由此File表示的目录。
    public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
    
    public static void main(String[] args) throws IOException {
            String Pathname = "a.txt";
            File abc = new File(Pathname);
            boolean file = abc.createNewFile();
            System.out.println(file+"
    "+abc.getAbsolutePath());
    
    
           File abc1 = new File("abc");
           abc1.mkdir();
    
           File abc2 = new File("abc\abc");
           abc2.mkdirs();
        }
    

    如果是对目录进行删除,删除的目录必须为空才能进行删除。

    目录遍历

    在file里面给我们提供了,可以直接获取file目录下面所有子文件或目录。

    public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
    public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
    

    代码实例:

    public static void main(String[] args) throws IOException {
            String Pathname = "../";
            File abc = new File(Pathname);
            String[] name = abc.list();
            for (String s : name) {
                System.out.println(s);
            }
        }
    
    

    使用list方法获取所有文件,然后使用增强for循环进行遍历。

    0x02 IO流概述

    IO流概述

    java里面的io流指的是对一些文件内容做一个输入输出的作用。也就是input和output,对文件进行读取和输入数据的操作。

    input:把数据从其他设备上读取到内存的流

    output:把数据从内存写出到其他设备的流。

    字节流

    在计算机里面所有的文本数据包括图片、文本、视频这些在存储的时候,都是以二进制的形式进行村粗的。字节流是以一个字节为单位,读写数据的流。

    字符流

    以一个字符为单位,读写数据的流

    0x03 字节流输出流

    OutputStream抽象类是字节输入流的超类,他定义了几个共性的方法。

    public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
    public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
    public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
    public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输
    出到此输出流。
    public abstract void write(int b) :将指定的字节输出流。
    

    在操作完成后,必须使用close方法将资源释放。

    FileOutputStream 类

    FileOutputStream类是文件输出流,用于将数据些出到文件当中。

    查看构造方法:

    public FileOutputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件。
    public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件。
    

    如果创建一个io流的对象,必须传入文件的路径,,如果没有该文件就会创建该文件,如果有就会清空原本有的数据。

    代码:

    public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileOutputStream FileoutDate = new FileOutputStream(file);
            FileoutDate.write(97);
            FileoutDate.close();
        }
    

    这里写进入一个97,但是打开文件会发现写进入的变成了a,这是因为我们这里是以字节进行写入的,,而97的ascii转换为字符后,就是a这个字符。

    这里还可以指定写出的数据长度。

    public class FileOutput {
        public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileOutputStream FileoutDate = new FileOutputStream(file);
            byte[] b = "abce3".getBytes();
            FileoutDate.write(b,2,2);
            FileoutDate.close();
        }
        }
    

    这里要先获取字符的字节类型数据,使用write写入,从第二位开始索引,写入2个字节。

    在程序开发中,有些数据可能没法一次执行获取所有结果,这时候我们如果以上面的方式来循环写入运行结果的话,每次循环就都会被清空一次,只获得最后一次的执行结果。
    那么这时候我们就可以使用到追加,把它追加进入,而不是直接覆盖重写。

    public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的
    文件。
    public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。
    

    代码:

     public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileOutputStream FileoutDate = new FileOutputStream(file,true);
            byte[] b = "abce3".getBytes();
            FileoutDate.write(b,2,2);
            FileoutDate.close();
        }
    

    这几行代码和前面的相同,只是在FileOutputStream 构造方法里面传入一个ture,表示使用追加模式,该模式默认为false。

    0x04 字节输入流

    InputStream抽象类是字节输入流的超类。可以读取字节数据到内存中。

    共性方法:

    public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
    public abstract int read() : 从输入流读取数据的下一个字节。
    public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
    

    FileInputStream 类

    FileInputStream是文件输入流,从文件中读取字节到内存中。

    构造方法:

    FileInputStream(File file) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系
    统中的 File对象 file命名。
    FileInputStream(String name) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件
    系统中的路径名 name命名。
    

    代码:

     public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileInputStream fileinputdata = new FileInputStream(file);
            int read = fileinputdata.read();
            System.out.println((char) read);
            read = fileinputdata.read();
            System.out.println((char) read);
            read = fileinputdata.read();
            System.out.println(read);
            fileinputdata.close();
        }
    

    使用read方法读取完后,地址会往后推一位,知道读取到没有,会返回-1。

    以上的方法都是读取单个字节,我们可以定义一个字节类型的数值,然后让他每次读取我们指定的长度。

    代码:

        public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileInputStream fileinputdata = new FileInputStream(file);
            int len;
            byte[] b = new byte[2];
            while ((len=fileinputdata.read(b))!=-1){
                System.out.println(new String(b,0,len));
            }
            fileinputdata.close();
        }
    

    这里定义了b变量用来接收每次读取的数据产的长度,然后定义一个len变量,接收每次读取的数据,这里可以直接把赋值放在循环条件里面,如果赋值的变量不等于-1,就一直循环,知道读取到-1,停止循环,前面说到如果没有数据读取会输出返回一个-1,结束循环。

    0x05 字符流

    在字节读写的时候,一些中文字符读写可能会显示乱码。因为一个中文字符可能占用多个字节。所以在一些读写的是字符数据的话,可以使用字符流来处理该数据。

    字符输入流

    Reader抽象类是表示用于读取字符流的超类,可以读取字符信息到内存中。

    共性方法:

    public void close() :关闭此流并释放与此流相关联的任何系统资源。
    public int read() : 从输入流读取一个字符。
    public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。
    

    FileReader 类

    构造方法:

    FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
    FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称。
    

    创建一个流对象,必须传入一个文件路径。

    构造方法定义代码:

     public static void main(String[] args) throws IOException {
        File file = new File("a.txt");
            FileReader filereader = new FileReader(file);
        }
    或者:
    public static void main(String[] args) throws IOException {
            
            FileReader filereader = new FileReader("a.txt");
        }
    

    读取单个字符数据:

    public static void main(String[] args) throws IOException {
    
            FileReader filereader = new FileReader("a.txt");
            int b;
            while ((b = filereader.read())!=-1){
                System.out.println((char)b);
            }
        }
    

    读取多个字符数据:

     public static void main(String[] args) throws IOException {
    
            FileReader filereader = new FileReader("a.txt");
            int b;
            char[] buf = new char[2];
            while ((b = filereader.read(buf))!=-1){
                System.out.println(new String(buf));
            }
        }
    

    字符输出流

    Writer抽象类是字符输出流的超累,将知道的字符信息写出到指定地方。

    共性方法:

    void write(int c) 写入单个字符。
    void write(char[] cbuf) 写入字符数组。
    abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len
    写的字符个数。
    void write(String str) 写入字符串。
    void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个
    数。
    void flush() 刷新该流的缓冲。
    void close() 关闭此流,但要先刷新它。
    

    FileWriter 类

    FileWriter类是写出字符到文件中的一个类,,构造时候使用默认的字符编码和默认的字节缓冲区。

    构造方法:

    FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。
    FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称。
    

    写出数据代码:

    public static void main(String[] args) throws IOException {
    
            FileWriter filewriter = new FileWriter("a.txt");
            filewriter.write("二狗");
            filewriter.write(97);
            filewriter.write(156);
            filewriter.flush();
            filewriter.close();
        }
    

    这里的代码,如果不写flush方法,也不写close是没法写入数据的。
    来看看这2个方法的区别:

    flush :刷新缓冲区,流对象可以继续使用。
    close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
    

    0x06 缓存流

    字节流和字符流每次读写都会访问硬盘,当读写频率增加其访问效率不高
    而使用缓存流读取时会将大量数据先读取到缓存中,以后每次读取先访问缓存,直到缓存读取完毕再到硬盘读取,缓存流写入数据也是一样,先将数据写入到缓存区,直到缓存区达到一定的量,才把这些数据一起写道硬盘中去,这样减少了IO操作。

    字节缓冲流: BufferedInputStream , BufferedOutputStream
    字符缓冲流: BufferedReader , BufferedWriter
    

    字节缓存输出流

    构造方法:

    public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
    public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
    
    

    代码:

        public static void main(String[] args) throws IOException {
    
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("A.txt"));
    
            int b ;
            while ((b = bis.read())!=-1){
                bos.write(b);
            }
        }
    

    上面的代码使用了字节缓存流的方式来进行了一个文件的复制粘贴,如果是使用字节输出流的方式是很慢的。而使用字节缓存流的方式读写的速度会很快。

    如果我们想让读写更快的话,可以定义一个每次读写的长度,让他每次读写固定到的长度。

        public static void main(String[] args) throws IOException {
    
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("A.txt"));
    
            int b ;
            byte[] bytes = new byte[8*1024];
            while ((b = bis.read(bytes))!=-1){
                bos.write(bytes,0,b);
            }
        }
    

    每次读写8*1024个字节,减少文件读写的时间。

    字符缓存流

    构造方法:

    public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
    public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。
    

    代码:

    public static void main(String[] args) throws IOException {
            BufferedReader bis = new BufferedReader(new FileReader("a.txt"));
                String b = null ;
                while ((b = bis.readLine())!=null){
                    System.out.println(b);
                }
                bis.close();
        }
    

    使用readline读取当行数据。

    0x07 序列化与反序列化

    概述

    java提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节包含对象的数据、对象的类型、对象的存储属性。字节序列写出到文件后,相当于可以持久报错了一个对象信息,这过程叫做序列化。

    而反过来,将存储在文件的字节序列从文件中读取出来,重构对象,重新用来创建对象,这步骤叫做反序列化。

    总结:简单来讲就是将一个对象,写入文件当中,而反序列化就是将写入文件的对象,读取出来。

    ObjectOutputStream 类

    ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。也就是对对象进行序列化的一个类。

    构造方法:

    public ObjectOutputStream(OutputStream out)
    

    注意事项:

    1.使用该类必须实现Serializable 接口,Serializable是一个标记接口,不实现此接口的类将不会时任何状态序列化或者是放序列化,会抛出NotSerializableException的异常。

    2.该类的所有的属性必须时可以序列化的,如果有一个属性不需要序列化的,测该属性必须标明时瞬态的,使用transient修饰符。

    代码:

    创建一个类:
    
    public class Method implements Serializable {
        public String name;
        public String address;
        public transient int age;
        public void method(){
            System.out.println(name +address);
        }
    
    }
    
    main方法:
    
    public static void main(String[] args) {
            Method e = new Method();
            e.name = "zhangsan";
            e.address = "beiqinglu";
            e.age = 20;
    
            try {
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.txt"));
                out.writeObject(e);
                out.close();
                System.out.println("ok");
    
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }finally {
            }
        }
    

    查看a.txt文件可以看到显示的字符都是乱码,这是因为将对象序列化了,写进入的是一个对象,而不是一些字符。

    ObjectInputStream类

    ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象

    构造方法:

    public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。
    

    反序列化方法1

    如果我们需要对序列化的对象,进行反序列化,将他从文件读取出来还原成对象那么我们这里调用ObjectOutputStream的readobject方法,读取一个对象。

    public final Object readObject () : 读取一个对象。
    

    代码:

     public static void main(String[] args) {
            Method e = null;
            try {
                FileInputStream fis = new FileInputStream("a.txt");
                ObjectInputStream ois = new ObjectInputStream(fis);
                 e = (Method) ois.readObject();
                ois.close();
                fis.close();
            } catch (IOException | ClassNotFoundException ioException) {
                ioException.printStackTrace();
            }
            System.out.println("name="+e.name);
            System.out.println("address ="+e.address);
            System.out.println("age="+e.age);
        }
    

    反序列化方法2

    当jvm方序列化对象的时候,能找到class属性,,但是class文件在序列化之后修改了,那么反序列化操作肯定是失败的,会抛出InvalidClassException。

    抛出InvalidClassException的一次原因有3种:

    1.该类的序列版本号与从流中读取的类描述符的版本号不匹配

    2.该类包含未知数据类型

    3.该类没有可访问的无参数构造方法

    Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序
    列化的对象和对应类是否版本匹配。

    public class Employee implements java.io.Serializable {
    // 加入序列版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public String address;
    // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
    public int eid;
    public void addressCheck() {
    System.out.println("Address check : " + name + " ‐‐ " + address);
    }
    }
    

    0x08 总结

    
    File类:用于创建删除文件与文件夹。
    
    FileoutputStream:字节输出流,用于将字节写出到文件中。
    
    FileinputStream:字节输出流,用于读取文件中的字节。
    
    FileReader类是表示用于读取字符流,可以读取字符信息到内存中。
    
    FileWriter类是写出字符到文件中的一个类,,构造时候使用默认的字符编码和默认的字节缓冲区。
    
    字节缓冲流: BufferedInputStream , BufferedOutputStream
    
    字符缓冲流: BufferedReader , BufferedWriter
    
    

    0x09 结尾

    这篇文章的内容比较多,在后面也写了个简单的小总结,这篇也写了我2天左右。后面去深入研究一下历史爆出的一些反序列化漏洞。

  • 相关阅读:
    乘坐飞机时,有什么事情是机长和机上工作人员不想让乘客知道的?
    北京有哪些被废弃的地方值得一看?推荐理由是什么?
    在读硕士或博士是如何养活自己的?
    怎样有效提高记忆力?
    北京值得去的、不为人知的景点(或展览馆、美术馆、公园)有哪些?
    你收藏了哪些藏品?
    如何抓到入侵网站的黑客?
    中国姓氏的区域性?
    python之入门,你好,中国
    Eclipse 内置浏览器
  • 原文地址:https://www.cnblogs.com/nice0e3/p/13458990.html
Copyright © 2020-2023  润新知