IO流技术

IO流技术介绍

什么是IO

输入(Input)指的是可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据)。
输出(Output)指的是程序输出数据给外部系统从而可以操作外部系统(核心含义是"写",将数据写出到外部系统)
java.io包为我们提供了相关的API,实现了对所有外部系统的输入输出操作。

什么是数据源

数据源data source,提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。
在这里插入图片描述
数据源分为:
源设备:为程序提供数据,一般对应输入流
目标设备:程序数据的目的地,一般对应输出流

流的概念

流是一个抽象、动态的概念,是一连串连续动态的数据集合。
对于输入流而言,数据源就像水箱,流(Stream)就像水管中流动着的水流,程序就是最终的用户。通过流(A Stream)将数据源(Source)中的数据(information)输送到程序(Program)中。
对于输出流而言,目标数据源就是目的地(dest),我们通过流(A Stream)将程序(Program)中的数据(information)输送到目的数据源(dest)中。
流与源数据源和目标数据源之间的关系:
在这里插入图片描述
输入/输出流的划分是相对程序而言,并不是相对数据源。

第一个简单的IO流程序

当程序需要读取数据源的数据时,就会通过IO流对象开启一个通向数据源的流,通过这个IO流对象的相关方法可以顺序读取数据源中的数据。
使用流读取文件内容(不规范的写法,仅用于测试):

