Java IO使用和总结

来源:互联网 发布:java ee为什么这么小 编辑:程序博客网 时间:2024/06/05 23:59

Java IO使用和总结

上篇文章提到了NIO的使用,这篇总结下IO的使用;不详谈语法,仅分析特点,从而明确它们的使用范围,这样就能够在合适的场合想到并应用它们。

1.IO的数据源和输出目标

IO的数据源和输出目标大致分为以下几类:

1)文件

2)管道

3)网络连接

4)内存缓存

5)系统输入输出

下面逐一分析

2.文件

/**2.文件 * 你可以根据该文件是二进制文件还是文本文件来选择使用FileInputStream或者FileReader * 如果你需要跳跃式地读取文件其中的某些部分,可以使用RandomAccessFile * 注意: * 有时候你可能需要读取文件的信息而不是文件的内容,举个例子,如果你需要知道文件的大小 * 和文件的属性。对于目录来说也是一样的,比如你需要获取某个目录下的文件列表。通过File * 类可以获取文件和目录的信息。 */

3.管道

/**3.管道(在上一篇NIO的总结中提到了它,并且举了一个示例) * Java IO中的管道为运行在同一个JVM中的两个线程提供了通信的能力。 * 你不能利用管道与不同的JVM中的线程通信(不同的进程)。在概念上,Java的管道不同于Unix/Linux * 系统中的管道。在Unix/Linux中,运行在不同地址空间的两个进程可以通过管道通信。在Java中,通 * 信的双方应该是运行在同一进程中的不同线程。 *  * 除了管道之外,一个JVM中不同线程之间还有许多通信的方式。实际上,线程在大多数情况下会传递 * 完整的对象信息而非原始的字节数据。但是,如果你需要在线程之间传递字节数据,Java IO的管道 * 是一个不错的选择。 *  * 管道的本质是一样的,只是在应用时,在IO和NIO中的语法不同而已 */

4.网络连接

/**4.IO 网络 * Java网络API用来在不同进程之间建立网络连接,而Java IO则用来在建立了连接之后的进程之间交换数据 * 下面的一个写法,不使用FileInputStream而是InputStream,是因为可以这样的话还可以同时接收来自网络的输入,再 * 延伸一下,java.io.InputStream类是所有Java IO输入流的基类。如果你正在开发一个从流中读取数据的组件,请尝试 * 用InputStream替代任何它的子类(比如FileInputStream)进行开发。这么做能够让你的代码兼容任何类型而非某种确定 * 类型的输入流 ;同样的,OutputStream也是一样。如下面一个例子 */
/*下面的一个写法,不使用FileInputStream而是InputStream,是因为可以这样的话还可以同时接收来自网络的输入*/public class MyClass {    public static void main(String[] args) {        InputStream inputStream = new FileInputStream("c:\\myfile.txt");        process(inputStream);    }    public static void process(InputStream input) throws IOException {        //do something with the InputStream    }}

5.内存缓存

/**5.IO 读取或者获得字节(字符数组)    可以把这个理解为内存缓存 * 5.1 用ByteArrayInputStream或者CharArrayReader封装字节或者字符数组,然后从数组中读取数据成为流, * 以同样的方式也可以用于读取字符数组,只要把字符数组封装在CharArrayReader上就行了。 * byte[] bytes = new byte[1024]; * InputStream input = new ByteArrayInputStream(bytes); *  * 5.2 把数据写到ByteArrayOutputStream或者CharArrayWriter中,只要调用toByteArray()或者toCharArray, * 所有写入的数据就会以数组的形式返回 * OutputStream output = new ByteArrayOutputStream(); * output.write("This text is converted to bytes".getBytes("UTF-8")); * byte[] bytes = output.toByteArray(); */

有一个深复制的问题,就可以使用这个思想来做; 如果一个对象数组List<Object>, 如何得到它的一份拷贝,我们所熟悉的add()方法,adllAll()方法,还有system.copy等都不能实现,我们需要的是一个新的List,而且其中的每一个对象都是新的,可以把Object 序列化,然后将Ist.LIst<Object>拷贝到内存中,以ByteArray的形式存放,然后从内存种读取这个 ByteArray到一个新的List<Object>中(反序列化),下面是示例:

/*引出一个深复制的问题,代码如下:*/public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();      ObjectOutputStream out = new ObjectOutputStream(byteOut);      out.writeObject(src);        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());      ObjectInputStream in = new ObjectInputStream(byteIn);      @SuppressWarnings("unchecked")      List<T> dest = (List<T>) in.readObject();      return dest;  }

