您的位置:寻梦网首页编程乐园Java天地Core JavaJava基础教程
Java 天地

Java基础教程

第一章 Java的产生与特点
第二章 Java程序编译与运行环境
第三章 Java程序设计基础
第四章 Java应用程序的基本结构
第五章 Java的类
第六章 Java图形用户接口
第七章 Java的多线程
第八章 Java的“异常”
第九章 Java的输入输出操作

第九章 Java的输入输出操作


9.1 Java 输入输出流

所有的程序语言都提供与本机文件系统交互的方式;Java也不例外。 我们将看看Java是怎样处理标准文件输入输出的(包 括stdin,stout,stderr)。 当你在网络上开发小程序时, 你必须注意直接文件输入输出是不安全因素的关键。 大多数用户设置他们的浏览器, 可让你自由的访问他们的文件系统, 但有时不让你访问。 当然, 如果你开发你内部的应用程序, 你也许需要直接访问文件。

标准输入输出: Unix的用户, 或其他基于命令行系统的用户(如DOS), 都知道标准输入输出的含义。 标准输入文件是键盘, 标准输出文件是你的终端屏幕。 标准错误输出文件也指向屏幕, 如果有必要, 它也可以指向另一个文件以便和正常输出区分。

系统类: Java通过系统类达到访问标准输入输出的功能。上面提到的三个文件在这个系统类中实现:

Stdin: System.in作为InputStream类的一个实例来实现stdin, 你可以使用read()和skip(long n)两个成员函数。read()让你从输入中读一个字节, skip(long n)让你在输入中跳过n个字节。

Stout: System.out作 为PrintStream来实现stdout, 你可以使用print()和println()两个成员函数。 这两个函数支持Java的任意基本类型作为参数。

Stderr: System.err同stdout一样实现stderr。 象System.out一样, 你可以访问PrintStream 成员函数。


9.2 标准输入输出例子

这里有一个例子, 功能象Unix里的cat或type:

  import java.io.* 
  class myCat{ 
    public void main(String args[]) throws IOException{
      int b;
      int count = 0; 
      while ((b = System.in.read()) != -1){
        count++; 
        System.out.print((char)b);
      }
      System.out.println(); //blank line 
      System.err.println("counted"+count+"total bytes.");
    }
  }

9.3 普通输入输出类

除了基本的键盘输入和屏幕输出外, 我们还需要联系文件的输入输出。 我们将学习下面几个类:  |  FileInputStream  |  DataInputStream  |  FileOutputStream  |  DataOutputStream

作为参考, 再列出一些特定应用的类:  |  PipedInputStream  |  BufferedInputStream  |  PushBackInputStream  |  StreamTokenizer  |  PipedOutputStream  |  BufferedOutputStream  |  RandomAccessFile

我们不在此讨论这些类, 但你可以在JAVA_HOME/src/java/io目录里查看每个类的成员函数定义。


9.4 文件

在我们进行文件操作时, 需要知道一些关于文件的信息。File类提供了一些成员函数来操纵文件和获得一些文件的信息。


9.4.1 创建一个新的文件对象

你可用下面三个方法来创建一个新文件对象:

  File myFile;
  myFile = new File("etc/motd"); 

  myFile = new File("/etc","motd"); 
    //more useful if the directory or filename are variables 

  File myDir = new file("/etc"); 
  myFile = new File(myDir,"motd"); 

这三种方法取决于你访问文件的方式。 例如, 如果你在应用程序里只用一个文件, 第一种创建文件的结构是最容易的。 但如果你在同一目录里打开数个文件, 则第二种或第三种结构更好一些。


9.4.2 文件测试和使用

一旦你创建了一个文件对象, 你便可以使用以下成员函数来获得文件相关信息:

文件名:  |  String getName()  |  String getPath()  |  String getAbslutePath()  |  String getParent()  |  boolean renameTo(File newName)

文件测试:  |  boolean exists()  |  boolean canWrite()  |  boolean canRead()  |  boolean isFile()  |  boolean isDirectory()  |  boolean isAbsolute()

一般文件信息:  |  long lastModified()  |  long length()

目录用法:  |  boolean mkdir()  |  String[] list()


9.4.3 文件信息获取例子程序

这里是一个独立的显示文件的基本信息的程序, 文件通过命令行参数传输:

  import java.io.*; 
  class fileInfo{ 
    File fileToCheck;
    public static void main(String args[]) throws IOException{ 
      if (args.length>0){ 
        for (int i=0;i<args.length;i++){
          fileToCheck = new File(args[i]); 
          info(fileToCheck); 
        } 
      } else{ 
        System.out.println("No file given.");
      }
    }
    public void info (File f) throws IOException {
      System.out.println("Name: "+f.getName());
      System.out.println("Path: "=f.getPath());
      if (f.exists()) {
        System.out.println("File exists.");
        System.out.print((f.canRead() ?" and is Readable":"")); 
        System.out.print((f.cnaWrite()?" and is Writeable":"")); 
        System.out.println(".");
        System.out.println("File is " + f.lenght() = " bytes.");
      } else {
        System.out.println("File does not exist.");
      }
    }
  }

