Java IO核心操作(二)

File类只是针对文件爱你本身进行操作,而如果想对文件内容进行操作,则可以使用RandomAccessFile类,此类属于随机读取类,可以随机地读取一个文件中指定位置的数据。

RandomAccessFile类的常用操作方法

这里写图片描述

需要注意的是如果使用rw方式声明RandomAccessFile对象时,要写入的文件不存在,系统将自动进行创建。

使用RandomAccessFile类写入数据

为了保证可以进行随机读取,所有写入的名字都是8个字节,写入的数字是固定的4个字节。

  • 写文件
import java.io.File ;
import java.io.RandomAccessFile ;
public class RandomAccessFileDemo01{
    // 所有的异常直接抛出,程序中不再进行处理
    public static void main(String args[]) throws Exception{
        File f = new File("d:" + File.separator + "test.txt") ; // 指定要操作的文件
        RandomAccessFile rdf = null ;       // 声明RandomAccessFile类的对象
        rdf = new RandomAccessFile(f,"rw") ;// 读写模式,如果文件不存在,会自动创建
        String name = null ;
        int age = 0 ;
        name = "zhangsan" ;         // 字符串长度为8
        age = 30 ;                  // 数字的长度为4
        rdf.writeBytes(name) ;      // 将姓名写入文件之中
        rdf.writeInt(age) ;         // 将年龄写入文件之中
        name = "lisi    " ;         // 字符串长度为8
        age = 31 ;                  // 数字的长度为4
        rdf.writeBytes(name) ;      // 将姓名写入文件之中
        rdf.writeInt(age) ;         // 将年龄写入文件之中
        name = "wangwu  " ;         // 字符串长度为8
        age = 32 ;                  // 数字的长度为4
        rdf.writeBytes(name) ;      // 将姓名写入文件之中
        rdf.writeInt(age) ;         // 将年龄写入文件之中
        rdf.close() ;               // 关闭
    }
};

使用RandomAccessFile类读取数据

读取时直接使用r的模式即可,以只读的方式打开文件。
读取时所有的字符只能按照byte数组的方式读取出来,而且所有的长度是8位。

  • 随机读取
import java.io.File ;
import java.io.RandomAccessFile ;
public class RandomAccessFileDemo02{
    // 所有的异常直接抛出,程序中不再进行处理
    public static void main(String args[]) throws Exception{
        File f = new File("d:" + File.separator + "test.txt") ; // 指定要操作的文件
        RandomAccessFile rdf = null ;       // 声明RandomAccessFile类的对象
        rdf = new RandomAccessFile(f,"r") ;// 以只读的方式打开文件
        String name = null ;
        int age = 0 ;
        byte b[] = new byte[8] ;    // 开辟byte数组
        // 读取第二个人的信息,意味着要空出第一个人的信息
        rdf.skipBytes(12) ;     // 跳过第一个人的信息
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ; // 读取一个字节
        }
        name = new String(b) ;  // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;   // 读取数字
        System.out.println("第二个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        // 读取第一个人的信息
        rdf.seek(0) ;   // 指针回到文件的开头
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ; // 读取一个字节
        }
        name = new String(b) ;  // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;   // 读取数字
        System.out.println("第一个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        rdf.skipBytes(12) ; // 空出第二个人的信息
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ; // 读取一个字节
        }
        name = new String(b) ;  // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;   // 读取数字
        System.out.println("第三个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        rdf.close() ;               // 关闭
    }
};

程序运行结果:
这里写图片描述

可以发现,程序中可以随机跳过12位读取信息,也可以回到开始点重新读取。
随机读写流可以实现对文件内容的操作,但是却过于复杂,所以一般情况下操作文件内容往往使用字节流或字符流。

字节流与字符流基本操作

在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据时要使用输入流读取数据,而当程序需要将一些数据保存起来时,就要使用输出流,可以通过下图表示出输入及输出的关系。
这里写图片描述
在java.io包中流的操作主要有字节流,字符流两大类,两类都有输入和输出操作。在字节流中输出的数据主要使用OutputStream类完成,输入使用InputStream类。在字符流中输出主要是使用Writer类完成,输入主要是使用Reader类完成。

在Java中IO操作也是有相应步骤的,以文件的操作为例,主要的操作流程如下:

  • 使用File类打开一个文件。
  • 通过字节流或字符流的子类指定输出的位置。
  • 进行读/写操作。
  • 关闭输入/输出。

下面分别介绍如何使用字节流或字符流保存或读取文件。

字节流

字节流主要操作byte类型数据,以byte数组为准,主要操作类是OutputStream类和InputStream类。

  • 字节输出流:OutputStream
    OutputStream是整个IO包中字节输出流的最大父类,此类的定义如下:
