zoukankan      html  css  js  c++  java
  • 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天左右。后面去深入研究一下历史爆出的一些反序列化漏洞。

  • 相关阅读:
    CSU 1333 Funny Car Racing
    FZU 2195 检查站点
    FZU 2193 So Hard
    ZOJ 1655 FZU 1125 Transport Goods
    zoj 2750 Idiomatic Phrases Game
    hdu 1874 畅通工程续
    hdu 2489 Minimal Ratio Tree
    hdu 3398 String
    洛谷 P2158 [SDOI2008]仪仗队 解题报告
    POJ 1958 Strange Towers of Hanoi 解题报告
  • 原文地址:https://www.cnblogs.com/nice0e3/p/13458990.html
Copyright © 2011-2022 走看看