输出流输入流

 

InputStream和OutputStream

  InputStream是所有表示位输入流的类之父类,OutputStream是所有表示位输出流的类之父类.它们都是抽象类,继承它们的子类要重新定义其中所定义的抽象方法..

  例1可以读取键盘输入流,in对象的read()方法一次读取一个字节的数据,读入的数据以int 类型返回.所以在使用out对象将数据显示出来时,就是十进制方式.

例1 StreamDemo.java

import java.io.*;

public class StreamDemo {
 public static void main(String[] args){
  try{
   System.out.print("输入字符:");
   System.out.println("输入字符十进制表示:" + System.in.read());
  }
  catch(IOException e){
   e.printStackTrace();
  }
 }

}

 

FileInputStream和FileOutputStream

  java.io.FileInputStream是InputStream的子类.

   当建立一个FileInputStream或FileOutputStream的实例时,必须指定文件位置及文件名称,实例被建立时文件的流就会开启;而不使用流时,必须关闭文件流,以释放与流相依的系统资源,完成文件读/写的动作.

  FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节.在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承载数据的中间角色.

  例2是使用FileInputStream与FileOutputStream的一个例子.程序可以复制文件,它会先从来源文件读取数据至一个byte数组中,然后再将byte数组的数据写入目的文件.

例2 FileStreamDemo.java

import java.io.*;

public class FileStreamDemo {
 public static void main(String[] args) {
  try {
   byte[] buffer = new byte[1024];

   // 来源文件
   FileInputStream fileInputStream = new FileInputStream(new File(
     args[0]));
   // 目的文件
   FileOutputStream fileOutputStream = new FileOutputStream(new File(
     args[1]));

   // available()可取得未读取的数据长度
   System.out.println("复制文件:" + fileInputStream.available() + "字节");

   while (true) {
    if (fileInputStream.available() < 1024) {
     // 剩余的数据比1024字节少
     // 一位一位读出再写入目的文件
     int remain = -1;
     while ((remain = fileInputStream.read()) != -1) {
      fileOutputStream.write(remain);
     }
     break;
    } else {
     // 从来源文件读取数据至缓冲区
     fileInputStream.read(buffer);
     // 将数组数据写入目的文件
     fileOutputStream.write(buffer);
    }
   }

   // 关闭流
   fileInputStream.close();
   fileOutputStream.close();

   System.out.println("复制完成");
  } catch (ArrayIndexOutOfBoundsException e) {
   System.out.println("using:java FileStreamDemo src des");
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

  程序中示范了两个read()方法,一个方法可以读入指定长度的数据至数组,另一个方法一次可以读入一个字节.每次读取之后,读取的光标都会往前进,如果读不到数据刚返回-1.使用available()方法可以获得还有多少字节可以读取.除了使用File来建立FileInputStream,FileOutputStream的实例之外,也可以直接使用字符串指定路径来建立.如

  //来源文件

  FileInputStream fileInputStream = new FileInputStream(args[0]);

     //目的文件

  FileOutputStream fileOutputStream = new FileOutputStream(args[1]);

  在不使用文件流时,记得使用close()方法自行关闭流,以释放与流相依的系统资源.

  FileOutputStream默认会以新建文件的方式来开启流.如果指定的文件名称已经存在,则原文件会被覆盖;如果想以附加的模式来写入文件,则可以在构建FileOutputStream实例时指定为附加模式.例如:

  FileOutputStream fileOutputStream = new FileOutputStream(args[1],true);

  构建方法的第二个append参数如果设定为true,在开启流时如果文件不存在则会新建一个文件,如果文件存在就直接开启流,并将写入的数据附加至文件末端.

 

BufferedInputStream 和 BufferedOutputStream

  java.io.BufferedInputStream与java.io.BufferedOutputStream可以为InputStream、OutputStream类的对象增加缓冲区功能,构建BufferedInputStream实例时,需要给定一个InputStream类型的实例,实现BufferedInputStream时,实际上最后是实现InputStream实例.同样地,在构建BufferedOutputStream时,也需要给定一个OutputStream实例,实现BufferedOutputStream时,实际上最后是实现OutputStream实例.

  BufferedInputStream的数据成员buf是一个位数组,默认为2048字节,BufferedOutputStream的数据成员buf 也是一个位数组,默认为512字节.

  例3是对例2的改写,不用自行设定缓冲区,比较简单且有效率.

例3 BufferedStreamDemo.java

import java.io.*;

public class BufferedStreamDemo {
 public static void main(String[] args){
  try{
   byte[] data = new byte[1];
   
   File srcfile = new File(args[0]);
   File desfile = new File(args[1]);
   
   BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcfile));
   BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(desfile));
   
   System.out.println("复制文件:" + srcfile.length() + "字节");
   
   while(bufferedInputStream.read(data) != -1){
    bufferedOutputStream.write(data);
   }
   
   //将缓冲区中的数据全部写出
   bufferedOutputStream.flush();
   
   //关闭流
   bufferedInputStream.close();
   bufferedOutputStream.close();
   
   System.out.println("复制完成");
   
  }
  catch(ArrayIndexOutOfBoundsException e){
   System.out.println("using java UseFileStream src des");
   e.printStackTrace();
  }
  catch(IOException e){
   e.printStackTrace();
  }
 }

}

  为了确保缓冲区中的数据一定被写出至目的地,建议最后执行flush()将缓冲区中的数据全部写出目的流中,这个范例的执行结果与例2的执行结果是相同的.

 

