【Java基础之五】Java中IO详解

1.Java IO简介

可能学过计算机组装与维修的同学都知道I/O设备,翻译过来也就是Input/Output(输入输出设备),在硬件中键盘、鼠标 属于 输入设备,显示器、打印机等属于输出设备,这里输入输出参考物是计算机本身。
java.io包从宏观上来理解和硬件有些相同,Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
流是一种有顺序的,有起点和终点的字节集合,是对数据传输的总成或抽象。即数据在两设备之间的传输称之为流,流的本质是数据传输,根据数据传输的特性将流抽象为各种类,方便更直观的进行数据操作。

2.Java IO使用场景

Java的IO包主要关注的是从原始数据源的读取以及输出原始数据到目标媒介。以下是最典型的数据源和目标媒介:

1.文件

2.管道

3.网络连接

4.内存缓存

5.System.in, System.out, System.error(注:Java标准输入、输出、错误输出)

2.1 Java IO 的一般使用原则

2.1.1 按数据来源(去向)分类

1 、是文件: FileInputStream, FileOutputStream, ( 字节流 )FileReader, FileWriter( 字符 )

2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 字节流 )

3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 )

4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字节流 )StringReader, StringWriter( 字符流 )

5 、网络数据流: InputStream, OutputStream,( 字节流 ) Reader, Writer( 字符流 )

2.1.2 按是否格式化输出分

1 、要格式化输出: PrintStream, PrintWriter

2.1.3 按是否要缓冲分

1 、要缓冲: BufferedInputStream, BufferedOutputStream,( 字节流 ) BufferedReader, BufferedWriter( 字符流 )

2.1.4 按数据格式分

1 、二进制格式(只要不能确定是纯文本的) : InputStream, OutputStream 及其所有带 Stream 结束的子类

2 、纯文本格式(含纯英文与汉字或其他编码方式); Reader, Writer 及其所有带 Reader, Writer 的子类

2.1.5 按输入输出分

1 、输入: Reader, InputStream 类型的子类

2 、输出: Writer, OutputStream 类型的子类

2.1.6 特殊需要

1 、从 Stream 到 Reader,Writer 的转换类: InputStreamReader, OutputStreamWriter

2 、对象输入输出: ObjectInputStream, ObjectOutputStream

3 、进程间通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

4 、合并输入: SequenceInputStream

5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader

决定使用哪个类以及它的构造进程的一般准则如下(不考虑特殊需要):

首先,考虑最原始的数据格式是什么: 原则2.1.4

第二,是输入还是输出:原则2.1.5

第三,是否需要转换流:原则2.1.6第1点

第四,数据来源(去向)是什么:原则2.1.1

第五,是否要缓冲:原则2.1.3 (特别注明:一定要注意的是 readLine() 是否有定义,有什么比 read, write 更特殊的输入或输出方法)

第六,是否要格式化输出:原则2.1.2

3.Java IO分类

按数据处理方式分 :字节流 & 字符流
按数据起点终点分 :输入流 & 输出流

关于类的分类图如下:

【Java基础之五】Java中IO详解_第1张图片

这是Java IO比较基本的一些处理流。

4.Java IO举例

4.1 文件

Demo:

package com.shuidi.iotest;

import java.io.File;

public class FileTest {

    public static void main(String[] args) {
        createFile();  
    }  

    /** 
     * File test
    */  
    public static void createFile() {
        File f = new File("create.txt");
        try{
            f.createNewFile();  
            System.out.println("该分区大小"+f.getTotalSpace()/(1024*1024*1024)+"G"); //返回由此抽象路径名表示的文件或目录的名称。  
            f.mkdirs();  //创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。  
            //f.delete(); //  删除此抽象路径名表示的文件或目录 
            System.out.println("文件名  "+f.getName());  //  返回由此抽象路径名表示的文件或目录的名称。
            System.out.println("文件父目录字符串 "+f.getParent());// 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。  
          } catch (Exception e) {  
              e.printStackTrace();  
       }
    }
}

输出:

该分区大小131G
文件名  create.txt
文件父目录字符串 null

4.2 字节流

4.2.1 输入流InputStream

package com.shuidi.iotest;

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

public class FileInputStreamTest {

