总结Java中IO流以及几种常见操作

前言:以前学习Java的时候对IO接触很少,所以总觉得这一块知识很复杂,所以就找个时间,总结一下流的处理、用法,巩固一下知识

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


1.IO流

IO流可以分为字符流、字节流两大类。 字节流一个字节占8位(bit),字符流根据不同字符所占的字节不同而读取的大小不同,常见中文一个字符占两个字节(16bit)。

IO流很巧妙的使用了设计模式《装饰模式》,外层的处理流包裹着内层的节点流,外层的处理流方法对内层的节点流方法调用,然后做对应的扩展

在这里插入图片描述

流的体系因为功能不同,但是有共性内容,形成继承体系。该体系一共有四个基类,而且都是抽象类。:

字节流:InputStream OutputStream 字符流:Reader Writer

类型说明
文件流对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream
缓冲流在读入或写出时,数据可以基于缓冲批量读写,以减少I/O的次数:BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream
内存流1.从/向内存数组读写数据: CharArrayReader、 CharArrayWriter、ByteArrayInputStream、ByteArrayOutputStream
2.从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream
转换流按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader,Writer的转换类):InputStreamReader、OutputStreamWriter
对象流字节流与对象实例相互转换,实现对象的序列化 :ObjectInputStream、ObjectOutputStream 注意: 1.读取顺序和写入顺序一定要一致,不然会读取出错。 2.在对象属性前面加transient关键字,则该对象的属性不会被序列化
打印流只有输出,没有输入,在整个IO包中,打印流是输出信息最方便的类,分为 PrintWriter(字符打印流)、PrintStream(字节打印流)
DataConversion数据流按基本数据类型读/写,可以字节流与基本类型数据相互转换:DataInputStream、DataOutputStream
过滤流在数据进行读或写时进行过滤:FilterReader、FilterWriter、FilterInputStream、FilterOutputStream
合并流把多个输入流连接成一个输入流 :SequenceInputStream
操作ZIP包流ZipInputStream可以读取zip格式的流,ZipOutputStream可以把多份数据写入zip包
操作JAR包流JarInputStream/JarOutputStream,派生自ZipInputStream/ZipOutputStream,它增加的主要功能是直接读取jar文件里面的MANIFEST.MF文件。因为本质上jar包就是zip包,只是额外附加了一些固定的描述文件。
管道流实现管道的输入和输出(进程间通信): PipedReader、PipedWriter、PipedInputStream、PipedOutputStream
Counting计数在读入数据时对行记数 :LineNumberReader、LineNumberInputStream
推回输入流通过缓存机制,进行预读 :PushbackReader、PushbackInputStream
接收和响应客户端请求流servletinputstream:用来读取客户端的请求信息的输入流 servletoutputstream:可以将数据返回到客户端
随机读取写入流RandomAccessFile 既可以读取文件内容,也可以向文件输出数据,RandomAccessFile 对象包含一个记录指针,标识当前读写处的位置,可以控制记录指针从IO任何位置读写文件
加密流对流加密/解密
CipherOutputStream 由一个 OutputStream 和一个 Cipher 组成 ,write() 方法在将数据写出到基础 OutputStream 之前先对该数据进行处理(加密或解密) ,
同样CipherInputStream是由InputStream和一个Cipher组成,read()方法在读入时,对数据进行加解密操作.
数字签名流DigestInputStream : 最大的特点是在读取的数据的时候已经调用MessageDigest实例的update方法,当数据从底层的数据流中读取之后就只可以直接调用MessageDigest实例的digest()方法了,从而完成对输入数据的摘要加密
DigestOutputStream :最大的特点是在向底层的输出流写入数据的时候已经调用MessageDigest实例的update方法,并作为MessageDigest的输入数据,之后就可以直接调用MessageDigest实例的digest()方法完成加密过程;同样的,是否对数据加密也是由该流的on(boolean b)方法进行控制的,如果设置成false,那么在写出数据的过程中便不会将数据传给update方法,那么此时它跟普通的输出流就没有任何区别了。

2.操作方法

不同的操作,我们可以选择对应的流来进行处理,下面是总结的操作方法和代码演示。 在这里插入图片描述

1.字节流

/**
 * @author :endwas
 * @description:io流test
 * @date :Created in 2021/3/25 14:22
 */
public class IOStream {
    private static void byteStream(String inputUrl, String outUrl) {
        //新建一个文件目录
        File file = new File(outUrl);
        try (FileInputStream in = new FileInputStream(inputUrl);
             FileOutputStream out = new FileOutputStream(file)) {
            //通过逐个读取,存入字节,实现文件拷贝
            int c;
            while ((c = in.read()) != -1) {
                out.write(c);
            }
            System.out.println("传输完成!");
        } catch (IOException e) {
            // log
        }
    }
    public static void main(String[] args) {
        byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
    }
}

2.字符流

在这里插入代码片/**
 * @author :endwas
 * @description:io流test
 * @date :Created in 2021/3/25 14:22
 */
public class IOStream {
    private static void charStream(String url, String outrul) {
        File f = new File(outrul);
        try (FileReader r = new FileReader(url);
             FileWriter w = new FileWriter(outrul)) {
             // 切记这个数字不是指读的数量而是读取的字符相对应的数字,一次两个字节
            int k;
            while ((k = r.read()) != -1){
                w.write(k);
            }
        } catch (IOException e) {
            // log
        }
    }

    public static void main(String[] args) {
//        byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
        charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
    }
}

3.缓冲流

本质只是套了层管道,让IO流能得到缓冲,提高效率

