读书笔记——Java IO

IO流的典型使用方式

1、缓存输入文件

BufferedReader in=new BufferedReader( new FileReader(文件名字) );

String s;

StringBuilder sb=new StringBuilder();

while((s=in.readLine())!=null){

  sb.append(s+"\n");
} in.close();

  字符串sb用来存储文件的内容。

2、从内存输入

  借助上一步的sb里面保存的文件的内容来进行处理,将内存中的内容输出到控制台。

  StringReader in=new StringReader(sb);

  while((int c=in.read())!=-1){

    System.out.print((char) c);
  }

  

3、格式化的内存输入

4、基本的文件输出

  FileWriter对象可以向文件写入数据。首先,创建一个与指定文件连接的FileWriter。实际上,我们通常会将其用BUfferedWriter包装一下用于缓冲输出。PrintWriter可以对数据进行格式化输出,方便阅读。

5、存储和恢复数据

  如果我们使用DataOutputStream输出数据,java保证我们可以使用DataInputStream准确的读取数据。

6、读写随机访问文件

  使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream(因为他们实现了同样的接口:DataInput和DataOutput)。另外,利用seek()可以再文件中到处移动,随便修改文件中的某个值。

7、管道流

  PipedInputStream ,PipedOutputStream ,PipedReader和 PipedWriter的价值在理解多线程之后就会体现出来。因为管道流用于任务间的通信。

文件读写的实用工具

  一种很常见的任务就是将文件读入到内存修改后在保存到外存中。TextFile类用来简化对文件的读写操作。

标准I/O

  标准I/O这个词是借鉴与Unix中的“程序所使用的单一信息流”这个概念。程序的所有输入都来自于标准输入,程序的所有输出都可以发送到标准输出,以及所有错误信息都可以发送到标准错误。标准I/O的意义在于:我们可以很容易的将程序联起来,一个程序的标准输出可以作为另一个程序的标准输入。

1、从标准输入中获取

  我们将System.in包装为BufferedReader就必须用InputStreamReader将System.in转换为Reader。

  BufferedReader stdin=new BufferedReader( new InpueStreamReader(System.in) );

  String s;

  while((s=stdin.readLine())!=null && s.length!=0){

    //s就是相当于从标准流中读取到的数据
    System.out.print(s);//将数据送到控制台
  }

2、将Sysytem.out转换为PrintWriter

  PrinterWriter out=new PrintWriter(System.out,true);

  out.println("Hello World!");

3、标准I/O重定向

  重定向操作的是字节流,而不是字符流;因此我们使用的是InputStream和OutputStream,而不是Reader和Writer。

  BufferedStream in=new BufferedStream( new FileInputStream(输入文件名字) );

  PrintStream out = new PrintStream( new BufferedStream(  new FileInputStram(输出到的文件名字) ) );

  System.setIn(in);

  System.setOut(out);

NIO

  提高文件I/O和网络I/O都有帮助。速度的提高来源于所使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。(安利一波:这个地方java编程思想狙的例子还是挺恰当的且容易理解)我们只是从缓冲器获取数据,并没有和通道进行交互。而通道要么向缓冲器发送数据,要么从缓冲器获取数据。唯一直接与通道交互的缓冲器是ByteBuffer,他是一个可以存储未加工的字节的 缓冲器。他是一个相当基础的类:告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用于以原始的字节形式或基本的数据类型输出和读取数据。但是没办法输出或读取对象,即使字符串对象也不可以。这种方式看着比较低级,但是正好,因为这是大多数操作系统中更有效的映射方式。

  将字节存放于ByteBuffer的方法之一:wrap(字节数组);

  FileChannel:旧I/O类库中有三个类被修改后用以产生FileChannel。这三个类是:FileInputStream(读一个文件),FileOutputStream(写一个文件)和RandomAccessFile(给一个文件随意位置添加内容)。他们都有一个getChannel方法用以返回一个FileChannel对象。一旦调用read()来告知FileChannel想ByteBuffer存储字节,就必须调用缓冲器上的flip(),让他做好让别人读取字节的准备。如果我们打算使用缓冲器执行进一步的read()操作,我们也必须要调用clear()来为每个read()做好准备。调用flip是为了告诉别人,我的缓冲器上的内容可以取下来,调用clear是为了告诉别人我的东西已经被取下来了,所以你可以继续read了。

  一个通道和另一个通道直接相连:TransferTo()和TransferFrom()。

1、转换数据

  缓冲器存放的是普通的字节,为了把他们转换为字符,有两种办法:①在输入的时候对其进行编码②在输出的时候对其进行解码

