IO流总结

流的概念

流(Stream)是指在计算机的输入输出操作中各部件之间的数据流动。按照数据的传输方向,流可分为输入流和输出流。Java语言里的流序列中的数据既可以时未经过加工的原始二进制数据,也可以是经过一i的那个编码处理后符合某种特定格式的数据。

流的分类

按大小

 按大小分为字节流字符流。字节流:读取的最小单位是一个字节(byte)。字符流:字符流以此可以读取一个字符(char)。

字节流与字符流的区别

java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都有输入和输出操作。在字节流中,输出数据是依靠OutputStream完成,输入使用InputStream;在字符流中,输出使用Writer类完成,输入使用Reader类完成。

字符流处理的单元为两个字节的Unicode字符,分为操作字符、字符数组或字符串,而字节流处理单元为一个字节,操作字节和字节数组。字节流时JVM将字节转化为2个字节的Unicode字符而成的,所以它对多国语言支持性能较好!如果是音频文件、图片、歌曲等 就用字节流,如果是关系到中文(文本)的,就用字符流。

所有文件的存储都是字节(byte)的存储,在磁盘上保存的并不是文件的字符而是先把字符编码成字节,再储存字节到磁盘。读取文件时,也是一个一个字节地读取以形成字节序列。

按传输方向

分为输入流输出流

按等级

分为:节点流(低级流)和处理流(高级流或过滤流)

节点流 : 可以向一个特定的地方读写数据
处理流 : 封装了节点流,通过封装的流的功能调用实现数据读写
流的链接:一个流对象经过其他流的多次包装,成为流的链接。

IO流

IO指的是输出与输出

                I即Input:输入,从文件到程序

                O即Output:输出,从程序到文件,写出数据

字节流

1.FIS和FOS

即 FileInputStream(文件输入流) 和 FileOutputStream(文件输出流)

直接上FileInputStream的源码:

public
class FileInputStream extends InputStream
 /* File Descriptor - handle to the open file */
    private final FileDescriptor fd;

    /**
     * The path of the referenced file
     * (null if the stream is created with a file descriptor)
     */
    private final String path;

    private FileChannel channel = null;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

大部分看不懂,截取关键的部分,由源码可以得到 FileInputStream 是继承自InputStream 这个父类的

再来看看它里面的方法:

 可见方法有点小多,但是现阶段就read(读文件)和close(关闭流)两个方法有用

再上 FileOutputStream的原码和方法列表

public
class FileOutputStream extends OutputStream
{
    /**
     * The system dependent file descriptor.
     */
    private final FileDescriptor fd;

    /**
     * True if the file is opened for append.
     */
    private final boolean append;

    /**
     * The associated channel, initialized lazily.
     */
    private FileChannel channel;

    /**
     * The path of the referenced file
     * (null if the stream is created with a file descriptor)
     */
    private final String path;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

 类似地,FileOutputStream继承自抽象类OutputStream,方法主要是write(写入)和close(关闭流)。

下面用两个流实现对文件地读写以及复制地操作:

public class TestFISAndFOS {

    File file;
    FileInputStream fis;
    FileOutputStream fos;

    @Before //在所有test运行之前先运行的内容
    public  void getFile(){
        file = new File("src\\com\\jmn\\io\\testio\\fisfos.txt");
    }

    @Test//写
    public void test() throws IOException {
        /*
          write(in)
         */
        fos = new FileOutputStream(file);
        fos.write("helloworld".getBytes());

    }

    @Test//读
    public void read() throws IOException {
        fis = new FileInputStream(file);
        /*
          int read():  返回值就是读取出来的字符,如果没有字符则返回-1
//         */
//        int i = -1;
//        while ((i = fis.read()) != -1){
//            System.out.println((char) i);
//        }
        /*
          int read(byte[] bus):读取文件内容到byte数组中
         */
        int i = -1;
        byte[] bus = new byte[1024];
        if((i = fis.read(bus)) != -1){
            System.out.println(new String(bus));
        }
    }
    @Test//文件的复制 ,将fisfos.txt 在相同路径下复制一个副本fisfos_copy.txt
    public void fileCopy() throws  Exception{
        //1.先读
        fis = new FileInputStream(file);
        file = new File("src\\com\\jmn\\io\\testio\\fisfos_copy.txt");
        fos = new FileOutputStream(file);
       int i = -1;
       //读单个
//       while ((i = fis.read()) != -1){
//           fos.write(i);
//       }
        //读byte[]
        byte[] bytes = new byte[1024];
        if((i = fis.read(bytes)) != -1){
            System.out.println(new String(bytes));
        }
    }

