Java基础深化和提高-------IO流

简介: Java基础深化和提高-------IO流



IO流技术介绍

什么是IO

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

什么是数据源

数据源data source,提供数据的原始媒介。常见的数据源有:数据 库、文件、其他程序、内存、网络连接、IO设备。如图所示。

数据源分为:源设备、目标设备。

1 源设备:为程序提供数据,一般对应输入流。

2 目标设备:程序数据的目的地,一般对应输出流。

流的概念

流是一个抽象、动态的概念,是一连串连续动态的数据集合。 对于输入流而言,数据源就像水箱,流(stream)就像水管中流动着 的水流,程序就是我们最终的用户。我们通过流(A Stream)将数 据源(Source)中的数据(information)输送到程序 (Program)中。 对于输出流而言,目标数据源就是目的地(dest),我们通过流(A Stream)将程序(Program)中的数据(information)输送到目 的数据源(dest)中。

流与源数据源和目标数据源之间的关系:

Oldlu提示

输入/输出流的划分是相对程序而言的,并不是相对数据源。

第一个简单的IO流程序

当程序需要读取数据源的数据时,就会通过IO流对象开启一个通向 数据源的流,通过这个IO流对象的相关方法可以顺序读取数据源中 的数据。

使用流读取文件内容(不规范的写法,仅用于测试)

import java.io.*;
public class TestI01 {
 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();
   }
  }
}

以上案例我们要注意以下几点:

1、我们读取的文件内容是已知的,因此可以使用固定次数的“int s= fis.read();”语句读取内容,但是在 实际开发中通常我们根本不知道文件的内容,因此我们在读取的时候需要配合while循环使用。

2、 为了保证出现异常后流的正常关闭,通常要将流的关闭语句要放到finally语句块中,并且要判断流 是不是null。

IO流的经典写法

使用流读取文件内容(经典代码,一定要掌握)

import java.io.*;
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 {
 //这种写法,保证了即使遇到异常情况,也会关闭流对象。
 if (fis != null) {
      fis.close();
     }
  } catch (IOException e) {
      e.printStackTrace();
    }
   }
 }
}

IO流新语法经典写法

在JDK7以及以后的版本中可以使用try-with-resource语法更优雅的 关闭资源。 java.lang.AutoCloseable接口: 在java.lang.AutoCloseable接口中包含了一个close方法,该方法用 于关闭资源。 只要是实现了java.lang.AutoCloseable接口的对象,都可以使用 try-with-resource关闭资源。 使用最新的try-with-resource简化(经典代码,一定要掌握)

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();
       }
   }

Oldlu建议 如上代码是一段非常典型的IO流代码,其他流对象的使用也 基本是同样的模式!

Java中流的概念细分

按流的方向分类:

输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。

输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。

按处理的数据单元分类:

字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、 FileOutputStream。

字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如 FileReader、FileWriter。

按处理对象不同分类:

节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、 DataInputStream等。

处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能, 如BufferedInputStream、BufferedReader等。处理流也叫包装流。

节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流 可以对节点流进行包装,提高性能或提高程序的灵活性。

Java中IO流类的体系

Java为我们提供了多种多样的IO流,我们可以根据不同的功能及性 能要求挑选合适的IO流,如图所示,为Java中IO流类的体系。

注:这里只列出常用的类,详情可以参考JDK API文档。

从上图发现,很多流都是成对出现的,比如: FileInputStream/FileOutputStream,显然是对文件做输入和输出 操作的。我们下面简单做个总结:

1 InputStream/OutputStream 字节流的抽象类。

2 Reader/Writer 字符流的抽象类。

3 FileInputStream/FileOutputStream 节点流:以字节为单位直接操作“文件”。

4 ByteArrayInputStream/ByteArrayOutputStream 节点流:以字节为单位直接操作“字节数组对象”。

5 ObjectInputStream/ObjectOutputStream 处理流:以字节为单位直接操作“对象”。

6 DataInputStream/DataOutputStream 处理流:以字节为单位直接操作“基本数据类型与字符串类型”。

7 FileReader/FileWriter 节点流:以字符为单位直接操作“文本文件”(注意:只能读写文 本文件)。

8 BufferedReader/BufferedWriter 处理流:将Reader/Writer对象进行包装,增加缓存功能,提高 读写效率。

9 BufferedInputStream/BufferedOutputStream 处理流:将InputStream/OutputStream对象进行包装,增加缓 存功能,提高读写效率

10 InputStreamReader/OutputStreamWriter 处理流:将字节流对象转化成字符流对象。

11 PrintStream 处理流:将OutputStream进行包装,可以方便地输出字符,更 加灵活。

Oldlu建议 上面的解释,一句话就点中了流的核心作用。大家在后面学 习的时候,用心体会。

Java中IO的四大抽象类

InputStream/OutputStream和Reader/writer类是所有IO流类的抽 象父类,我们有必要简单了解一下这个四个抽象类的作用。然后, 通过它们具体的子类熟悉相关的用法。

InputStream

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

常用方法:

OutputStream

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

常用方法:

Reader

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

Writer

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

常用流详解

文件字节流