public abstract class OutputStream extends Object implements Closeable,Flushable

从以上定义中可以发现,OutputStream类是一个抽象类,如果要使用此类,则首先必须通过子类实例化对象。如果现在要操作的是一个文件,则可以使用FileOutputStream类,通过向上转型后,可以为OutputStream实例化,在OutputStream类中的主要操作方法如下表所示

OutputStream类的常用方法

这里写图片描述

向文件中写入字符串

import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo01{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        OutputStream out = null ;   // 准备好一个输出的对象
        out = new FileOutputStream(f)  ;    // 通过对象多态性,进行实例化
        // 第3步、进行写操作
        String str = "Hello World!!!" ;     // 准备一个字符串
        byte b[] = str.getBytes() ;         // 只能输出byte数组,所以将字符串变为byte数组
        out.write(b) ;                      // 将内容输出,保存文件
        // 第4步、关闭输出流
        out.close() ;                       // 关闭输出流
    }
};

字节输入流InputStream

既然程序可以向文件中写入内容,则可以通过InputStream从文件中把内容读取出来。InputStream类的定义如下:

public abstract class InputSream extends Object implements Closeable

与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类,如果现在从文件中读取,子类肯定是FileInputStream。InputStream类中的主要方法如表所示:
这里写图片描述

从文件中读取内容

开辟指定大小的byte数组

import java.io.File ;
import java.io.InputStream ;
import java.io.FileInputStream ;
public class InputStreamDemo03{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        InputStream input = null ;  // 准备好一个输入的对象
        input = new FileInputStream(f)  ;   // 通过对象多态性,进行实例化
        // 第3步、进行读操作
        byte b[] = new byte[(int)f.length()] ;      // 数组大小由文件决定
        int len = input.read(b) ;       // 读取内容
        // 第4步、关闭输出流
        input.close() ;                     // 关闭输出流\
        System.out.println("读入数据的长度:" + len) ;
        System.out.println("内容为:" + new String(b)) ;    // 把byte数组变为字符串输出
    }
};

在使用FileInputStream读取时如果指定的路径不存在,则程序运行会出现异常

除了以上方式,也可以通过循环从文件中一个个地把内容读取进来,直接使用read()方法即可。

使用read()通过循环读取

import java.io.File ;
import java.io.InputStream ;
import java.io.FileInputStream ;
public class InputStreamDemo04{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        InputStream input = null ;  // 准备好一个输入的对象
        input = new FileInputStream(f)  ;   // 通过对象多态性,进行实例化
        // 第3步、进行读操作
        byte b[] = new byte[(int)f.length()] ;      // 数组大小由文件决定
        for(int i=0;i<b.length;i++){
            b[i] = (byte)input.read() ;     // 读取内容
        }
        // 第4步、关闭输出流
        input.close() ;                     // 关闭输出流\
        System.out.println("内容为:" + new String(b)) ;    // 把byte数组变为字符串输出
    }
};

另一种方式的读取

import java.io.File ;
import java.io.InputStream ;
import java.io.FileInputStream ;
public class InputStreamDemo05{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        InputStream input = null ;  // 准备好一个输入的对象
        input = new FileInputStream(f)  ;   // 通过对象多态性,进行实例化
        // 第3步、进行读操作
        byte b[] = new byte[1024] ;     // 数组大小由文件决定
        int len = 0 ; 
        int temp = 0 ;          // 接收每一个读取进来的数据
        while((temp=input.read())!=-1){
            // 表示还有内容,文件没有读完
            b[len] = (byte)temp ;
            len++ ;
        }
        // 第4步、关闭输出流
        input.close() ;                     // 关闭输出流\
        System.out.println("内容为:" + new String(b,0,len)) ;  // 把byte数组变为字符串输出
    }
};

文件读到末尾了,则返回的内容为-1。
以上程序代码中要判断temp接收到的内容是否是-1,正常情况下是不会返回-1的,只有当输入流的内容已经读到底,才会返回这个数字,通过此数字可以判断输入流中是否还有其他内容。

以上几种读取字节流的方式,读者最好都掌握,因为随着开发的需要,都有可能使用。

字符输出流Writer

Write本身是一个字符流,此类的定义如下:

public abstract class Writer extends Object implements Appendable,Closeable,Flushable

此类也是一个抽象类,如果要使用此类,则肯定要使用其子类,此时如果是向文件中写入内容,应该使用FileWriter的子类。Write类的常用方法如表所示:
这里写图片描述

向文件中写入数据

import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo01{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        Writer out = null ; // 准备好一个输出的对象
        out = new FileWriter(f)  ;  // 通过对象多态性,进行实例化
        // 第3步、进行写操作
        String str = "Hello World!!!" ;     // 准备一个字符串
        out.write(str) ;                        // 将内容输出,保存文件
        // 第4步、关闭输出流
        out.close() ;                       // 关闭输出流
    }
};

