JavaIO流基础使用

1、概念细分

1.1、流的方向

  • 输入流:数据流从数据源到程序以InputStream、Reader结尾的流
  • 输出流:数据流从程序到目的地以OutputStream、Writer结尾的流

1.2、处理的数据单元

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

1.3、处理对象不同分类

  • 节点流:可以直接从数据源或目的地读写数据,如:FileInputStream、FileReader、DataInputStream等
  • 处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理,提高程序的性能,如:BufferedInputStream、BufferedReader等。处理流也叫包装流

常用的流有如下类:(所有需要指定文件路径的类,都可以使用File类进行包装)

在这里插入图片描述

2、文件字节/字符流

重要:

  1. 所有IO类使用完毕流后,一定要在finally中关闭流,否则会占用内存
  2. 关闭的时候,要遵循后开先关的顺序
  3. 编写完毕输出流后,一定要调用**flush()**方法,才能渲染成功

2.1、文件字节流

字节流可以处理很多数据类型的数据,如:图片,文本,视频等,但是中文字符会占据两个字节

返回值为Ascii码值

  • 使用字节流读取中文文件

    1个中文

    GBK:占用两个字节

    UTR-8:占用三个字节

2.1.1、FileInputStream输入

指定的文件地址必须存在,否则会异常:java.io.FileNotFoundException: d:\test1.txt (系统找不到指定的文件。

1、基本使用

任务:在idea中获取D盘中的test.txt文本文件里的内容

package com.tcc.test;

import com.tcc.entity.User;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            int r1 = fis.read(); // 读取一个字,转为Ascii码值接收   a:97
            int r2 = fis.read(); // 读取一个字,转为Ascii码值接收   b:98
            int r3 = fis.read(); // 读取一个字,转为Ascii码值接收   c:99
            System.out.println(r1);
            System.out.println(r2);
            System.out.println(r3);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2、进阶使用

这样一个字节一个字节的读,速度慢,消耗性能,我们可以写一个遍历

package com.tcc.test;

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

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            int temp = 0;
            // 如果读取不到值,read()方法就会返回-1
            while ((temp = fis.read()) != -1){
                System.out.println(temp); // 97 98 99
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
3、转换为AscII码显示

直接把temp强转为char类型即可

System.out.println((char)temp); // a b c
4、读取图片

和读取文本文件一样,只不过路径指向了张图片

fis = new FileInputStream("d:/test.png");
  • 结果:(只截取了部分…)

    在这里插入图片描述

2.1.2、FileOutputStream输出

指定的文件地址可以不存在,如果不存在则创建,存在则修改

1、基本使用

任务:将上面读取的文本文件内容输出到新指定的test1.txt文本文件中(相当于复制粘贴)

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            // 指定的文件不存在,则会创建,否则会修改
            fos = new FileOutputStream("d:/test1.txt");
            int temp = 0;
            while ((temp = fis.read()) != -1){
                fos.write(temp);
            }
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fos != null){
                    fos.close();
                }
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2、追加输入

如果我们在原来代码下面再创建一个输出流,然后white一个AscII码为97的字符(a),则会发现不是在原来的内容上追加,而是把里面的内容修改为了:a

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            // 指定的文件不存在,则会创建,否则会修改
            fos = new FileOutputStream("d:/test1.txt");
            int temp = 0;
            while ((temp = fis.read()) != -1){
                fos.write(temp);                
            }
            fos.flush();
            // 重新创建一个对象,然后往里面写入a,结果是替换,不是追加
            fos = new FileOutputStream("d:/test1.txt");
            fos.write(97); // a
            fos.flush();

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fos != null){
                    fos.close();
                }
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

那么怎么在原来的基础上新增呢,只需要在构造方法后面添加一个参数即可

在这里插入图片描述

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            // 指定的文件不存在,则会创建,否则会修改
            fos = new FileOutputStream("d:/test1.txt");
            int temp = 0;
            while ((temp = fis.read()) != -1){
                fos.write(temp);
            }
            fos.flush();
            // 重新创建一个对象,然后往里面写入a,结果是替换,不是追加 如果在后面添加一个布尔类型的参数;是否追加,为true则追加
            fos = new FileOutputStream("d:/test1.txt",true);
            fos.write(97); // a
            fos.flush();

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fos != null){
                    fos.close();
                }
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2.1.3、注意事项

AscII码是处理不了中文的

  1. 修改test.txt文本文件内容,添加中文

    在这里插入图片描述

  2. 运行代码,查看是否能转换成功显示(转换失败)

    在这里插入图片描述

