Java 基础 – IO知识体系

往期推荐

IO流是什么

  • Java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。
  • 在Java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)
  • 通过流的形式允许Java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。

IO流的分类

按照流的流向划分

  • 输入流(InputStream): 只能从中读取数据,而不能向其写入数据。
  • 输出流(OutputStream):只能向其写入数据,而不能向其读取数据。

??注意:输入流和输出流是相对于程序而言的。

按照处理数据单位划分

  • 字节流(InputStream、OutputStream):数据流中最小的数据单元是字节。
  • 字符流(Reader、Writer):数据流中最小的数据单元是字符。

按照流的角色划分

  • 节点流:可以从或向一个特定的地方(节点)读写数据,直接连接数据源
  • 处理流 不直接连接数据源,是对一个已存在的流的连接和封装,是一种典型的装饰器设计模式,使用处理流主要是为了更方便的执行输入输出工作。

IO流的体系分类

按操作对象划分

  1. 文件操作:FileInputStream、FileOutputStream、FileReader、FileWriter

  2. 管道操作:PipedInputStream

  3. 数组操作:

    • 字符数组:CharArrayReader、CharArrayWriter
    • 字节数组:ByteArrayInputStream、ByteArrayOutputStream
  4. 缓冲操作:BufferedWriter、BufferedInputStream

  5. 基本数据类型操作:DataOutputStream、DataInputStream

  6. 对象序列化操作:ObjectOutputStream、ObjectInputStream

  7. 转化操作:InputStreamReader、OutputStreamWriter

  8. 打印控制:PrintStream、PrintWriter

2.png

按操作方式划分

0004.png

字节输入/输出流

 InputStream类 和 OutputStream类

InputStream类:是一个抽象类 ,是所有字节输入流类的父类。

OutputStream类:是一个抽象类,是所有字节输出流的父类。

InputStream中定义的一些子类共性的成员方法

  • public abstract int read() 从输入流中读取一个数据,返回的是数据的ASCII值。

  • public int read(byte[] b) 从输入流中读取一定数量的字节,默认从第0个位置读取b.length长度的数据,参数byte[]数组表示起缓冲作用,存储每次读取的多个字节。

  • int read(byte b[], int off, int len)从第 off 位置读取 len 长度字节的数据放到byte数组中。

  • int available() 返回可读的字节数量。

  • long skip(long n) 跳过指定个数的字节不读取。

  • public void close() 关闭此输入流并释放与该流关联的所有系统资源。

OutputStream中定义的一些子类共性的成员方法

  • void close() 关闭此输出流并释放与此流有关的所有系统资源。

  • void flush() 刷新此输出流,将缓冲区中的数据写出。

  • abstract void write(int b) 将指定的字节写入此输出流。

  • void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写出。

  • void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

 FileInputStream类 和 FileOutPutStream类

FileInputStream:是文件字节输入流,表示从本地文件中读取数据到内存中。

FileOutPutStream:是文件字节输出流,表示将内存中的数据写入到本地文件中。
)

FileInputStream的使用

//1、创建一个FileInputStream对象,构造方法中传递写入数据的目的地
FileInputStream fis = new  FileInputStream("C:\\Java\\test.txt");

//或通过File文件对象来创建FileInputStream
File file = new File(C:\\Java\\test.txt");
FileInputStream fis2 = new FileInputStream(file);


//2.1调用FileInputStream对象中的方法read,从该输入流中读取一个字节的数据
int s = fis.read();
System.out.println((char)s);

//2.2或循环读取整个文件数据
int data=0;
//返回数据的下一个字节,如果到达文件末尾则返回-1
while((data=fis.read())!=-1){
    System.out.println((char)data);
}

//2.3或从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
byte[] b = new byte[6];
//存储到数组b中
fis.read(b);
//将字节数组转换成字符串输出
String s1 = new String(b);
System.out.println(s1);

//3.关闭流
fis.close();
复制代码

FileOutputStream的使用

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("C:\Study\aaa.tx",true);
    byte[] b ={99,98,97};
    //将字节数组b写入到输出流中
    fos.write(b);
    fos.close();
}
复制代码

