JAVA IO详解

前言:  

    Java IO 操作主要指的是使用Java进行输入、输出操作,Java中的所有操作类都存放在java.io包中,而整个java.io包中最重要的就是5个类和一个接口,5个类指的是File、OutputStream、ImputStream、Writer、Reader。一个接口指的是Serializable。掌握了这些就等于掌握了IO操作的核心。

一、操作文件的类——File

1.File类的基本介绍

        在整个io包中,唯一表示与文件本身有关的类就是File类。使用File类可以进行创建或删除文件等常用操作。要想使用File类,则首先要观察File类的构造方法,此类的常用构造方法如下所示:

        public File(String pathname)  ->实例化File类的时候,必须设置好路径

表1 File类中的常用方法和常量

javaID工具 java io工具类_javaID工具

2.使用File类操作文件

实例操作一:创建一个新文件

直接使用creatNewFile()创建一个新文件,但是此方法使用了throws关键字声明,所以使用中,必须使用try..cath进行异常处理。

注意:如果在不同的操作系统中,则路径的分隔符表示是不一样的,例如:

           Windows 中使用反斜杠表示目录的分隔符  “\”

           Linux 中使用正斜杠表示目录的分隔符 “/”

因此,为了达到可移植性的目的,在操作文件时,一定要学会使用File.separator表示分隔符

实例操作二:删除一个指定的文件

直接使用File类中的delete()方法

注意:在删除文件前应该保证文件存在,所以最好在删除之前判断一下文件是否存在,在此,可以使用File类提供的exists()方法,此方法返回boolean类型

//以下给出两个实例相结合的代码

import java.io.File;
import java.io.IOException;
/*
  * 创建文件,注意文件路径,采用拼凑给出路径,以便适合不同操作系统
  * 删除文件,创建新文件时要判断文件是否已经存在
  */
public class IoDemo1 {

     public static void main(String[] args) {
         //File file = new File("d:\\test.txt");  //普通创建文件路径,但不同系统分隔符不一样
         File file = new File("d:"+ File.separator +"text.txt");  
         try {
             if(file.exists()){           //判断文件是否存在
                 file.delete();          //删除文件,并且保证文件存在,需要加判断
             }else {
                 file.createNewFile();       //创建一个文件
             }
             
         } catch (IOException e) {
             e.printStackTrace();
         }
     }

 }

实例操作四:创建一个文件夹

直接使用mkdir()方法

实例操作五:列出指定目录的全部文件

在File类中,定义了两个列出文件夹内容的方法:

           public String [] list()     列出全部名称,返回一个字符串数组

返回一个File对象数组

实例操作六:判断一个给定的路径是否是目录

直接使用isDirectory()方法判断

//以下给出以上三个实例的代码

import java.io.File;

/*
  * 创建文件夹,直接使用 mkdir()方法
  * 显示文件夹中所有文件名称,使用list()方法,返回一个字符串数组
  * 显示文件夹中所有文件的完整路径,使用listFiles()方法,返回的是File文件数组
  * 判断一个给定的路径是否是目录,使用isDirectory()方法,返回时boolean
  */
public class IoDemo2 {
     public static void main(String[] args) {
         File f = newFile("d:"+ File.separator+"mldn");  //声明文件夹地址
         //创建文件夹
         
         File file = new File("d:"+File.separator);
         String str[] = file.list();
         File files[] = file.listFiles();               //列出所有文件
         for (int  i = 0; i < str.length; i++) {
//            System.out.println(str[i]);
             System.out.println(files[i]);
         }
         if (f.isDirectory()) {             /判断路径是否为目录
             System.out.println(f.getPath()+"路径是目录");
         }else {
             System.out.println(f.getPath()+"路径不是目录");
         }
     }
 }

二、RandomAccessFile类

File类只是针对文件本身进行操作,而如果要对文件内容进行操作,则可以使用RandomAccessFile类,此类属于随机读取类,即可以随机读取一个文件中指定位置的数据。

例如:假设给定以下三个数据:

     zhangsan,  30

             lisi , 31

             wangwu,32

表2:RandomAccessFile类的常用操作方法

javaID工具 java io工具类_字节流_02

                                                                                

