Java I/O

I/O

流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。Java把一切输入抽象成输入流,把一切输出抽象成输出流(也可以根据流方向来定义,流向内部的是输入流,流向外部的是输出流)。

Java的I/O架构由输入流/输出流/File/Serializable四部分组成。

一、File

File是文件和目录路径名的抽象表示。File中定义了对文件和目录的基本操作:

  1. 路径相关 如getAbsolutePath()
  2. 文件属性 如getParent()、getName()等
  3. 文件读写权限 如canRead()、setWritable、setReadable
  4. 文件的创建、删除 如 createNewFile、delete(当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败)
  5. 创建文件夹 mkdir(创建单级目录)、mkdirs(创建多级目录)
  6. 获取路径下的文件、文件夹 list(只会列出路径下的文件、文件夹名称)、listFiles(列出路径下的文件)

二、Serializable

Serializable是Java用来进行序列化的。序列化就是把Java对象转换为字节序列的过程。序列化之后字节序列可以用于存储和传输(存储和传输就涉及到了I/O操作)。

在Java中只要让需要序列化的类实现Serializable即可,同时Android提供了另一种序列化方式Parcelable接口,最后就是网络通信常用的json,以上就是常见的序列化方式。

序列化-Serializable

序列化:

        Bean bean = new Bean();
        bean.setIndex(100);
        bean.setName("number");
        FileOutputStream outputStream = new FileOutputStream(file);
         objectOutputStream = new ObjectOutputStream(outputStream);
         objectOutputStream.writeObject(bean);
         objectOutputStream.close()

通过上述代码我们可以将bean序列化并写入file中。通过分析源码我们了解序列化过程如下:

  • 写入流的头信息

头信息包含两个字段他们是在ObjectStreamConstants类中定义的

    /**
     * Magic number that is written to the stream header.
     */
    final static short STREAM_MAGIC = (short)0xaced;

    /**
     * Version number that is written to the stream header.
     */
    final static short STREAM_VERSION = 5;

头信息存放在序列化后的字节序列的头部,在反序列化时会校验其值。

  • 之后调用writeObject开始真正的序列化流程,在writeObject会先获取传入的要序列化对象的本地描述并封装到一个名叫ObjectStreamClass的类中,该类实例在构建的时候会存储诸如类名、是否实现Serializable接口、传入类的serialVersionUID、传入类的字段属性信息、writeObjectMethod 、readObjectMethod等等。其中很多信息都是通过反射获取的。

  • 获取本地描述实例之后会把这些信息写入流中,此时并未写对象的信息,即字段的值。

  • 最后写入对象信息,写的时候会先写基本类型的字段值再写非基本类型字段值

    至此序列化过程就完成了。

    反序列化-Serializable

    • 首先会先检查字节序列头部信息是否匹配
    • 之后调用readObject() 读取流中的字节,首先会读取到类的描述信息ObjectStreamClass 对象,然后利用该对象内存储处的信息去实例化对象(反射创建对象未使用构造器)然后给对象字段进行赋值,完成后返回反序列化后的对象。

    注意:

    静态变量无法序列化

    transient关键字修饰的变量无法序列化

    多次序列化同一对象,内部只会序列化一次之后再次序列化会返回已序列化的编号。

    Parcelable

    使用:

    
    public class ABean implements Parcelable {
        private  int index;
        private String name;
    
        protected ABean(Parcel in) {
            index = in.readInt();
            name = in.readString();
        }
       //反序列化功能由CREATOR完成,在CREATOR的内部标明的如何创建序列对象和数组
        public static final Creator<ABean> CREATOR = new Creator<ABean>() {
            @Override
          //从Parcel中反序列化对象
            public ABean createFromParcel(Parcel in) {
                //其内部调用Parcel的一系列readXXX方法实现反序列化过程
                return new ABean(in);
            }
    
            @Override
            public ABean[] newArray(int size) {
                return new ABean[size];
            }
        };
    
        public int getIndex() {
            return index;
        }
    
        public void setIndex(int index) {
            this.index = index;
        }
    
        public String getName() {
            return name == null ? "" : name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
        //序列化过程:
        //重写writeToParcel方法,我们要在这里逐一对需要序列化的属性用Parcel的一系列writeXXX方法写入
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(index);
            dest.writeString(name);
        }
    }
    
    
    