    public static void main(String[] args) {
        int count=0;  //统计文件字节长度  
        InputStream streamReader = null;   //文件输入流  
        try {
            streamReader = new FileInputStream(new File("FileInoutStreamTest.jpg"));
            System.out.println("---长度是: "+ streamReader.available() +" 字节");
            byte[] b = new byte[1024];
            int temp;
            while( (temp = streamReader.read(b)) != -1) {  //读取文件字节,并递增指针到下一个字节  
                if (temp != 1024 && temp != -1) {
                    System.out.println("---最后读出的长度是: "+ temp +" 字节");  
                }
                count++;
            }
            System.out.println("---最后读出的长度是: "+ temp +" 字节");  
            System.out.println("---长度是: "+ count +" 字节");  
          } catch (IOException e) {  
              //TODO 自动生成的 catch 块  
              e.printStackTrace();  
          } finally {  
              try{  
                 if (streamReader != null) { streamReader.close(); } 
              }catch (IOException e) {  
                 //TODO 自动生成的 catch 块  
                 e.printStackTrace();  
              }
        }  
    } 
}

输出:

---长度是: 51281 字节
---最后读出的长度是: 81 字节
---最后读出的长度是: -1 字节
---长度是: 51 字节

4.2.2 输出流OutputStream

package com.shuidi.iotest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamTest {

    public static void main(String[] args) {
        // TODO自动生成的方法存根 
        byte[] buffer = new byte[1024];   //一次取出的字节数大小,缓冲区大小 
        int numberRead = 0;
        FileInputStream input = null;
        FileOutputStream out = null;
        try {
            input = new FileInputStream("FileInoutStreamTest.jpg");
            out = new FileOutputStream("FileOutputStreamTest.jpg"); //如果文件不存在会自动创建  
            while ((numberRead = input.read(buffer))!=-1) {  //numberRead的目的在于防止最后一次读取的字节小于buffer长度
                out.write(buffer, 0, numberRead);       //否则会自动被填充0  
            }
        } catch (IOException e) {
            // TODO自动生成的 catch 块 
            e.printStackTrace();  
        } finally {
            try {
                input.close();
                out.close();  
            } catch (IOException e) {
                // TODO自动生成的 catch 块  
                e.printStackTrace();  
            }         
        }  
    }
}

无输出

4.2.3 输入输出流DataInputStream/DataOutputStream

Demo:

package com.shuidi.iotest;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamTest {

      public static void main(String[]args){
         Member[] members = {new Member("Justin",90),
                            new Member("momor",95),
                            new Member("Bush",88)};
         try {  
            DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("DataStreamTest.txt"));  

            for(Member member:members){
                //写入UTF字符串  
               dataOutputStream.writeUTF(member.getName());
               //写入int数据  
               dataOutputStream.writeInt(member.getAge());
            }  

            //所有数据至目的地 
            dataOutputStream.flush();
            //关闭流  
            dataOutputStream.close();

            DataInputStream dataInputStream = new DataInputStream(new FileInputStream("DataStreamTest.txt"));  

            //读出数据并还原为对象 
            for(int i = 0;i < members.length;i++){
               //读出UTF字符串 
               String name = dataInputStream.readUTF();
               //读出int数据 
               int score = dataInputStream.readInt();
               members[i] = new Member(name,score);
            }  

            //关闭流  
            dataInputStream.close();

            //显示还原后的数据  
            for(Member member : members){
               System.out.printf("%s\t%d%n",member.getName(),member.getAge());
            }
         } catch (IOException e){
             e.printStackTrace();
         }
      }
}

输出:

Justin  90
momor   95
Bush    88

4.2.4 PushbackInputStream

package com.shuidi.iotest;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class PushBackInputStreamTest {

    public static void main(String[] args) throws IOException {  
        String str = "hello,rollenholt";  
        PushbackInputStream push = null; // 声明回退流对象  
        ByteArrayInputStream bat = null; // 声明字节数组流对象  
        bat = new ByteArrayInputStream(str.getBytes());  
        push = new PushbackInputStream(bat); // 创建回退流对象,将拆解的字节数组流传入  
        int temp = 0;  
        while ((temp = push.read()) != -1) { // push.read()逐字节读取存放在temp中,如果读取完成返回-1  
           if (temp == ',') { // 判断读取的是否是逗号  
              push.unread('u'); //回到temp的位置  
              temp = push.read(); //接着读取字节  
              System.out.print("(回退" + (char) temp + ") "); // 输出回退的字符  
           } else {  
              System.out.print((char) temp); // 否则输出字符  
           }  
        }  
    }
}

