美文网首页
Java进阶-IO流

Java进阶-IO流

作者: 指尖轻敲 | 来源:发表于2019-01-09 00:02 被阅读19次

File类


File类是io包下的一个常用类,可以对硬盘中的文件进行读取信息以及增删的操作,这里我们简单介绍几个最基本的API,想了解更多可以查看文档。

import java.io.IOException;

public class FileDemo {
    public static void main(String[] args){
        // 构造方法接受一个绝对路径
        File file = new File("D:\\learn\\Java\\src\\1");
        // 判断该文件或者目录是否存在
        if(file.exists()){
            // 判断是目录还是文件
            if(file.isDirectory()){
                // 如果是目录,删除掉创建成文件
                file.delete();
                try{
                    file.createNewFile();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }else if(file.isFile()){
                // 如果是文件,删除掉创建成文件夹
                file.delete();
                file.mkdir();
            }
        }else{
            // 创建该目录
            file.mkdir();
            //file.mkdirs(); 创建多层目录
        }
    }
}

isDirectory()会抛出异常,这里需要捕获一下。

遍历

上面介绍了读写和判断文件的方法,接下来了解一下遍历文件的方法,在Java中提供了两个功能类似的方法list()listFiles(),它两都可以获取到File对象下的所有直接子文件。但是list返回的是一个String类型数组。而listFiles返回的是一个File类型的数组,这在遍历嵌套目录时比较方便。

如果有嵌套就递归遍历:

import java.io.File;
import java.io.IOException;
public class FileUtils  {
    public void listFiles(File file) throws IOException {
        if(!file.exists()){
            throw new IllegalArgumentException("找不到该目录");
        }
        if (!file.isDirectory()){
            throw new IllegalArgumentException("这是个文件,不是目录");
        }
        File[] filesList = file.listFiles();
        for(File file_ : filesList){
            if(file_.isDirectory()){
                listFiles(file_);
            }else{
                System.out.println(file_);
            }
        }
    }
    public static void main(String[] args) {
        File file = new File("D:\\learn\\Java");
        FileUtils util = new FileUtils();
        try {
            util.listFiles(file);
        } catch (IOException e) {
            System.out.println("请确定传入的文件对象");
        }
    }
}

RandomAccessFile类


上面介绍的File类只可以进行文件信息的读取而不能对文件内容读写。RandomAccessFile类就提供了对文件内容读写的操作。

RandomAccessFile的使用需要进行异常处理,以下代码忽略处理部分

  • 构造方法:第一个参数是File对象,第二个参数是要做的操作,rw是读写。

  • getFilePointer():获取指针的位置,随着写操作指针会自动后移。

  • seek():把指针位置初始化到最开始0位,读操作之前需要此操作。

  • close():操作完之后需要关闭一下。

  • read和write方法都接受一个字节或者字节数组作为参数。

// File构造函数直接传一个文件名,默认是当前项目根目录
File file = new File("2");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
System.out.println(raf.getFilePointer()); //0
raf.write('a');
System.out.println(raf.getFilePointer()); //1
raf.seek(0);
// 创建一个长度为raf当前长度的byte类型数组
byte[] by = new byte[(int) raf.length()];
raf.read(by);
System.out.println(Arrays.toString(by)); // [97]
raf.close();

IO流


IO流按照流向分为输入流输出流,按照流的类型又分为字节流字符流

这里需要搞明白,输入输出和读写的对应关系,可能会搞混。把自己当成文件自身去考虑,输入就是读取到文件;输出就是文件向外写出。

1、字节流

FileInputStream类

先来看字节输入流,当然这是一个读操作:

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

public class IOUtils {
    public static void print(String fileName) throws IOException {
        FileInputStream fis = new FileInputStream(fileName);
        int b;
        while ((b = fis.read()) != -1){
            System.out.println(Integer.toHexString(b)); //转成16进制的字符串
        }

    }
    public static void main(String[] args) throws IOException {
        print("D:\\learn\\Java\\src\\1");
    }
}
  • 这里FileInputStream构造函数接受了一个字符串作为参数。(更多构造方法查看文档)

