Java SE核心API(9) —— 文件数据IO操作(文件流、缓冲流、对象流)

版权声明:本文为博主原创文章,遵循GPL开源协议精神,转载请注明出处。 https://blog.csdn.net/Robot__Man/article/details/79492227

一、IS与OS

1.1 输入流与输出流

  流根据方向不同分为输入流与输出流,参照点为当前程序。输入流用来读取数据,输出流用来写出数据。

1.2 节点流与处理流

  按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
  节点流:可以从或向一个特定的地方(节点)读写数据。处理流:是对一个已存在的流的链接和封装,通过所封装的流的功能调用实现数据读写。
  处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
  通常节点流也称为低级流,处理流也称为高级流或过滤流。
  低级流,是真实负责读写数据的流。读写操作中,必须要有低级流,数据源明确。读写可以没有高级流,高级流也不能独立存在,必须用于处理其他流,处理其他流的目的是简化读写数据中的操作。

1.3 IS和OS常用的方法

1.3.1 InputStream常用方法

  InputStream是所有字节输入流的父类,其定义了基础的读取方法,常用的方法如下:
  int read(),读取一个字节,以int形式返回,该int值的“低八位”有效,若返回值为-1,则表示EOF。
  int read(byte[] d),尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量。

1.3.2 OutputStream常用方法

  OutputStream是所有字节输出流的父类,其定义了基础的写出方法,常用的方法如下:
  void write(int d),写出一个字节,写的是给定的int的“低八位”。
  void write(byte[] d),将给定的字节数组中的所有字节全部写出。


二、文件流

2.1 文件输出流(FOS)

package day07;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * java.io.FileOutputStream
 * 文件输出流,是一个低级流,作用是向文件中写出字节
 * 
 * @author xxx
 *
 */
public class FOSDemo {
    public static void main(String[] args) throws IOException {
        /*
         * 默认创建的FOS是覆盖写操作
         * FOS会先将文件数据(若有数据)全部删除,然后再开始写。
         */
        FileOutputStream fos = new FileOutputStream("fos.txt");

        String str = "Hello world!";

        /*
         * String -> byte[]
         * 
         * byte getBytes()
         * 将当前字符串按照系统默认字符集转换为一组字节
         * 
         * byte getBytes(String csn)
         * 按照给定的字符集将当前字符串转换为一组字节
         */
        byte[] data = str.getBytes("utf-8");

        fos.write(data);

        System.out.println("写出完毕!");
        fos.close();
    }
}
package day07;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件输出流
 * 追加写操作
 * @author xxx
 *
 */
public class FOSDemo2 {
    public static void main(String[] args) throws IOException {
        /*
         * 在创建FOS时,若指定第二个参数,并且该值为true时,则是追加写
         * 操作,那么本次通过FOS写出的内容会被追加到该文件末尾。
         */
        FileOutputStream fos = new FileOutputStream("fos.txt",true);

        fos.write("Hello java!".getBytes("utf-8"));
        System.out.println("写出完毕!");
        fos.close();
    }
}

2.2 文件输入流(FIS)

package day07;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * java.io.FileInputStream
 * 文件输入流,是一个低级流,用于从文件中读取字节
 * @author xxx
 *
 */
public class FISDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("fos.txt");

        byte[] data = new byte[100];        
        int len = fis.read(data);
        String str = new String(data, 0, len, "utf-8");
        System.out.println(str);

        fis.close();
    }
}

2.3 使用文件流复制文件

package day07;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 使用文件流复制文件
 * @author xxx
 *
 */
public class CopyDemo1 {
    public static void main(String[] args) throws IOException {
        /*
         * 使用文件输入流读取源文件,再使用文件输出流向目标文件中写。
         * 顺序从原文件中读取每个字节并写入到目标文件即可完成复制。
         */
        FileInputStream src = new FileInputStream("fos.txt");
        FileOutputStream des = new FileOutputStream("cp_fos.txt");

        byte[] data = new byte[1024 * 10];//10K
        int len = -1;

        long start = System.currentTimeMillis();
        while ((len = src.read(data)) != -1) {
            des.write(data, 0, len);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完毕!耗时:"+(end - start)+"ms");
        src.close();
        des.close();
    }
}

三、缓冲流

3.1 缓冲输入流(BIS)与缓冲输出流(BOS)

  在读取数据时若以字节为单位读取数据,会导致读取次数过于频繁从而大大的降低读取效率。为此我们可以通过提高一次读取的字节数量减少读写次数来提高读取的效率。
  BufferedInputStream是缓冲字节输入流。其内部维护着一个缓冲区(字节数组),使用该流在读取一个字节时,该流会尽可能多的一次性读取若干字节并存入缓冲区,然后逐一的将字节返回,直到缓冲区中的数据被全部读取完毕,会再次读取若干字节从而反复。这样就减少了读取的次数,从而提高了读取效率。
  BIS是一个处理流,该流为我们提供了缓冲功能。

  在向硬件设备做写出操作时,增大写出次数无疑会降低写出效率,为此我们可以使用缓冲输出流来一次性批量写出若干数据减少写出次数来提高写出效率。
  BufferedOutputStream缓冲输出流内部维护着一个缓冲区,每当我们向该流写数据时,都会先将数据存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。

package day07;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 缓冲流
 * java.io.BufferedInputStream
 * java.io.BufferedOutputStream
 * 缓冲字节输入输出流是一对高级流,使用它们可以加快读写效率
 * 
 * 高级流可以处理其他流,但是无论添加了多少高级流,最底下都要有低级流,因为低级流
 * 是真实读写数据的流,高级流都是处理数据的。
 * 高级流处理其他流就形成了流的链接,并且有效的组合不同的高级流可以得到叠加的效果。
 * @author xxx
 *
 */
public class CopyDemo2 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("m.mp4");
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("m_cp3.mp4");
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        int d = -1;