package com.itbaizhan.IOStream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestIO {
    public static void main(String[] args) {
        try {
            //创建输入流
            FileInputStream fis=new FileInputStream("d:/a.txt");//文件内容:abc
            //一个字节一个字节的读取数据
            int s1=fis.read();//打印输出字符a对应的ascii码值97
            int s2=fis.read();//打印输出字符b对应的ascii码值98
            int s3=fis.read();//打印输出字符c的对应的ascii码值99
            int s4=fis.read();//由于文件内容已经读取完毕,返回-1
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s3);
            System.out.println(s4);
            //流对象使用完,必须关闭!不然,总占用系统资源,最终会造成系统崩溃
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

读取的文件内容是已知的,因此可以使用固定次数的"int s=fis.read();"语句读取内容,但是在实际开发中通常我们根本不知道文件的内容,因此我们在读取的时候需要配合while循环使用
为了保证出现异常后流的正常关闭,通常要将流的关闭语句要放到finally语句块中,并且要判断流是不是null。

IO流的经典写法

使用流读取文件内容:

package com.itbaizhan.IOStream;

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

public class Test2 {
    public static void main(String[] args) {
        FileInputStream fis=null;
        try{
            fis=new FileInputStream("d:/a.txt");//文件内容:abc
            StringBuilder sb=new StringBuilder();
            int temp=0;
            //当temp等于-1时,表示已经到了文件结尾,停止读取
            while ((temp = fis.read())!=-1){
                sb.append((char) temp);
            }
            System.out.println(sb);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
             //这种写法保证了即使遇到异常情况,也会关闭流对象
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

IO流新语法经典写法:

在JDK7以及以后得版本中可以使用try-with-resource语法更优雅的关闭资源。
java.lang.AutoClosable接口:
在java.lang.AutoClosable接口中包含了一个close方法,该方法用于关闭资源。
只要是实现了java.lang.AutoCloseable接口的对象,该方法用于关闭资源。
只要是实现了java.lang.AutoClosable接口的对象,都可以使用try-with-resource关闭资源。

package com.itbaizhan.IOStream;

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

public class Test3 {
    public static void main(String[] args) {
        //使用try-with-resource方式关闭资源
        //在try中打开资源,不需要在代码中添加finally块关闭资源
        try(FileInputStream fis=new FileInputStream("d:/a.txt")) {
            StringBuilder sb=new StringBuilder();
            int temp=0;
            while ((temp=fis.read())!=-1){
                sb.append((char)temp);
            }
            System.out.println(sb);
        } catch (Exception e) {
            e.printStackTrace();
        } ;

    }
}

Java中流的概念细分

在这里插入图片描述
按流的方向分类:

  • 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)
  • 输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)
    按处理的数据单元分类:
  • 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream
  • 字符流:以字符为单位获取数据Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。
    按处理对象不同分类:
  • 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等
  • 处理流:不直接连接到数据源或目的地,是“处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
    节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
    在这里插入图片描述

Java中IO流类的体系

Java为我们提供了多种多样的IO流,我们可以根据不同的功能以及性能要求挑选合适的IO流,以下为IO流类的体系。
在这里插入图片描述

  • InputStream/OutputStream 字节流的抽象类
  • Reader/Writer 字符流的抽象类
  • FileInputStream/FileOutputStream 节点流(以字节为单位直接操作"文件")
  • ByteArrayInputStream/ByteArrayOutputStream 节点流(以字节为单位直接操作"字节数组对象")
  • ObjectInputStream/ObjectOutputStream 处理流(以字节为单位直接操作对象)
  • DataInputStream/DataOutputStream 处理流(以字节为单位直接操作“基本数据类型与字符串类型”)
  • FileReader/FileWriter
  • 节点流:以字符为单位直接操作"文本文件"(只能读写文本文件)
  • BufferedReader/BufferedWriter 处理流:将Reader/Writer对象进行包装,增减缓存功能,提高读写效率。
  • BufferedInputStream/BufferedOutputStream 处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率
  • InputStreamReader/OutputStreamWriter 处理流:将字节流对象转化成字符流对象
  • PrintStream 处理流:将OutputStream进行包装,方便输出字符,更加灵活

Java中IO的四大抽象类

InputStream类/OutputStream类/Reader类/Writer类 是IO流类的抽象父亲。

InputStream

此抽象类是表示字节输入流的所有类的父亲。InputStream是一个抽象类,不可以实例化。数据的读取需要由子类来实现。根据节点不同,派生了不同节点流子类。
继承自InputStream的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
常用方法

方法名使用说明
int read()读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。如果未读出字节则返回-1(返回值为-1表示读取结束)
void close()关闭输入流对象,释放相关系统资源

OutputStream

此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。

方法名使用说明
void write(int n)向目的地中写入一个字节
void close()关闭输出流对象,释放相关系统资源

Reader

用于读取的字符流抽象类,数据单位为字符

方法名使用说明
int read()读取一个字符的数据,并将字符的值作为int类型返回(0-65535之间的一个值,即Unicode值),如果未读出字符则返回-1,表示读取结束
void close()关闭流对象,释放相关系统资源

Writer

Writer用于输出的字符流抽象类,数据单位为字符

方法名使用说明
void write(int n)向输出流中写入一个字符
void close()关闭输出流对象,释放相关系统资源

常用类详解

文件字节流

FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)
FileOutputStream通过字节的方式写数据到文件中,适合所有类型的文件(图像、视频、文本文件等)
FileInputStream文件输入字节流

package com.itbaizhan.IOStream;

import java.io.FileInputStream;

public class TestFileInputStream {
    public static void main(String[] args) {
        //使用try-with-resource方式关闭资源
        //在try中打开资源,不需要在代码中添加finally块关闭资源
       try(FileInputStream fis=new FileInputStream("d:/a.txt");) {
            StringBuilder sb=new StringBuilder();
            int temp=0;
            while ((temp=fis.read())!=-1){
                sb.append((char) temp);
            }
           System.out.println(sb);
       }catch (Exception e){
           
       }
    }
}

FileOutputStream文件输出字节流

package com.itbaizhan.IOStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class TestFileOutputStream {
    public static void main(String[] args) {
        String str="Old Lu";
        //true表示内容会追加到文件末尾;false表示重写整个文件内容,默认为false
        try(FileOutputStream fos=new
                FileOutputStream("d:/a.txt",true)){
            //将整个字节数组写入到文件中
            fos.write(str.getBytes());
            //将数据从内存中写入到磁盘中
            fos.flush();
        }catch (IOException e){
             e.printStackTrace();
        }
    }
}

通过字节缓冲区提高读写效率

通过创建一个指定长度的字节数组作为缓冲区,以此来提高IO流的读写效率。该方式适用于读取较大文件时的缓冲区定义。注意:缓冲区的长度一定是2的整数幂,一般情况下1024长度较为合适。

package com.itbaizhan.IOStream;

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

public class TestFileByteBuffer {
    public static void main(String[] args) {
        long time1=System.currentTimeMillis();
        copyFile("d:/1.jpg","d:/2.jpg");
        long time2=System.currentTimeMillis();
        System.out.println(time2-time1);
    }

    /**
     *
     * @param src
     * @param desc
     */
    public static void copyFile(String src,String desc){
        //"后开的先关闭"按照他们被创建顺序的逆序来关闭
        try(FileInputStream fis=new FileInputStream(src);
            FileOutputStream fos=new FileOutputStream(desc)){
         //创建一个缓冲区,提高读写效率
            byte[] buffer=new byte[1024];
            int temp=0;
            while ((temp=fis.read(buffer))!=-1){
                //将缓存数组中的数据写入文件中,写入的是读取的真实长度
                fos.write(buffer,0,temp);
            }
            //将数据从内存中写入到磁盘中
            fos.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

}

在使用字节缓冲区时,

  • 为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为:read(byte[] b);写入时的方法为:write(byte[] b,int off,int length)
  • 程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。

缓冲字节流

Java缓冲流本身并不具有IO流的读取和写入功能,只是在别的流(节点流或者其他处理流)上加入缓冲功能提高效率,就像把别的流包装起来一样,因此缓冲流是一种处理流(包装流)。
BufferedInpuutStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组来提高操作流的效率。

使用缓冲流实现文件的高效率复制

package com.itbaizhan.IOStream;

import java.io.*;

public class TestFileBufferStream {
    public static void main(String[] args) {
        long time1=System.currentTimeMillis();
        copyFile("d:/1.jpg","d:/2.jpg");
        long time2=System.currentTimeMillis();
        System.out.println(time2-time1);
    }
    public static void copyFile(String source,String destination){
        //实例化节点流
        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos=new FileOutputStream(destination);
             //实例化处理流
             BufferedInputStream bis=new BufferedInputStream(fis);
             BufferedOutputStream bos=new BufferedOutputStream(fos)
        ) {
            int temp=0;
             while ((temp=bis.read())!=-1){
                 bos.write(temp);
             }
             bos.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

在关闭流时应该先关闭最外层的包装流,即“后开的先关闭”
缓存区的大小默认是8192字节,也可以使用其他构造方法自己指定大小

文件字符流

文件字节流可以处理所有文件,但是处理的是文本文件,也可以使用文件字符流,以字符为单位进行操作。
文件字符输入流

package com.itbaizhan.IOStream;

import java.io.FileReader;
import java.io.IOException;

public class TestFileReader {
    public static void main(String[] args) {
        //创建文件字符输入流对象
        try(FileReader fr=new FileReader("d:/a.txt"))
        {
            StringBuilder sb=new StringBuilder();
            //读取文件
            int temp=0;
            while((temp=fr.read())!=-1){
                sb.append((char) temp);
            }
            System.out.println(sb);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

文件字符输出流

package com.itbaizhan.IOStream;

import jdk.management.resource.internal.FutureWrapper;

import java.io.FileWriter;
import java.io.IOException;

public class TestFileWriter {
    public static void main(String[] args) {
        //创建文件字符输出流对象
        try (FileWriter fw = new FileWriter("d:/aa.txt")) {
            fw.write("您好尚学堂\r\n");
            fw.write("您好,SenSen\r\n");
            fw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

缓冲字符流

BufferedReader/BufferedWriter增加了缓存机制,大大提高了读写文本文件的效率
字符输入缓冲流
BufferedReader是针对字符输入流的缓冲流对象,提供了更方便的按行读取的方法(readLine());在使用字符流读取文本文件时,可以使用该方法以行为单位进行读取。

package com.itbaizhan.IOStream;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TestBufferedReader {
    public static void main(String[] args) {
        //创建文件字符输入流对象
        try (FileReader fr = new FileReader("d:/aa.txt");
             //创建字符缓冲处理流,缓冲区默认大小为8192个字符
             BufferedReader br=new BufferedReader(fr)) {
            //操作流
            String temp="";
            //readLine():读取一行文本
            while((temp=br.readLine())!=null){
                System.out.println(temp);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

字符输出缓冲流
BufferedWriter是针对字符输出流的缓冲流对象,在字符输出缓冲流中可以使用newLine();方法实现换行处理。

package com.itbaizhan.IOStream;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class TestBufferedWriter {
    public static void main(String[] args) {
        //创建文件字符输出流对象
        try (FileWriter fw = new FileWriter("d:/sxt.txt");
             //创建字符输出缓冲流对象
             BufferedWriter bw=new BufferedWriter(fw)
        ) {
            //操作缓冲流
            bw.write("您好Gidle");
            bw.write("您好SenSen");
            //换行
            bw.newLine();
            bw.write("浔阳江头夜送客");
            bw.newLine();
            bw.write("枫叶荻花秋瑟瑟");
            bw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

  • readLine()方法是BufferedReader的方法,可以对文本文件进行更加方便的读取操作。
  • newLine()方法BufferedWriter的方法,可以使用newLine()方法换行。

为文件中的内容添加行号

package com.itbaizhan.IOStream;

import java.io.*;

public class TestLineNumber {
    public static void main(String[] args) {
        //创建字符输入缓冲流与文件字符输入流
        try (BufferedReader br = new BufferedReader(new FileReader("d:/sxt.txt"));
             //创建字符输出缓冲流与文件字符输出流
             BufferedWriter bw=new BufferedWriter(new FileWriter("d:/sxt2.txt"))) {
            String temp="";
            //定义序号变量
            int i=1;
            while ((temp=br.readLine())!=null){
                //将读取到的内容添加序号,并输出到指定文件中
                bw.write(i+","+temp);
                //换行处理
                bw.newLine();
                //序号变量累加
                i++;
            }
            //刷新
            bw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

转换流

InputStreamReader 提供字节流到字符流之间的转换/OutputStreamWriter提供字符流到字节流之间的转换
通过转换流解决乱码
ANSI(American National Standards Institute)美国国家标准协会

package com.itbaizhan.IOStream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class TestInputStreamReader {
    public static void main(String[] args) {
        //创建文件字节输入流对象
        try (FileInputStream fis = new FileInputStream("d:/sxt.txt");
             //创建转换流(字节到字符的转换)流对象,并在该对象中指定编码
            InputStreamReader isr=new InputStreamReader(fis,"gbk")) {
            StringBuilder sb=new StringBuilder();
            //操作流对象
            int temp=0;
            while((temp=isr.read())!=-1){
                sb.append((char) temp);

            }
            System.out.println(sb);

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

    }
}

通过字节流读取文本文件并添加行号

package com.itbaizhan.IOStream;

import java.io.*;

public class TestLineNumber2 {
    public static void main(String[] args) {
        //创建字符输入缓冲流、输入字节到字符转换流、文件字节输入流对象
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/sxt.txt")));
             BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:/sxt4.txt")))) {
            //操作流
            String temp="";
            //序号变量
            int i=1;
            //按照行读取
            while((temp=br.readLine())!=null){
                bw.write(i+","+temp);
                //换行
                bw.newLine();
                //序号累加
                i++;
            }
            //刷新
            bw.flush();

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

通过转换流实现键盘输入屏幕输出

System.in是字节流对象,代表键盘的输入。
System.out是字节流对象,代表输出到屏幕。

package com.itbaizhan.IOStream;

import java.io.*;

public class TestKeyboardInput {
    public static void main(String[] args) {
        //创建键盘输入相关流对象
        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             //创建向屏幕输出相关流对象
             BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out))) {
            while (true){
                bw.write("请输入:");
                bw.flush();
                //获取键盘输入的字符串
                String input=br.readLine();
                //判断输入的内容是否含有退出关键字
                if("exit".equals(input)||"quit".equals(input)){
                    bw.write("Bye Bye !");
                    bw.flush();
                    break;
                }
                //将读取到键盘输入的字符串,输出到屏幕
                bw.write("您输入的是:"+input);
                bw.newLine();
                bw.flush();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

通过转换流实现键盘输入屏幕输出

package com.itbaizhan.IOStream;

import java.io.*;

public class TestConvertStream {
    public static void main(String[] args) {
        //创建字符输入和输出流:使用转换流将字节流转换成字符流
        BufferedReader br=null;
        BufferedWriter bw=null;
        try{
            br=new BufferedReader(new InputStreamReader(System.in));
            bw=new BufferedWriter(new OutputStreamWriter(System.out));
            //使用字符输入和输出流
            String str=br.readLine();
            //一直读取,直到用户输入了exit为止
            while(!"exit".equals(str)){
                //写到控制台
                bw.write(str);
                bw.newLine();//写一行后换行
                bw.flush();//手动刷新
                //再读一行
                str=br.readLine();
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw!=null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

字符输出流

在Java的IO流中专门提供了用于字符输出的流对象PrintWriter。该对象具有自动刷新缓冲字符输出流,特点是可以按行写出字符串,并且可通过println()方法实现自动换行

package com.itbaizhan.IOStream;

import java.io.*;

public class TestPrintWriter {
    public static void main(String[] args) {
        //创建字符输出流对象
        try (PrintWriter pw = new PrintWriter("d:/sxt5.txt")){
            //调用不带换行方法完成内容的输出
            pw.print("abc");
            pw.print("def");
            //调用带有自动换行的方法完成内容的输出
            pw.println("行路难");
            pw.println("难于上青天");
            pw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

通过字符输出流添加行号

package com.itbaizhan.IOStream;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;

public class TestLineNumber3 {
    public static void main(String[] args) {
        //创建字符输入缓冲流对象与文件字符输入流对象
        try (BufferedReader br = new BufferedReader(new FileReader("d:/sxt.txt"));
        //创建字符输出流对象
             PrintWriter pw=new PrintWriter("d:/sxt6.txt")
        ) {
            //操作流
            String temp="";
            //定义序号变量
            int i=1;
            while ((temp=br.readLine())!=null){
                pw.println(i+","+temp);
                //序号累加
                i++;
            }
            //刷新
            pw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

数据流

数据流将基本数据类型与字符串类型作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。
DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int double String等)方法

package com.itbaizhan.IOStream;

import java.io.*;

public class TestDataStream {
    public static void main(String[] args) {
        //创建数据输出流对象与文件字节输出流对象
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/data"));
             //创建数据输入流对象与文件字节输入流对象
             DataInputStream dis=new DataInputStream(new FileInputStream("d:/data"))
        ) {
            //将如下数据写入到文件中
            dos.writeChar('a');
            dos.writeInt(10);
            dos.writeDouble(Math.random());
            dos.writeBoolean(true);
            dos.writeUTF("天天开心");
            //手动刷新缓冲区:将流中数据写入到文件中
            dos.flush();
            //直接读取数据:读取顺序要与写入的顺序一致,否则不能正确读取数据
            System.out.println("char"+dis.readChar());
            System.out.println("int"+dis.readInt());
            System.out.println("double"+dis.readDouble());
            System.out.println("boolean"+dis.readBoolean());
            System.out.println("String"+dis.readUTF());

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

使用数据流时,读取顺序一定要与写入的顺序一致,否则不能正确读取数据。

对象流

ObjectInputStream/ObjectOutputStream可以对某个对象进行读写操作。

处理基本数据类型数据

ObjectInputStream/ObjectOutputStream处理基本数据类型

package com.itbaizhan.IOStream;

import java.io.*;

public class TestObjectStreamBasicType {
    public static void main(String[] args) {
        //创建对象输出流对象与文件字节输出流对象
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data2"));
        //创建对象输入流对象与文件字节输入流对象
             ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/data2"))
             ) {
            //将如下数据写入到文件中
            oos.writeInt(10);
            oos.writeDouble(Math.random());
            oos.writeChar('A');
            oos.writeBoolean(true);
            oos.writeUTF("天天开心");
            oos.flush();
            //必须要按照写入的顺序读取数据
            System.out.println("int:"+ois.readInt());
            System.out.println("double:"+ois.readDouble());
            System.out.println("char:"+ois.readChar());
            System.out.println("boolean:"+ois.readBoolean());
            System.out.println("String:"+ois.readUTF());
        }catch (IOException e){

        }

    }
}

对象流不仅可以读写对象,也可以读写基本数据类型,读写基本数据类型时读取的顺序一定要与写入顺序一致,否则不能正确读取数据

Java对象的序列化和反序列化

在这里插入图片描述

序列化和反序列化是什么

当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,当通过http协议发送字符串信息或者在网络上直接发送Java对象。发送方需要把这个Java对象转换成字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为Java对象的过程称为对象的反序列化。

序列化涉及的类和接口

ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只要实现了Serializable接口的类的对象才能被序列化。Serializable接口是一个空接口,只起到标记作用。

将对象序列化到文件

在这里插入图片描述
ObjectOutputStream可以将一个内存中的java对象通过序列化的方式写入到磁盘的文件中。被序列化的对象必须要实现Serializable序列化接口,否则会被抛出异常。

package com.itbaizhan.IOStream;

import java.io.Serializable;

public class Users implements Serializable {
    private int userid;
    private String username;
    private String userage;

    public Users() {
    }

    public Users(int userid, String username, String userage) {
        this.userid = userid;
        this.username = username;
        this.userage = userage;
    }

    /**
     * 获取
     * @return userid
     */
    public int getUserid() {
        return userid;
    }

    /**
     * 设置
     * @param userid
     */
    public void setUserid(int userid) {
        this.userid = userid;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return userage
     */
    public String getUserage() {
        return userage;
    }

    /**
     * 设置
     * @param userage
     */
    public void setUserage(String userage) {
        this.userage = userage;
    }

    public String toString() {
        return "Users{userid = " + userid + ", username = " + username + ", userage = " + userage + "}";
    }
}

//序列化对象
package com.itbaizhan.IOStream;

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

public class TestObjectOutputStream {
    public static void main(String[] args) {
        //创建对象输出字节流与文件输出字节流对象
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data3"))) {
             //创建Users对象
            Users user=new Users(1,"周周","18");
            //将对象序列化到文件中
            oos.writeObject(user);
            //刷新
            oos.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}


将对象反序列到内存中

在这里插入图片描述

package com.itbaizhan.IOStream;

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

public class TestObjectInputStream {
    public static void main(String[] args) {
        //创建对象输入字节流与文件字节输入流对象
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/data3"))) {
             //将对象反序列化到内存中
            Users users=(Users) ois.readObject();
            System.out.println(users);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

File类在IO中的作用

当以文件作为数据源或目标时,除了可以使用字符串作为文件以及位置的指定以外,可使用File类指定。

package com.itbaizhan.IOStream;

import java.io.*;

public class TestFile {
    public static void main(String[] args) {
        //创建字符缓冲流与文件字符输入流对象
        try (BufferedReader br = new BufferedReader(new FileReader(new File("d:/sxt.txt")));
             //创建字符输出流对象
             PrintWriter pw=new PrintWriter(new File("d:/sxt8.txt"))) {
            //操作流
            String temp="";
            int i=1;
            while ((temp=br.readLine())!=null){
                pw.println(i+","+temp);
                i++;
            }
            pw.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

装饰器模式构建IO流体系

装饰器模式简介

装饰器模式是GOF23种设计模式中较为常用的一种模式,它可以实现对原有类的包装和装饰,使新的类具有更强的功能。

package com.itbaizhan.IOStream;
class Iphone{
    private String name;
    public Iphone(String name){
        this.name=name;
    }
    public void show(){
        System.out.println("我是"+name+",可以在屏幕上显示");
    }

}
class TouyingPhone{
    public Iphone iphone;
    public TouyingPhone(Iphone p){
        this.iphone=p;
    }
    public void show(){
        iphone.show();
        System.out.println("可以投影还可以在屏幕上显示");
    }
}
public class TestDecoration {
    public static void main(String[] args) {
        Iphone iphone=new Iphone("iphone30");
        iphone.show();
        System.out.println("装饰后=====");
        TouyingPhone touyingPhone=new TouyingPhone(iphone);
        touyingPhone.show();
    }
}

IO流体系中的装饰器模式

IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。

FileInputStream fis=new FileInputStream(src);
BufferedInputStream bis=new BufferedInputStream(fis);

显然BufferedInputStream装饰了原有的FileInputStream,让普通的FileInputStream也具备了缓存功能,提高了效率。

FileUtils类中常用方法的介绍

FileUtils的api文档中常用的方法进行总结和讲解:

方法名使用说明
cleanDirectory清空目录,但不删除目录
contentEquals比较两个文件的内容是否相同
copyDirectory将一个目录内容拷贝到另一个目录,可以通过FileFilter过滤需要拷贝的文件
copyFile将一个文件拷贝到一个新的地址
copyFileToDirectory将一个文件拷贝到某个目录下
copyInputStreamToFile将一个输入流中的内容拷贝到某个文件
deleteDirectory删除目录
deleteQuietly删除文件
listFiles列出指定目录下的所有文件
openInputSteam打开指定文件的输入流
readFileToString将文件内容作为字符串返回
readLines将文件内容按行返回到一个字符串数组中
size返回文件或目录的大小
write将字符串内容直接写到文件中
writeByteArrayToFile将字节数组内容写到文件中
writeLines将容器中的元素的toString方法返回的内容依次写入文件中
writeStringToFile将字符串内容写到文件中
package com.itbaizhan.IOStream;

import java.io.File;
import org.apache.commons.io.FileUtils;
public class TestUtils1 {
    public static void main(String[] args) throws Exception {
        String content = FileUtils.readFileToString(new File("d:/a.txt"), "UTF-8");
        System.out.println(content);
    }
}

使用FileUtils工具类实现目录拷贝

使用FileUtils完成目录拷贝,在拷贝过程中可以通过文件过滤器(FileFilter)选择拷贝内容

package com.itbaizhan.IOStream;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;

public class TestFileUtilsDemo2 {
    public static void main(String[] args) throws IOException {
        FileUtils.copyDirectory(new File("d:/aaa"), new File("d:/bbb"), new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //使用FileFilter过滤目录和以html结尾的文件
                if(pathname.isDirectory()||pathname.getName().endsWith("html")){
                    return true;
                }else {
                    return false;
                }
            }
        });
    }
}

本章总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值