Java:IO流与文件基础

Posted 子烁爱学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:IO流与文件基础相关的知识,希望对你有一定的参考价值。

Java:IO流与文件基础

说明:

  本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦。

走进流

什么是流

  流:指的是从源到目的地的字节的有序序列。

  

  在Java中,可以从其中读取一个字节序列的对象称作 输入流,可以向其中写入一个字节序列的对象称作 输出流
    ☑ 这些字节序列的来源可以是:文件、网络连接、内存块等。
    ☑ 抽象类InputStream和OutputStream是构成输入/输出(I/O)的基础。
    ☑ 因为面向字节的流不便于处理以Unicode形式存储的信息(字符),所以从抽象类Reader 和 Writer中继承出来一个专门处理字符流的类层次结构。
    ☑ 适配器类InputStreamReader将InputStream转换为Reader,OutputStreamWriter将OutputStream转换为Writer

IO流家族体系

对流的分类

☑ 依据流相对于程序的另一个端点的不同:
  
☐ 节点流(全黄):以特定源(如磁盘文件、内存某区域或线程之间的管道)为端点构造的输入/输出流
  ☐ 过滤流(半黄):以其他已存在的流为端点构造的输入/输出流。
☑ 依据流中的数据单位不同:
  
☐ 字节流:流中的数据以8位字节为单位进行读写,以InputStream和OutputStream为共同父类
  ☐ 字符流:流中的数据以16位字符为单位进行读写,以Reader和Writer为共同父类

家族体系图(原创)

  

家族体系的顶层方法

  顶层方法不是很多,但是需要我们去记住,下面列出了所有的顶层方法,遇到问题是结合上图和下表可以清理思路。

  A.abstract class OutputStream

返回类型方法及解释
 void close()            关闭此输出流并释放与此流有关的所有系统资源。
 void flush()            刷新此输出流并强制写出所有缓冲的输出字节。
 void write(byte[] b)            将 b.length 个字节从指定的 byte 数组写入此输出流。
 void write(byte[] b, int off, int len)            将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract  void write(int b)            将指定的字节写入此输出流。

  B.abstract class InputStream

返回类型方法及解释
 int available()            返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
 void close()            关闭此输入流并释放与该流关联的所有系统资源。
 void mark(int readlimit)            在此输入流中标记当前的位置。
 boolean markSupported()            测试此输入流是否支持 mark 和 reset 方法。
abstract  int read()            从输入流中读取数据的下一个字节。
 int read(byte[] b)            从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
 int read(byte[] b, int off, int len)            将输入流中最多 len 个数据字节读入 byte 数组。
 void reset()            将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
 long skip(long n)            跳过和丢弃此输入流中数据的 n 个字节。

  C.abstract class Reader

返回类型方法及解释
abstract  void close()            关闭该流并释放与之关联的所有资源。
 void mark(int readAheadLimit)            标记流中的当前位置。
 boolean markSupported()            判断此流是否支持 mark() 操作。
 int read()            读取单个字符。
 int read(char[] cbuf)            将字符读入数组。
abstract  int read(char[] cbuf, int off, int len)            将字符读入数组的某一部分。
 int read(CharBuffer target)            试图将字符读入指定的字符缓冲区。
 boolean ready()            判断是否准备读取此流。
 void reset()            重置该流。
 long skip(long n)            跳过字符。

  D.abstract class Writer  

返回类型方法及注释
 Writer append(char c)            将指定字符添加到此 writer。
 Writer append(CharSequence csq)            将指定字符序列添加到此 writer。
 Writer append(CharSequence csq, int start, int end)            将指定字符序列的子序列添加到此 writer.Appendable。
abstract  void close()            关闭此流,但要先刷新它。
abstract  void flush()            刷新该流的缓冲。
 void write(char[] cbuf)            写入字符数组。
abstract  void write(char[] cbuf, int off, int len)            写入字符数组的某一部分。
 void write(int c)            写入单个字符。
 void write(String str)            写入字符串。
 void write(String str, int off, int len)            写入字符串的某一部分。

 

输出输入流的嵌套

说明

  单独使用节点流的情况在程序中较少出现。
  一般常通过过滤流将多个流套接在一起,利用各种流的特性共同处理数据,套接的多个流构成了一个流链   

  优点:

    方便数据的处理并提高处理的效率。

  

着重介绍的几个过滤流

  ☑ BufferedInputStream
    ☐ 在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
    ☐ 在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
  ☑ BufferedOuputStream
    ☐ 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
  ☑ BufferedReader和BufferedWriter同样也是提供缓存区。
  ☑ DataInputStream
    ☐ 包含用于读取基本类型数据的全部接口

  说明:所有的这些在下面我们都会重新强调并实现。

 