输出:

hello(回退u) rollenholt

4.2.5 SequenceInputStream

有些情况下,当我们需要从多个输入流中向程序读入数据。此时,可以使用合并流,将多个输入流合并成一个SequenceInputStream流对象。SequenceInputStream会将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 合并流的作用是将多个源合并合一个源。其可接收枚举类所封闭的多个字节流对象。

package com.shuidi.iotest;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

public class SequenceInputStreamTest {
      /** 
       * @param args 
       *            SequenceInputStream合并流,将与之相连接的流集组合成一个输入流并从第一个输入流开始读取, 
       *            直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 
       *            合并流的作用是将多个源合并合一个源。可接收枚举类所封闭的多个字节流对象。 
       */  
      public static void main(String[] args) {
         doSequence();  
      }  

      private static void doSequence() {  
         // 创建一个合并流的对象  
         SequenceInputStream sis = null;  
         // 创建输出流。  
         BufferedOutputStream bos = null;  
         try {  
            // 构建流集合。  
            Vector vector = new Vector();  
            vector.addElement(new FileInputStream("SequenceInputStreamTest1.txt"));  
            vector.addElement(new FileInputStream("SequenceInputStreamTest2.txt"));  
            vector.addElement(new FileInputStream("SequenceInputStreamTest3.txt"));  

            Enumeration e = vector.elements();  

            sis = new SequenceInputStream(e);  

            bos = new BufferedOutputStream(new FileOutputStream("SequenceInputStreamTest4.txt"));  

            // 读写数据  
            byte[] buf = new byte[1024];  
            int len = 0;  
            while ((len = sis.read(buf)) != -1) {  
               bos.write(buf, 0, len);  
               bos.flush();  
            }  
         } catch (FileNotFoundException e1) {  
            e1.printStackTrace();  
         } catch (IOException e1) {  
            e1.printStackTrace();  
         } finally {  
            try {  
               if (sis != null)  
                  sis.close();  
            } catch (IOException e) {  
               e.printStackTrace();  
            }  
            try {  
               if (bos != null)  
                  bos.close();  
            } catch (IOException e) {  
               e.printStackTrace();  
            }  
         }  
      }  
}

输入:

SequenceInputStreamTest1.txt1
SequenceInputStreamTest2.txt2
SequenceInputStreamTest3.txt3

输出:

SequenceInputStreamTest4.txt123

4.2.6 PrintStream

System.out.print():System.out这个对象就是PrintStream,这个我们不做过多示例

4.3 字符流

4.3.1 FileReader/PrintWriter

package com.shuidi.iotest;

import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;

public class FileReaderTest {
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        // TODO自动生成的方法存根  
        char[] buffer = new char[512];   //一次取出的字节数大小,缓冲区大小  
        int numberRead = 0;  
        FileReader reader = null;        //读取字符文件的流  
        PrintWriter writer = null;    //写字符到控制台的流  

        try {  
           reader = new FileReader("FileReaderTest.txt");  
           writer = new PrintWriter(System.out);  //PrintWriter可以输出字符到文件,也可以输出到控制台  
           while ((numberRead = reader.read(buffer))!=-1) {  
              writer.write(buffer, 0, numberRead);  
           }  
        } catch (IOException e) {  
           // TODO自动生成的 catch 块  
           e.printStackTrace();  
        }finally{  
           try {  
              reader.close();  
           } catch (IOException e) {  
              // TODO自动生成的 catch 块  
              e.printStackTrace();  
           }  
           writer.close();       //这个不用抛异常  
        }
    }  
}
输出:
FileReaderTest.txt:shuidi

shuidi

4.3.2 BufferedReader/BufferedWriter

