IO流、File操作

2018-01-30  本文已影响0人  少博先生

流是对字节序列的抽象,我们可以将其想象成一个水流,区别就是流的不是水而是字节。

一、分类

1、输入流/输出流

水流是有方向的,java中的流也是有方向的,分为输入流、输出流。这个输入输出是相对的,都是以程序的角度来看的。从程序将数据写入文件叫做输出流,将数据从文件读入程序叫做输入流。

2、字符流/字节流

字节流处理字节的最小单位是单个字节,通常用来处理二进制数据,比如图片文件、音频文件等。最基本的字节流输入流是InputStream, 最基本的字节流输出流是OutputStream。
字符流处理字节的最小单位是Unicode码元,占两个字节,通常用来处理文本数据。它存在的意义是比如像汉字,占着两个字节,如果使用字节流一个字节一个字节读取的话读的都是半个字,无法显示。最基本的字符流输入流是Reader,最基本的字符输出流是Writer。


image.png
3、节点流/处理流

节点流可以从特定的数据源读取数据(节点可以是文件、内存)。
处理流是在节点流或处理流的基础上,通过数据处理能够提供更加强大的数据存取功能。


节点流类型
处理流类型

二、字节流

1、InputStream

InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类 。
常用方法:
abstract int read() // 从输入流中读取数据的下一个字节
int read(byte[] b) // 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中
int read(byte[] b, int off, int len) // 将输入流中最多 len 个数据字节读入 byte 数组
long skip(long n) // 跳过和丢弃此输入流中数据的 n个字节
void close() // 关闭此输入流并释放与该流关联的所有系统资源

2、OutputStream

OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。
常用方法:
void write(byte[] b) // 将 b.length 个字节从指定的 byte 数组写入此输出流
void write(byte[] b, int off, int len) // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
abstract void write(int b) // 将指定的字节写入此输出流
void close() // 关闭此输出流并释放与此流有关的所有系统资源
void flush() // 刷新此输出流并强制写出所有缓冲的输出字节

3、FileInputStream

FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流,继承自InputStream。
构造方法:
FileInputStream(File file)
FileInputStream(String name)

4、FileOutputStream

FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。
FileOutputStream(File file)
FileOutputStream(File file, boolean append) //append代表是否追加
FileOutputStream(String name)
FileOutputStream(String name, boolean append)

package file;

import java.io.*;

public class FileInputStreamTest {