  • 调用read()方法读取一个字节,当等于-1时就说明读取完了

如果只能一个一个字节的读那肯定不行,所以read方法还重载了多个参数的方法,可以将数据读到一个字节数组中。

byte[] b = new byte[10];
FileInputStream fis = new FileInputStream(fileName);
int bytes = fis.read(b, 0 , b.length); //10
System.out.println(bytes);
for (int i = 0; i < bytes; i++){
    System.out.println(b[i]);
}
  • 这里指定了字节数组长度为10,如果文件中字节数多余10就会被截取。

  • read方法第一个参数是要读取到的字节数组;第二个参数是字节数组的其实偏移量;第三个参数是读取的最大字节数,如果这里是9那么最多读取九个字节也是会截取的。

  • bytes是读入字节数组缓存区的总字节数。

FileOutputStream类

FileOutputStream和FileInputStream的方法使用基本一致,只是把read换成了write。直接上个复制文件的小demo吧!

public static void print(String fileName, String outFileName) throws IOException {
byte[] b = new byte[10];
FileInputStream fis = new FileInputStream(fileName);
FileOutputStream fos = new FileOutputStream(outFileName);
int bytes;
while((bytes = fis.read(b, 0, b.length)) != -1){
    fos.write(b, 0, bytes);
    // 清空输出流
    fos.flush();
}
fis.close();
fos.close();
}
public static void main(String[] args) throws IOException {
    print("D:\\learn\\Java\\src\\1","D:\\learn\\Java\\src\\2");
}

这里FileOutputStream构造方法,如果只有一个参数,那么当前文件不存在就直接创建,如果当前文件已存在则删除已存在的文件再创建。

如果文件已存在我想在文件中继续追加怎么办呢?添加第二个参数true即可,这时再执行一下发现目标文件中拷贝过来的内容出现了两次。

FileOutputStream fos = new FileOutputStream(outFileName, true);
DataOutputStream/DataInputStream

数据流是普通输入输出流的扩展,它允许应用程序以基本的java数据类型进行写入和读出。

  • 构造方法接收的参数是一个普通字节流对象。
DataOutputStream dos = new DataOutputStream(new FileOutputStream(fileName));
dos.writeInt(1);
dos.writeUTF("哈喽");
dos.writeDouble(2.5);
DataInputStream dis = new DataInputStream(new FileInputStream(fileName));
System.out.println(dis.readInt()); //1
System.out.println(dis.readUTF()); //哈喽
System.out.println(dis.readDouble()); //2.5
BufferedInputStream/BufferedOutputStream

缓冲字节数据流为流提供了缓冲区,先把数据读到缓冲区中,最后在一起写入。

  • 构造方法同样是接收一个普通字节流对象

  • 之前普通的字节输出流不使用flush()也可以,只是最好加上,但是缓冲输出流就必须要加上了flush()方法。

  • 因为是先读到缓冲区,最后一次性写入,所以性能要比普通的字节流要高很多。

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFileName));
int b;
while ((b = bis.read()) != -1){
    bos.write(b);
    bos.flush();
}
bos.close();
bis.close();

2、字符流

InputStreamReader/OutputStreamWriter

接下要说的就是字符流流,何为字符流,它可以以指定的编码类型读取字节并解码为字符。如果没有指定编码类型,会使用项目默认的编码。

  • 构造方法的第一个参数是一个普通的字节流对象,第二个参数就是编码类型,不指定就使用默认的。

  • 将读出来的数据使用强制转换转成char类型的。

InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
int c;
while ((c = isr.read()) != -1){
   System.out.println((char) c);
}
  • 和之前一样,也可以指定一个字符数组读到数组中。

  • read()方法如果没有参数,返回的就是读取的内容;如果有参数读字符数组时,返回的是读取的字符个数

char[] cArr = new char[1024];
while ((c = isr.read(cArr, 0, cArr.length)) != -1) {
    // 把字符数组转成字符串
    String str = new String(cArr, 0, c);
    System.out.println(str);
}

输出流也是一样的,结合上面介绍的去用就可以了,不做过多的介绍:

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outFileName));
while ((c = isr.read(cArr, 0, cArr.length)) != -1) {
    osw.write(cArr, 0, c);
}
FileReader/FileWriter

FileReader/FileWriter是InputStreamReader/OutputStreamWriter的子类,FileWriter的第二个参数是是否追加到文件。