2、获取基本类型

  尽管ByteBuffer只有保存字节类型数据的功能,但是它可以从其所容纳的字节中产生各种不同的基本类型数据的方法。向ByteBuffer插入基本类型数据最简单的办法是:利用asCharBuffer()、asShortBuffer()等获取该缓冲器上的视图,然后使用视图上的put()方法。

3、视图缓冲器

  可以让我们通过某个特定的基本数据类型的视图查看其底层的ByteBuffer。正如上面视图提供给我们插入基本类型数据的方法,这样使得我们可以很方便的插入数据。当然,视图还可以允许我们从ByteBuffer一次一个地或者成批的读取基本类型数值。

4、用缓冲器操纵数据

  只有ByteBuffer可以和Channel打交道,IntBuffer、CharBuffer等等都是ByteBuffer的视图,即他们的底层都是通过ByteBuffer实现的。

5、缓冲器的细节

  mark    标记

  position位置

  limit      界限

  capacity 容量

  capacity()返回缓冲区容量

  clear()清空缓冲区,将position设置为0,limit设置为容量。通常调用此方法覆写缓冲区

  flip()将limit设置为position,position设置为0。此方法用于准备从缓冲区读取已经写入的数据

  limit()返回limit的值

  limit( int lim)设置limit的值

  mark()将mark设置为position

  position()返回position的值

  position( int pos)设置position的值

  remaining()返回limit-position

  hasRemaining()如果position与limit之间有元素,返回true

6、内存映射文件

  内存映射文件允许我们创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,我们就可以假定整个文件都放在内存中,而且完全把它当做一个大数组来进行处访问。这种方法极大地简化了修改文件的代码。另外,这种方式直接调用系统底层的缓存,没有JVM和系统之间的复制操作,所以效率大大的提高了。而且由于它这么快,还可以用它来在进程(或线程)间传递消息,基本上能达到和“共享内存页”相同的作用,只不过它是依托实体文件来运行的。

  MappedByteBuffer,这是一种特殊类型的直接缓冲器,从ByteBuffer继承而来。

  MappedByteBuffer对象的创建——FileChannel提供了map方法来把文件映射为MappedByteBuffer: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为MappedByteBuffer,mode指出了可访问该内存映像文件的方式,共有三种,分别为:
MapMode.READ_ONLY(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException。
MapMode.READ_WRITE(读/写): 对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的(无处不在的“一致性问题”又出现了)。
MapMode.PRIVATE(专用): 可读可写,但是修改的内容不会写入文件,只是buffer自身的改变,这种能力称之为”copy on write”

7、对文件加锁

  java的文件锁直接映射到了本地操作系统的加锁工具。lock或者tryLock()。两者区别:lock是阻塞式的,tryLock是非阻塞式的。

压缩

  压缩类库是按照字节方式处理的。不过有时我们可能*使用两种类型的混合流(注意:我们可以通过InputStreamReader和OutputStreamWriter在两种类型之间转换)。

1、用GZIP进行简单压缩

  单个文件进行处理。压缩类的使用非常直观——直接将输出流封装成GZIPOutputStream或者ZipOutputStream,并将输入流封装成GZIPInputStream或者ZipInputStream即可。其他全部操作就是通常的IO读写。

2、用Zip进行多文件保存

  支持对多个文件进行压缩。这个类库是标准的Zip格式,所以能与当前那些可以通过因特网下载的压缩工具很好的协作。

3、Java档案文件

  Java ARcive——JAR文件。

对象序列化

  当你创建对象时,只要你需要,他就会一直存在。但是在程序终止时,无论如何他都不会继续存在。如果对象能够在程序不运行时仍能保存其信息,那将非常有用。这样,在下次运行程序时,该对象将被重建并且拥有的信息与程序上次运行时她所拥有的信息相同。当然,你也可以将对象信息也如文件或者数据库来实现相同的效果,但是在万物都是对象的精神中,如果能够将一个对象声明为持久化的,并为我们处理掉所有细节,那将会显得十分方便。

  Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。“持久性”就会意味着对象的生存周期并不取决于程序是否在执行。

  通过将一个序列化对象写入磁盘,然后再重新调用程序时恢复该对象,就能够实现持久性的效果。必须在程序中显式地序列化(serialze)和反序列化还原(deserialize)。如果需要一个更严格的持久性机制,可以考虑Hibernate之类的工具。

  序列化主要是为了支持两种特性:一是Java的远程方法调用(RMI);二是Java Beans。

上一篇:读书笔记-----Java并发编程实战(一)线程安全性


下一篇:[读书笔记]java中的类加载器