    public static void main(String[] args) {
        try {
            //这个可以打印 Hello,world
            FileInputStream fileInputStream = new FileInputStream("D://a.txt");
            int ch = fileInputStream.read();
            while(-1 != ch){
                System.out.print((char) ch);
                ch = fileInputStream.read();
            }
            /**********************************/
            //这个没有打印,因为fileInputStream中的东西已经被读完了
            byte[] buffer = new byte[8];
            fileInputStream.read(buffer);
            for(int i = 0; i < buffer.length; i++){
                System.out.print((char)buffer[i]);
            }
            /**********************************/
            //这个打印了 Hello,wo  刚好是八个字符,先把输入流中的东西读入缓存字节数组中,然后再读
            FileInputStream fileInputStream1 = new FileInputStream("D://a.txt");
            byte[] buffer1 = new byte[8];
            fileInputStream1.read(buffer1);
            System.out.println();
            for(int i = 0; i < buffer1.length; i++){
                System.out.print((char) buffer1[i]);
            }
            /**********************************/
            //在D盘下创建了一个b.txt, 内容是 wangxban ,首先写了一个wangxb,然后再在后面加一个an
            FileOutputStream fileOutputStream = new FileOutputStream("D://b.txt");
            byte[] buffer2 = {'w','a','n','g','x','b'};
            fileOutputStream.write(buffer2);
            fileOutputStream.write(buffer2, 1,2);
            /**********************************/
            //在D盘下创建一个b文件夹,然后在b文件夹中创建一个b.txt
            //直接这样写会报错,因为没有b这个文件夹
            /*FileOutputStream fileOutputStream1 = new FileOutputStream("D:\\b\\b.txt");
            fileOutputStream1.write(buffer2);*/
            //先创建一个b文件夹,然后再往里面写
            File file = new File("D://b");
            if(!file.exists()){
                file.mkdir();
            }
            FileOutputStream fileOutputStream1 = new FileOutputStream("D://b//b.txt");
            fileOutputStream1.write(buffer2);

            fileInputStream.close();
            fileInputStream1.close();
            fileOutputStream.close();
            fileOutputStream1.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
a.txt中的内容

控制台执行结果:


image.png
5、BufferedInputStream

BufferedInputStream:字节缓冲输入流,提高了读取效率。缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减少io对硬盘的访问次数。缓冲区就像水桶,把数据想象成水,把要处理的数据先放入水桶,装满后再做处理。先把数据放置到缓冲区上,等到缓冲区满了以后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样可以有效地减少对硬盘的访问次数。
构造方法:
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)

package file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class BufferStreamDemo {
    public static void main(String[] args) {
        try {
            //读取
            FileInputStream fileInputStream = new FileInputStream("D://b.txt");
            BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
            int a = bufferedInputStream.read();
            while(-1 != a){
                System.out.print((char) a);
                a = bufferedInputStream.read();
            }
            /***************************************/
            //写入
            FileOutputStream fileOutputStream = new FileOutputStream("D://c.txt");
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            byte[] bytes = {'w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b',
                    'w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b'};
            bufferedOutputStream.write(bytes);
            //flush的作用是将清空缓存区的数据,如果不flush,不会将内容写入文件
            //一定要写flush,并且flush要写在close之前,不然会导致java.io.IOException: Stream Closed
            bufferedOutputStream.flush();
            fileOutputStream.close();
            bufferedOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

三、字符流

1.Reader

Reader:读取字符流的抽象类.
常用方法:
int read() // 读取单个字符
int read(char[] cbuf) // 将字符读入数组
abstract int read(char[] cbuf, int off, int len) // 将字符读入数组的某一部分
long skip(long n) // 跳过字符
abstract void close() // 关闭该流并释放与之关联的所有资源

2.Writer

Writer:写入字符流的抽象类.
常用方法:
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) // 写入字符串的某一部分
Writer append(char c) // 将指定字符添加到此 writer
Writer append(CharSequence csq) // 将指定字符序列添加到此 writer
Writer append(CharSequence csq, int start, int end) // 将指定字符序列的子序列添加
abstract void close() // 关闭此流,但要先刷新它
abstract void flush() // 刷新该流的缓冲

package file;

import java.io.*;

public class BufferedReaderWriterDemo {

    /*mkdirs()可以建立多级文件夹, mkdir()只会建立一级的文件夹, 如下:
            new File("/tmp/one/two/three").mkdirs();
    执行后, 会建立tmp/one/two/three四级目录
    new File("/tmp/one/two/three").mkdir();
    则不会建立任何目录, 因为找不到/tmp/one/two目录, 结果返回false*/

    public static void main(String[] args) {
        try {
            FileReader fileReader = new FileReader("D://test1.txt");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            File file = new File("D://test1");
            if(!file.exists()){
                file.mkdir();
            }
            FileWriter fileWriter = new FileWriter("D://test1/test1.txt");
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            String str = bufferedReader.readLine();
            while(str != null){
                bufferedWriter.write(str);
                bufferedWriter.newLine();
                str = bufferedReader.readLine();
            }

            bufferedWriter.flush();
            fileReader.close();
            bufferedReader.close();
            fileWriter.close();
            bufferedWriter.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、字节缓冲流

ByteArrayOutputStream/ByteArrayInputStream

package file;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ByteArrayStreamDemo {
    //ByteArrayOutputStream在创建实例时,内部创建一个byte型的数组缓冲区,
    //然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写
    //入或读出byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用
    //ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去。

    public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("D://a.txt");
            //在创建ByteArrayOutputStream类实例时,内存中会创建一个byte数组类型的缓冲区,
            //缓冲区会随着数据的不断写入而自动增长
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            //将内容读入到buffer中
            while((len = fileInputStream.read(buffer)) != -1){
                //将每次督导字节数组中的内容写入内存缓冲区
                byteArrayOutputStream.write(buffer, 0 , len);
            }
            //可使用toByteArray()和toString()获取数据
            byte[] data = byteArrayOutputStream.toByteArray();
            //关闭ByteArrayOutputStream无效,此类中的方法在关闭此流后仍可被调用,
            //而不会产生任何IOException
            fileInputStream.close();
            String result = new String(data, "UTF-8");
            System.out.println(result);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

五、数据流

DataOutputStream/DataInputStream

package file;

import java.io.*;

public class DataStreamDemo {
    //来读取和写各种类型的数据,如字符串、boolean、double、float
    //FileOutPutStream也能写,但只能写int、byte
    //一定要注意 DataOutputStream 与DataInputStream配合使用,而且二者读写的顺序要一样
    public static void main(String[] args) {
        //定义字节数组流
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
            dataOutputStream.writeUTF("Hello");  //字符串
            dataOutputStream.writeChar('C');      //单个字符
            dataOutputStream.writeBoolean(true);
            dataOutputStream.writeDouble(3D);
            dataOutputStream.writeFloat(3F);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            //返回从这个输入流中读取余下的字节数
            System.out.println(byteArrayInputStream.available());
            DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
            System.out.println(dataInputStream.readUTF());
            System.out.println(dataInputStream.readChar());
            System.out.println(dataInputStream.readBoolean());
            System.out.println(dataInputStream.readDouble());
            System.out.println(dataInputStream.readFloat());
            dataOutputStream.close();
            dataInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

六、打印流

PrintStream/PrintWriter

package file;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class PrintStreamDemo {
    public static void main(String[] args) {
        testPrintStream();
        //testPrintWriter();
    }

    public static void testPrintStream(){
        PrintStream printStream = null;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("D://e.txt");
            printStream = new PrintStream(fileOutputStream);
            if(printStream != null){
                System.setOut(printStream);
            }
            for(int c = 0; c <= 60; c++){
                //上面设置了输出到打印流中,控制台不打印
                System.out.print(c + ",");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void testPrintWriter(){
        String s = null;
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        try {
            FileWriter fileWriter = new FileWriter("D://f.txt");
            PrintWriter printWriter = new PrintWriter(fileWriter);
            while((s = bufferedReader.readLine()) != null){
                if(s.equalsIgnoreCase("exit")){
                    break;
                }
                System.out.println(s.toUpperCase());
                printWriter.println(s.toUpperCase());
            }
            printWriter.println("*************" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
            printWriter.flush();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

七、对象流

ObjectOutputStream/ObjectInputStream

package file;

import java.io.*;

public class ObjectStreamDemo {
    public static void main(String[] args) {
        User user = new User();
        user.age = 12;
        user.name = "wade";
        user.name = "911";
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("D://g.txt");
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(user);
            objectOutputStream.flush();
            objectOutputStream.close();
            FileInputStream fileInputStream = new FileInputStream("D://g.txt");
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            User user1 = (User) objectInputStream.readObject();
            System.out.println(user1.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    static class User implements Serializable{
        private int age;
        private String name;
        //transient透明的,用来修饰的变量在序列化时不予考虑,当其不存在
        private transient String phone;

        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", phone='" + phone + '\'' +
                    '}';
        }
    }
}

八、文件操作

1、删除文件或文件夹
package file;

import java.io.File;

public class FileDemo1 {
    public static boolean deleteFilesOrDir(String fileName){
        File file = new File(fileName);
        if(!file.exists()){
            System.out.println("文件删除失败," + fileName + "不存在");
            return false;
        }else{
            if(file.isFile()){
                return FileDemo1.deleteFiles(fileName);
            }else{
                return FileDemo1.deleteDir(fileName);
            }
        }
    }

    public static boolean deleteFiles(String fileName){
        File file = new File(fileName);
        if(file.exists() && file.isFile()){
            if(file.delete()){
                System.out.println("文件删除成功");
                return true;
            }else{
                System.out.println("文件删除失败");
                return false;
            }
        }else{
            System.out.println("文件删除失败," + fileName + " 不存在");
            return false;
        }
    }

    public static boolean deleteDir(String dir){
        if(!dir.endsWith(File.separator)){
            dir += File.separator;
        }
        File dirFile = new File(dir);
        if(!dirFile.exists() || !dirFile.isDirectory()){
            System.out.println("文件夹删除失败 !" + dir + " 不存在");
            return false;
        }
        boolean flag = true;
        File[] files = dirFile.listFiles();
        for(int i = 0; i < files.length; i++){
            if(files[i].isFile()){
                flag = FileDemo1.deleteFiles(files[i].getAbsolutePath());
                if(!flag){
                    break;
                }
            }else if(files[i].isDirectory()){
                flag = FileDemo1.deleteDir(files[i].getAbsolutePath());
                if(!flag){
                    break;
                }
            }
        }
        if(!flag){
            System.out.println("删除文件夹失败");
        }
        if(dirFile.delete()){
            System.out.println("文件夹" + dir + "删除成功!");
            return true;
        }else{
            return false;
        }
    }
}
2、移动文件或文件夹
package file;

import java.io.File;

public class FileDemo2 {

    public static void main(String[] args) {
//        System.out.println("调用移动文件");
//        String sourceFileName = "D://b//bbb.txt";
//        String targetFileName = "D://test2//bbb.txt";
//        FileDemo2.moveFile(sourceFileName, targetFileName);

        System.out.println("调用移动目录");
        String sourceDir = "D://b//b";
        String targetDir = "D://test1";
        FileDemo2.moveDir(sourceDir, targetDir, true);
    }

    public static boolean moveFile(String sourceFileName, String targetFileName){
         return FileDemo2.moveFile(sourceFileName, targetFileName, true);
    }

    public static boolean moveDir(String sourceDirName, String targetFileName){
        return FileDemo2.moveDir(sourceDirName, targetFileName, false);
    }

    public static boolean moveFile(String sourceFileName, String targetFileName, boolean isOverlay){
        File sourceFile = new File(sourceFileName);
        if(!sourceFile.exists()){
            System.out.println("文件" + sourceFileName + "不存在,移动失败");
            return false;
        }else if(!sourceFile.isFile()){
            System.out.println(sourceFileName + "不是文件,移动失败");
            return false;
        }
        File targetFile = new File(targetFileName);
        if(targetFile.exists()){
            if(isOverlay){
                System.out.println("目标文件已存在,准备删除它");
                if(!FileDemo1.deleteFilesOrDir(targetFileName)){
                    System.out.println("文件移动失败,文件" + targetFileName + "删除失败");
                    return false;
                }
            }else{
                System.out.println("文件移动失败,文件" + targetFileName + "已存在");
                return false;
            }
        }else{
            if(!targetFile.getParentFile().exists()){
                System.out.println("文件" + targetFile + "所在目录不存在,正在创建");
                if(!targetFile.getParentFile().mkdirs()){
                    System.out.println("移动文件失败,创建文件所在的文件夹失败");
                    return false;
                }
            }
        }

        if(sourceFile.renameTo(targetFile)){
            System.out.println("移动源文件" + sourceFileName + "到" + targetFileName + "成功");
            return true;
        }else{
            System.out.println("移动源文件" + sourceFileName + "到" + targetFileName + "失败");
            return false;
        }
    }


    public static boolean moveDir(String sourceDirName, String targetDirName, boolean isOverlay){
        File sourceDir = new File(sourceDirName);
        if(!sourceDir.exists()){
            System.out.println("源目录" + sourceDirName + "不存在,移动目录失败");
            return false;
        }else if(!sourceDir.isDirectory()){
            System.out.println("移动目录失败," + sourceDirName + "不是目录");
            return false;
        }
        //如果目标文件名不是以文件分隔符结尾,自动添加文件分隔符
        if(!targetDirName.endsWith(File.separator)){
            targetDirName += File.separator;
        }
        File targetDir = new File(targetDirName);
        if(targetDir.exists()){
            if(isOverlay){
                System.out.println("该目录已存在,准备删除它");
                if(!FileDemo1.deleteFilesOrDir(targetDirName)){
                    System.out.println("移动目录失败,因目标目录已存在,删除目录" + targetDirName + "失败");
                    return false;
                }
            }else{
                System.out.println("移动目录失败," + targetDirName + "已存在!");
                return false;
            }
        }else{
            System.out.println("该目录不存在,正在创建");
            if(!targetDir.mkdirs()){
                System.out.println("移动目录失败,创建目标目录失败");
                return false;
            }
        }
        boolean flag = true;
        File[] files = sourceDir.listFiles();
        for(int i = 0; i < files.length; i++){
            if(files[i].isFile()){
                flag = FileDemo2.moveFile(files[i].getAbsolutePath(),
                        targetDirName + files[i].getName(), isOverlay);
                if(!flag){
                    break;
                }
            }else if(files[i].isDirectory()){
                flag = FileDemo2.moveDir(files[i].getAbsolutePath(),
                        targetDirName + files[i].getName(), isOverlay);
                if(!flag){
                    break;
                }
            }
        }
        if(!flag){
            System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "失败");
            return false;
        }
        //删除原目录
        if(FileDemo1.deleteDir(sourceDirName)){
            System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "成功");
            return true;
        }else{
            System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "失败");
            return false;
        }
    }

}

九、总结

1、InputStream/OutputStream,Reader/Writer 四个最基本的抽象类。
2、FileInputStream/FileOutputStream,FileReader/FileWriter 以File开头的类都是对文件进行读写操作的。
3、InputStreamReader/OutputStreamWriter 转化流,将字节流转换为字符流。
4、DataInputStream/DataOutputStream 通过数据流直接写基本类型数据,两者配合使用,读写顺序必须一致。
5、PrintStream/PrintWriter 打印流,没有相应的输入和输出。
6、ObjectInputStream/ObjectOutputStream 对象流,序列化对象需要实现Serializable接口,如果某个字段的安全性较高,不想被序列化,可以使用tranisent修饰,序列化对象时就会忽略此变量。
上一篇下一篇

猜你喜欢

热点阅读