DatainputStream 和 DataOutputStream 

  java.io.DataInputStream 和 java.io.DataOutputStream 可提供一些对Java基本数据类型写入的信息,成员数据的类型假设都是Java的基本数据类型,这样的需求不必要使用到与Object输入、输出的流对象,可以使用DataInputStream 、DataOutputStream来写入或读出数据.

  示例:先设计一个Member类.

例4 Member.java

public class Member {
 private String name;
 private int age;
 
 public Member(){
  
 }
 
 public Member(String name,int age){
  this.name = name;
  this.age = age;
 }
 
 public void setName(String name){
  this.name = name;
 }
 
 public void setAge(int age){
  this.age = age;
 }
 
 public String getname(){
  return name;
 }
 
 public int getAge(){
  return age;
 }
}

 

  下面打算将Member类实例的成员数据写入文件中,并打算在读入文件数据后,将这些数据还原为Member对象.

例5 DataStreamDemo.java

import java.io.*;

public class DataStreamDemo {
 public static void main(String[] args){
  Member[] members = {new Member("A", 91),
                     new Member("B", 80),
                     new Member("C", 86)};
  
  try{
   DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(args[0]));
   
   for(Member member:members){
    //写入UTF字符串
    dataOutputStream.writeUTF(member.getname());
    //写入int字符串
    dataOutputStream.writeInt(member.getAge());
   }
   
   //读出所有数据至目的地
   dataOutputStream.flush();
   //关闭流
   dataOutputStream.close();
   
   DataInputStream dataInputStream = new DataInputStream(new FileInputStream(args[0]));
   //读出数据并还原为对象
   for(int i = 0; i < members.length; i++){
    //读出UTF字符串
    String name = dataInputStream.readUTF();
    //读出int数据
    int score = dataInputStream.readInt();
    members[i] = new Member(name,score);
    
   }
   //关闭流
   dataInputStream.close();
   
   //显示还原后的数据
   for(Member member:members){
    System.out.printf("%s/t%d%n",member.getname(),member.getAge());
   }
  }
  catch(IOException e){
   e.printStackTrace();
  }
                    
 }

}

  在从文件中读出数据时,不用费心地自行判断读入字符串时或读入int类型时何时该停止,使用对应的readUTF()或readInt()方法可以正确地读入完整类型数据.同样地,DataInputStream、DataOutputStream并没有改变InputStream或OutputStream的行为.

 

 

