欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

IO流【字节流、字符流、缓冲流、转换流、序列化流】

程序员文章站 2024-02-18 15:20:40
...

1、IO流

IO流就是把数据以流的形式在其它设备和内存中传输,就像水流;

根据流动方向课分为:

  • 输入流 :把数据从其他设备上读取到内存中的流。
  • 输出流 :把数据从内存 中写出到其他设备上的流

IO流【字节流、字符流、缓冲流、转换流、序列化流】

2、字节流

所有的文件都是以二进制存储在计算机上的,都是一个字节一个字节的

字节输出流【OutPutStream】

java.io.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) :将指定的字节输出流。

FileOutputStream类

一个参数的构造方法传递一个文件路径字符串或一个File对象,此构造方法是当文件存在时,会重写里面的内容,每次执行都是一个新的文件,不会追加写在里面,若要追加写,则应该用另一个构造方法。

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

这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据

public class Demo1 {
    public static void main(String[] args) throws IOException {
        //字节输出流,此构造方法数据不追加
        FileOutputStream stream = new FileOutputStream(new File("E:\\aaa\\1.txt"));

        //一次写一个字节
        stream.write(97);
        stream.write(98);
        stream.write(99); //运行结果为:abc

        //写一个字节数组
        stream.write("你好".getBytes());//运行结果为:你好

        //写入一个指定数组的指定索引处
        stream.write("你好Hello".getBytes(),3,8);//运行结果为:Hello

        stream.close();
    }
}

追加写:

public class Demo2 {
    public static void main(String[] args) throws IOException {
        //字节输出流,此构造方法数据追加
        FileOutputStream stream = new FileOutputStream(new File("E:\\aaa\\1.txt"),true);

        //换行
        stream.write("\r\n".getBytes());
        //一次写一个字节
        stream.write(97);
        stream.write(98);
        stream.write(99); 

        //写一个字节数组
        stream.write("你好".getBytes());

        //写入一个指定数组的指定索引处
        stream.write("你好Hello".getBytes(),3,8);

        stream.close();
    }
}

Windows系统里,换行符号是\r\n

字节输入流【InputStream】

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

FileInputStream类

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

读取字节read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1

public class Demo3 {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(new File("E://aaa//1.txt"));

        //读一个字节
//        int read = fileInputStream.read();
//        System.out.println((char) read);
//
//        while((read = fileInputStream.read()) != -1){
//            System.out.println((char)read);
//        }

        //数组读取
        byte[] b = new byte[1024];
        int len;
        while((len=fileInputStream.read(b)) != -1){
            System.out.println(new String(b,0,len));
        }

//        fileInputStream.read(b);
//        System.out.println(new String(b));
        fileInputStream.close();
    }
}

练习:文件复制

将E:\aaa\bb\1.jpg文件复制到E:\aaa\cc\2.jpg下

public class Demo4 {
    public static void main(String[] args) throws IOException {
        long l = System.currentTimeMillis();
        FileInputStream fileInputStream = new FileInputStream(new File("E:\\aaa\\bb\\1.jpg"));
        FileOutputStream fileOutputStream = new FileOutputStream(new File("E:\\aaa\\cc\\2.jpg"));

        byte[] b = new byte[1024];
        int len;
        while ((len = fileInputStream.read(b)) != -1){
            fileOutputStream.write(b,0,len);
        }

        fileInputStream.close();
        fileInputStream.close();
        long l2 = System.currentTimeMillis();
        System.out.println(l2-l);
    }
}

流的关闭原则:先开后关,后开先关

3、字符流

字符输入流【Reader】

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

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

FileReader类

java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区

构造方法

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

一个一个字符读取;

public class Demo5 {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader(new File("E:\\aaa\\1.txt"));

        int read = fileReader.read();
        System.out.println((char) read);
        while((read = fileReader.read()) != -1){
            System.out.println((char) read);
        }
        fileReader.close();
    }
}

字符输出流【Writer】