package com.shuidi.iotest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedReaderWriterTest {

      public static void main(String[] args) {  
         // TODO自动生成的方法存根  
         try {  
            concennateFile("BufferedReader.txt");  
         } catch (IOException e) {  
            // TODO自动生成的 catch 块  
            e.printStackTrace();  
         }  
      }  

    @SuppressWarnings("resource")
    public static void concennateFile(String fileName) throws IOException{
        String str;

        //构建对该文件您的输入流
        BufferedWriter writer = new BufferedWriter(new FileWriter("BufferedWriter.txt"));
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
        try {
            while ((str = reader.readLine()) != null) {
                writer.write(str);
                writer.newLine();
            }
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
BufferedReader.txt:
1
2
3
4
5
BufferedWriter.txt:
1
2
3
4
5

4.3.1 StreamTokenizer

这个类非常有用,它可以把输入流解析为标记(token), StreamTokenizer 并非派生自InputStream或者OutputStream,而是归类于io库中.

package com.shuidi.iotest;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StreamTokenizer;

public class StreamTokenizerTest {

    /** 
     * 统计字符数 
     * @param fileName 文件名 
     * @return    字符数 
     */  
    public static void main(String[] args) {  
        String fileName = "StreamTokenizerTest.txt";
        statis(fileName);
    }

    public static long statis(String fileName) {  

        FileReader fileReader = null;  
        try {
            fileReader = new FileReader(fileName);  

            //创建分析给定字符流的标记生成器  
            StreamTokenizer st = new StreamTokenizer(new BufferedReader(  
                    fileReader));  

            //ordinaryChar方法指定字符参数在此标记生成器中是“普通”字符。  
            //下面指定单引号、双引号和注释符号是普通字符  
            st.ordinaryChar('\'');  
            st.ordinaryChar('\"');  
            st.ordinaryChar('/');  

            String s;  
            int numberSum = 0;  
            int wordSum = 0;  
            int symbolSum = 0;  
            int total = 0;  

            //nextToken方法读取下一个Token.  
            //TT_EOF指示已读到流末尾的常量。  
            while (st.nextToken() != StreamTokenizer.TT_EOF) {  

                //在调用 nextToken 方法之后,ttype字段将包含刚读取的标记的类型  
                switch (st.ttype) {  

                //TT_EOL指示已读到行末尾的常量。  
                case StreamTokenizer.TT_EOL:  
                    break;  

                //TT_NUMBER指示已读到一个数字标记的常量  
                case StreamTokenizer.TT_NUMBER:  
                    //如果当前标记是一个数字,nval字段将包含该数字的值  
                    s = String.valueOf((st.nval));  
                    System.out.println("数字有:"+s);  
                    numberSum ++;  
                    break;  

                //TT_WORD指示已读到一个文字标记的常量  
                case StreamTokenizer.TT_WORD:  
                    //如果当前标记是一个文字标记,sval字段包含一个给出该文字标记的字符的字符串  
                    s = st.sval;  
                    System.out.println("单词有: "+s);  
                    wordSum ++;  
                    break;  
                default:  
                    //如果以上3中类型都不是,则为英文的标点符号  
                    s = String.valueOf((char) st.ttype);  
                    System.out.println("标点有: "+s);  
                    symbolSum ++;  
                }  
            }  
            System.out.println("数字有 " + numberSum+"个");  
            System.out.println("单词有 " + wordSum+"个");  
            System.out.println("标点符号有: " + symbolSum+"个");  
            total = symbolSum + numberSum +wordSum;  
            System.out.println("Total = " + total);  
            return total;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return -1;  
        } finally {  
            if (fileReader != null) {  
                try {  
                    fileReader.close();  
                } catch (IOException e1) {  
                }  
            }  
        }  
    }  

}

输入:

StreamTokenizerTest.txt
'haha'
word
"I love coding" ,。
23223 3523

输出:

标点有: '
单词有: haha
标点有: '
单词有: word
标点有: "
单词有: I
单词有: love
单词有: coding
标点有: "
单词有: ,。
数字有:23223.0
数字有:3523.0
数字有 2个
单词有 6个
标点符号有: 4个
Total = 12

5.参考文献

Java.io 详解
Java IO最详解
Java基础之I/O流详解
JavaI/O流详解
Java之I/O流详解
Java中的IO详解

你可能感兴趣的:(JAVA相关)