FileInputStream   通过字节的方式读取文件,适合读取所有类型的文 件(图像、视频、文本文件等)。

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

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){
            e.printStackTrace();
       }
   }

FileOutputStream文件输出字节流

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

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

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

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();
       }
   }
}

注意 在使用字节缓冲区时,我们需要注意:

1、为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法 为:read(byte[] b);写入时的方法为:write(byte[ ] b, int off, int length)

2、程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法 关闭的情况。

缓冲字节流

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

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

下面我们通过两种方式(普通文件字节流与缓冲文件字节流)实现 一个文件的复制,来体会一下缓冲流的好处。

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();
       }
   }
}

注意

1、在关闭流时,应该先关闭最外层的包装流,即“后开的先关闭”。

2、缓存区的大小默认是8192字节,也可以使用其它的构造方法自己指定大小。

文件字符流

前面介绍的文件字节流可以处理所有的文件,如果我们处理的是文 本文件,也可以使用文件字符流,它以字符为单位进行操作。

文件字符输入流

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();
       }
   }
}

文件字符输出流

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

缓冲字符流

BufferedReader/BufferedWriter增加了缓存机制,大大提高了读 写文本文件的效率。

字符输入缓冲流

BufferedReader是针对字符输入流的缓冲流对象,提供了更方便的 按行读取的方法:readLine(); 在使用字符流读取文本文件时,我们 可以使用该方法以行为单位进行读取。

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();方法实现换行处理。

public class TestBufferedWriter {
    public static void main(String[] args) {
  //创建文件字符输出流对象
        try(FileWriter fw = new FileWriter("d:/sxt.txt");
            //创建字符输出缓冲流对象
            BufferedWriter bw = new BufferedWriter(fw)){
            //操作缓冲流
            bw.write("您好尚像素");
            bw.write("您好Oldlu");
            //换行
            bw.newLine();
            bw.write("何以解忧");
            bw.newLine();
            bw.write("唯有学堂");
            bw.flush();
       }catch (IOException e){
            e.printStackTrace();
       }
   }
}

注意

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

为文件中的内容添加行号

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)美国国家标准协会

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();
       }
   }
}

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

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();
    }
   }
  }
 }
}

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

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是字节流对象,代表输出到屏幕。

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();
       }
   }
}

字符输出流

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

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("Oldlu");
            pw.println("sxt");
            pw.flush();
       }catch(IOException e){
            e.printStackTrace();
       }
   }
}

通过字符输出流添加行号

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等)的方 法。

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();
       }
   }
}

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

对象流

我们前边学到的数据流只能实现对基本数据类型和字符串类型的读 写,并不能读取对象(字符串除外),如果要对某个对象进行读写 操作,我们需要学习一对新的处理流: ObjectInputStream/ObjectOutputStream。

处理基本数据类型数据

ObjectInputStream/ObjectOutputStream处理基本数据类型。

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){
            e.printStackTrace();
       }
   }
}

注意

1、对象流不仅可以读写对象,还可以读写基本数据类型。

2、读写基本数据类型时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。

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

序列化和反序列化是什么

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

序列化涉及的类和接口

ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写 到一个目标输出流中。

ObjectInputStream代表对象输入流,它的readObject()方法从一个 源输入流中读取字节序列,再把它们反序列化为一个对象,并将其 返回。 只有实现了Serializable接口的类的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用。

将对象序列化到文件

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

创建对象

public class Users implements Serializable {
    private int userid;
    private String username;
    private String userage;
    public Users(int userid, String username, String userage) {
        this.userid = userid;
        this.username = username;
        this.userage = userage;
    }
    public Users() { }
    public int getUserid() {
        return userid;
   }
    public void setUserid(int userid) {
        this.userid = userid;
   }
    public String getUsername() {
        return username;
   }
    public void setUsername(String username)
   {
        this.username = username;
   }
    public String getUserage() {
        return userage;
   }
    public void setUserage(String userage) {
        this.userage = userage;
   }
    @Override
    public String toString() {
 return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", userage='" + userage + '\'' +
                '}';
   }

序列化对象

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

将对象反序列化到内存中

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类指定。

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种设计模式中较为常用的一种模式。它可以实 现对原有类的包装和装饰,使新的类具有更强的功能。

装饰器模式

class Iphone {
 private String name;
 public Iphone(String name) {
     this.name = name;
}
 public void show() {
    System.out.println("我是" + name + ",可以在屏幕上显示");
  }
}
class TouyingPhone {
    public Iphone phone;
    public TouyingPhone(Iphone p) {
        this.phone = p;
 }
 // 功能更强的方法
 public void show() {
     phone.show();
     System.out.println("还可以投影,在墙壁上显示");
 }
}
public class TestDecoration {
 public static void main(String[] args) {
     Iphone phone = new Iphone("iphone30");
     phone.show();
     System.out.println("===============装饰后");
     TouyingPhone typhone = newTouyingPhone(phone);
     typhone.show();
 }
}

IO流体系中的装饰器模式

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

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

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

Apache commons-io工具包的使用

Apache基金会介绍

Apache软件基金会(也就是Apache Software Foundation,简称 为ASF),是专门为支持开源软件项目而办的一个非盈利性组织。 在它所支持的Apache项目与子项目中,所发行的软件产品都遵循 Apache许可证(Apache License)。 官方网址为: www.apache.org 。 很多著名的Java开源项目都来源于这个组织。比如:commons、 kafka、lucene、maven、shiro、struts等技术,以及大数据技术 中的:hadoop(大数据第一技术)、hbase、spark、storm、 mahout等。

commons-io工具包

Apache的commons-io工具包中提供了IOUtils/FileUtils,为我们 提供了更加简单、功能更加强大的文件操作和IO流操作功能。非常 值得大家学习和使用。

下载与添加commons-io包