/**
 * @author :endwas
 * @description:io流test
 * @date :Created in 2021/3/25 14:22
 */
public class IOStream {
    private static void bufferStream(String url, String outUrl) {
        //新建一个文件目录
        File file = new File(outUrl);
        try (FileInputStream in = new FileInputStream(url);
             FileOutputStream out = new FileOutputStream(file);
             BufferedInputStream bis = new BufferedInputStream(in);
             BufferedOutputStream bos = new BufferedOutputStream(out)) {
            //通过逐个读取,存入字节,实现文件拷贝
            int c;
            while ((c = bis.read()) != -1) {
                bos.write(c);
            }
            System.out.println("传输完成!");
        } catch (IOException e) {
            // log
        }
    }
    public static void main(String[] args) {
//        byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
//        charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
        bufferStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
    }
}

BufferedWriter and BufferedReader 注:这种方法读取会将读取的行末尾换行或者空格删去;

 private static void bufferReader(String url, String outUrl) {
        File f = new File(url);
        File f2 = new File(outUrl);
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
             BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(f2))) {
            String line;
            while ( (line = bufferedReader.readLine())!= null){
                bufferedWriter.write(line);
            }
        } catch (IOException e) {
            // log
        }

    }
    public static void main(String[] args) {
//        byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
//        charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
//        bufferStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
        bufferReader("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");

    }

4.对象流

一般作用于对象序列化,要注意实现Serializable 接口 对象流输入流:,不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错 下列代码转自:https://blog.csdn.net/qq877728715/article/details/103844382

@Data
@Getter
@Setter
@ToString
@AllArgsConstructor
class User implements Serializable {
    private static final long serialVersionUID = -2119298670197329888L;

    private Integer id;
    private String userName;
    private String passWord;
}
/**
 * 对象流: 不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错java.io.EOFException
 * 1、写出后读取
 * 2、读取的顺序与写出保持一致
 * 3、不是所有的对象都可以序列化Serializable,必须要实现Serializable接口
 * <p>
 * ObjectOutputStream ObjectInputStream
 */
public class ObjectStream0 {

    @Test
    public void tesObjectStream() throws IOException, ClassNotFoundException {
        // 写出 -->序列化
        ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("obj.ser")));
        // 操作数据类型 +数据
        oos.writeUTF("编码辛酸泪");
        oos.writeInt(18);
        oos.writeBoolean(false);
        oos.writeChar('a');

        //对象
        oos.writeObject("谁解其中味");
        oos.writeObject(new Date());
        Employee emp = new Employee("马云", 400);
        oos.writeObject(emp);
        oos.flush();
        oos.close();

        // 读取 -->反序列化
        ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("obj.ser")));
        //序列化写出和反序列化读取读取顺序必须一致
        String msg = ois.readUTF();
        int age = ois.readInt();
        boolean flag = ois.readBoolean();
        char ch = ois.readChar();
        System.out.println(flag);
        // 对象的数据还原
        Object str = ois.readObject();
        Object date = ois.readObject();
        Object employee = ois.readObject();

        if (str instanceof String) {
            String strObj = (String) str;
            System.out.println(strObj);
        }
        if (date instanceof Date) {
            Date dateObj = (Date) date;
            System.out.println(dateObj);
        }
        if (employee instanceof Employee) {
            Employee empObj = (Employee) employee;
            System.out.println(empObj.getName() + "-->" + empObj.getSalary());
        }
        ois.close();
    }


    /**
     * 序列化集合到文件中
     * @throws IOException
     * @throws ClassNotFoundException
     */
    @Test
    public void testSerializeObject() throws IOException, ClassNotFoundException {
        String path = "user.ser";
        List<User> userList = new ArrayList<>();

        for (int i = 0 ;i<100000 ;i++) {
            userList.add(new User(i,"admin"+i,"123"+i));
        }

        serializationObjectToFile(Arrays.asList(userList), path);

    }

    /**
     * 反序列化文件中集合对象
     * @throws IOException
     * @throws ClassNotFoundException
     */
    @Test
    public void testDeSerializeObject() throws IOException, ClassNotFoundException {
        String path = "user.ser";
        List<User> userList = deserializationObjectToFile(path);
        System.out.println(userList);

    }

    /**
     * 序列化对象到指定文件
     *
     * @param list 对象
     * @param path 文件路径
     */
    public static <T> void serializationObjectToFile(List<T> list, String path) {

        try (
                //1.选择流
                // 写出->序列化
                ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(path)));
        ) {
            oos.writeObject(list);
            oos.flush();
            System.out.println("序列化成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 反序列指定文件中的集合
     *
     * @param path 文件路径
     */
    public <T> List<T> deserializationObjectToFile(String path) {
        try (
                //1.选择流
                // 读取->反序列化
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
        ) {
            System.out.println("反序列化成功");
            List<T> list = (List<T>) ois.readObject();
            return  list;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

注意点:

  1. 当调用read等无参方法时候切记这个数字不是指读的数量而是读取的字符/字节相对应的数字,当使用read(bytes[], 0, length) 等方法时候返回的是读取读取数组长度;
  2. BufferedWriter and BufferedReader的字符流会将读取的行末尾换行或者空格删去;
  3. 对象流输入流:,不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错

参考: https://blog.csdn.net/qq_41097354/article/details/90574770

https://blog.csdn.net/qq877728715/article/details/103844382

end
  • 作者:Endwas(联系作者)
  • 发表时间:2021-03-26 14:12
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论

    在试试
    利姆露  @ 灰
    ...
    阿灰
    不错啊~ 瞅瞅瞅瞅
    灰灰  @ 阿灰
    真不戳,,试试你的评论