注意:使用rw方式声明RandomAccessFile对象时,要写入的文件不存在,系统自动创建

//以下给出相应代码

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo1 {

     public static void main(String[] args) throws IOException {
         File file = new File("d:"+File.separator+"text.txt");
         RandomAccessFile rdf = null;
         rdf = new RandomAccessFile(file,  "rw");
         String name = "zhangsan";
         int age = 30;
         rdf.writeBytes(name);
         rdf.writeInt(age);
         rdf.close();
     }

 }

三、字节流

1.字节输出流:OutputStream

OutputStream 是整个IO包中字节输出流的最大父类,此类定义如下:

      public abstreact class OutputStream

      extends Object

      implements Closeable,Flushable

从以上定义中可以看出,OutputStream 类是一个抽象类,即不能直接使用,需要通过子类实例化对象。如果现在操作的是一个文件,可以使用FileOutputStream类,通过向上转型后,可以为OutputStream 实例化。                                                                                                          表3:OutputStream类的常用方法

No.

方法或常量

类型

描述

1

public void close() throws IOException

普通

关闭输出流

2

public void flush() throws IOException

普通

刷新缓冲区

3

public void write(byte[] b) throws IOException

普通

将一个byte数组写入数据流

4

public void write(byte[] b,int off,int len) throws IOException

普通

将一个指定范围的byte数组写入数据流

5

public abstract void write(int b) throwsIOException

普通

将一个字节数据写入数据流

2.字节输入流

既然程序可以向文件中写入内容,则就可以通过InputStream从文件中把内容读取进来,首先来看InputStream类的定义:

public abstract class InputStream

        extends Object

        implementsCloseable

与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类,如果现在是从文件中读取,子类肯定是FileInputStream

                                                                                                               表4:InputStream类的常用方法

No.

方法或常量

类型

描述

1

public int available() throws IOException

普通

可以取得输入文件的大小

2

public void close() throws IOException

普通

关闭输入流

3

public abstract int read() throws IOException

普通

读取内容,以数字的方式读取

4

public int read(byte[] b) throws IOException

普通

将内容读到byte数组之中,同时返回读入的个数


注意:追加新内容,则需要使用FileOutStream类的另外一个构造方法,OutputStream out = new FileOutputStream(file,true); //表示追加内容

           InputStream类 同上

//以下给出实现代码

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/*
  *字节流 :
  *1.使用File类打开一个文件
  *2.通过字节流的子类指定输出的位置
  *3.进行读写操作
  *4.关闭输入输出 
  */
public class StreamDemo {
     public static void main(String[] args) throws IOException {
         File file = new File("d:"+File.separator+"text.txt");
         //OutputStream out = new FileOutputStream(file);   //参数不带true则再次运行时会取代原有文件内容
         OutputStream out = new FileOutputStream(file,true); //表示追加内容
 //        String s = "hellow !";
         String s = "\r\nhellow !";       //追加内容换行
         byte b[] = s.getBytes();        //将字符串转换成字节数组
         out.write(b);
         out.close();
         
         InputStream in = new FileInputStream(file);
         byte b1[] = new byte[(int)file.length()];    //设置数组的大小
         in.read(b1);              //把内容取出,读到b1数组中
         in.close();
         System.out.println(new String(b1));   //输出
     }
 }

四、字符流

1.字符输出流Writer

Writer 本身就是第一个字符流的输出类,此类的定义如下:

public abstract class Writer

             extends Object

              implements Appendable ,Closeable ,Flushable

此类也是一个抽象类,应该使用FileWriter的子类,                     

                                                                                                          表5:Writer 类的常用方法

No.

方法或常量

类型

描述

1

public abstract void close() throws IOException

普通

关闭输出流

2

public void write(String str) throws IOException

普通

将字符串输出

3

public void write(char[] cbuf) throws IOException

普通

将字符数组输出

4

public abstract void flush() throws IOException

普通

强制性清空缓存


2.字符输入流Reader

Reader是使用字符的方式从文件之中取出数据,Reader类的定义如下;

public abstract class Reader
              extends Object
               implements Readable, Closeable


Reader 本身也是抽象类,如果现在要从文件中读取内容,则可以直接使用 FileReader 子类。


                                                                                         表6:Reader类的常用方法