ObjectInputStream和ObjectOutputStream

  在Java程序执行的过程中,很多数据都是以对象的方式存在于内存中,有时会希望直接将内存中的整个对象存储至文件,而不是只存储对象中的某些基本类型成员信息,而在下一次程序运行时,希望可以从文件中读出数据并还原为对象.这时可以使用java.io.ObjectInputStream和java.io.ObjectOutputStream来进行这项工作.

  如果要直接存储对象,定义该对象的类必须实现java.io.Serializable接口.不过Serializable接口中并没有规范任何必须实现的方法,所以这里所谓实现的意义,其实像是对对象贴上一个标志,代表该对象是可序列化的(Serializable).

  为了说明如何直接存储对象,先实现一个User类.

例6 User.java

import java.io.Serializable;

public class User implements Serializable {
 private static final long serialVersionUID = 1L;
 
 private String name;
 private int number;
 
 public User(){
  
 }
 
 public User(String name,int number){
  this.name = name;
  this.number = number;
 }
 public void setName(String name){
  this.name = name;
 }
 
 public void setNumber(int number){
  this.number = number;
 }
 
 public String getName(){
  return name;
 }
 
 public int getNumber(){
  return number;
 }
}

  注意到serialVersionUID,它代表了可序列化对象的版本.如果没有提供这个版本信息,则实现Serializable接口的类会自动依类名称、实现的接口、成员等来产生.如果是自动产生的,则下次更改User类,自动产生的serialVersionUID也会跟着变更,从文件读回对象时若两个对象的serialVersionUID不相同,就会丢出java.io.InvalidClassException.如果想要维持版本信息的一致,则要明确声明serialVersionUID.

  ObjectInputStream 和 ObjectOutputStream 为 InputStream、OutputStream的实例加上了可以让使用者写入对象与读出对象的功能.在写入对象时,要使用 writeObject()方法,读出对象时则使用readObject()方法,被读出的对象都是以Object类型返回.

例7 ObjectStreamDemo.java

import java.io.*;
import java.util.*;

public class ObjectStreamDemo {
 public static void main(String[] args){
  User[] users = {new User("A",101),new User("B",102),new User("C",103)};
  
  try{
   //写入新文件
   writeObjectsToFile(users,args[0]);
   //读取文件数据
   users = readObjectsFromFile(args[0]);
   //显示读回的对象
   for(User user:users){
    System.out.printf("%s/t%d/n",user.getName(),user.getNumber());
   }
   System.out.println();
   
   users = new User[2];
   users[0] = new User("D", 104);
   users[1] = new User("E", 105);
   
   //附加新对象至文件
   appendObjectsToFile(users,args[0]);
   
   //读取文件数据
   users = readObjectsFromFile(args[0]);
   
   //显示读回的对象
   for(User user:users){
    System.out.printf("%s/t%d/n", user.getName(),user.getNumber());
   }
  }
  catch(ArrayIndexOutOfBoundsException e){
   System.out.println("没有指定文件名");
   //e.printStackTrace();
  }
  catch(FileNotFoundException e){
   e.printStackTrace();
  }
  catch(Exception e){
   e.printStackTrace();
   System.out.println("没有指定文件名");
   
  }
 }
 
 //将指定的对象写入至指定的文件
 public static void writeObjectsToFile(Object[] objs,String filename){
  File file = new File(filename);
  
  try{
   ObjectOutputStream objOoutputStream = new ObjectOutputStream(new FileOutputStream(file));
   for(Object obj:objs){
    //将对象写入文件
    objOoutputStream.writeObject(obj);
   }
   //关闭流
   objOoutputStream.close();
  }
  catch(IOException e){
   e.printStackTrace();
  }
  
 }
 
 //将指定文件中的对象数据读回
 public static User[] readObjectsFromFile(String filename)
                       throws FileNotFoundException{
  File file = new File(filename);
  
  //如果文件不存在就丢出异常
  if (!file.exists())
   throw new FileNotFoundException();
  
  //使用List先存储读回的对象
  List<User> list = new ArrayList<User>();
  
  try{
   FileInputStream fileInputStream = new FileInputStream(file);
   ObjectInputStream objInputStream = new ObjectInputStream(fileInputStream);
   
   while (fileInputStream.available() > 0){
    list.add((User) objInputStream.readObject());
   }
   objInputStream.close();
  }
  catch(ClassNotFoundException e){
   e.printStackTrace();
  }
  catch(IOException e){
   e.printStackTrace();
  }
  
  User[] users = new User[list.size()];
  return list.toArray(users);
 }
 
