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

news2025/1/12 8:50:55

目录

IO流技术介绍

 什么是IO

 什么是数据源

 流的概念

第一个简单的IO流程序 

 IO流的经典写法

 IO流新语法经典写法

Java中流的概念细分 

 按流的方向分类:

 按处理的数据单元分类:

 按处理对象不同分类:

 Java中IO流类的体系

 Java中IO的四大抽象类

 InputStream

 OutputStream

Reader

Writer

常用流详解 

文件字节流

 FileInputStream文件输入字节流

FileOutputStream文件输出字节流

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

 缓冲字节流

 文件字符流

文件字符输入流

文件字符输出流

缓冲字符流

字符输入缓冲流 

字符输出缓冲流

 为文件中的内容添加行号

转换流

通过转换流解决乱码

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

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

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

字符输出流

通过字符输出流添加行号

数据流

 对象流

 处理基本数据类型数据

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

 序列化和反序列化是什么

 序列化涉及的类和接口

 将对象序列化到文件

序列化对象

将对象反序列化到内存中

File类在IO中的作用

装饰器模式构建IO流体系

装饰器模式简介

 装饰器模式

IO流体系中的装饰器模式

 Apache commons-io工具包的使用

 Apache基金会介绍

 commons-io工具包

 FileUtils类中常用方法的介绍

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

IOUtils的妙用

 IOUtils的使用


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对象的过程称为对象的反序列化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CleanMyMac X真正好用的Mac电脑系统优化软件应用工具

最用户友好的Mac问题修复程序。删除系统垃圾、不需要的应用程序和恶意软件,并调整您的Mac以获得最高速度。对于速度较慢的计算机,CleanMyMac就能立即使用。 CleanMyMac2023之所以能够获得众多Mac的老用户们的喜爱,是因为其有着非常强大功能提…

数据可视化之大数据平台可视化

一 前言 在简化数据量和降低大数据应用的复杂性中,大数据分析发挥着关键的作用。可视化是其中一个重要的途径,它能够帮助大数据获得完整的数据视图并挖掘数据的价值。大数据分析和可视化应该无缝连接,这样才能在大数据应用中发挥最大的功效。…

nodejs+mysql航空飞机票销售酒店预订系统vue

(1)对机票预订管理系统进行需求分析、确定所需要的模块;建立数据字典、数据流等;书写可行性分析和需求分析说明书。 (2)对机票预订管理系统进行概要设计:建立软件体系结构,画出用例图、E-R图等;书写数据要求说明书和各…