6.系统输入输出

/**6.替换系统流  系统输入输出 * System.in, System.out, System.err本身没什么可讲的。但是有一点;尽管System.in, System.out,  * System.err这3个流是java.lang.System类中的静态成员(译者注:这3个变量均为final static常量), * 并且已经预先在JVM启动的时候初始化完成,你依然可以更改它们。只需要把一个新的InputStream设 * 置给System.in或者一个新的OutputStream设置给System.out或者System.err,之后的数据都将会在新 * 的流中进行读取、写入。如下案例: *  * 注意:flush的调用,什么时候调用flush? * 1)FileOutPutStream继承outputStream,并不提供flush()方法的重写,所以无论内容多少write都会 * 将二进制流直接传递给底层操作系统的I/O,flush无效果 * 而Buffered系列的输入输出流函数单从Buffered这个单词就可以看出他们是使用缓冲区的,应用程序 * 每次IO都要和设备进行通信,效率很低,因此缓冲区为了提高效率,当写入设备时,先写入缓冲区, * 等到缓冲区有足够多的数据时,就整体写入设备,所以它需要使用flush方法手动强制写出; * 2)close()方法中包含了flush()的操作,但是马上就关闭流,所以如果想强制刷新缓冲区,然后继续使用 * 流的话,就用flush */
OutputStream output = new FileOutputStream("c:\\data\\system.out.txt");PrintStream printOut = new PrintStream(output);System.setOut(printOut)

下面讲述一些IO中的其他相关需要学习其特点和注意的地方

7

/**7.PushbackInputStream * 可以获取输入流之后,处理,然后再反过来放入输入源中。 */

8

/**8 关于流的整合问题 * Stream可以与Reader或者Writer整合 * Reader或者Writer可以与Buffer整合 */

9

/**9.之前说到了不在service层try catch,而是在方法体上throws异常:http://blog.csdn.net/Jintao_Ma/article/details/52853865 * 但是如果在service层关闭流的时候可能要用到try catch finally,这个时候,上层如何回复消息给用户? * 个人认为一个比较好的解决方法是:在service的方法体上加上返回值,然后将service内部的异常信息描 * 述成用户能识别的内容,之后放入返回值中,在controller层捕获异常, * 1)有异常 2)没有异常,那么有两种情况 * 2.1)service层的异常,2.2)确实没有异常 这三种情况都可以返回给用户
public static void main(String[] args) throws FileNotFoundException, IOException {try(FileInputStream fileInputStream = new FileInputStream(new File("test.txt"))){System.out.println("Channel2.main()");}}public BaseResult controllerMethod{ /*假设是controller层次*/BaseResult baseResult = new BaseResult();try {BaseResult = serviceMethod();} catch (Exception e) {BaseResult.setIsSuccess(false);BaseResult.serErrMsg("业务异常");}finally{return BaseResult;}}public BaseResult serviceMethod() throws Exception(){BaseResult baseResult = new BaseResult();try{InputStream input = new FileInputStream("c:\\data\\input-text.txt");doSomethingWithData(input);/*对输入流做一些处理*/}catch(IOException e){System.out.println(e.getMessage());baseResult.setIsSuccess(false);baseResult.serErrMsg("流异常");return baseResult;  /*这个return 在finally执行后再执行*/}finally{input.close();/*关闭流*/}doOthers();/*可能会有异常,在方法体上抛出*/baseResult.setIsSuccess(true);return baseResult;}

那么还有没有其他的方法呢,其实是有的,那就是在关闭流的时候不使用finally,这样的话就不用try catch,就可以在方法体上throws异常,从而不影响我们之前文章总结的理论; JDK7及以上就提供了这种方法,称作try with resources, 顾名思义就是在try参数中加上资源,如下:

public static void main(String[] args) throws FileNotFoundException, IOException {try(FileInputStream fileInputStream = new FileInputStream(new File("test.txt"))){System.out.println("Channel2.main()");}}

另外再提到一点,关于异常处理,在C++中有异常模板,在Java中有异常框架,这一部分后面有时间再探究(这里备注下,提醒还有关于异常处理更标准的方法)

10

/**10.关于RandomAccessFile的使用 可以在任意位置读取文件 * seek 指定一个指针 * getFilePointer 方法 */

11

/**11.DataInputStream与DataOutputStream可以处理java基本类型的数据 */

12

/**12.ObjectInputStream与ObjectOutputStream 处理java序列化的对象 */

0 0
原创粉丝点击