    @After //每运行一个test之后必须要运行的内容
    public  void close() throws IOException {
        //非空判断 关闭流,释放资源
        if(fis != null) fis.close();
        if(fos != null) fos.close();
    }
}

2.BIS与BOS(带缓存的输入输出流)

BIS:BufferedInputStream 

BOS:BufferedOutputStream 

缓冲流: 内部定义了一个缓冲区

就不看源码了,这里我们看看他们的继承关系:

由于他们和上两个流有共同的父类,所以方法都差不多,值得一提的是带缓存的输出流(:BufferedOutputStream )有一个重要的方法,那就是flush(刷新缓冲池)方法:

他的作用是:清空缓冲区,将缓冲区中的数据全部写出。缓冲区具有的优势是减少了读写次数,所以比起前两种流,带缓存的流效率会大大提高。
那么什么是缓冲区呢?这里讲一个比喻,有一个工人需要将很多砖头从一个地方运送到另一个地方,他可以选择自己一块一块地搬过去,这种就是普通的流,当然他同样可以选择使用一个小推车来装载很多的砖块,一次就可以运送很多砖块,效率大大提高。那么,这个案例中的 小推车就相当于这里的缓冲区。
下面来看看带缓冲区流的原理:

 值得一提的是,带缓存的流中需要用flush方法来让缓存区的数据读入或取出,因为缓冲区存在着一个机制,即缓冲区满了才会读入或写出。那么多少大小才会让缓冲区满呢?我们来看看源码!

 可以清晰地看见默认缓冲区大小被设置成了8k,但是在日常练习中一般是不会溢出的,所以我们常用flush方法来使数据强制从缓冲区里面刷出。

3.对象序列化

思考一个问题,如何将对象通过流存入磁盘呢?很容易想到方法:将对象转化为字节,然后存入。对象序列化就是来干这件事情的。那么什么是对象序列化呢?其实,就是刚刚提出问题的内容:可以将对象表示为一个字节序列,该字节序列包括对象的数据、对象的类型信息,和对象中数据的类型,将序列化的对象保存在文件中。同样,也可以逆行:从文件中读取对象的的字节序列,然后将对象创建出来,这被称为:反序列化。

总结:

序列化:对象转化为字节序列

反序列化:字节序列转化为对象

那么,如何进行对象序列化?

必须实现序列化和反序列化的对象必须实现 Serializable(1.没有任何方法需要实现, 2.仅表示当前对象可以序列化和反序列化) 序列化使用writeObject(Object o) 反序列化用readObject()

对象序列化的注意事项:必须实现Serializable接口,该接口没有方法,建议为对象添加类版本号,避免版本升级后造成的不兼容问题 transient关键字:修饰属性可以对没必要的属性值忽略,从而对对象序列化后的字节序列“瘦身”。瘦身即  transient关键字,它的作用是专门针对对象瘦身,表不需要序列化的对象可以省略属性的序列化,减少字节序列的生成。注:Person类在下面给出

在进行对象序列化时会遇到对象序列化兼容性问题:对旧接口添加字段,但旧接口得request对象或response对象未添加serializableuid,这样如果强行加字段就会导致调用方不兼容导致出错。即最好给对象加入uid,如下:

package com.jmn.io.testio;

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = -1496217388455779693L;
    private  String name = "张三";
    public  Integer age = 18;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 下面来介绍一种对象的输入输出流。

4.OIS和OOS

即对象输入流(ObjectInputStream)和对象输出流(ObjectOutputStream)。

 举一个实例:

package com.jmn.io.testio;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.*;


public class TestOOSAndOIS {
    File file;
    ObjectOutputStream oos;
    ObjectInputStream ois;

    @Before
    public void getFile() {
        file  = new File("src/com/fs/jmn/io/testio/person.txt");
    }

    @Test
    public  void  testPerson() throws Exception {
        //序列化
        oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(new Person());
        //System.out.println(new Person());

        //反序列化
        ois = new ObjectInputStream(new FileInputStream(file));
        Object o = ois.readObject();
        System.out.println(o);
    }

    @Test
    public  void  restPerson2() throws IOException, ClassNotFoundException {
        oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(new Person());
        //System.out.println(new Person());

        ois = new ObjectInputStream(new FileInputStream(file));
        Object o = ois.readObject();
        System.out.println(o);
    }
    @After
    public  void close() throws Exception {
        if (oos != null) oos.close();
        if (ois != null) ois.close();
    }
}

注:Person类已在上文给出。

来看看输出到Person文件中储存的字节序列(序列化)

�� sr com.jmn.io.testio.Person�<^6fT�� L aget Ljava/lang/Integer;L namet Ljava/lang/String;xpsr java.lang.Integer⠤���8 I valuexr java.lang.Number������  xp   t 张三

再来看看控制台(反序列化)

Person{name='张三', age=18}
Person{name='张三', age=18}

是不是很有意思!

4.字符流(介绍一种ISR和OSW)

字符流有两个抽象类: Reader和Writer 。 字符流的底层是字节流,每次处理一个字符(char)。 常见字符集: UTF-8:是针对Unicode的一种可变长度字符编码。 GBK:汉字编码字符集。常用方法有Reader和Writer。

ISR和OSW

即:InputStreamReader(字符输入流) 和 OutputStreamWriter(字符输出流)。

来看看他们的继承关系

 字符流可以很方便的往文件中输出字符,而不用去转换类型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值