【雷达通信】阵列信号处理(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

聊聊Go里面的闭包

以前写 Java 的时候,听到前端同学谈论闭包,觉得甚是新奇,后面自己写了一小段时间 JS,虽只学到皮毛,也大概了解到闭包的概念,现在工作常用语言是 Go,很多优雅的代码中总是有闭包的身影&#xff0…

嵌入式图形开发框架Qt——让牙科手术开始迈入机器人时代

Neocis软件工程总监,Jim Tieman: “我们Neocis是一家致力于提高科医生能力和促进病人护理的牙科机器人公司。之前我们有一个Microsoft Foundations Class (MFC)的应用程序,由于现在团队擅长MFC开发的技术员并不多,维护起来也很费劲。之前这个…

AST 初探深浅,代码还能这样玩?

AST 听起来好像是个很新的东西,那么具体有什么用,好不好用就在这篇文章中找到答案吧~ 我们简单将这个词拆分抽象、语法、树,如果我们能够顺利将这个词拆分,那么我们也就掌握了其核心所在 抽象:抽象的反义词是具象&…

微信小程序|使用小程序制作一个2048小游戏

文章目录一、文章前言二、创建小程序三、功能开发一、文章前言 此文主要通过小程序实现2048游戏,游戏操作简单,容易上手。 规则:正常打开游戏的界面,会只有两个2,每次移动后都会出现一个2,数字大了之后会出…

RabbitMQ初步到精通-第八章-Java-AMQP-Client源码分析

目录 第八章-Java-AMQP-Client源码分析 1、背景 1.1 客户端介绍 1.2 看源码好处 1.3 如何看源码 2、生产者 3、消费者监听 4、创建连接 5、消费者消费 6. 总结: 第八章-Java-AMQP-Client源码分析 1、背景 1.1 客户端介绍 通过前面几章的学习,大家对…

工作以来一直在CRUD,Spring源码该怎么阅读?这份价值百万的源码解析让你如有神助!

初学SpringBoot框架时,第一次启动服务,直呼什么鬼?只需要简单的几步配置,几个核心的注解,就可以快速实现工程的搭建和运行; 虽然从Spring框架迁移到SpringBoot框架,在初期会有很多的不适应&…

谈谈对跨域(跨源)的一些理解

一、相关概念 1、什么是跨域? 跨域又称为跨源,是指在违反了浏览器的同源政策,也就是协议、域名和端口号三者不完全一致的情况下产生的。只要客户端与浏览器的三者有一项不同,就属于不同源,就会产生跨域。 很多初级开…

G 蛋白偶联受体与小分子化合物的相互作用

化学遗传学 (Chemogenetics) 是指一种蛋白被改造与先前未被识别的小分子化合物相互作用的过程。多种蛋白的改造已被报道,包括激酶、非激酶的酶类、G 蛋白偶联受体 (GPCRs) 和配体门控离子通道。化学遗传学技术 DREADDs (Designer receptors exclusively activated b…

MapReduce分区、排序、Combiner

Shuffle MapReduce的Map阶段与Reduce阶段之间有一个Shuffle的过程,包括分区、排序等内容。数据从Map阶段出来后,会进入一个环形缓冲区(默认100M),环形缓冲区中会同时记录数据和索引,当使用了80%的时候&…

PostgreSQL主从数据库数据同步

运行环境 操作系统:Debian 11.5 数 据 库:PostgreSQL 14.6 主数据库:192.168.8.68 从数据库:192.168.8.69 使用apt-get安装postgresql,安装方法可以参考 https://blog.csdn.net/itbs/article/details/127909359?…

智能家居环境小护士(原理图、pcb、源码、设计报告)

目录 ARM-STM32校园创新大赛 1 题 目: 智能家居环境小护士 1 摘要 1 引言 2 系统方案 3 整套系统的工作原理是:单片机是整套系统的控制核心,温湿度传感器负责测试环境中的温湿度;烟雾传感器负责检测空气中的有毒气体,…

数据同步工具DataX介绍和原理

目录1. DataX介绍2. 框架设计3. 架构1. DataX介绍 DataX是一个各种数据源之间的离线数据同步工具 DataX的设计理念是一种星型数据链路。DataX作为中间传输载体负责连接各种数据源,通过reader从一个数据源读取数据,再通过writer将数据写入另一个数据源。…

Hadoop运行模式

hgfhfg Hadoop运行模式包括:本地模式、伪分布式模式以及完全分布式模式。 Hadoop官方网站:Apache Hadoop 一、本地运行模式 官方Grep案例 1. 创建在hadoop-2.7.2文件下面创建一个input文件夹 mkdir input 2. 将Hadoop的xml配置文件复制到input cp et…

FTP替代产品方案的优异性体现在哪些方面?

多年来,FTP一直是最常见的交换文件的方式,FTP-FTPS-SFTP似乎是FTP的不断迭代更新,但是究竟是技术更新导致FTP过时?还是它真的已经满足不了企业的需求了? 之前,大家选择FTP往往是因为它简单易得的特性&…

在 MySQL 中模拟外部联接 (LEFT、RIGHT、INNER JOIN、OUTER JOIN)

上周的文章详细介绍了 SELECT 查询中的外部联接。它是一种 JOIN 类型,可以从相关表中返回匹配和不匹配的行。遗憾的是,并非所有数据库(DB)供应商都支持它,包括 MySQL。但这没关系,因为可以通过组合其他三种…

【Java】构造方法及类的初始化

一. 利用构造方法给对象初始化 1. 构造方法的概念 构造方法(也称为构造器)是一个特殊的成员方法,其名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。 构造方法的作用就是给对象中的成…