2.2、文件字符流

字符流只能修改文本文件的数据,中文只占据一个字节

返回值为Unicode编码值

2.2.1、FileReader输入

和字节流的方法基本一样,这里就直接写进阶写法和转码了

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("d:/test.txt");
            int temp = 0;
            // 进阶写法
            while ((temp = fr.read()) != -1){
                // 转换编码	unicode是可以转换中文的
                System.out.println((char)temp); // abc张三
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fr != null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
2.2.2、FileWriter输出
1、基本使用

也和字节流的方法一样,但是可以直接写入中文

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("d:/test.txt");
            // 不存在的文本文件,不存在就创建
            fw = new FileWriter("d:/test1.txt");
            int temp = 0;
            // 进阶写法
            while ((temp = fr.read()) != -1){
                fw.write(temp);
            }
            fw.write("直接输入文本");
            fw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fr != null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2、输出换行

只需要在字符串内加入\r\n即可换行

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("d:/test.txt");
            // 不存在的文本文件,不存在就创建
            fw = new FileWriter("d:/test1.txt");
            int temp = 0;
            // 进阶写法
            while ((temp = fr.read()) != -1){
                fw.write(temp);
            }
            // \r\n实现换行效果
            fw.write("\r\n直接输入文本");
            fw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fr != null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2.3、总结

1、相同点
  1. 字符流和字节流的方法用法基本相同
  2. 字符流和字节流都需要在finally中执行close()方法
  3. 字符流和字节流的输入流都需要指定真实存在的路径,输出流都可以不指定真实存在的文件,也都必须要执行flush()方法才能渲染成功
2、不同点
  1. 字符流返回的值为AscII编码值,字节流返回的为Unicode编码值
  2. 字符流可以处理图片,视频,文本(英文)类型的内容,字节流只能处理文本类型的内容(可以直接写入字符串类型内容)
  3. 字符流在都处理文本文件的时候,如果想写入的时候实现换行效果,可以在字符串内加上\r\n实现换行
  4. 在字节流中,中文占据两个字节**(即GBK码表中一个中文占两个字节)**,而在字符流中,中文占据一个字节。

3、缓冲流

通过下面的代码,我们可以发现,我们写的代码,都是读取一个字符,写入一个字符,这样当文件很大的时候,就会执行的非常慢,影响性能,那么我们可不可以一下读取指定的字符,然后一下子写入读取到的,这样就可以循环不几次,可以加快性能

在这里插入图片描述

3.1、添加缓冲区(方法一)

我们先修改文本文件,让内容多一些,这样才能测试明显

测试的时候,先使用原来的,查看方法执行完需要多久,再使用优化后的,可以明显发现执行时间变短了

记得white()方法也要修改参数

1、字节缓冲区

这里就使用上面(可以复制粘贴)的例子来举例了

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("d:/test.txt");
            fos = new FileOutputStream("d:/test1.txt");
            int temp = 0;
            // 每次取1024个字节,取值必须为2的整数次幂
            byte[] bytes = new byte[1024];
            // 这里read()方法,可以放入参数    当获取不到也会获得-1
            while ((temp = fis.read(bytes)) != -1){
                // 这里指,从0开始,到获取到的长度结束
                fos.write(bytes,0,temp);
            }
             fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fos != null){
                    fos.close();
                }
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
  • 结果

    在这里插入图片描述

2、字符缓冲区

这里也拿上面(可以复制粘贴的)来举例了

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("d:/test.txt");
            // 不存在的文本文件,不存在就创建
            fw = new FileWriter("d:/test1.txt");
            int temp = 0;
            char[] chars = new char[1024];
            while ((temp = fr.read(chars)) != -1){
                fw.write(chars,0,temp);
            }
            fw.write("直接输入文本");
            fw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fr != null){
                    fr.close();
                }
                if(fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
3、区别

read()里面的参数类型不一样

字节流:byte[] bytes = new byte[1024];

字符流:char[] chars = new char[1024];

3.2、使用JDK缓冲流(方法二)(建议)

这个是使用JDK自带的类,就和我们上面写的方法其实差不多的,但是他的数组参数为8192,还有自己封装了一些好用的方法,所以我们建议使用jdk自带的

java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)

当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这是如果使用缓冲流就能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地。

1、字节缓冲流