9.5 输入流

InputStream  SequenceInputStream  FileInputStream  PipedInputStream  ByteArrayInputStream  FileterInputStream  StringBufferInputStream

DataInputStream  LineNumberInputStream  PushbackInputStream  BufferedInputStream 有好几个类是专门用来处理文件输入的。 下面是文件输入类的层次结构:


9.5.1 FileInputStream 对象

FileInputStream典型地表示一种顺序访问的文本文件。 通过使用FileInputStream你可以访问文件的一个字节、 几个字节或整个文件。


9.5.2 打开FileInputStream

为一个文件打开输入流FileInputStream, 你必须将文件名或文件对象传送给结构:

  FileInput Stream myFileStream;
  myFileStream = new FileInputStream ( "/etc/motd"); 

你还可以象下边这样从FileInputStream里读文件信息:

  File myFile;
  FileInputSteam myFileStream;
  myFile = new File("/etc/motd");
  myFileStream = new FileInputStream(myFile); 

一旦FileInputStream输入流打开, 你就可以从里面读取信息了。read()成员函数有以下几种选项:

  int read() //reads one byte //return -1 at end of stream
  int read(byte b[]) 
             //fills entire array,if possible 
             //returns number of bytes read 
             //returns -1 if end of stream is reached 
  int read(byte b[],int offset, int len) 
            //reads len bytes into b starting at b[offset] 
            //Returns number of bytes read, 
            //or -1 if end of stream is reached. 

9.5.3 关闭FileInputStream

当你完成一个文件的操作, 你可选两种方法关闭它: 显式关闭和隐式关闭, 隐式关闭是自动垃圾回收时的功能。

显式关闭如下:

  myFileStream.close(); 

9.6 例程: 显示一个文件

如果文件的访问权限足够, 你可以在TextArea对象里显示文件内容。

下面是显示文件的程序片断:

    FileInputStream fis;
    TextArea ta;
    public vod init(){
        byte b[] = new byte [1024];
        int I; //make it big enough or wait until you
                //know the size of the file
        String s;
        try {
             fis = new FileInputStream("/etc/motd");
         } catch(FileNotFoundException e) {
             /*do something appropriate */
         }
         try {
             I= fis.read(b);
         } catch(IOException e) {
                 /* do something appropriate */
         }
         s = new String(b, 0);
         ta = new TextArea(s,5,40);
         add (ta);
     } 


9.7 DataInputStreams

DataInputStreams与FileInputStreams差不多。Data流可以直接读任意一种变量类型, 如浮点 、整数和字符等。 一般来说, 对二进制文件使用DataInputStream流。


9.7.1 打开和关闭DataInputStreams

打开和关闭DataInputStreams对象时, 其方法与FileInputStreams相同:

  DataInputStreams myDataStream;
  FileInputStreams myFileStream; 
      //get a file handle
  myFileStream = new FileInputStream("/usr/db/stock.dbf"); 
      //open,or "chain" a data input file
  myDataStream = new DataOutputStream(myFileStream); 
      //Now we can use both input streams to access our file 
      //j(If we want to...)
  myFileStream.read(b); 
  I = myDataStrea.readInt();
      //close the data friel explicityly 
      //Always close the "topmost" file stream
  myDataStream.close();
  myFileStream.close(); 

9.7.2 读DataInputStreams

当你从DataInputStreams流里访问文件时, 你可以使用与FileInputStream流相同的成员函数read()。 但你也可以使用其他访问方法来读取不同种类的数据:

 |  byte readByte()  |  int readUnsignedByte()  |  short readShort()  |  int readUnsighedShort()  |  char readChar()  |  int readInt  |  long readLong()  |  float readFloat()  |  double readDouble()  |  String readLine()

以上每一个成员函数都读取相应的数据对象。 象String readLine()成员函数, 你可使用
,
,
,或EOF作为字符串结束符。

读 一 个 长 整 型, 例 如:

  long serialNo;
  ... 
  serialNo = myDataStream.readLong(); 

9.8 URL 输入流