可以看到此类的构造方法和它的父类不同,直接接收一个文件名或对象。而且构造时不能指定编码类型。

FileReader fr = new FileReader(fileName);
FileWriter fw = new FileWriter(outFileName,true);
int c;
while ((c = fr.read()) != -1){
    System.out.println((char) c);
    fw.write(c);
    fw.flush();
}
fw.close();
BufferedReader/BufferedWriter

这两个类的构造方法和之前略有不同,他们接收一个字符流作为第一个参数,第二个参数可以指定缓冲区的大小(没有就是默认大小,一般默认就可以)。

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFileName)));

以上代码使用InputStreamReader构造了比较多的对象会有些麻烦,。我们也可以直接给构造函数传递一个FileReader对象,效果是一样的。

BufferedReader br = new BufferedReader(new FileReader(fileName));
BufferedWriter bw = new BufferedWriter(new FileWriter(outFileName));

这里输入流的read方法基本和前面的一致,不多做介绍,需要说一下它的readLine()方法。顾名思义,该方法就是一行一行到读取。

  • readLine方法的返回值就是读取到的内容,读完则返回null。

  • 可以发现该方法是无法识别换行的,所以我们需要手动写换行方法。

String str;
while ((str = br.readLine()) != null){
    bw.write(str);
    bw.newLine(); //换行
    bw.flush();
}
br.close();
bw.close();

这里我们也可以使用PrintWriter类来写入数据。

PrintWriter类的构造方法可以接收字符串也可以接收流对象,而且如果第一个参数是流对象还可以接收第二个参数来实现自动刷新缓冲区,不用再自己手写flush()方法了。

PrintWriter pw = new PrintWriter(new FileWriter(outFileName), true);
while ((str = br.readLine()) != null){
    pw.println(str);
}
pw.close();

这里的println方法同时解决了换行问题,不用手写换行了。

更多的IO类和方法可以参考API文档,文档是个好东西。

序列化和反序列化


先明确一点,什么是序列化什么是反序列化?

将Object对象转换成byte序列就是序列化,反之就是反序列化。

这里介绍最基本的类ObjectOutputStreamObjectInputStream

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(outFileName));
Course c = new Course("1", "高数");
oos.writeObject(c);
oos.flush();
oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(outFileName));
Course co = (Course)ois.readObject();
System.out.println(co);
ois.close();

一个对象要想实现序列化操作必须要实现Serializable接口。

很多时候并不是所有属性都想进行序列化或者某个属性想要自己进行序列化,我们可以使用transient 关键之修饰该属性。这样该属性就不会进行默认的序列化。

public transient String name;

相关文章

  • Java进阶-IO流

    File类 File类是io包下的一个常用类,可以对硬盘中的文件进行读取信息以及增删的操作,这里我们简单介绍几个最...

  • Java之IO流详解

    title: Java之IO流详解tags: Java IO流categories: Java IO流 大多数应用...

  • Java进阶

    注:采转归档,自己学习查询使用 Java进阶01 String类Java进阶02 异常处理Java进阶03 IO基...

  • 【Java进阶营】Java之IO流

    IO流及其概述和分类 一:IO流用来处理数据之间的传输,Java对数组的数据是用流的方式,Java用于操作流在IO...

  • 从0开始复习java(9)--IO

    Java的io通过java.io包下的类和接口支持。主要有输入、输出流,又分为字节流和字符流。Java的io流使用...

  • java IO入门笔记

    1.java IO流的概念,分类,类图 1.1. java IO 流的概念 java的io是实现输入和输出的基础,...

  • Java IO详解

    1 Java IO流的概念,分类 1.1 Java IO流的概念 java的IO是实现输入和输出的基础,可以方便的...

  • Java的IO和NIO

    Java的IO和NIO 一、Java的IO Java的IO功能在java.io包下,包括输入、输出两种IO流,每种...

  • java io 流

    java io 流 io 流总览 io 流主要提供四个接口 InputStream: 输入字节流 OutputSt...

  • 28、说说Java 中 IO 流

    说说Java 中 IO 流 Java 中 IO 流分为几种? 1、按照流的流向分,可以分为输入流和输出...

网友评论

      本文标题:Java进阶-IO流

      本文链接:https://www.haomeiwen.com/subject/nydirqtx.html