 public static void appendObjectsToFile(Object[] objs, String filename)
                    throws FileNotFoundException{
  File file = new File(filename);
  
  //如果文件不存在则丢出异常
  if (!file.exists())
   throw new FileNotFoundException();
  
  try{
   //附加模式
   ObjectOutputStream objOutputStream = new ObjectOutputStream(
                                   new FileOutputStream(file,true)){
    //如果要附加对象至文件后
    //必须重新定义这个方法
    protected void writeStreamHeader() throws IOException{}
   };
   
   for (Object obj : objs){
    //将对象写入文件
    objOutputStream.writeObject(obj);
   }
   objOutputStream.close();
  }
  catch (IOException e){
   e.printStackTrace();
  }
 }
}

  注意,在试图将对象附加至一个先前已写入对象的文件时,由于ObjectOutputStream在写入数据时,还会加上一个特别的流头(Stream Header),所以在读取文件时会检查这个流头,如果一个文件中被多次附加对象,那么该文件中会有多个流头,这样读取检查时就会发现不一致,这会丢出java.io.StreamCorrupedException.为了解决这个问题,可以重新定义ObjectOutputStream的writeStreamHeader()方法.如果是以附加的方式来写入对象,就不写入流头:

  ObjectOutputStream objOutputStream = new ObjectOutputStream(

                                                                                     new FileOutputStream(file,true)) {

                                                                                       protected void writeStreamHeader()

                                                                                             throws IOException{}

};

  序列化对象的功能并不只应用于文件的读取或写入,也可以应用于其他领域.例如将对象直接通过网络进行传送或是传送影像的位数组数据等.

 

SequenceInputStream

   若要将一个文件侵害为数个文件,再将之组合还原为一个文件,最基本的作法是使用数个FileInputStream来打开分割后的文件,然后一个一个文件的读取,并使用同一个FileOutputStream实例写到同一个文件中,必须要自行判断每一个分割文件的读取是否完毕,如果完毕就读取下一个文件.

  如果使用java.io.SequenceInputStream就不用这么麻烦,SequenceInputStream可以看作是数个InputStream对象的组合.当一个InputStream对象的内容读取完毕后,它就会取出下一个InputStream对象,直到所有的InputStream对象都读取完毕为止.

 

PrintStream

ByteArrayInputStream 和 ByteArrayOutputSTream

PushbackInputStream

 

File类

 一个File的实例被建立时,它就不能再被改变内容.File实例除了用作一个文件或目录的抽象表示之外,它还提供了不少相关操作方法:可以用它来对文件系统作一些查询与设定的动作.要注意的是,不管是文件还是目录,在Java中都是以File的实例来表示..

  以下是一个设定与操作File实例的简单示例,可以指定查询某个目录下的所有文件与目录名称.

FileDemo.java

import java.io.*;
import java.util.*;

public class FileDemo {
 public static void main(String[] args){
  try{
   File file = new File(args[0]);
   if (file.isFile()){//是否为文件
    System.out.println(args[0] + "文件");
    System.out.print(file.canRead()? "可读" :"不可读");
    System.out.print(file.canWrite() ? "可写": "不可写");
    System.out.println(file.length() + "字节");
   }
   else{
    //列出所有的文件及目录
    File[] files = file.listFiles();
    ArrayList<File> fileList = new ArrayList<File>();
    
    for (int i = 0; i < files.length; i++){
     //先列出目录
     if (files[i].isDirectory()) {
      //取得路径名
      System.out.println("[" + files[i].getPath() + "]");
     }
     else{
      //文件先存入fileList,待会再列出
      fileList.add(files[i]);
     }
    }
    //列出文件
    for(File f: fileList){
     System.out.println(f.toString());
    }
    System.out.println();
   }
  }
  catch(ArrayIndexOutOfBoundsException e){
   System.out.println("using: java FileDemo pathname");
   
  }
 }

}

  File类主要是文件的抽象代表,若要作文件输出/输入,必须配合其他相关类来使用.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值