Java进阶-IO流

2019-01-09  本文已影响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构造函数直接传一个文件名,默认是当前项目根目录
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");
    }
}

如果只能一个一个字节的读那肯定不行,所以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]);
}
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

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

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

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

InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
int c;
while ((c = isr.read()) != -1){
   System.out.println((char) c);
}
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()方法。顾名思义,该方法就是一行一行到读取。

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;
上一篇 下一篇

猜你喜欢

热点阅读