java.io.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()刷新该流的缓冲。

FileWriter类

public class Demo6 {
    public static void main(String[] args) throws IOException {
        FileWriter fileWReader = new FileWriter(new File("E:\\aaa\\2.txt"));

        fileWReader.write("你好java");

        fileWReader.flush();
        //fileWReader.close();
    }
}

注意:

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了

4、Properties类

方法:

  • public Object setProperty(String key, String value) : 保存一对属性。

  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。

  • public Set<String> stringPropertyNames() :所有键的名称的集合

  • public void load(InputStream inStream): 从字节输入流中读取键值对

public class Demo7 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.load(new FileReader("E:\\aaa\\2.txt"));

        Set<String> key = properties.stringPropertyNames();
        for (String s : key) {
            System.out.println(s+"::"+ properties.getProperty(s));
        }
    }
}
/*
运行结果:
334::你好
file::java
*/

5、缓冲流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率,缓冲流读写方法与基本的流是一致的。

字节缓冲流

public class Demo8 {
    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("E:\\aaa\\bb\\1.zip"));
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("E:\\aaa\\cc\\2.zip"));
                ){
            int b;
            byte[] bytes = new byte[1024*3];
            while((b = bufferedInputStream.read(bytes)) != -1){
                bufferedOutputStream.write(bytes,0,b);
            }

        }catch (IOException e){
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");
    }
}
//4032毫秒

通过复制一个大文件发现,缓冲流的效率远远高于普通的字节流

字符缓冲流

public class Demo9 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new FileReader("E:\\aaa\\1.txt"));

        String s = bufferedReader.readLine();
        String s2 = bufferedReader.readLine();
        String s3 = bufferedReader.readLine();

        System.out.println(s);
        System.out.println(s2);
        System.out.println(s3);
        bufferedReader.close();
    }
}

练习:文本排序

将下图文件中的内容按序号排序:

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo10 {
    public static void main(String[] args) throws IOException{
        BufferedReader bufferedReader = new BufferedReader(new FileReader("E:\\aaa\\1.txt"));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("E:\\aaa\\3.txt"));
        Map<String ,String> map = new HashMap<>();

        String s = null;
        while((s = bufferedReader.readLine()) != null){
            String[] split = s.split("\\.");
            map.put(split[0],split[1]);
        }
        Set<String> strings = map.keySet();
        for (String string : strings) {
            System.out.println(string+"::"+map.get(string));
        }
        //


        for (int i = 1; i <= map.size(); i++) {
            String st = String.valueOf(i);

            String s1 = map.get(st);

            bufferedWriter.write(st+"."+s1);
            bufferedWriter.newLine();
        }

        bufferedWriter.close();
        bufferedReader.close();
    }
}

6、转换流

计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码

常见的编码集:GBKASCII字符集utf-8ISO-8859-1字符集

如果文件的编码和解码的编码集不同就会出现乱码。

InputStreamReader类&OutputStreamWriter类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。
转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。
默认编码集为:utf-8

import java.io.*;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        //OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("E:\\aaa\\6.text"),"Utf-8");
        InputStreamReader ir = new InputStreamReader(new FileInputStream("E:\\aaa\\6.text"),"GBK");

        int r ;
        while((r = ir.read()) != -1){
            System.out.print((char) r);
        }
        // ow.write("你好java");
        //ow.close();
    }
}

7、序列化

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象

ObjectOutputStream类&ObjectInputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

被序列化或反序列化的对象的类需要实现Serializable 接口,否则就会出现序列化或反序列化异常

Person类:

import java.io.Serializable;

public class Person implements Serializable{
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class Demo1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Person p = null//new Person("张三");

//        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("E:\\aaa\\a.txt"));
//
//        oo.writeObject(p);
//
//        oo.close();

        ObjectInputStream oi = new ObjectInputStream(new FileInputStream("E:\\aaa\\a.txt"));

        Object o = oi.readObject();
        System.out.println(o.toString());

    }
}