整个程序与OutputStream的操作流程并没有什么太大的区别,唯一的好处是,可以直接输出字符串,而不用将字符串变为byte数组之后再输出

字符输入流Reader

Reader是使用字符的方式从文件中取出数据,Reader类的定义如下:

public abstract class Reader extends Object implements Readable,Closeable 

Reader本身也是一个抽象类,如果现在要从文件中读取内容,则可以直接使用FileReader子类。Reader类的常用方法如表所示:
这里写图片描述

从文件中读取内容

import java.io.File ;
import java.io.Reader ;
import java.io.FileReader ;
public class ReaderDemo01{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        Reader input = null ;   // 准备好一个输入的对象
        input = new FileReader(f)  ;    // 通过对象多态性,进行实例化
        // 第3步、进行读操作
        char c[] = new char[1024] ;     // 所有的内容都读到此数组之中
        int len = input.read(c) ;       // 读取内容
        // 第4步、关闭输出流
        input.close() ;                     // 关闭输出流
        System.out.println("内容为:" + new String(c,0,len)) ;  // 把字符数组变为字符串输出
    }
};

使用循环的方式读取内容

import java.io.File ;
import java.io.Reader ;
import java.io.FileReader ;
public class ReaderDemo02{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        Reader input = null ;   // 准备好一个输入的对象
        input = new FileReader(f)  ;    // 通过对象多态性,进行实例化
        // 第3步、进行读操作
        char c[] = new char[1024] ;     // 所有的内容都读到此数组之中
        int temp = 0 ;  // 接收每一个内容
        int len = 0 ;       // 读取内容
        while((temp=input.read())!=-1){
            // 如果不是-1就表示还有内容,可以继续读取
            c[len] = (char)temp ;
            len++ ;
        }
        // 第4步、关闭输出流
        input.close() ;                     // 关闭输出流
        System.out.println("内容为:" + new String(c,0,len)) ;  // 把字符数组变为字符串输出
    }
};

字节流与字符流的区别

字节流和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢?
实际上字节流在操作本身不会用到缓存区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,如图所示:
这里写图片描述

下面以两个写文件的操作为主进行比较,但是在操作时字节流和字符流的操作完成之后都不关闭输出流。

使用字节流不关闭执行

import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo05{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        OutputStream out = null ;   // 准备好一个输出的对象
        out = new FileOutputStream(f)  ;    // 实例化
        // 第3步、进行写操作
        String str = "Hello World!!!" ;     // 准备一个字符串
        byte b[] = str.getBytes() ;         // 只能输出byte数组,所以将字符串变为byte数组
        out.write(b) ;      // 写入数据
        // 第4步、关闭输出流
        // out.close() ;                        // 关闭输出流
    }
};

程序运行结果:

Hello Word!!!

此时没有关闭字节流操作,但是文件中也依然存在了输出的内容,证明字节流是直接操作文件本身的。而下面继续使用字符流完成,再观察效果。

使用字符流不关闭执行

import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo03{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        Writer out = null ; // 准备好一个输出的对象
        out = new FileWriter(f)  ;  // 通过对象多态性,进行实例化
        // 第3步、进行写操作
        String str = "Hello World!!!" ;     // 准备一个字符串
        out.write(str) ;                        // 将内容输出,保存文件
        // 第4步、关闭输出流
        // out.close() ;                        // 此时,没有关闭
    }
};

程序运行结果

程序运行后会发现文件中没有任何内容,这是因为字符流操作时使用了缓存区,而在关闭字符流时会强制性地将缓存区中的内容进行输出,但是如果程序没有关闭,则缓冲区中的内容是无法输出的,所以得出结论:字符流使用了缓冲区,而字节流没有使用缓冲区。

提问:什么叫缓冲区?
回答:缓冲区可以简单地理解为一段内存区域,在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。

强制性清空缓冲区

import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo04{
    public static void main(String args[]) throws Exception{    // 异常抛出,不处理
        // 第1步、使用File类找到一个文件
        File f= new File("d:" + File.separator + "test.txt") ;  // 声明File对象
        // 第2步、通过子类实例化父类对象
        Writer out = null ; // 准备好一个输出的对象
        out = new FileWriter(f)  ;  // 通过对象多态性,进行实例化
        // 第3步、进行写操作
        String str = "Hello World!!!" ;     // 准备一个字符串
        out.write(str) ;                        // 将内容输出,保存文件
        // 第4步、关闭输出流
        out.flush() ;   // 强制性清空缓冲区中的内容
        // out.close() ;                        // 此时,没有关闭
    }
};

提问:使用字节流好还是字符流好?
回答:使用字节流更好,所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值