BufferedInputStream、BufferedOutputStream

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;

        try {
            fis = new FileInputStream("d:/test.txt");
            // 包装类,把InputStream类型当做构造方法,嵌套进去进行封装
            bis = new BufferedInputStream(fis);
            fos = new FileOutputStream("d:/test1.txt");
            bos = new BufferedOutputStream(fos);
            int temp = 0;
            while ((temp = bis.read()) != -1){
                bos.write(temp);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                // 要遵循后开先关的顺序
                if(bos != null){
                    bos.close();
                }
                if(fos != null){
                    fos.close();
                }
                if(bis != null){
                    bis.close();
                }
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
2、字符缓冲流

BufferedReader、BufferedWriter

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader br = null;
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            fr = new FileReader("d:/test.txt");
            br = new BufferedReader(fr);
            fw = new FileWriter("d:/test1.txt");
            bw = new BufferedWriter(fw);
            int temp = 0;
            while ((temp = br.read()) != -1){
                bw.write(temp);
            }
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(br != null){
                    br.close();
                }
                if(fr != null){
                    fr.close();
                }
                if(bw != null){
                    bw.close();
                }
                if(fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
3、字符缓冲流自带方法

常用的有1、readLine():读取一行 2、newLine():换行

如果是读取一行的话,跳出循环的条件就不是 -1了,而是null

temp也不是int类型的了,是String类型

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader br = null;
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            fr = new FileReader("d:/test.txt");
            br = new BufferedReader(fr);
            fw = new FileWriter("d:/test1.txt");
            bw = new BufferedWriter(fw);
            // 改为String类型接收
            String temp = "";
            // 读取一行,跳出循环条件为null
            while ((temp = br.readLine()) != null){
                bw.write(temp);
                // 如果读取一行的话,一定要执行换行的方法,否则可能和预想结果不一致
                bw.newLine();
            }
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(br != null){
                    br.close();
                }
                if(fr != null){
                    fr.close();
                }
                if(bw != null){
                    bw.close();
                }
                if(fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

4、转换流

InputStreamReader、OutputStreamWriter用来实现将字节流转化成字符流。比如,如下场景

System.in是字节流对象,代表键盘的输入,如果我们想按行接收用户的输入时,就必须用到缓冲字符流BufferedReader的特有方法readLine(),但是经过观察就会发现,在创建BufferedReader的构造方法的参数必须是一个Reader对象,这时候我们的转换流InputStreamReader就派上用场了

System.out也是字节流对象,代表输出到显示器,并且要将读取的一行字符串直接显示到控制台,就需要用到字符流的white(String str)方法,所以我们要使用OutputStreamWriter将字节流转化为字符流

在这里插入图片描述

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

当我们输入abc时,只能读取到第一个a:97(AscII码值)

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        InputStream in = System.in;
        int r = 0;
        try {
            r = in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(r);
    }
}
  • 结果

    在这里插入图片描述

这时候我们就需要一下读取一行了,把字符节转换为字符流

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        try {
            // 一下读取一行
            String s = br.readLine();

            // 将接收到的String类型的数据,写入到控制台上
            bw.write(s);
            bw.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 结果

    在这里插入图片描述

5、字符输出流

专门用于字符输出的流对象:PrintWriter。该对象具有自动刷新缓冲字符输出流

特点是:

  • 可以按行写出字符串
  • 可通过println()方法实现自动换行
  • 不需要执行flush()方法渲染
package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        PrintWriter pw = null;
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("d:/test.txt"));
            pw = new PrintWriter("d:/test1.txt");
            String temp = "";
            while ((temp = br.readLine()) != null){
                pw.println(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(pw != null){
                    pw.close();
                }
                if(br != null){
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

6、字节数组流

ByteArrayInputStream 和 ByteArrayOutputStream 经常用在需要流和数组之间转化的情况

说白了,FileInputStream是把文件当做数据源,BytesArrayInputStream则是把内存中的"字节数组对象"当做数据源

6.1、字节数组输入流

ByteArrayInputStream

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        ByteArrayInputStream bais = null;
        byte[] bytes = "abcd".getBytes();
        try {
            bais = new ByteArrayInputStream(bytes);
            int temp = 0;
            while ((temp = bais.read()) != -1){
                System.out.println((char)temp);// abcd
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 结果

    在这里插入图片描述

6.2、字节数组输出流

ByteArrayOutputStream

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            baos.write('a');
            baos.write('b');
            baos.write('c');
            // 转换为byte数组
            byte[] bytes = baos.toByteArray();
            for (byte aByte : bytes) {
                System.out.println(aByte); // 97 98 99
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 结果

    在这里插入图片描述

7、数据流

数据流将基本数据类型字符串类型祖籍为数据源,从而允许与机器无关的方式从底层输入输出流中操作java基本数据类型与字符串类型

DataInputStream 和 DataOutputStream提供了可以存取与机器无关的所有java基础类型数据(如:int、double、String)等的方法

7.1、数据输出流

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        DataOutputStream dos = null;
        try {
            dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("d:/test1.txt")));
            dos.writeByte(1);
            dos.writeInt(2);
            dos.writeShort(3);
            dos.writeLong(4);
            dos.writeFloat(5.0f);
            dos.writeDouble(6.0);
            dos.writeBoolean(true);
            dos.writeChar('a');
            dos.writeUTF("我是第九个");

            dos.flush();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(dos != null){
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果(乱码不用管)

    在这里插入图片描述

7.2、数据输入流

读取的类型顺序一定要和写入的顺序一样

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new BufferedInputStream(new FileInputStream("d:/test1.txt")));

            byte b = dis.readByte();
            System.out.println(b);

            int i = dis.readInt();
            System.out.println(i);

            short s = dis.readShort();
            System.out.println(s);

            long l = dis.readLong();
            System.out.println(l);

            float f = dis.readFloat();
            System.out.println(f);

            double d = dis.readDouble();
            System.out.println(d);

            boolean b2 = dis.readBoolean();
            System.out.println(b2);

            char c = dis.readChar();
            System.out.println(c);

            String s2 = dis.readUTF();
            System.out.println(s2);

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(dis != null){
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

8、对象流

对象的本质是用来组织和存储数据的,对象本身也是数据。那么,能不能将对象存储到硬盘上的文件中呢?能不能将对象通过网络传输到另一个电脑呢?我们可以通过序列化和反序列化来实现这些需求。

序列化和反序列化是什么

序列化:就是将对象转化为字符流的过程。

反序列化:就是将字节流转化为对象的过程。

被序列化的对象类型需要实现Serializable序列化接口,此接口是标志接口。

为保证序列化与反序列化的过程稳定,建议在类中添加序列化版本号。

可以transient关键字,指定成员变量不被序列化。

8.1、对象输出流

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

8.1.1、操作数据类型

拥有和DataOutputStream一样的方法

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("d:/test1.txt")));
            oos.writeByte(1);
            oos.writeInt(2);
            oos.writeShort(3);
            oos.writeLong(4);
            oos.writeFloat(5.0f);
            oos.writeDouble(6.0);
            oos.writeBoolean(true);
            oos.writeChar('a');
            oos.writeUTF("我是第九个");

            oos.flush();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(oos != null){
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果(乱码不用管)

    在这里插入图片描述

8.1.2、操作对象
  1. 先创建一个对象,实现Serializable接口

    package com.tcc.entity;
    
    import java.io.Serializable;
    
    /**
     * @author 宇辰
     * @date 2022/9/26-11:27
     **/
    public class User implements Serializable {
    
        /**
         * 这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!
         * java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,
         * 在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,
         * 如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。
         */
        private static final long serialVersionUID = 1L;
    
        private String name;
        private String sex;
        private Integer age;
    
        public User(String name, String sex, Integer age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public User() {
        }
    }
    
    
  2. 对象创建完毕后,我们开始把对象进行序列化操作,并放到test1.txt文件里面

    package com.tcc.test;
    
    import com.tcc.entity.User;
    
    import java.io.*;
    
    /**
     * @author 宇辰
     * @date 2022/8/31-8:53
     **/
    public class Test {
        public static void main(String[] args) {
            ObjectOutputStream oos = null;
            try {
                oos = new ObjectOutputStream(new FileOutputStream("d:/test1.txt"));
                User user = new User("张三","男",20);
                oos.writeObject(user);
                oos.flush();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(oos != null){
                        oos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 结果(因为我们使用的是字节流存入的,所以存入的是字节,乱码正常)

      在这里插入图片描述

8.2、对象输入流

ObjectInputStream 代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

8.2.1、操作数据类型

拥有和DataInputStream一样的方法 读取的顺序也要和存入的顺序相同

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("d:/test1.txt")));

            byte b = ois.readByte();
            System.out.println(b);

            int i = ois.readInt();
            System.out.println(i);

            short s = ois.readShort();
            System.out.println(s);

            long l = ois.readLong();
            System.out.println(l);

            float f = ois.readFloat();
            System.out.println(f);

            double d = ois.readDouble();
            System.out.println(d);

            boolean b2 = ois.readBoolean();
            System.out.println(b2);

            char c = ois.readChar();
            System.out.println(c);

            String s2 = ois.readUTF();
            System.out.println(s2);

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(ois != null){
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

8.2.2、操作对象

使用对象输出流创建的对象user

package com.tcc.test;

import com.tcc.entity.User;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("d:/test1.txt"));
            User user = (User)ois.readObject();
            System.out.println(user.toString());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(ois != null){
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

9、随机访问流

它不属于IO的四大类,是单独的类

RandomAccessFile可以实现两个作用:

  1. 实现对一个文件做读和写的操作

  2. 可以访问文件的任意位置,不想其他流只能按照先后顺序读取

    在开发某些客户端软件时,经常用到这个功能强大的可以“任意操作文件内容”的类。比如,软件的使用次数和使用日期,可以通过本类访问文件中保存次数和日期的地方进行比对和修改。

    java很少开发客户端软件,所以在java开发中这个类用的相对较少。

这个流的三个核心方法

  1. RandomAccessFile(String name, String mode)name用来确定文件;mode取r(读)或rw(可读写),通过mode可以确定流对文件的访问权限。
  2. seek(long a)用来定位流对象读写文件的位置,a确定读写位置距离文件开头的字节个数,指针默认从0开始。
  3. getFilePointer()获得流的当前读写位置。
package com.tcc.test;

import com.tcc.entity.User;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        RandomAccessFile raf = null;
        int[] ints = new int[]{10,20,30,40,50,60};
        try {
            // rw 可读可写
            raf = new RandomAccessFile("d:/test1.txt","rw");
            // 写入定义的数字
            for (int i : ints) {
                raf.writeInt(i);
            }

            // 读取第二个数字  指针默认从0开始    一个Int类型占4个字节,所以4为第二个数字的第一个字节
            raf.seek(4);
            System.out.println(raf.readInt());; // 20

            System.out.println("------------------------------------");

            // 隔一个读一个
            for (int i = 0; i < 5; i+=2) {
                raf.seek(i * 4);
                System.out.print(raf.readInt() + "\t"); // 10 30 50
            }

            System.out.println();

            // 把30替换为35
            raf.seek(8);
            raf.writeInt(35);
            for (int i = 0; i < 5; i+=2) {
                raf.seek(i * 4);
                System.out.print(raf.readInt() + "\t"); // 10 35 50
            }

            System.out.println();
            
            // 获取指针所在位置
            System.out.println(raf.getFilePointer()); // 20 就是在第六位的第一个字节上 公式:(6-1) * 4
            System.out.println(raf.readInt());// 60

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

10、工具类/小任务

1、小任务

1.1、实现与控制台连续对话

实现与控制台连续对话,直到输入exit就退出

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        try {
            bw.write("请输入:");
            bw.flush();
            while (true){
                String s = br.readLine();
                if("exit".equals(s)){
                    bw.write("已关闭");
                    bw.flush();
                    break;
                }
                bw.write("您输入的是:" + s);
                // 换行
                bw.newLine();
                bw.write("请继续输入:");
                bw.flush();
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 结果

    在这里插入图片描述

1.2、实现显示行号
package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader br = null;
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            fr = new FileReader("d:/test.txt");
            br = new BufferedReader(fr);
            fw = new FileWriter("d:/test1.txt");
            bw = new BufferedWriter(fw);
            // 改为String类型接收
            String temp = "";
            // 定义行号
            int i = 1;
            // 读取一行,跳出循环条件为null
            while ((temp = br.readLine()) != null){
                bw.write("第"+i+"行:" + temp);
                // 如果读取一行的话,一定要执行换行的方法,否则可能和预想结果不一致
                bw.newLine();
                i++;
            }
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(br != null){
                    br.close();
                }
                if(fr != null){
                    fr.close();
                }
                if(bw != null){
                    bw.close();
                }
                if(fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 结果

    在这里插入图片描述

2、工具类

2.1、复制粘贴

此处使用的是字节流进行输入输出

package com.tcc.test;

import java.io.*;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test {
    public static void main(String[] args) {
        fileCopy("d:/testPic.jpg","d:/testPic1.jpg");
    }

    public static void fileCopy(String src, String targetSrc){
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;

        try {
            fis = new FileInputStream(src);
            bis = new BufferedInputStream(fis);
            fos = new FileOutputStream(targetSrc);
            bos = new BufferedOutputStream(fos);
            int temp = 0;
            while ((temp = bis.read()) != -1){
                bos.write(temp);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
                try {
                    if(bos != null){
                        bos.close();
                    }
                    if(fos != null){
                        fos.close();
                    }
                    if(bis != null){
                        bis.close();
                    }
                    if(fis != null){
                        fis.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

        }
    }
}
  • 结果

    在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值