除了基本文件访问外, Java还提供了通过网络使用URL访问对象的功能。 在下面这个例子里, 我们用getDocumentBase()成员函数并显式指定URL对象来访问声音和图象。

  String imageFile = new String ("images/Duke/T1.gif");
  images[0] = getImage(getDocumentBase(),imageFile(); 

如果我们愿意, 可以直接使用URL:

  URL imageSource;
  imageSource = new URL("http://555-1212.com/~info");
  images[0] = getImage(imageSource,"Duke/T1.gif"); 

我们可以为相应的URL打开输入流。 例如, 下面的程序里包括一个数据文件:

  InputStream is;
  byte buffer[] = new byte[24];
  is = new URL(getDocumentBase(),dataname).openStream(); 

现在我们可以使用is, 就象使用FileInputStream对象一样:

  is.read(buffer.0,buffer.length);

注意: 有些用户设置了他们的浏览器安全属性, 可以不让你的程序访问他们的文件。


9.9 OutputStreams

上面我们谈到了读数据, 那么如何实现写数据呢? 象输入流一样, 输出流也有类似的层次结构:

OutputStream

FileOutputStream |  PipedOutputStream |  ByteArrayOutputStream FilterOutputStream

DataOutputStream |  PrintStream |  BufferedOutputStream

我们将分析FileOutputStream和DataOutputStream类来完成我们碰到的输出流问题。 其它的输出流包含了更多的信息和成员函数。 象输入流的源文件一样, 这些文件在 $JAVA_HOME/src/java/io目录下。


9.9.1 FileOutputStream类

FileOutputStream对象用于向一个文本文件写数据。 象输入文件一样, 你得先打开这个文件后才能写这个文件。


9.9.2 打开一个FileOutputStream对象

要打开一个FileOutputStream对象, 象打开一个输入流一样, 你可以将字符串或文件对象作为参数:

  FileOutputStream myFileStream;
  myFileStream = new FileOutputStream("/etc/motd"); 

象输入流一样, 你也可这样使用:

  File myFile;
  FileOutputStream myFileStream;
  myFile = new File("/etc/motd");
  myFileStream = new FileOutputStream(myFile); 

9.9.3 写入一个流

一旦文件被打开, 你便可以使用write()函数向文件里写一些数据。 就象输入流的read()函数一样, 你可有三种方法:

  void write(int b);   //writes out one byte 
  void write(byte b[]);   //writes out entire array 
  void write (byte b[],int offset,int length);
        //write out length bytes of b[],starting at b[offset] 

9.9.4 关闭一个FileOutputStream对象

关闭输出流和关闭输入流方法一样, 你可以使用显式方法: myFileStream.close(); 你也可以让系统自动关闭它。


9.10 例子: 存储信息

下面有一个程序, 让用户输入一些姓名和电话号码。每一个姓名和号码将加在文件里。 用户通过点“Done"按钮来告诉系统整个列表已输入完毕。

一旦用户输入完整个列表, 程序将创建一个输出文件并显示或打印出来。 例如:

555-1212,Tom 123-456-7890,Peggy L. 234-5678,Marc 234-5678,Ron 876-4321,Beth&Brian 33.1.42.45.70,Jean-Marc

下面是程序的源代码:

  import java.io.*; 
      //Phones.java 
      //A simple database creation program 
  class Phones {
    static FileOutputStream fos;
    public static final int lineLength = 81;
    public static void main(String args[]) throws IOExciption {
      byte[] phone = new byte[lineLength];
      byte[] name = new byte[lineLenght];
      int I;
      fos = new FileOutputStream("phone.numbers");
      while (true) {
        System.err.println("Enter a name (enter `done` to quit)");
        readLine(name);
        if ("done".equalsIgnoreCase(new String(name,0,0,4))) {
          break;
        }
        System.err.println("Enter the phone number");
        readLine(phone);
        for ( i=0;phone[i]!= 0;i++) {
          fos.write(phone[i]);
        } fos.write(`,`);
        for (i=0;name[i]!= 0;I++) {
          fos.write(name[i]);
        }
        fos.write(``);
      }
      fos.close();
    }
    private static void readLine(byte line[]) throws IOException { 
      int i=0,b=0; 
      while ((i<lineLengh-1))&&((b=System.ini.read())!=``)) {
        line[i++] = (byte)b;
      } line[i]=(byte) 0;
    }
  } 

9.11 BufferedOutput流

如果你处理的数据量很多, 或向文件写很多次小数据, 你可以使用一个BufferedOutput流。 BufferedOutput流提供和FileOutputStream类同样的写操作方法, 但所有输出全部存放在一个缓冲区里。 当你填满缓冲区, 它将一次性写入磁盘。 或者你主动将缓冲区写入磁盘。


9.11.1 创建BufferedOutput流

如果要创建一个BufferedOutput流, 首先需要一个FileOutput流。然后将缓冲区链接到FileOutput流:

  FileOutputStream myFileStream;
  BufferedOutputStream myBufferStream; 
      //get a file handle
  myFileStream = new FileOutputStream("/usr/db/stock.dbf");
      //chain a buffered output stream
  myBufferSSstream = new BufferedOutputStream(myFileStream); 

9.11.2 更新和关闭BufferedOutput流