常用流

思维导图

  

文件流

文件流包括

    ☐ FileReader/FileWriter类
    ☐ FileInputStream/FileOutputStream类

创建文件流

    ☐FileInputStream(File file) 
            通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
    ☐FileInputStream(String name) 
            通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

实例

public class FileCopy{
        public static void main(String[] args) throws IOException{
                FileInputStream in=new FileInputStream("FileCopy.java");
                FileOutputStream out=new FileOutputStream("FileCopy.txt");
                int c;
                while( (c=in.read())!=-1)
                        out.write(c);
                in.close();
                out.close();
        }
}

缓存流

  说明:

    缓存流是过滤流,以InputStream/OutputStream为前端流,并可指定缓冲区大小,如:
      public BufferedInputStream(InputStream in)
      public BufferedInputStream(InputStream in, int size)
    BufferedReader增加readLine()
    BufferedWriter增加newLine():写入一个换行符。  

  演示:

public class BufferedIO{
    public static void main(String[] args) throws IOException{
        BufferedReader in=new BufferedReader(
                new FileReader("BufferedIO.java"));
        PrintWriter out=new PrintWriter( new BufferedWriter(
                new FileWriter("BufferedIO.txt")));
        String s;
        int linecnt=1;
        StringBuilder sb=new StringBuilder();
        while((s=in.readLine())!=null){
            sb.append(linecnt+":"+s+"\\n");
            out.println(linecnt+":"+s);
            linecnt++;
        }
        in.close();
        out.close();
        System.out.print(sb.toString());
    }
}

说明:

  PrintWriter
  向文本输出流打印对象的格式化表示形式,即在写入的同时可以对写入的数据进行格式化。

数据流

数据流包括:

  DataInputStream/DataOutputStream类

读写基本数据类型的接口方法:

  

演示:

public class DataIO{
    public static void main(String[] args) throws IOException{
        DataOutputStream out=new DataOutputStream(
    new BufferedOutputStream(new FileOutputStream("data.txt")));
        out.writeBoolean(false); out.writeChar(\'c\');
        out.writeByte(1); out.writeShort(2);
        out.writeInt(3); out.writeLong(4L);
        out.writeFloat(5.0f); out.writeDouble(6.0);
        out.writeUTF("hello world!"); out.close();

        DataInputStream in=new DataInputStream(
             new BufferedInputStream( new FileInputStream("data.txt")));
        System.out.println(in.readBoolean()+";"+in.readChar()+";");
        System.out.println(in.readByte()+";"+in.readShort()+";");
        System.out.println(+in.readInt()+";"+in.readLong());
        System.out.println(in.readFloat()+";"+in.readDouble()+";");
        System.out.println(in.readUTF()); in.close();
    }

  说明:

    ☑readUTF()与writeUTF()
      writeBytes(String)和writeChars(String)方法在DataInputStream中没有对应的方法恢复出String
      用DataOutputStream写字符串并使得DataInputStream能恢复出字符串的方法是使用writeUTF()和readUTF()
    ☑ UTF-8
      ASCII字符→单字节形式;非ASCII字符→多字节形式
      字符串长度→UTF-8字符串的前两字节中
      Java中使用的是UTF-8的变体,UTF-8只是读写过程中的字符串形式,程序中Unicode

标准IO:

标准输入:键盘
标准输出:加载Java程序的命令窗口
  Java在System类中定义了三个标准I/O流,是System类的三个静态变量
    ☐System.in(public static final InputStream in):标准输入流
    ☐System.out(public static final PrintStream out):标准输出流
    ☐System.err(public static final PrintStream err):标准错误输出流
  
程序从键盘读入数据:
  ☐ int ch=System.in.read();
  ☐ System.in.read()从键盘缓冲区读入一个字节的数据,返回的是整型值(低位字节为输入数据,高位字节全为零)
  ☐ System.in.read()的执行使得整个程序被挂起,直到用户从键盘输入数据才继续运行
  ☐ 从键盘逐行读入:嵌套BufferedReader和InputStreamReader

实例:

public class StandardIO {
    public static void main(String[] args) {
        String s;
        BufferedReader in = new BufferedReader(
                       new InputStreamReader(System.in));
        System.out.println("Please input: ");
        try {
            s = in.readLine();
            while (!s.equals("exit")) {
                System.out.println(" read: " + s);
                s = in.readLine();
            }
            System.out.println("End of Inputting");
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

java.util.Scanner类:从控制台读取输入

说明

  Scanner sc=new Scanner(System.in);
    Scanner将控制台输入按照分隔符模式进行分割,分隔符模式默认为匹配空格
  扫描结果通过各种next*()方法转化为不同类型的值
    next() 获取一个字符串
    nextByte() 获取一个byte类型的整数
    nextShort() 获取一个short类型的整数
    nextInt() 获取一个int类型的整数
    nextLong() 获取一个long类型的整数
    nextFloat() 获取一个float类型的数
    nextDouble() 获取一个double类型的数
    nextLine() 获取一行文本(即以回车键为结束标志)

输入输出的重定向

  System.setIn(InputStream)
  System.setOut(PrintStream)
  System.setErr(PrintStream)

  

随机存取文件

说明

    到目前为止所学习的Java流式输入/输出都是顺序访问流,即流中的数据必须按顺序进行读写当需要随机读写磁盘文件中的内容时,用RandomAccessFile类(既可对文件读,又可对文件写)

RandomAccessFile与其他顶层类的关系

  

  

构造方法

  

构造方法摘要
RandomAccessFile(File file, String mode)            创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)            创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
mode值含意
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

操作

文件指针操作
    long getFilePointer() //返回当前文件指针
    void seek(long pos) //文件指针定位到指定位置
    long length() //返回文件长度
    int skipBytes(int n) //从当前位置开始跳过n字节
读操作(实现了DataInput接口)
    readBoolean(), readChar(), readInt(), readLong(), readFloat(), readDouble(), readLine(), readUTF()等
写操作(实现了DataOutput接口)
    writeBoolean(), writeChar(), writeUTF(), writeInt(), writeLong(), writeFloat(), writeDouble()等

实例

public class RandomAccessTest {
    public static void main(String[] args) throws IOException {
        long filePoint = 0;
        String s;
        RandomAccessFile file = new RandomAccessFile(
                    "RandomAccessTest.java", "r");
        long fileLength = file.length(); // 获取文件长度
        while (filePoint < fileLength) {
            s = file.readLine(); // 读一行字符,并移动文件指针
            System.out.println(s); // 输出显示读入的一行字符
            file.skipBytes(5);
            filePoint = file.getFilePointer(); // 获取当前文件指针
        }
        file.close();
    }
}

 

对象的串行化 

  将对象保存到外存,称为对象的永久化。对象永久化的关键是将对象的状态以一种串行格式表示出来,以便以后读取能够将该对象重构出来。对Java对象的这一读写过程称为对象的串行化

常在下列情况下使用

  1.Java远程方法调用(Remote Method Invocation)
  2.Java Bean / EJB
  3.对象永久化

实现串行化

  • 用ObjectOutputStream/ObjectInputStream实现对象的串行化
    ◇ 通过ObjectOutputStream的writeObject方法将一个对象写入到流中
      ▪ public final void writeObject(Object obj) throws IOException
    ◇ 通过ObjectInputStream的readObject方法将一个对象从对象流中读出
      ▪ public final Object readObject() throws IOException, ClassNotFoundException
    ◇ ObjectOutputStream实现了java.io.DataOutput接口
    ◇ ectInputStream实现了java.io.DataInput接口

实例

/*输出对象*/
public class SerializeDate {
    SerializeDate( ){
        Date d = new Date( );
        try {
            ObjectOutputStream s= new ObjectOutputStream(
                    new FileOutputStream(“date.ser”));
            s.writeObject(d);
            s.close( );
        }catch( IOException e){
            e.printStackTrace( );
        }
    }
    public static void main(String args[]){
        SerializeDate b = new SerializeDate();
    }
} 
/*输入对象*/
public class UnSerializeDate {
    Date d = null;
    UnSerializeDate() {
        try {
            ObjectInputStream s = new ObjectInputStream(
                    new FileInputStream("date.ser"));
            Object o=s.readObject();
            if(o instanceof Date)
                d = (Date) o; 
            s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        UnSerializeDate us = new UnSerializeDate();
        System.out.println(us.d.toString());
    }
}

注意:
  一个类只有实现了Serializable接口,其对象才是可串行化的

(未完待续.....)

 

以上是关于Java:IO流与文件基础的主要内容,如果未能解决你的问题,请参考以下文章

JAVA IO流相关代码(究极整理,字符流与字节流之间的转化)

漫谈Java IO之普通IO流与BIO服务器

java io流与序列化反序列化

Java基础笔记(三:文件与数据流)

java学习之字符流与字节流的转换

技术博客