上面是基本使用,首先要实现Parcelable接口以及其要求实现的两个方法describeContents、writeToParcel,然后还需要定义一个名字为CREATOR的 Creator对象。

序列化是通过writeToParcel方法实现的,在该方法内部会先写入传入类的类名然后调用我们实现的writeToParcel方法按顺序写入。

反序列化是通过readParcelable方法实现的,在该方法内部会先通过反射获取我们在类内部定义的CREATOR,然后调用CREATOR的createFromParcel来创建实例对象,这样就完成了反序列化。

参考链接:

https://blog.csdn.net/willway_wang/article/details/106745481

https://www.cnblogs.com/wangle12138/p/8257016.html

三、输入输出流

分类:

  • 按数据流的方向不同分类:输入流、输出流
  • 按最小的数据单元分类:字节流、字符流
  • 按照功能不同分成节点流和处理流

输入流

Java中所有输入流都是InputStream或Reader的子类。

输出流

所有输出流都是OutputStream或Writer的子类。

字节流

字节流是指以 8 位(即 1 byte,8 bit)作为一个数据单元,数据流中最小的数据单元是字节的流。InputStream/OutputStream 是字节流的基类。

InputStream

输入字节流基类,其子类有FileInputStream、DataInputStream、BufferedInputStream、ByteArrayInputStream、ObjectInputStream等

  • public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
  • public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的
  • public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。
  • public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用,
  • public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取
  • public int close( ) :我们在使用完后,必须对我们打开的流进行关闭.
OutputStream

输出字节流基类,其子类有FileOutputStream、DataOutputStream、BufferedOutputStream、ByteArrayOutputStream、ObjectOutputStream等

  • public void write(byte b[ ]):将参数b中的字节写到输出流。
  • public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。
  • public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。
  • public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。
  • public void close( ) : 关闭输出流并释放与流相关的系统资源。

字符流

Reader

字符输入流基类,其子类有BufferedReader、InputStreamReader、StringReader等

  • public int read() throws IOException; //读取一个字符,返回值为读取的字符
  • public int read(char cbuf[]) throws IOException;/读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量/
  • public abstract int read(char cbuf[],int off,int len) throws IOException;
Writer

字符输出流基类,其子类有BufferedWriter、OutputStreamWriter、StringWriter等

  • public void write(int c) throws IOException;//将整型值c的低16位写入输出流
  • public void write(char cbuf[]) throws IOException;//将字符数组cbuf[]写入输出流
  • public abstract void write(char cbuf[],int off,int len) throws IOException;//将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流
  • public void write(String str) throws IOException;//将字符串str中的字符写入输出流
  • public void write(String str,int off,int len) throws IOException;//将字符串str 中从索引off开始处的len个字符写入输出流

节点流

Java根据流是否直接处理数据分为节点流和处理流,节点流是真正直接处理数据的流,在使用完毕后需要关闭。FileInputStream,FileOutputStrean,FileReader,FileWriter,StringReader,StringWriter等都是节点流。

处理流

处理流是装饰加工节点流的,它是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。BufferedImputStrean,BufferedOutputStream,BufferedReader ,BufferedWriter,InputStreamReader,OutputStreamWriter等都是处理流。

比如Bufferedxxx流都是带缓冲的流

最后补充一个图(来源于网络):

四、RandomAccessFile

它是Java提供的随机存取文件内容的类,在构造函数中传入所需权限(读、写等)。其提供了seek函数,该函数可以把文件光标定位到指定位置从而从此开始读写文件内容。它还提供readInt、readDobule、writeInt等读写基本数据类型的方法。底层原理是内存映射文件方式(nio包),即把文件映射到内存后再操作,省去了频繁磁盘io( 待验证 )。

RandomAccessFile简介与使用_LJHSkyWalker的博客-CSDN博客_randomaccessfile

posted @ 2020-09-21 16:45  Robin132929  阅读(17)  评论(0编辑  收藏