ByteArrayInputStream类 和 ByteArrayOutputStream类

ByteArrayInputStream(字节数组输入流):是字节数组输入流,它里面包含一个内部的缓冲区(就是一个字节数组 ),该缓冲区含有从流中读取的字节。

ByteArrayOutputStream(字节数组输出流):是字节数组输出流,会将数据写入到定义的缓冲区中,默认缓冲区大小为32,会自动扩容。

ByteArrayInputStream的使用

public static void main(String[] args) {
    byte[] bytes = {97,98,99,100};
    //从字节数组的索引下标为1的位置开始读取2个元素
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes,1,2);
    int data;
    while((data =bais.read())!=-1){
        System.out.print(data);
    }
    输出:9899
}
复制代码

ByteArrayOutputStream的使用

public static void main(String[] args) throws IOException {
    //创建一个文件输入流
    FileInputStream in = new FileInputStream(new File("C:\Study\aaa.txt"));
    //创建一个字节数组输出流
    ByteArrayOutputStream out = new ByteArrayOutputStream();

    int data = 0;
    //循环遍历文件输入流中的数据
    while((data = in.read()) != -1){
        //将数据写出到字节数组的缓冲区中
        out.write(data);
    }
    //获取内存缓冲区中的数据
    byte[] bytes = out.toByteArray();
    for (int i = 0; i < bytes.length; i++) {
        System.out.println((char)bytes[i]);
    }

    in.close();
    out.close();

}
复制代码

ObjectInputStream类 和 ObjectOutpuStream类

ObjectInputStream:是反序列化流,一般和ObjectOutputStream配合使用。

ObjectOutputStream:将java对象序列化然后存入文件中,然后用ObjectInputStream读取出来。

 class Data implements Serializable {
    private int n;
    public Data(int n){
        this.n=n;
    }
    @Override
    public String toString(){
        return Integer.toString(n);
    }
}
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Data w=new Data(2);
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out"));
        //序列化对象,把对象写到worm.out里面
        out.writeObject("Worm storage\n");
         //序列化对象,把对象写到worm.out里面
        out.writeObject(w);
        out.close();
        //从worm.out里面读取对象 
        ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out"));
        //读取String对象
        String s=(String)in.readObject();
        //读取Data对象
        Data d=(Data)in.readObject();
        System.out.println(s+"Data = "+d);
    }
}
复制代码

字符读取/写出流

Reader类 和 Wirter类

  • Reader是用于读取字符的抽象类
  • Writer是用于写出字符的抽象类

Reader类常见子类有:

  • FileReader:文件输入流
  • BufferedReader: 带缓冲区的字符输入流
  • InputStreamReader:实现字节流和字符流之间的转换

Writer类常见子类有:

  • FileWriter:文件输出流
  • BufferedWriter:带缓冲区的字符输出流
  • OutputStreamWriter:实现字符流转换为字节

Reader一些子类共性的方法

  • int read(CharBuffer target) 读取字节到字符缓存中。

  • int read() 读取单个字符。

  • int read(char cbuf[]) 读取字符到指定的 char 数组中。

  • int read(char cbuf[], int off, int len) 从 off 位置读取 len 长度的字符到 char 数组中。

  • long skip(long n) 跳过指定长度的字符数量不读取。

Writer一些子类共性的方法

  • void write(int c) 写入一个字符。

  • void write(char cbuf[]) 写入一个字符数组。

  • void write(char cbuf[], int off, int len) 从字符数组的 off 位置写入 len 数量的字符。

  • void write(String str) 写入一个字符串。

  • void write(String str, int off, int len) 从字符串的 off 位置写入 len 数量的字符。

  • Writer append(CharSequence csq)以追加的方式写入一个字符序列。

 FileReader类 和 FileWriter类