 下载地址 https://commons.apache.org/proper/commons-io/download_i o.cgi

 添加jar包

FileUtils类中常用方法的介绍

打开FileUtils的api文档,我们抽出一些工作中比较常用的方法,进 行总结和讲解。总结如下:

读取文件内容,并输出到控制台上(只需一行代码!)

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"), "gbk");
 System.out.println(content);
 }
}

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

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

import java.io.File;
import java.io.FileFilter;
import org.apache.commons.io.FileUtils;
public class TestFileUtilsDemo2 {
      public static void main(String[] args) throws Exception {
      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;
      }
     }
   });
  }
}

IOUtils的妙用

打开IOUtils的api文档,我们发现它的方法大部分都是重载的。所 以,我们理解它的方法并不是难事。因此,对于方法的用法总结如 下:

我们没有必要对每个方法做测试,只是演示一下读入d:/sxt.txt文件 内容到程序中,并转成String对象,打印出来。

IOUtils的使用

import java.io.*;
import org.apache.commons.io.IOUtils;
public class TestIOUtilsDemo {
    public static void main(String[] args) throws Exception {
        String content = IOUtils.toString(new FileInputStream("d:/sxt.txt"),"utf-8");
        System.out.println(content);
   }
}

本章总结

按流的方向分类:

     输入流:数据源到程序(InputStream、Reader读进来)。

     输出流:程序到目的地(OutputStream、Writer写出去)。

按流的处理数据单元分类:

    字节流:按照字节读取数据(InputStream、 OutputStream)。

    字符流:按照字符读取数据(Reader、Writer)。

按流的功能分类:

    节点流:可以直接从数据源或目的地读写数据。

    处理流:不直接连接到数据源或目的地,是处理流的流。通 过对其他流的处理提高程序的性能。

IO的四个基本抽象类:InputStream、OutputStream、 Reader、Writer

InputStream的实现类:

    FileInputStream

    BufferedInputStream

    DataInputStream

    ObjectInputStream

OutputStream的实现类:

    FileOutputStream

    BufferedOutputStream

    DataOutputStream

    ObjectOutputStream

Reader的实现类

    FileReader

    BufferedReader

    InputStreamReader

Writer的实现类

    FileWriter

    BufferedWriter

    OutputStreamWriter

    PrintWriter

把Java对象转换为字节序列的过程称为对象的序列化。

把字节序列恢复为Java对象的过程称为对象的反序列化。

目录
相关文章
|
1月前
|
存储 Java 数据处理
|
1月前
|
Java API
java中IO与NIO有什么不同
java中IO与NIO有什么不同
|
22天前
|
存储 Java
探索 Java IO 流的多种实现方式
【4月更文挑战第4天】Java IO 流是处理输入输出的关键组件,包括文件流(FileInputStream/FileOutputStream)、字符流(FileReader/FileWriter)、缓冲区流(BufferedInputStream/BufferedOutputStream)、转换流(InputStreamReader/OutputStreamWriter)、数据流(DataInputStream/DataOutputStream)、对象流(ObjectInputStream/ObjectOutputStream)、随机访问文件流(RandomAccessFile)和管道流。
|
24天前
|
搜索推荐 Java
Java基础(快速排序算法)
Java基础(快速排序算法)
24 4
|
1月前
|
Java 关系型数据库 MySQL
Flink1.18.1和CDC2.4.1 本地没问题 提交任务到服务器 报错java.lang.NoClassDefFoundError: Could not initialize class io.debezium.connector.mysql.MySqlConnectorConfig
【2月更文挑战第33天】Flink1.18.1和CDC2.4.1 本地没问题 提交任务到服务器 报错java.lang.NoClassDefFoundError: Could not initialize class io.debezium.connector.mysql.MySqlConnectorConfig
52 2
|
1月前
|
Java
|
2月前
|
Java 数据处理
如何玩转Java IO?
【2月更文挑战第7天】
219 0
如何玩转Java IO?
|
2月前
|
存储 算法 Java
Java基础深化和提高 ---- 数据结构
Java基础深化和提高 ---- 数据结构
70 0
|
2月前
|
网络协议 Java 数据处理
Java基础深化和提高 ---- 网络编程
Java基础深化和提高 ---- 网络编程
102 0
|
2月前
|
算法 小程序 Java
Java基础深化和提高-------多线程与并发编程
Java基础深化和提高-------多线程与并发编程
122 0