和普通FileOutput流一样, 向BufferedOutput流里的每一次写操作和写入磁盘操作并不是一一对应的。 要想在程序结束之前将缓冲区里的数据写入磁盘, 除非填满缓冲区, 否则只有显式调用flush()函数:

      //force left-over data to disk
  myBufferStream.flush(); 
      //close the data file explicitly 
      //Always close the "topmost" file stream
  myBufferStream.close();
  myFileStream.close(); 

9.12 DataOutput流

和DataInputStream对应, Java还提供了DataOutput流。 使用DataOutput流, 我们可以向文件写入二进制数据。


9.12.1 打开和关闭DataOutput流对象

打开和关闭DataOutput流对象与打开、 关闭FileOutput流对象方法一样:

  DataOutputStream myDataStream;
  FileOutputStream myFileStream;
  BufferedOutputStream myBufferStream;
        //get a file handle
  mhyFileStream = new FileOutputStream("/usr/db/stock.dbf");
        //chain a buffered output stream (for efficiency);
  myBufferStream = new BufferedOutputStream(myFileStream);
        //chain a data output file
  myDataStream = new DataOutputStream(myBufferStream); 
        //Now we can use both input streams to access our file 
        //(iiIf we want to ...)
  myBufferStream.write(b);
  myDataStream.writeInt(i); 
        //close the data file explicitly 
        //Always colse the "topmost" file stream
  myDataStream.close();
  myBuffersStream.close();
  myFileStream.close(); 

9.12.2 向DataOutput流写数据

FileOutput流里的write()函数各种方法都适用于DataOutput流。你还可以看到DataInput流的类似函数方法:  |  void writeBoolean (boolean v)  |  void writeByte (int v)  |  void writeShort (int v)  |  void writeChar (int v)  |  void writeInt (int v)  |  void writeFloat (float v)  |  void writeDouble (double v)  |  void writeBytes (string s)  |  void writeChars (string s)

对字符串来说, 有两种选择: byte和char。 记住byte是8位数据而char是16位数据。 如果你想利用Unicode字符的优点, 你应使用writeChars()函数。


9.12.3 输出记数

在使用二进制数据输出时常用的另外一个函数是size()。 这个函数返回写入文件数据的总字节数。 你也可用size()函数将数据文件分成四字节为单位的块, 例如:

  ... 
  int bytesLeft = myDataStream.size()%4;
  for (int I = 0; I< bytesLeft; I++) {
    myDataStrea.write(0);
  }
  ... 

9.13 随机访问文件

我们读文件常常不是从头至尾顺序读的。 你也许想将一文本文件当作一个数据库, 读完一个记录后, 跳到另一个记录, 它们在文件的不同地方。Java提供了RandomAccessFile类让你操作这种类型的输入输出。


9.13.1 创建随机访问文件

打开随机访问文件有两种方法:

用文件名 myRAFile = new RandomAccessFile(String name,String mode); 
用文件对象 myRAFile = new RandomAccessFile(File file,String mode); 

mode参数决定了访问文件的权限, 如只读`r`或读写`wr`等。

例如, 我们打开一个数据库更新数据:

  RandomAccessFile myRAFile; 
  myRAFile = new RandomAccessFile("/usr/db/stock.dbf","rw"); 

9.13.2 访问信息

RandomAccessFile对象的读写操作和DataInput/DataOutput对象的操作方式一样。 你可以使用在DataInputStream 和DataOutputStream里出现的所有read()和write()函数。

还有几个函数帮助你在文件里移动指针:
long getFilePointer(); 返回当前指针
void seek(long pos); 将文件指针定位到一个绝对地址。 地址是相对于文件头的偏移量。 地址0表示文件的开头。
long length(); 返回文件的长度。 地址"length()"表示文件的结尾。


9.13.3 增加信息

你可以使用随机访问文件来设置成增加信息模式:

  myRAFile = new RandomAccessFile("/tmp/java.log","rw");
  myRAFile.seek(myRAFile.length()); 
      //Any subsequent write()s will be appended to the file 

9.13.4 追加信息例子: 下面是一个在已存在文件后面追加字符串的例子:

  import java.io.IOException;
  import java.io.RandomAccessFile; 
  class raTest {
    public static void main(String args[]) throws IOException {
      RandomAccessFile myFAFile;
      String s = "Information to Append  Hi mom!"; 
            //open our random access file
      myRAFile = new RandomAccessFile("/tmp/java.log","rw");
            //move to the end of the file
      myRAFile.seek(myRAFile.length());
            //Start appending!
      myRAFile.writeBytes(s); 
      myRAFile.close(); 
    }
  } 

本章小结

1. Java通过系统类达到访问标准输入输出的功能。

2. 你可以创建、读、写文件。


(作者: 袁小春  来源: 不详)