FileReader的构造方法

  • FileReader(String fileName) 根据本地文件的全路径名创建一个文件字符输入流。

  • FileReader(File file) 根据一个File对象创建一个文件字符输入流。

  • FileReader(String fileName, Charset charset) 根据本地文件的全路径名创建一个文件字符输入流,并指定字符集。

  • FileReader(File file, Charset charset)根据一个File对象创建一个文件字符输入流,并指定字符集。

FileReader的使用

public static void main(String[] args) throws IOException {
    FileReader fileReader = new FileReader(new File("C:\Study\aaa.txt"));
    int data;
    while ((data =fileReader.read()) != -1) {
        System.out.println(data);
    }
}
复制代码

FileWriter的构造方法

  • FileWriter(String fileName) 根据存在的文件的全路径文件名称来创建一个文件字符写出流。

  • FileWriter(String fileName, boolean append)以上一个构造方法相比多了一个append,表示是否追加写,默认为否。

  • FileWriter(File file)根据一个文件对象File来创建一个FileWriter文件字符写出流。

  • FileWriter(String fileName, Charset charset) 根据指定的编码和文件名称创建一个文件字符写出流。

FileWriter的使用

public static void main(String[] args) throws IOException {
    FileWriter fw = new FileWriter(new File("C:\Study\aaa.txt"));
    fw.write("FileWriter");
    fw.close();
}
复制代码

缓冲流

  • 字节缓冲流:BufferedOutputStream字节缓冲输出流,BufferedInputStream字节缓冲输入流.
  • 字符缓冲流:BufferedReader字符缓冲输出流,BufferedWriter字符缓冲输入流.

BufferedInputStream类 和 BufferedOutputStream类

BufferedInputStream构造方法

  • BufferedInputStream(InputStream in) 根据InputStream输入流创建一个 BufferedInputStream。

  • BufferedInputStream(InputStream in, int size) 根据InputStream输入流和指定的缓冲区大小创建一个BufferedInputStream。

BufferedOutputStream构造和常用方法

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

  • BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。

  • void flush() 使用flush刷新,才会把缓冲区中的字节全部写入文件中。

  • void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。

  • void write(int b) 将指定的字节写入缓冲的输出流。

使用 BufferInputStream 和 BufferOutputStream 实现非文本文件的复制

