java IO流详解(下)

来源:互联网 发布:淘宝登陆异常 编辑:程序博客网 时间:2024/05/16 10:50

java IO流(上)

接着上一篇文章的内容,我们在说完了InputStream、OutputStream和Reader、Writer之后,来看一些有趣的类,最后我们来看一些具体应用的实例,去体会在实际项目中怎么组织这些类来让它们发挥作用。

(一)ZIP文档,ZipInputStream类


ZIP文档以压缩格式存储一个或多个文件,每个ZIP文档都有一个头,包含诸如每个文件名字和所使用的压缩方法等信息。在java中,我们通常使用ZipInputStream来读入ZIP文档。

在ZipInputStream对象中我们可能有一个或多个项(文件),你可能需要浏览文档中每个单独的项,所以java使用叫做ZipEntry的类来表示在ZIP中的一个个项。下面我们通过一个略长的例子来学习一下操作ZIP的所有方法和它们的用法。

package Stream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintWriter;import java.util.Scanner;import java.util.zip.ZipEntry;import java.util.zip.ZipInputStream;import java.util.zip.ZipOutputStream;/** *  * @author QuinnNorris *  *         使用java中的ZipInputStream和ZipOutputStream类操作ZIP文件 */public class ZIPStream {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        System.out.println("根目录是:" + System.getProperty("user.dir"));        String fileName = "example.zip";        try (FileOutputStream fos = new FileOutputStream(fileName);                ZipOutputStream zos = new ZipOutputStream(fos);) {            for (int i = 0; i < 10; i++) {                zos.putNextEntry(new ZipEntry("No." + i + ".txt"));                // zos.write(("这是第" + i + "个txt文件").getBytes());                PrintWriter pw = new PrintWriter(zos);                pw.print("这是第" + i + "个txt文件");                pw.flush();                zos.closeEntry();            }        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            System.err.println("没有在根目录:" + System.getProperty("user.dir")                    + "下找到:" + fileName);            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            System.err.println("将文件加入压缩包时发生未知错误。");            e.printStackTrace();        }        try (FileInputStream fis = new FileInputStream(fileName);                ZipInputStream zis = new ZipInputStream(fis);) {            while (zis.getNextEntry() != null) {                /*                 * byte[] b = new byte[1024]; zis.read(b);                 * System.out.println(new String(b));                 */                Scanner sn = new Scanner(zis);                if (sn.hasNextLine())                    System.out.println(sn.nextLine());                zis.closeEntry();            }        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}
  1. 在一开始,我们通过System.getProperty(“user.dir”)先输出这个.java文件所在的位置,为我们之后的工作带来方便。
  2. 之后我们使用了带资源的try块(jdk1.7后可用),带资源的try块会为你自动调用close方法,省去了很多不必要的麻烦。
  3. 通过FileOutputStream与ZipOutputStream的套用,我们构建出了可以进行操作的输出流。
  4. 我们打算在这个zip文件中写入10个txt文件,for循环做标记。
  5. 调用putNextEntry方法,这个方法将参数中的ZipEntry对象作为一个新的条目加入ZIP文件中。
  6. ZipEntry对象的构造器传入String作为这个条目的名字,不要忘记后缀。
  7. 被注释掉的write方法是ZipOutputStream自带的写出方法,参数是byte[],如果要使用write一般都需要类型转换。
  8. 我们不使用原生的write方法,创建PrintWriter对象,使用print方法写入内容,调用flush方法将内容填充进去。
  9. 调用closeEntry方法表示这个条目已经操作结束。
  10. 期间会出现两个异常,这里我们选择catch来捕获,如果你想throws也没有问题。
  11. 下面的try块同样带资源, 根据需要,我们将文件和zip关联起来,使用FileInputStream,ZipInputStream这两个类。
  12. 调用getNextEntry方法,如果需要操作ZipEntry对象,为它赋个名字,这里我们不需要。
  13. 被注释的方法是原生read方法。
  14. 我们通过Scanner类来获取zis对象中的内容,将每行输出到控制台。
  15. 调用closeEntry方法走向下一个条目。

通过以上的步骤完成了创建zip文件和在控制台显示其中每个txt文件的内容。

(二)序列化对象流、ObjectOutputStream类


当你要存储数据时,使用固定长度的数组或者是集合类库中的列表是个不错的选择。但是如果你想要同时存储不同类型的数据呢?或许可以使用类似Object[]的数组来存储,但是这里要强势推荐一个对象序列化的通用机制来存放任何对象。我们通过这个方法将任何对象写出到流中,并在之后将其读回。而且这种序列化的方法有很多优点,最著名的优点就是通过序列化我们可以得到一个对象的clone,如果你在很多项目中都见过这个接口——Serializable。说明对象克隆使用的正是这种方法。

package Stream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** *  * @author QuinnNorris *  *         序列化对象流会深拷贝复制对象 */public class ObjectStream {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        String fileName = "example.txt";        try (FileOutputStream fos = new FileOutputStream(fileName);                ObjectOutputStream oos = new ObjectOutputStream(fos);                FileInputStream fis = new FileInputStream(fileName);                ObjectInputStream ois = new ObjectInputStream(fis)) {            Student s1 = new Student(19, "XiaoZhang", null);            Student s2 = new Student(20, "XiaoWang", s1);            oos.writeObject(s2);            Student getS2 = (Student) ois.readObject();            getS2.getFriend().setAge(5);            // 将getS2的Student类型的friend属性的age值改为5.            if (19 == s2.getFriend().getAge())                // 如果序列化不是深拷贝,那么getS2和s2指向的应该是同一个s1,这个if判断不会正确                System.out.println("序列化是深拷贝");            // 结果确实输出了:“序列化是深拷贝”,证明getS2和s2只想的对象已经不同,经过了深拷贝        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

(三)IO流典型使用方式


这一段主要来自于thinking in java,但我们采用了try-catch方法,原书中为了方便直接使用throws来回避异常的问题,主要是各种常用的IO流使用手段。

1.缓冲输入文件

如果想打开一个文件用于字符输入,可以使用String或File对象作为文件名。为了提高速度,我们想要为这个文件进行缓冲,我们将产生的引用传递给BufferedReader构造器。通过这个类的readLine()方法,这就是我们最终对象和进行读取的接口。

package Stream;import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;/** *  * @author QuinnNorris *  *         缓冲输入文件 */public class Stream1 {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        try (FileReader fir = new FileReader("example.txt");        // 直接接受一个File文件或String,和FileInputStream用法一样。                BufferedReader br = new BufferedReader(fir)        // 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。        // 构造器接受一个Reader对象,类似包装器        ) {            String nextLine = null;            while ((nextLine = br.readLine()) != null) {                System.out.println(nextLine);            }        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

2.基本的文件输出

FileWriter对象可以向文件写入数据。首先创建一个与指定文件连接的FileWriter。实际上,我们会通过用BufferedWriter将其包装起来用以缓冲输出。为了提供格式化的方法我们还可以使用PrintWriter方法。

package Stream;import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;/** *  * @author QuinnNorris *  *         基本文件输出 */public class Stream2 {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        try (FileWriter fw = new FileWriter(new File("example.txt"));                BufferedWriter bw = new BufferedWriter(fw);                PrintWriter pw = new PrintWriter(bw)) {            for (int i = 0; i < 10; i++)                pw.print("这是第" + i + "行文字。"                        + System.getProperty("line.separator"));            pw.flush();            // 在当前目录下多出个txt文件,有十行内容。        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

3.存储和恢复数据

如果我们使用DataOutputStream写入数据,java保证我们可以使用DataInputStream准确的读取数据——无论读和写数据的平台多么不同,这一点是非常有价值的。

package Stream;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;/** *  * @author QuinnNorris *  *         存储和恢复数据 */public class Stream3 {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        String fileName = "example.txt";        try (FileOutputStream fos = new FileOutputStream(fileName);                BufferedOutputStream bos = new BufferedOutputStream(fos);                DataOutputStream dos = new DataOutputStream(bos);) {            dos.writeDouble(3.14);            dos.writeUTF("圆周率");        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        try (FileInputStream fis = new FileInputStream(fileName);                BufferedInputStream bis = new BufferedInputStream(fis);                DataInputStream dis = new DataInputStream(bis)) {            System.out.println(dis.readDouble());            System.out.println(dis.readUTF());        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}
1 0