Java IO流

来源:互联网 发布:java 生成xml格式报文 编辑:程序博客网 时间:2024/06/06 09:58

         Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,Java中把不同的输入/输出源(键盘、文件、网络连接等)抽象表述为“流”(Stream),通过流的方式允许Java程序使用相同的方式来访问不同的输入/输出源。 Java把所有传统的个流类型(类或抽象类)都放在java.io包中,用以实现输入/输出功能。

        按照不同的分类方式,可以将流分为不同的类型,下面我们从不同的角度对流进行分类:

输入流和输出流

按照流的流向来分,可以分为输入流和输出流:

(1)输入流:只能从中读取数据,而不能向其写出数据。

(2)输出流:只能向其写出数据,而不能从中读取数据。

  此处的输入、输出涉及到一个方向问题,数据从内存到硬盘,通常我们称为输出流------也就是说,这里的输入、输出是从程序运行所在内存的角度来划分的。

         Java的输入流主要由InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类。

字节流和字符流 

字节流和字符流区别非常简单,它们的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同:字节流操作的最小数据单元是8位的字节,而字符流操作的最小数据单元是16位的字符。

字节流主要由InputStream和OutputStream作为基类,而字符流则主要由Reader和Writer作为基类。

节点流和处理流

按照角色分,可以分为节点流和处理流。

可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流常常也被称为低级流。处理流则用于对一个已经存在的流进行连接或封装,通过封装后流来实现数据读/写功能,处理流也称为高级流。

 

流的概念模型

Java把所有设备里的有序数据抽象成流模型简化了输入/输出的处理。Java的IO流共涉及40多个类,这些看上去杂乱无章,但实际上是有规则的。Java的IO流的40多个类都是从4个抽象基类派生出来的:

(1)InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。

(2)OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。

对于InputSteam和Reader而言,它们把输入设备抽象成一个“水管”,这个水管里的每个“水滴”依次排列,如下图所示:

Java IO流 - quanquan127@126 - 学无止境

 输入流使用隐式指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或Reader里取出一个或多个“水滴”后,记录指针自动向后移动。

        对于OutputStream和Writer而言,它们同样把输出设备抽象成一个“水管”,只是这个水管里没有任何水滴,如下图所示:

Java IO流 - quanquan127@126 - 学无止境

                                                                                      

       如上图所示,当我们执行输出时,程序相当于依次把“水滴”放入到输出流的水管中,输出流同样采用隐式的记录指针来标识当前水滴即将放入的位置,每当程序想OutputStream或Writer里输出一个或多个水滴后,记录指针自动向后移动。

InputStream和Reader

在InputStream里包含如下三个方法:

(1)int read():从输入流中读取单个字节(相当于从图15.5所示水管中取出一滴水),返回所读去的字节数据(字节数据可以转换成int类型)。

(2)int read(byte[ ] b):从输入流中读取最多b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。

(3)int read(byte[ ] b,int off,int len):从输入流中读取最多len字节的数据,并将其存储在数组b中,放入b数组中时,并不是从数组起点开始,而是从offset位置开始,返回实际读取的字节数。

在Reader里包含如下三个方法:

(1)int read():从输入流中读取单个字符,返回所读取的字符数据(字符数据可直接转换为int类型)。

(2)int read(char[ ] cbuf):从输入流中读取最多cbuf.length个字符的数据,并将其存储在字符数组cbuf中,返回实际读取的字节数。

(3)int read(char[ ],int off,int len):从输入流中读取最多len个字符的数据,并将其存储在字符数组cbuf中,放入cbuf数组中时,并不是从数组起点开始,而是从offset位置开始,返回实际读取的字符数。

InputStream和Reader都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流-----会直接和指定文件关联。下面的程序示范了使用FileInputStream来读取自身的效果:

 

public class FileInputStreamTest

