zoukankan      html  css  js  c++  java
  • 高级Java工程师必备 ----- 深入分析 Java IO (三)

    概述

    Java IO即Java 输入输出系统。不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO(文件、控制台、网络),我们还要考虑具体和它们的通信方式(顺序、随机、二进制、按字符、按字、按行等等)。Java类库的设计者通过设计大量的类来攻克这些难题,这些类就位于java.io包中。

    在JDK1.4之后,为了提高Java IO的效率,Java又提供了一套新的IO,Java New IO简称Java NIO。它在标准java代码中提供了高速的面向块的IO操作。本篇文章重点介绍Java IO,关于Java NIO请参考我的另两篇文章: 

    高级Java工程师必备 ----- 深入分析 Java IO (一)BIO

    高级Java工程师必备 ----- 深入分析 Java IO (二)NIO

    Java IO类库的框架

    首先看个图:

     Java IO的类型

    虽然java IO类库庞大,但总体来说其框架还是很清楚的。从是读媒介还是写媒介的维度看,Java IO可以分为:

    1. 输入流:InputStream和Reader
    2. 输出流:OutputStream和Writer

    而从其处理流的类型的维度上看,Java IO又可以分为:

    1. 字节流:InputStream和OutputStream
    2. 字符流:Reader和Writer

    下面这幅图就清晰的描述了JavaIO的分类:

    -字节流字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer

    我们的程序需要通过InputStream或Reader从数据源读取数据,然后用OutputStream或者Writer将数据写入到目标媒介中。其中,InputStream和Reader与数据源相关联,OutputStream和writer与目标媒介相关联。

    Java IO的基本用法

    Java IO :字节流

    通过上面的介绍我们已经知道,字节流对应的类应该是InputStream和OutputStream,而在我们实际开发中,我们应该根据不同的媒介类型选用相应的子类来处理。下面我们就用字节流来操作文件媒介:

    例1,用字节流写文件

    public static void writeByteToFile() throws IOException{
        String hello= new String( "hello word!");
         byte[] byteArray= hello.getBytes();
        File file= new File( "d:/test.txt");
         //因为是用字节流来写媒介,所以对应的是OutputStream 
         //又因为媒介对象是文件,所以用到子类是FileOutputStream
        OutputStream os= new FileOutputStream( file);
         os.write( byteArray);
         os.close();
    }

    例2,用字节流读文件

    public static void readByteFromFile() throws IOException{
        File file= new File( "d:/test.txt");
         byte[] byteArray= new byte[( int) file.length()];
         //因为是用字节流来读媒介,所以对应的是InputStream
         //又因为媒介对象是文件,所以用到子类是FileInputStream
        InputStream is= new FileInputStream( file);
         int size= is.read( byteArray);
        System. out.println( "大小:"+size +";内容:" +new String(byteArray));
         is.close();
    }

    CopyFileDemo

    package com.chenhao.io.byteIO;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * @author ChenHao
     *
     */
    public class CopyFileDemo {
    
        /**
         * @param args
         * @throws FileNotFoundException 
         */
        public static void main(String[] args) {
            String src ="E:/xp/test";
            String dest="e:/xp/test/4.jpg";
            try {
                copyFile(src,dest);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                System.out.println("文件不存在");
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("拷贝文件失败|关闭流失败");
            }
        }
        /**
         * 文件的拷贝
         * @param  源文件路径
         * @param  目录文件路径
         * @throws FileNotFoundException,IOException
         * @return 
         */
        public static void copyFile(String srcPath,String destPath) throws FileNotFoundException,IOException {
            //1、建立联系 源(存在且为文件) +目的地(文件可以不存在)  
            File src =new File(srcPath);
            File dest =new File(destPath);
            if(! src.isFile()){ //不是文件或者为null
                System.out.println("只能拷贝文件");
                throw new IOException("只能拷贝文件");
            }
            //2、选择流
            InputStream is =new FileInputStream(src);
            OutputStream os =new FileOutputStream(dest);
            //3、文件拷贝   循环+读取+写出
            byte[] flush =new byte[1024];
            int len =0;
            //读取
            while(-1!=(len=is.read(flush))){
                //写出
                os.write(flush, 0, len);
            }
            os.flush(); //强制刷出
            
            //关闭流
            os.close();
            is.close();
        }
    
    }

    Java IO :字符流

    同样,字符流对应的类应该是Reader和Writer。下面我们就用字符流来操作文件媒介:

    例3,用字符流读文件

    public static void writeCharToFile() throws IOException{
        String hello= new String( "hello word!");
        File file= new File( "d:/test.txt");
        //因为是用字符流来读媒介,所以对应的是Writer,又因为媒介对象是文件,所以用到子类是FileWriter
        Writer os= new FileWriter( file);
        os.write( hello);
        os.close();
    }

    例4,用字符流写文件

    public static void readCharFromFile() throws IOException{
        File file= new File( "d:/test.txt");
        //因为是用字符流来读媒介,所以对应的是Reader
        //又因为媒介对象是文件,所以用到子类是FileReader
        Reader reader= new FileReader( file);
        char [] byteArray= new char[( int) file.length()];
        int size= reader.read( byteArray);
        System. out.println( "大小:"+size +";内容:" +new String(byteArray));
        reader.close();
    }

    Java IO :字节流转换为字符流

    字节流可以转换成字符流,java.io包中提供的InputStreamReader类就可以实现,当然从其命名上就可以看出它的作用。其实这涉及到另一个概念,IO流的组合,后面我们详细介绍。下面看一个简单的例子:

    例5 ,字节流转换为字符流

    public static void convertByteToChar() throws IOException{
        File file= new File( "d:/test.txt");
        //获得一个字节流
        InputStream is= new FileInputStream( file);
        //把字节流转换为字符流,其实就是把字符流和字节流组合的结果。
        Reader reader= new InputStreamReader( is);
        char [] byteArray= new char[( int) file.length()];
        int size= reader.read( byteArray);
        System. out.println( "大小:"+size +";内容:" +new String(byteArray));
        is.close();
        reader.close();
    }

    Java IO:文件媒介操作

    例6 ,File操作

    public class FileDemo {
      public static void main(String[] args) {
             //检查文件是否存在
            File file = new File( "d:/test.txt");
             boolean fileExists = file.exists();
            System. out.println( fileExists);
             //创建文件目录,若父目录不存在则返回false
            File file2 = new File( "d:/fatherDir/subDir");
             boolean dirCreated = file2.mkdir();
            System. out.println( dirCreated);
             //创建文件目录,若父目录不存则连同父目录一起创建
            File file3 = new File( "d:/fatherDir/subDir2");
             boolean dirCreated2 = file3.mkdirs();
            System. out.println( dirCreated2);
            File file4= new File( "d:/test.txt");
             //判断长度
             long length = file4.length();
             //重命名文件
             boolean isRenamed = file4.renameTo( new File("d:/test2.txt"));
             //删除文件
             boolean isDeleted = file4.delete();
            File file5= new File( "d:/fatherDir/subDir");
             //是否是目录
             boolean isDirectory = file5.isDirectory();
             //列出文件名
            String[] fileNames = file5.list();
             //列出目录
            File[]   files = file4.listFiles();
      }
    }

    随机读取File文件

    通过上面的例子我们已经知道,我们可以用FileInputStream(文件字符流)或FileReader(文件字节流)来读文件,这两个类可以让我们分别以字符和字节的方式来读取文件内容,但是它们都有一个不足之处,就是只能从文件头开始读,然后读到文件结束。

    但是有时候我们只希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置,具体看下面的例子:

    例7,随机读取文件

    public static void randomAccessFileRead() throws IOException {
         // 创建一个RandomAccessFile对象
        RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");
         // 通过seek方法来移动读写位置的指针
         file.seek(10);
         // 获取当前指针
         long pointerBegin = file.getFilePointer();
         // 从当前指针开始读
         byte[] contents = new byte[1024];
         file.read( contents);
         long pointerEnd = file.getFilePointer();
        System. out.println( "pointerBegin:" + pointerBegin + "
    " + "pointerEnd:" + pointerEnd + "
    " + new String(contents));
         file.close();
    }

    例8,随机写入文件

    public static void randomAccessFileWrite() throws IOException {
         // 创建一个RandomAccessFile对象
         RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");
         // 通过seek方法来移动读写位置的指针
         file.seek(10);
         // 获取当前指针
         long pointerBegin = file.getFilePointer();
         // 从当前指针位置开始写
         file.write( "HELLO WORD".getBytes());
         long pointerEnd = file.getFilePointer();
         System. out.println( "pointerBegin:" + pointerBegin + "
    " + "pointerEnd:" + pointerEnd + "
    " );
         file.close();
    }

    Java IO:BufferedInputStream和BufferedOutputStream

    BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候,能提升IO性能。

    使用BufferedInputStream十分简单,只要把普通的输入流和BufferedInputStream组合到一起即可。我们把上面的例2改造成用BufferedInputStream进行读文件,请看下面例子:

    例10 ,用缓冲流读文件

    public static void readByBufferedInputStream() throws IOException {
         File file = new File( "d:/test.txt");
         byte[] byteArray = new byte[( int) file.length()];
         //可以在构造参数中传入buffer大小
         InputStream is = new BufferedInputStream( new FileInputStream(file),2*1024);
         int size = is.read( byteArray);
         System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
         is.close();
    }

    BufferedOutputStream的情况和BufferedInputStream一致,在这里就不多做描述了。

    copyFile

    package com.chenhao.io.buffered;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    /**
     * 字节流文件拷贝+缓冲流 ,提高性能
     * 缓冲流(节点流)
     * @author ChenHao
     *
     */
    public class BufferedByteDemo {
    
        public static void main(String[] args) {
            String src ="E:/xp/test";
            String dest="e:/xp/test/4.jpg";
            try {
                copyFile(src,dest);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                System.out.println("文件不存在");
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("拷贝文件失败|关闭流失败");
            }
        }
        /**
         * 文件的拷贝
         * @param  源文件路径
         * @param  目录文件路径
         * @throws FileNotFoundException,IOException
         * @return 
         */
        public static void copyFile(String srcPath,String destPath) throws FileNotFoundException,IOException {
            //1、建立联系 源(存在且为文件) +目的地(文件可以不存在)  
            File src =new File(srcPath);
            File dest =new File(destPath);
            if(! src.isFile()){ //不是文件或者为null
                System.out.println("只能拷贝文件");
                throw new IOException("只能拷贝文件");
            }
            //2、选择流
            InputStream is =new BufferedInputStream(new FileInputStream(src));
            OutputStream os =new BufferedOutputStream( new FileOutputStream(dest));
            //3、文件拷贝   循环+读取+写出
            byte[] flush =new byte[1024];
            int len =0;
            //读取
            while(-1!=(len=is.read(flush))){
                //写出
                os.write(flush, 0, len);
            }
            os.flush(); //强制刷出
            
            //关闭流
            os.close();
            is.close();
        }
    
    }

    Java IO:BufferedReader和BufferedWriter

    BufferedReader、BufferedWriter 的作用基本和BufferedInputStream、BufferedOutputStream一致,具体用法和原理都差不多 ,只不过一个是面向字符流一个是面向字节流。同样,我们将改造字符流中的例4,给其加上buffer功能,看例子:

    public static void readByBufferedReader() throws IOException {
         File file = new File( "d:/test.txt");
         // 在字符流基础上用buffer流包装,也可以指定buffer的大小
         Reader reader = new BufferedReader( new FileReader(file),2*1024);
         char[] byteArray = new char[( int) file.length()];
         int size = reader.read( byteArray);
         System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
         reader.close();
    }

    另外,BufferedReader提供一个readLine()可以方便地读取一行,而FileInputStream和FileReader只能读取一个字节或者一个字符,因此BufferedReader也被称为行读取器.

    public static void keyIn() throws IOException {
     try (//InputStreamReader是从byte转成char的桥梁
          InputStreamReader reader = new InputStreamReader(System.in);
          //BufferedReader(Reader in)是char类型输入的包装类
          BufferedReader br = new BufferedReader(reader);) {
             
             String line = null;
             while ((line = br.readLine()) != null) {
                 if (line.equals("exit")) {
                     //System.exit(1);
                     break;
                 }
                 System.out.println(line);
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
    }

    Java IO: 序列化与ObjectInputStream、ObjectOutputStream

    Serializable

    如果你希望类能够序列化和反序列化,必须实现Serializable接口,就像所展示的ObjectInputStream和ObjectOutputStream例子一样。

    ObjectInputStream

    ObjectInputStream能够让你从输入流中读取Java对象,而不需要每次读取一个字节。你可以把InputStream包装到ObjectInputStream中,然后就可以从中读取对象了。代码如下:

    ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
    MyClass object = (MyClass) input.readObject(); //etc.
    input.close();

    在这个例子中,你读取的对象必须是MyClass的一个实例,并且必须事先通过ObjectOutputStream序列化到“object.data”文件中。

    在你序列化和反序列化一个对象之前,该对象的类必须实现了java.io.Serializable接口。

    ObjectOutputStream

    ObjectOutputStream能够让你把对象写入到输出流中,而不需要每次写入一个字节。你可以把OutputStream包装到ObjectOutputStream中,然后就可以把对象写入到该输出流中了。代码如下:

    ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("object.data"));
    MyClass object = new MyClass();  output.writeObject(object); //etc.
    output.close();

    例子中序列化的对象object现在可以从ObjectInputStream中读取了。

    同样,在你序列化和反序列化一个对象之前,该对象的类必须实现了java.io.Serializable接口。

  • 相关阅读:
    metaClass
    First-class citizen
    class-metaclass-Class vs. type
    eval-Evaluation
    编程语言:数据+算法;
    线程的关系
    线程的核心是算法,是面向过程的
    多线程与控制结构-面向过程
    线程安全-上线文-运行环境
    面向对象的语言学本质
  • 原文地址:https://www.cnblogs.com/java-chen-hao/p/11083740.html
Copyright © 2011-2022 走看看