新io

  1. JDK1.4的java.nio.*包中引入了新的javaI/O类库,其目的在于提高速度,旧的I/O包已经使用nio重新实现过,以便充分利用这种速度提高。因此,即便我们不显示的使用nio编写代码。也能从中受益
  2. 速度的提高在于所使用的结构更接近与操作系统执行的I/O方式:通道和缓冲器
  3. ByteBuffer是唯一直接与通道交互的缓冲器,可以存储未加工字节的缓存器;java.nio.ByteBuffer是相当基础的类,通过告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用于以原始的字节形式或基本数据类型输出和读取数据。没办法输出或读取对象;通过告知分配多少存储空间来创建一个ByteBuffer对象。
  4. Reader和Writer这种字符模式类不能用于产生通道。
  5. java.nio.channerls.Chanels类提供了实用方法,用以在通道中产生Reader和Writer
  6. 通道是一个相当基础的东西,可以向它传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式访问。
  7. 将字节存放于ByteBuffered的方法:
    1. 使用一种“put”方法直接对它们进行填充,填入一个或多个字节,或基本数据类型的值。
    2. 也可以使用warp()方法将已存在的字节数组“包装”到ByteBuffer中。一旦如此,就不再复制底层的数组,而是把它作为所产生的ByteBuffer的存储器。我们称之为数组支持的ByteBuffer
  8. 对于只读访问,必须显示的使用静态的allocate()方法分配ByteBuffer。allocateDirect()是更高的速度,但是由于它依赖于平台,随着平台不同而不同,故需要谨慎使用
  9. 一旦调用FileChaal.read(ByteBuffer)来告知FileChannal向ByteBuffer存储字节,就必须使用缓冲器上的flip();如果我们打算使用缓冲器执行进一步的read()操作,必须调用clear()来为每个read()做好准备
    1. flip():反转此缓冲区,将限制设置为当前位置,然后将位置设置为0
    2. rewind():返回到数据开始部分,与flip()不同,不会修改限制位置。
    3. FileChannal.read():返回-1表示我们已经到达了输入的末尾。
    4. flip()是准备缓存器,以便他的信息可以由write()提取。write()操作之后,信息仍在缓冲器中,接着clear()操作则对所有的内部指针重新安排,以便缓冲器在另一个read()操作期间能够做好接收数据的准备
  10. 特殊方法transferTo()和transferFrom()允许我们将一个通道和另一个通道直接相连
  11. 转换数据:缓冲期容纳的是普通字节,为了把它们转换成字符,a:我们在输入它们的时候对其进行编码;b:在将其从缓存器输出时对他们进行解码。可以使用java.nio.charset.Charset类实现这些功能,该类提供了把数据编码成多种不同类型的字符集的工具。示例:
    String encoding = System.getPropertuy(“file.encoding”);//发现默认字符集,他会产生代表字符集名称的字符串
    Charset charset = Charset.forName(encoding);//产生Charset对象,可以使用该对象对字符串进行解码
    charset.decode(buff);//解码为char
  12. 获取基本类型
    1. 尽管ByteBuffer只能保存字节类型数据,但是它具有可以从其所容纳的字节中产生出各种不同基本类型值得方法
    2. 插入基本类型数据:利用asCharBuffer()等获得该缓冲器上的视图,然后使用视图的put()方法。适用于所有的基本数据类型,除了ShoreBuffer的put()方法,需要进行类型转换——put((short)476767);
  13. 视图缓冲器(view buffer):可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。
    1. 对视图的任何操作都会映射成为对ByteBuffer中数据的修改
    2. 方法
      1. hasRemaining()
      2. position()
    3. 字节存放次序
      1. 两只方式:
        1. 大端:将最重要的字节存放在地址最低的存储器单元——正常顺序
        2. 小端:将最重要的字节存放在地址最高的存储器单元
      2. 可以使用带有参数ByteOrder.BIG_ENDIAN或ByteOrder.LITTLE_ENDIAN方法改变ByteBuffer的字节排序方式。
      3. 使用array()方法显示视图底层的字节,此方法是“可选的”,只能对有数组支持的缓冲器调用此方法。
  14. 用缓冲器操纵数据
    1. ByteBuffer是将数据移进移除通道的唯一方式,并且我们只能创建一个独立的基本类型缓冲器,或者使用“as”方法从ByteBuffer中获得。即,我们不能把基本类型的缓冲器转换成ByteBuffer,但是我们可以经由视图缓冲器将基本类型数据移进移出ByteBuffer
    2. 例如:如果想把一个字节数组放到文件中,先将字节数组包装起来(使用ByteBuffer.wrap()),在用getChannal()方法在FileOutputStream上开一个通道,接着将来自于ByteBuffer的数据写到FileChannal中。
  15. 缓冲器的细节
    1. Buffer由数据和可以高效的访问及操纵这些数据的四个索引组成,这四个索引是:mark(标记), position(位置), limit(界限), capacatiy(容量).在缓冲器中插入或者提取数据会更新这些索引,用于反映发生的变化。
    2. 下面是用于设置和复位索引以及查询它们的值的方法。

      一旦调用缓冲器上相对的get()和put()方法,position指针会随之发生相应的改变。
      当调用包含一个索引参数的get()和put()方法(参数指明方法发生的位置)。但是,这些方法不会改变position的位置。
      reset():把position的值设为mark的值。
      rewind():调用之后缓冲器的状态——返回到数据开始部分(position),与flip()不同,不会修改限制位置(limit)。
  16. 内存映射文件
    1. 内存映射文件允许我们创建和修改哪些因为太大而不能放入内存的文件。
    2. 有了内存映射文件,我们可以假定整个文件都放在了内存中,而且可以完全把他当作非常大的数组来访问。
    3. 例子
      MappedByteBuffer out = new RandomAccessFile(“test.dat”, “rw”).getChannel().map(FileChannal.MapMode.READ_WRITE, 0, length);
    4. 调用map()产生MappedByteBuffer(继承ByteBuffer而来),这是一种特殊类型的直接缓冲器。
      注:我们必须指定映射文件的初始位置和映射区域的长度——我们可以映射某个大文件的较小部分
    5. 性能:“映射文件访问”往往可以更加显著的加快速度。
    6. System.namoTime()——得到系统时间
    7. 映射文件中的所有输出必须使用RandomAccessFile.
  17. 文件加锁
    1. 允许我们同步访问某个作为共享资源的文件。竞争同一个文件的两个线程可能在不同的java虚拟机上,或者一个是java线程,一个是操作系统中其他的某个本地线程。文件锁对其他的操作系统进程是可见的,因为java的文件加锁直接映射到了本地操作系统的加锁工具
    2. 通过对FileChannel调用tryLock()或lock(),就可以获得整个文件的FileLock。(SocketChannal,DatagramChannal,ServerSocketChannal不需要加锁,因为他们是从单进程实体继承而来,我们通常不在两个进程之间共享网络)
      1. tryLock(long position, long size, boolean shared):是非阻塞式的,它设法获取锁,但是如果不能获得(当其他一些进程已经持有相同的锁,并且不共享锁),它将直接从方法调用返回
      2. lock(long position, long size, boolean shared):是阻塞式的,他要阻塞进程直至锁可以获得,或调用lock()的线程中断,或调用lock()的通道关闭。
        注:枷锁区域为:size-position,第三个参数指定是否共享锁。
      3. 使用FileLock.release()可以释放锁。
      4. 无参数的枷锁方法将根据文件尺寸的变化而变化,它会对整个文件进行加锁,甚至文件变大也是如此。
        有参数的你将获得某一个区域上的锁,当文件增大时,区域之外的部分不会被锁定。
      5. 锁的类型(共享或者独占)可以通过FileLock.isShared()进行查询。
      6. 对共享或者独占锁的支持必须有底层的操作系统支持。
      7. 对映射文件的部分加锁:对巨大的文件进行部分加锁,以便其他进程可以修改文件中未加锁的部分。例如:数据库。
      8. ByteBuffer.slice():用于创建一个共享了原始缓冲区子序列的新缓冲区。新缓冲区的position值是0,而其limit和capacity的值都等于原始缓冲区的limit 和position的差值。slice()方法将新缓冲区数组的offset值设置为原始缓冲区的position值,然而,在新缓冲区上调用 array()方法还是会返回整个数组。
      9. 如果有java虚拟机,它会自动释放锁,或者关闭加锁的通道,不过也可以显示的为FileLock对象调用release()释放锁。