{

public static void main(String[] args) throws IOException

{

FileInputStream fis=new FileInputStream("FileInputStreamTest.java");

byte[] bbuf=new byte[1024];

int hasRead=0;

while((hasRead=fis.read(bbuf))>0)

{

//取出"竹筒"中的水滴(字节),将字节数组转换成字符串输入

System.out.print(new String(bbuf,0,hasRead));

}

fs.close();

}

}

上面的程序中的粗体字代码部分是使用FileInputStream循环“取水”的过程,运行上面程序将会输出上面程序的源代码。
下面的程序时使用FileInputStream来执行输入,并用FileOutputStream来执行输出,用以实现复制FileOutputStreamTest.java文件的功能。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class FileOutputStreamTest
{
 public static void main(String[] args) throws IOException
 {
  FileInputStream fis=null;
  FileOutputStream fos=null;
  try
  {
   fis=new FileInputStream("FileOutputStreamTest.java");
   fos=new FileOutputStream("newFile.txt");
   byte[] bbuf=new byte[1024];
   int hasRead=0;
   while((hasRead=fis.read(bbuf))>0)
   {
    fos.write(bbuf,0,hasRead);
   }
  }
  catch (IOException ioe)
  {
   ioe.printStackTrace();
  }
  
  finally
  {
   if(fis!=null)
   {
    fis.close();

   }
   if(fos!=null)
   {
    fos.close();
   }
  }
 }
}

运行上面的程序后,newFile.txt文件的内容和FileOutputStreamTest.java文件中的内容完全相同。
处理流

处理流可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入\输出方法,让程序员只需要关心高级流的操作。我们要识别处理流非常简单,只有流的构造器参数不是一个物理节点,而是存在的流,那么这种流一定是处理流:而所有节点流都是直接以物理IO节点作为构造参数的,下面程序使用PrintStream处理流来包装InputStream,使用处理流后的输出流在输出是更加方便。

class PrintStreamTest
{
 public static void main(String[] args)
 {
  PrintStream ps=null;
  try
  {
   FileOutputStream fos=new FileOutputStream("test.txt");
   ps=new PrintStream(fos);
   ps.println("普通字符串");
   ps.println(new PrintStreamTest());
  
  }
  
  catch (IOException ioe)
  {
   ioe.printStackTrace();
  }
  finally
  {
   ps.close();
  }
  System.out.println("Hello World!");
 }
}

上面程序先定义了一个节点输出流FileOutputStream,然后程序使用PrintStream来包装了该节点流,然后使用PrintStream输出字符串、输出对象。
输入输出体系
Java IO流 - quanquan127@126 - 学无止境
 上面的表中粗体字标出的类代表节点流,必须直接与指定的物理节点关联;斜体字标出的类代表抽象基类,无法直接创建实例。
转换流 
在输入/输出体系里还提供了2个转换流,这两个转换流用于实现将字节流转换成字符流,其中InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符输出流。Java只提供了将字节流转换成字符流的转换流,没有提供将字符流转换成字节流的转换流。 
下面介绍将标准输入流(System.in)即键盘输入,转换成字符输入流。标准输入流是InputStream类的实例,使用不太方便,而且键盘输入的内容是文本内容,我们可以使用InputStreamReader将其转换成字符输入流,并将Reder再次包装成BufferedReader,一行一行读取内容,程序如下:

import java.io.*;
import java.lang.System;
class KeyinTest
{
 public static void main(String[] args) throws Exception
 {
  BufferedReader br=null;
  try
  {

//将System.in对象转换成Reader对象
   InputStreamReader reader=new InputStreamReader(System.in);

//将普通Reader包装成BufferedReader
   br=new BufferedReader(reader);
   String buffer=null;
   while((buffer=br.readLine())!=null)
   {
    if(buffer.equals("exit"))
    {
     System.exit(1);
    }
    System.out.println("输入内容为:"+buffer);
   }
  }
  catch (IOException ioe)
  {
   ioe.printStackTrace();
  }
  finally
  {
   if(br!=null)
   {
    br.close();
   }
  }
  System.out.println("Hello World!");
 }
}


  

原创粉丝点击