No.

方法或常量

类型

描述

1

public abstract void close() throws IOException

普通

关闭输出流

2

public int read() throws IOException

普通

读取单个字符

3

public int read(char[] cbuf) throws IOException

普通

将内容读到字符数组之中,返回读入的长度

字符流与字节流的区别:


³ 字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。

//以下给出相应代码

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

/*
  * 字符流:与字节流使用步骤差不多
  */
public class WriterReaderDemo {
     public static void main(String[] args) throws IOException {
         File file = new File("d:"+File.separator+"text.txt");
//        Writer writer = new FileWriter(file);
         Writer writer = new FileWriter(file,true);     //文件内容追加与字节流相同
         String str = "\r\nzhangyang";
         writer.write(str);        //直接输入字符串,不需要进行转换
         writer.close();
         
         Reader reader = new FileReader(file);
         char c[] = new char[(int)file.length()];       //这里是将内容读取到字符数组
         reader.read(c);
         reader.close();
         System.out.println(new String(c));
     }
 }

五、转换流——OutputStreamWriter和InputStreamReader

在整个 IO 包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流 - 字符流的转换类。

OutputStreamWriter :
          是 Writer 的子类,将输出的字符流变为字节流,
          即:将一个字符流的输出对象变为字节流输出对象。

² InputStreamReader :
         是 Reader 的子类,将输入的字节流变为字符流,
         即:将一个字节流的输入对象变为字符流的输入对象。
转换步骤
          如果以文件操作为例,则在内存中的字符数据需要通过 OutputStreamWriter 变为字节流才能保存在文件之中,读取的时候需要将读入的字节流通过 InputStreamReader 变为字符流。

//将字节输出流变成字符输出流

importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.OutputStreamWriter;
importjava.io.Writer;
publicclass OutputStreamWriterDemo01 {
  publicstaticvoid main(String[]args) throws Exception {// 所有的异常抛出
  File f = new File("d:" +File.separator +"test.txt");
  Writerout = null;
  out= newOutputStreamWriter(newFileOutputStream(f));// 字节流变为字符流
  out.write("hello world");  // 使用字符流输出
  out.close();
  }
}

//将字节输入流变成字符输入流

importjava.io.File;
importjava.io.FileInputStream;
importjava.io.InputStreamReader;
importjava.io.Reader;
publicclass InputStreamReaderDemo01 {
  publicstaticvoid main(String[]args) throws Exception {// 所有的异常抛出
  File f = new File("d:" +File.separator +"test.txt");
  Readerreader =null;
  reader= newInputStreamReader(newFileInputStream(f));// 将字节流变为字符流
  char c[] =newchar[1024];
  intlen =reader.read(c);
  reader.close();
  System.out.println(new String(c, 0, len));
  }
}

六、对象序列化

1.基本概念与Serializable接口

对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。

如果一个类的对象想被序列化,则对象所在的类必须实现 java.io.Serializable 接口
此接口定义如下:

public interface  
  Serializable 
  {}
 
//定义可序列化类 
 
importjava.io.Serializable;
publicclass PersonimplementsSerializable {  // 此类的对象可以被序列化
  private Stringname;  // 声明name属性
  privateintage; // 声明age属性
  public Person(String name,int age) {  // 通过构造方法设置属性内容
  this.name = name;
  this.age = age;
  }
  public StringtoString() {  // 覆写toString()方法
  return"姓名:" +this.name +";年龄:" +this.age;
  }
}

要想完成对象的输入或输出,还必须依靠对象输出流( ObjectOutputStream )和对象输入流( ObjectInputStream )

使用对象输出流输出序列化对象的步骤,有时也称为序列化,而使用对象输入流读入对象的过程,有时也称为反序列化

对象输出流: ObjectOutputStream

一个对象如果要想进行输出,则必须使用 ObjectOutputStream 类
此类定义如下:

public class  
 ObjectOutputStream
                       extends  
 OutputStream 
 
                       implements  
 ObjectOutput 
 , 
 ObjectStreamConstants

                                                                                         表7  此类常用方法



No.

方法或常量

类型

描述

1

public ObjectOutputStream(OutputStream out) throws IOException

构造

传入输出的对象

