java IO流详解(下)

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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值