Java—BIO (Block IO)

来源:互联网 发布:厦门雅迅网络很烂吗 编辑:程序博客网 时间:2024/06/06 03:05

一、IO基础知识

1、byte——字节,是计算机中的实际存储格式;byte[]是字节数组,可以通过指定的编码格式转换成char。

2、char——字符,可根据不同编码格式解析成byte序列。

  • java中使用的是unicode中utf-16be编码,其中汉子占用2字节,英文占用2字节;
  • utf-8——中文占用3字节,英文占用1字节;
  • gbk——中文占用2字节,英文占用1字节。
public static void main(String[] args) throws UnsupportedEncodingException {       String s1 ="字符串ABC";       byte[] bytes_utf_8=s1.getBytes();//使用项目默认编码转换成字节序列       byte[] bytes_gbk=s1.getBytes("gbk");//使用指定编码(gbk)转换成字节序列       System.out.println("utf_8:");       for(byte b1:bytes_utf_8){           //把byte转换为16进制的int进行展示           System.out.print(Integer.toHexString(b1 & 0xff)+" ");       }       System.out.println();       System.out.println("gbk:");       for(byte b1:bytes_gbk){           //把byte转换为16进制的int进行展示           System.out.print(Integer.toHexString(b1 & 0xff)+" ");       }    }

b1 & 0xff——位与操作 ffffffe5 & 000000ff = 000000e5 即e5

打印结果:
这里写图片描述

如图,根据不同的编码格式转换,得到的byte也不一样;
utf_8的汉字占用3个byte,gbk的汉字占用2个byte;
字节序列与字符串的相互转换必须采用相同的编码方式,否则会出现乱码。

3、String——字符串,可以理解成char[]。


二、文件操作

1、File

  • File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问
  • File类的常用API

    File file1=new File(“xxxx”)//构造函数
    file1.exists() //判断目录、文件是否存在
    file1.isDirectory()//判断是否目录
    File[] files=file1.listFiles();//列出该目录下的所有File

  • File简单运用——列出指定目录下所有的文件名

    /**     * 列出指定目录下所有的内容     * @param dir       * @author Bdong     */    public static void listDirectory(File dir){        //目录不存在        if(!dir.exists()){            throw new IllegalArgumentException("目录:"+dir+"不存在!");        }        //不是目录        if(!dir.isDirectory()){            throw new IllegalArgumentException(dir+"不是目录!");        }        //列出所有File        File[] files=dir.listFiles();        if(files != null && files.length > 0){            for(File file:files){                if(file.isDirectory()){                    //如果file是目录,则递归调用该方法                    listDirectory(file);                }else{                    //如果是文件,就打印文件名                    System.out.println(file);                }            }        }    }
public static void main(String args[]){        File file1=new File("/Users/Bdong/Downloads/coreJava");        FileUtil.listDirectory(file1);    }

这里写图片描述

2、RandomAccessFile

  • 提供对文件内容的访问,既可以读文件,也可以写文件。支持随机访问文件,可以访问文件的任意位置。
  • RandomAccessFile使用简单流程
    1. 打开文件

RandomAccessFile raf = new RandomeAccessFile(file,"rw")//有两种模式"rw"(读写)  "r"(只读)文件指针,打开文件时指针在>开头 pointer = 0
  • 写方法
    raf.write(int)//只写一个字节(后8位),同时指针指向下一个位置,准备再次写入

  • 读方法
    int b = raf.read()//读一个字节
  • 文件读写完成以后一定要关闭
    raf.close()

  • 三、字节流

    1、InputStream&OutputStream接口

    • InputStream抽象了流读取数据的方式

      输出流基本方法
      int b = in.read() //读取一个字节到流;-1是 EOF
      in.read(byte[] buf) //批量读取
      in.read(byte[] buf,int start,int size) //批量读取

    • OutputStream抽象了流写出数据的方式

      输出流基本方法
      out.write(int b) //从流写出一个byte,b的低8位
      out.write(byte[] buf)//将buf字节数组批量写
      out.write(byte[] buf,int start,int size)

    2、FileInputStream&FileOutputStream

    • FileInputStream具体实现从文件中读取byte数据到流
    • FileOutputStream具体实现从流向文件中写出byte数据
    • 具体实例
        /**     * 批量方式拷贝文件,大文件时速度最快     * @param srcFile     * @param destFile     * @throws IOException       * @author Bdong     */    public static void copyFile(File srcFile, File destFile) throws IOException{        if(!srcFile.exists()){            throw new IllegalArgumentException("文件:"+srcFile+"不存在!");        }        if(!srcFile.isFile()){            throw new IllegalArgumentException(srcFile+"不是文件!");        }        //输入流        FileInputStream fis =new FileInputStream(srcFile);        //输出流        FileOutputStream fos =new FileOutputStream(destFile);        //缓冲区        byte[] buf =new byte[8*1024];        int bytes;        while((bytes=fis.read(buf, 0, buf.length))!=-1){            fos.write(buf,0,bytes);            fos.flush();        }        fos.close();        fis.close();    }

    批量读取和写出的方式速度最快

    3、DataInputStream&DataOutputStream

    • 对”流”功能的扩展,可以更加方面的读取int,long,char等类型数据,如readInt/writeInt();readDouble/writeDouble();
    FileOutputStream out = new FileOutputStream(file);DataOutputStream dos =new DataOutputStream(out);dos.writeInt(10);
    • 通过查看源码可知,实际上writeInt是由write实现的,write只能写八位,那么写一个int需要写4次每次8位

    这里写图片描述

    以10为例;
    二进制为:0000-0000-0000-0000-0000-0000-0000-1010;
    先将10右移24位,再作与运算(& 0xFF),得到最高八位:0000-0000,写入;
    ……以此类推;
    最后将10做与运算(& 0xFF),得到最低八位:0000-1010,写入;
    完成int类型写入。

    4、BufferedInputStream&BufferedOutputStream

    带缓冲区的操作


    四、字符流

    1、相关概念

    • java的文本(char):16位无符号整数,是字符的unicode编码(双字节编码);
    • 文件:由byte组成的数据序列;
    • 文本文件:是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储;

    2、Reader&Writer

    • Reader——按照编码解析byte流为char流
    • Writer——按照编码解析char流到byte流

    3、InputStreamReader&OutputStreamWriter

    可指定字符编码

    4、FileReader&FileWriter

    读取文件方便,但不可指定字符编码

    5、BufferdReader&BufferdWriter

    • readLine 读一行,返回string,不能识别换行
    • BufferdReader常与PrintWriter搭配使用
    BufferedReader br = new BufferedReader(                new InputStreamReader(                        new FileInputStream("/Users/Bdong/Downloads/test.txt")));        PrintWriter pw = new PrintWriter("/Users/Bdong/Downloads/test1.txt");        String line ;        while((line = br.readLine())!=null){            System.out.println(line);//一次读一行,并不能识别换行            pw.println(line);            pw.flush();        }        br.close();        //bw.close();        pw.close();

    五、对象序列化和反序列化

    1、概念

    • 对象序列化是指将Object转换成byte序列,反之称为反序列化
    • 对象序列化和反序列化主要用于对象的保存和网络传输
    • 序列化流(ObjectOutputStream)writeObject
    • 反序列化 (ObjectInputStream)readObject

    2、序列化接口(Serializable)

    • 对象必须实现序列化接口(Serializable )才能进行序列化,否则将出现异常!

    3、transient关键字

    • transient关键字修饰的成员变量不进行jvm默认的序列化,但可以自己完成这个元素的序列化,可以帮我们提高性能
      这里写图片描述
      从ArrayList中可以看到,elementData被transient修饰,所以不被jvm默认序列化;
      但ArrayList自己重写了自定义序列化/反序列化方法:writeObject、readObject
        /**     * Save the state of the <tt>ArrayList</tt> instance to a stream (that     * is, serialize it).     *     * @serialData The length of the array backing the <tt>ArrayList</tt>     *             instance is emitted (int), followed by all of its elements     *             (each an <tt>Object</tt>) in the proper order.     */    private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException{        // Write out element count, and any hidden stuff        int expectedModCount = modCount;        s.defaultWriteObject();        // Write out size as capacity for behavioural compatibility with clone()        s.writeInt(size);        // Write out all elements in the proper order.        for (int i=0; i<size; i++) {            s.writeObject(elementData[i]);        }        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }    }
    /**     * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,     * deserialize it).     */    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        elementData = EMPTY_ELEMENTDATA;        // Read in size, and any hidden stuff        s.defaultReadObject();        // Read in capacity        s.readInt(); // ignored        if (size > 0) {            // be like clone(), allocate array based upon size not capacity            ensureCapacityInternal(size);            Object[] a = elementData;            // Read in all elements in the proper order.            for (int i=0; i<size; i++) {                a[i] = s.readObject();            }        }    }

    我们也可以在必要的时候按照如上方法自定义序列化

    4、关于子父类构造函数的序列化的一些结论

    • 一个类实现了序列化接口,其子类不用实现序列化接口了;
    • 序列化会递归调用父类构造函数;
    • 反序列化不会调用构造函数,因为是从序列中读取;
    • 对子类进行反序列化,如果父类没有实现序列化,父类构造函数会被调用;
    0 0