2

public final void writeObject(Object obj) throws IOException

普通

输出对象

对象输入流:ObjectInputStream

使用 ObjectInputStream 可以直接把被序列化好的对象反序列化回来
ObjectInputStream 的定义如下:

public class  
 ObjectInputStream 
 
                                     extends  
 InputStream 
 
                                      implements  
 ObjectInput 
 , 
 ObjectStreamConstants

                                                                                                     表8:此类常用方法



No.

方法或常量

类型

描述

1

public ObjectInputStream(InputStream in) throws IOException

构造

构造输入对象

2

public final Object readObject() throws IOException, ClassNotFoundException

普通

从指定位置读取对象

//以下给出具体实现代码

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

 /*
  * 对象序列化:把一个对象变为二进制的数据流存储
  * 被序列化的类要实现 java.io.Serializable 接口
  * 还必须依靠对象输出流 (ObjectOutputStream)和对象输入流 (ObjectInputStream)
  * 并且对象输入流输出流  的构造方法 参数为 输出流  输入流对象 ,OutputStream和InputStream的 对象
  */
public class ObjectStreamDemo {
     public static void main(String[] args) throws IOException, ClassNotFoundException {
         File file = new File("d:"+File.separator+"text.txt");
         OutputStream out = new FileOutputStream(file);      //文件输出流
         ObjectOutputStream oos = new ObjectOutputStream(out);   //实例化对象
         oos.writeObject(new person("张三", "12"));    //保存对象到文件
         oos.writeObject(new person("李四", "11"));
         oos.close();              //关闭输出
         
         InputStream in = new FileInputStream(file);
         ObjectInputStream ois = new ObjectInputStream(in);
         Object o = ois.readObject();

         ois.close();
         System.out.println(o);
         
     }
 }
class person implements Serializable{

     /**
      * 
      */
     private static final long serialVersionUID = 1L;
     private String name;
     private String age;
    @Override
    public String toString() {
         return "person [name=" + name + ", age=" + age + "]";
     }
     public person(String name, String age) {
         super();
         this.name = name;
         this.age = age;
     }
 }

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

/*
  * 序列化多个文件:
  * 使用对象数组保存多个对象
  */
public class ObjectStreamDemo1 {
     public static void main(String[] args) throws IOException, ClassNotFoundException {
         Student s[] = {new Student("战三", 19),new Student("lis ", 19)};
         ser(s);
         Object o[] = dlser();
         for (int  i = 0; i < o.length; i++) {
             Student student = (Student)o[i];
             System.out.println(student);
         }
         
     }
     public static void ser(Object o[]) throws IOException{
         File file = new File("d:"+File.separator+"text.txt");
         OutputStream out = new FileOutputStream(file);
         ObjectOutputStream oos = new ObjectOutputStream(out);
         oos.writeObject(o);
         oos.close();
         
     }
     public static Object[] dlser() throws IOException, ClassNotFoundException{
         File file = new File("d:"+File.separator+"text.txt");
         InputStream in = new FileInputStream(file);
         ObjectInputStream ois = new ObjectInputStream(in);
         Object o[] = (Object[])ois.readObject();
         ois.close();
         return o;
     }
 }
class Student implements Serializable{

     /**
      * 
      */
     private static final long serialVersionUID = 1L;
     private String name;
     private int age;
     @Override
     public String toString() {
         return "Student [name=" + name + ", age=" + age + "]";
     }
     public Student(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
 }

Externalizable接口与Serializable接口实现序列化的区别 

No.

区别

Serializable

Externalizable

1

实现复杂度

实现简单,Java对其有内建支持

实现复杂,由开发人员自己完成

2

执行效率

所有对象由JAVA统一保存,性能较低

开发人员决定那个对象保存,可能的速度提升

3

保存信息

保存时占用空间大

部分存储,可能的空间减少


transient关键字


当使用 Serializable 接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用 transient 关键字进行声明

例如: Student中name不想被序列化

class Student implements Serializable{

     /**
      * 
      */
     private static final long serialVersionUID = 1L;
     private transientString name;
     private int age;
     @Override
     public String toString() {
         return "Student [name=" + name + ", age=" + age + "]";
     }
     public Student(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
 }