        while ((d = bis.read()) != -1) {
            bos.write(d);
        }
        System.out.println("复制完毕!");
        bis.close();
        bos.close();
    }
}

3.2 BOS的flush方法

  使用缓冲输出流可以提高写出效率,但是这也存在着一个问题,就是写出数据缺乏即时性。有时我们需要在执行完某些写出操作后,就希望将这些数据确实写出,而非在缓冲区中保存直到缓冲区满后才写出。这时我们可以使用缓冲流的一个方法flush。
  void flush(),清空缓冲区,将缓冲区中的数据强制写出。

package day07;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 缓冲输出流写出数据的缓冲区问题
 * @author xxx
 *
 */
public class BOSDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bos.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //所有字节被存入缓冲区,等待一次性写出
        bos.write("Hello world!".getBytes("utf-8"));
        //强制将缓冲区中的字节一次性写出
        //bos.flush();
        System.out.println("写出完毕!");
        //关闭流之前,缓冲输出流会将缓冲区内容一次性写出
        bos.close();
    }
}

四、对象流

4.1 对象序列化

  对象是存在于内存中的,有时候我们需要将对象保存到硬盘上,又有时候我们需要将对象传输到另一台计算机上等等这样的操作。这时我们需要将对象装换为一个字节序列,而这个过程就称为对象序列化。相反,我们有这样一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。

4.2 使用OOS实现对象序列化

  ObjectOutputStream是用来对对象进行序列化的输出流,其实现对象序列化的方法为:
  void writeObject(Object o),该方法可以将给定的对象转换为一个字节序列后写出。

4.3 使用OIS实现对象反序列化

  ObjectInputStream是用来对对象进行反序列化的输入流,其实现对象反序列化的方法为:
  Object readObject(),该方法可以从流中读取字节并转换为对应的对象。

package day07;
/**
 * 该类用于测试作为对象流读写对象使用
 * 
 * 当一个类需要被对象流读写,那么该类必须实现java.io.Serializable接口
 * @author xxx
 *
 */

import java.io.Serializable;
import java.util.List;

public class Person implements Serializable{
    /**
     * 当一个类实现了Serializable接口后,应当添加一个常量:serialVersionUID
     * 该常量为当前类的序列化版本号,若不定义,系统会根据当前类的结构生成,但是只要类的
     * 结构发生改变,版本号也会相应发生改变。
     * 
     * 版本号影响着反序列化的结果。即:
     * 当OIS对一个对象进行反序列化时,会检查该对象与类的版本是否一致。若一致:反序列化成功,
     * 但是若该对象与类的结构不一致时,则采用兼容模式,能还原的属性都还原。
     * 若不一致:反序列化直接抛出版本不一致异常。
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private String gender;
    /*
     * transient关键字用来修饰属性
     * 当被修饰后,该类实例在使用OOS进行对象序列化时,该属性值被忽略。
     * 从而达到对象“廋身”的目的
     */
    private transient List<String> otherInfo;

    public Person() {

    }

    public Person(String name, int age, String gender, List<String> otherInfo) {
        super();
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.otherInfo = otherInfo;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public List<String> getOtherInfo() {
        return otherInfo;
    }

    public void setOtherInfo(List<String> otherInfo) {
        this.otherInfo = otherInfo;
    }

    public String toString() {
        return name+","+age+","+gender+","+otherInfo;
    }
}
package day07;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 对象流
 * 对象流是一种高级流,作用是方便读写java中的对象。
 * java.io.ObjectOutputStream
 * 对象输出流,可以将给定的对象转换为一组字节后输出。
 * @author xxx
 *
 */
public class OOSDemo {
    public static void main(String[] args) throws IOException {
        Person p = new Person();
        p.setName("苍老师");
        p.setAge(20);
        p.setGender("女");
        List<String> otherInfo = new ArrayList<String>();

        otherInfo.add("是一名演员");
        otherInfo.add("爱好是写大字");
        otherInfo.add("促进中日文化交流");
        otherInfo.add("广大男性同胞的启蒙老师");
        p.setOtherInfo(otherInfo);

        FileOutputStream fos = new FileOutputStream("person.obj");
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        /*
         * ObjectOutputStream的writeObject方法可以将给定对象转换为一组字节后写出。
         * 这些字节比该对象实际内容要大,因为除了数据外还有结构等描述信息。
         * 
         * 下面的代码实际上经历了两个操作:
         * 1、oos将Person对象转换为一组字节
         *   将一个对象转换为一组字节的过程称为:对象序列化
         *   
         * 2、再通过fos将这组字节写入到硬盘
         *   将该对象转换的字节写入到硬盘做长久保存的过程称为:对象持久化
         */
        oos.writeObject(p);
        System.out.println("写出对象完毕!");
        oos.close();
    }
}
package day07;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * java.io.ObjectInputStream
 * 对象输入流,作用是可以进行对象反序列化,读取一组字节并还原为对象。
 * OIS读取的字节必须是由OOS将对象序列化得到的字节,否则会抛出异常。
 * @author xxx
 *
 */
public class OISDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream("person.obj");
        ObjectInputStream ois = new ObjectInputStream(fis);

        //对象反序列化
        Person p = (Person)ois.readObject();

        System.out.println(p);
        ois.close();
    }
}

猜你喜欢

转载自blog.csdn.net/Robot__Man/article/details/79492227