Java IO流详解与面试

一、Java中有几种流

1、按照流的流向分为:输入流和输出流。
输出流:把程序(内存)中的内容输出到磁盘、光盘等存储设备中。
输入流:读取外部数据到程序中。
2、按处理数据单位不同分为:字节流和字符流。
字节流:每次读取或者写出一个字节,出现中文时会有乱码形成。
字符流:每次读取或者写出两个字节,可正确处理中文。
3、按流的角色分为:节点流和处理流。
节点流:从或者向一个特定的节点读写数据,直接与数据源相连,用于输入或输出。
处理流(缓冲流):对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。处理流的构造方法总是要带一个节点流的对象做参数。
在这里插入图片描述

二、常用方法

1、InputStream

  • int read(): 从输入流中读取一个字节的二进制数据,返回的是所读取的字节的int型,该方法为抽象方法
  • int read(byte[] b): 将多个字节读取到数组中,填满整个数组,返回传入数组参数个数
  • int read(byte[] b, int off, int len): 从输入流中读取长度为len的数据,从数组b中下标为off的位置开始放置读入的数据,读完返回读取的字节数
  • void close(): 关闭数据流
  • int available(): 返回目前可以从数据流中读取的字节数
  • long skip(): 跳过数据流中hiding数量的字节不读取,返回值表示实际跳过的字节数。

另:为什么read()无参方法读取一个字节返回的是一个int类型而不是一个byte类型?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件的底层都是以二进制形式存储的,一个字节是8个二进制,也就是说实际read后的是类似于(11111110,00001010)这样的东西,如果每次读取都返回byte,有可能在读到中间的时候遇到11111111(文件的底层按照补码来存储),那么这11111111是byte类型的-1,而程序遇到-1就会停止,后面的数据就会读不到,所以在读取的时候按照int类型接收,如果出现11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就会变成int类型的255了,这样可以保证数据被读完。

2、OutputStream

  • void write(int i): 将字节i写入到数据流中,它只输出所读入参数的最低8位,该方法是抽象方法,需要在其输出流子类中实现才能使用
  • void write(byte[] b): 将数组b中的全部字节写入到数据流
  • void write(byte[] b, int off, int len): 将数组b从下标off开始的len个字节写入到数据流
  • void close(): 关闭输出流
  • void flush(): 刷新此输出流并强制写出所有缓冲的输出字节

三、选择字节流还是字符流?

1、大多数情况下使用字节流会更好,因为大多数IO操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的
2、如果对于操作需要通过IO在内存中频繁处理字符串的情况使用字符流会更好一些,因为字符流具有缓冲区,提高了性能
3、什么是缓冲区?
缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁的操作一个资源性能会变低,所以为了提高性能可以将一部分数据暂时读写到缓冲区,以后直接在该区域读写数据,提高性能

四、序列化

1、序列化
把Java对象转换为字节序列的过程成为对象的序列化,也就是将对象写入到IO流中。
序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行二独立存在。
要一个对象实现序列化,该对象必须实现Serializable接口。Serializable接口中没有定义任何方法,知识作为一个标记来指示实现该接口的类可以进行序列化。
序列化只能保存对象的非静态成员变量,而不能保存任何成员方法和静态成员变量,并且保存的知识变量的值,变量的修饰符对序列化没有影响。
有一些对象类不具有可持久化性,因为其数据的特性决定了它会经常变化,如Thread对象或者流对象,对于这类对象要使用transient关键字标明,否则编译器会报错,任何使用transient关键字修饰的成员变量都不会被保存,对于一些需要保密的数据就要使用transient关键字修饰。
在这里插入图片描述

@Test//对象的序列化
    public void Test(){
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("test.dat"));
            oos.writeObject(new Student(1,"mxf",18));
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

此时需要注意Student类

public class Student implements Serializable {

    //这个static final long 类型的变量是非常重要的,用来保证序列化和反序列化后的对象一致
    private static final long serialVersionUID=3435234657L;
    private int id;
    private String name;
    private int age;

    public Student() {
    }
   ..........

2、反序列化
反序列化就是从IO流中恢复对象。

@Test//对象的反序列化
    public void Test1(){
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("test.dat"));
            //把文件内容读取出来
            Object o = ois.readObject();
            System.out.println(o);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois!=null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

3、序列化的应用

  • 所有需要网络传输的对象都需要实现序列化接口。建议所有的javaBean都实现Serializable接口
  • 对象的类名、实例变量都会被序列化,方法、类变量、transient实例变量都不会被序列化
  • 序列化对象的引用类型成员变量也必须是可序列化的,否则会报错

好的到这里就结束啦!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值