@Test
public void testBufferedStream(){
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        //1.造文件
        File srcFile = new File("test.jpg");
        File destFile = new File("test4.jpg");
        //2.造流
        //2.1造节点流
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        //2.2造缓冲流,可以合并书写
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);

        //3.文件读取、写出操作
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bis.read(buffer)) != -1){
            bos.write(buffer,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4.关闭流
        if (bos != null){
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bis != null){

            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

BufferedReader类 和 BufferedWriter类

BufferedReader的构造方法

  • BufferedReader(Reader in)创建默认大小的输入缓冲区缓冲字符输入流。

  • BufferedReader(Reader in, int sz) 创建一个使用指定大小为sz的输入缓冲区的缓冲字符输入流。

BufferedWriterr的构造方法

  • BufferedWriter(Writer out) 根据字符写出流创建一个字符缓冲写出流,缓冲区默认大小是8192个字符。

  • BufferedWriter(Writer out, int sz) 根据字符写出流和指定的缓冲区大小创建一个字符缓冲写出流。

使用 BufferedReader 和 BufferedWriter 实现文本文件的复制

@Test
public void testBufferedReaderBufferedWriter(){
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        //创建文件和相应的流
        br = new BufferedReader(new FileReader(new File("dbcp.txt")));
        bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

        //读写操作
        //方式一:使用char[]数组
        //            char[] cbuf = new char[1024];
        //            int len;
        //            while((len = br.read(cbuf)) != -1){
        //                bw.write(cbuf,0,len);
        //    //            bw.flush();
        //            }

        //方式二:使用String
        String data;
        while((data = br.readLine()) != null){
            //方法一:
            //                bw.write(data + "\n");//data中不包含换行符
            //方法二:
            bw.write(data);//data中不包含换行符
            bw.newLine();//提供换行的操作

        }


    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭资源
        if(bw != null){

            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(br != null){
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}
复制代码

转换流

  • 转换流提供了在字节流和字符流之间的转换

  • Java API提供了两个转换流:

    • InputstreamReader:将 Inputstream 转换为 Reader
    • OutputStreamWriter:将 Writer 转换为 OutputStream
  • 字节流中的数据都是字符时,转成字符流操作更高效。

  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。

InputstreamReader类 和 OutputStreamWriter类

nputStreamReader的构造方法

  • InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader流。

  • InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。

  • InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。

public static void main(String[] args) throws IOException {
    //a:创建InputStreamWriter对象,构造方法中传递字节输入流InputStream和指定的编码
    FileInputStream fis = new FileInputStream("C:\Study\a.txt");
    InputStreamReader isw = new InputStreamReader(fis,"utf-8");

    //:使用InputStreamWriter对象中的方法read读文件
    int len=0;
    while((len=isw.read())!=-1){
        System.out.print((char)len);
    }
    //c:释放资源
    isw.close();
    fis.close();

}
复制代码

OutputStreamWriter的构造方法

  • OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter流。

  • OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter流。

  • OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter流。

public static void main(String[] args) throws IOException{
    //a:创建OutputStreamWriter对象,构造方法中传递字节输出流OutputStream和指定的编码表
    FileOutputStream os = new FileOutputStream("C:\Study\a.txt",true);
    OutputStreamWriter osw = new OutputStreamWriter(os,"utf-8");

    //b:使用OutputStreamWriter对象中的方法write,把字符转换成字节存储到缓冲区中(编码)
    osw.write("Akiang");

    //c:使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    osw.flush();

    //释放资源
    osw.close();

}
复制代码

对象流

  • ObjectOutputStream:内存中的对象—>存储中的文件、通过网络传输出去:序列化过程
  • ObjectInputStream:存储中的文件、通过网络接收过来 —>内存中的对象:反序列化过程

对象的序列化

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。

  • 序列化是RMI(Remote Method Invoke-远程方法调用)过程的参数和返回值都必须实现的机制,RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础。

  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出 NotserializableEXception 异常

    • Serializable
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:

    • private static final long serialVersionUID;
    • serialVersionUID 用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容
    • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。故建议显式声明。
  • 简单来说,Java 的序列化机制是通过在运行时判断类的 serialversionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialversionUID 与本地相应实体类的 serialversionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

实现序列化的对象所属的类需要满足

  1. 需要实现接口:Serializable(标识接口)
  2. 当前类提供一个全局常量:serialVersionUID(序列版本号)
  3. 除了当前 Person 类需要实现 Serializable 接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)

补充:ObjectOutputStreamObjectInputStream 不能序列化 statictransient 修饰的成员变量

对象流的使用

序列化代码实现

序列化:将对象写入磁盘或进行网络传输

要求被序列化对象必须实现序列化

@Test
public void testObjectOutputStream(){
    ObjectOutputStream oos = null;

    try {
        //1.创建对象,创建流
        oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
        //2.操作流
        oos.writeObject(new String("我爱北京天安门"));
        oos.flush();//刷新操作

        oos.writeObject(new Person("王铭",23));
        oos.flush();

        oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
        oos.flush();

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(oos != null){
            //3.关闭流
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

复制代码

反序列化代码实现

反序列化:将磁盘的对象数据源读出

@Test
public void testObjectInputStream(){
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("object.dat"));

        Object obj = ois.readObject();
        String str = (String) obj;

        Person p = (Person) ois.readObject();
        Person p1 = (Person) ois.readObject();

        System.out.println(str);
        System.out.println(p);
        System.out.println(p1);

    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        if(ois != null){
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}
复制代码

?以上就是对Java中IO流的简单介绍,如果有错误的地方,还请留言指正,如果觉得本文对你有帮助那就点个赞吧??

1111.gif

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享