IO流(包含常用IO体系图)

一.File类

File类是java.IO包中很重要的一个类.

File类的对象**可以表示文件,还可以表示目录.**在程序中一个File类对象可以代表一个文件或目录.

File类对象可以对文件或目录的属性进行操作.如:文件名,最后修改日期等等.

File对象无法操作文件的具体数据,即不能直接对文件进行读写操作.

1.File类的构造方法

构造方法的语法是

new  File("文件路径");

绝对路径与相对路径:

绝对路径:特别详细的文件路径,如"E:\qq\All Users\QQ\Misc\com.tencent.qqshow\default_av_boy_friend.swf"这就是一个绝对路径.

相对路径:相对路径是两个文件相对同一个父级的路径.

2.File类的常用方法

File类的方法也有很多,大多都是对文件的属性进行操作的.

mport java.io.File;
import java.io.IOException;
import java.util.Date;

public class FileDemo {

    public static void main(String[] args) {

        File file = new File("G:\\Demo.txt");
        System.out.println(file.exists());//判断文件是否存在
        System.out.println(file.isAbsolute());//判断是否是绝对路径
        System.out.println(file.isFile());//判断是不是一个文件
        System.out.println(file.isDirectory());//判断是不是一个目录
        System.out.println(file.canWrite());//判断文件是否可写
        System.out.println(file.canRead());//判断文件是否可读
        System.out.println(file.length());//获取文件内容的长度,以字节为单位
        /*
            lastModified()可以获取到文件最后的修改时间,并且返回类型是long类型
            我们就可以利用这个来创建Date对象来进行相关操作
         */
        System.out.println(new Date(file.lastModified()));//Mon Jul 11 14:12:09 CST 2022

        try {
            //创建新文件,创建成功返回true,否则返回false,有可能抛出异常,必须捕捉
            System.out.println(file.createNewFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以上就是一些关于属性方面,File类定义的一些方法.

但是有些时候我们还需要创建一下新的文件.这时候我们就需要特别注意两个方法mkdirs()与mkdir().

import java.io.File;

public class FileDemoPro {

    public static void main(String[] args) {

        File file = new File("G:\\Demo.word\\123.txt\\11");
        System.out.println(file.exists());//判断该路径对应的文件是否存在
        /*
            这里要说一下mkdirs()与mkdir()的区别
            两个都是创建文件夹,返回的也都是boolean类型,表示有没有创建成功
            区别在于如果就像上面那样G:\Demo.word\123.txt\11这样的目录
            mkdirs()就可以创建,如果不存在的话,此方法把这条路径补充完整
            mkdir()只有缺少路径的最后一个文件夹的时候才能创建成功
         */
        System.out.println(file.mkdirs());
        //只能删除该路径最底下的那个文件夹,并且那个文件夹必须是空的
        System.out.println(file.delete());
    }
}

那这些一样我们既然File类传入的是一个文件(夹),有时候我们还需要遍历这个文件夹,确定这个文件到底里面的文件是什么,这就需要我们的遍历方法.

import java.io.File;

public class FileDemoProMax {


    public static void main(String[] args) {

        File file = new File("F:\\深信服实习\\云计算基础");
        //我们用list这个方法返回的是该路径下所有文件(夹)的名字,这个name不能调用File类中方法
        String[] strings = file.list();
        for (String name:  strings) {
            System.out.println(name);
            /*
                IT基础
                云计算相关技术
                虚拟化基础
             */
        }
        //因此如果我们想要能对这个路径下里面的文件(夹)进行相关操作,就要使用另外一个方法
        File[] files = file.listFiles();
        for (File file1: files) {
            System.out.println(file1);
            /*
                F:\深信服实习\云计算基础\IT基础
                F:\深信服实习\云计算基础\云计算相关技术
                F:\深信服实习\云计算基础\虚拟化基础
             */
        }
    }
}

File类我们认识了之后我们再来认识IO,

二.输入与输出(I/O)的概念

在我们利用程序进行相关的读写操作时,程序是我们的"中间人".也就是说,这里的输入输出都是相对于程序而言的.

当我们把硬盘的数据读入程序中,这个就是**输入.**即input.

当我们把程序中的数据写到外部设备或者内存中时,这就是输出.即output.

那么输入和输出的流就叫做i/o流.

在这里插入图片描述

三.字节流与字符流

我们根据数据流编码格式可以把io流分为字节流与字符流.

流按照数据的传输方向分为:输入流,输出流.

1.字节流

**每次读取的数据以字节为单位.**一次读一个字节.可以读任何文件.

这里面主要有两个类.

字节输入流:FileInputStream 这个类就是用来进行读操作的

字节输出流:FileOutputStream 这个类就是用来进行写操作的

import java.io.*;

public class IODemo {

    public static void main(String[] args) throws IOException {

        //File file = new File("G:/56.txt");
        FileInputStream in = new FileInputStream("G:\\123.txt");
        /*
            FileOutputStream out = new FileOutputStream(file);
            构造方法里面也可以传入file对象
         */
        FileOutputStream out = new FileOutputStream("G:/56.txt");
        int b;
        //-1表示文件已经被读完了
        while ((b = in.read()) != -1) {
            System.out.println(b);
            out.write(b);
        }
    }
}

这个read方法有两个注意的点

1.为什么b能接收read的值

其实就是因为这个方法的返回值是一个int类型.只要不是-1,文件就还没有读取完毕.

其次就是write方法参数也是int类型的.

2.为什么我们非要用b来接收?

这个意思并不是我们定义的变量必须是’b’,其他的当然也可以.假如如果没有这句话,那么代码就会变成这样.

while (in.read() != -1) {
            out.write(in.read());
        }

在条件判断里已经读一次,然后写的时候又读了一次.因此就会有遗漏.但是我们用b来表示就解决了这个问题.

上面我们举得例子是每次读取单个字节,当然我们根据API还发现,read方法可以读取一个数组,并且上面我们没有处理异常.在这里我们就利用传递数组来详细剖析.

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

public class IODemoProMax {

    public static void main(String[] args) {

        FileInputStream in = null;
        FileOutputStream out = null;

        int b =0;
        try {
            in = new FileInputStream("G:\\123.txt");
            out = new FileOutputStream("G:\\12.txt");
        } catch (FileNotFoundException e) {
            //如果找不到文件抛出异常
            e.getMessage();
        }
        try {
            //表示字节数组一下只能传输五个字节
            byte[] b1 = new byte[5];
            //记录这个数组里装的几个有效字节
            int size;
            //当对象确定建立好的时候,就可以进行读写操作,与此同时,建立了通道
            //因为我们使用的是字节数组进行传输,read方法会返回这个数组内有几个有效字节
            while (((size = in.read(b1) ) != -1)) {
                /*
                    这里我们不能用这个方法,尽管确实这个参数可以传一个数组
                    因为我们的数组在前几次可能会存满,最后一次如果没存满比如说只存了三个
                    那么最后一个数组前面三个元素就是确确实实要传进去的
                    但是后面两位因为之前存满过,并没有删除元素,因此后两个元素还是使用的是之前那组数组的后两位
                    out.write(b1);
                 */
                /*
                    因此以前的pro版本的那个方法,就不适合我们使用了
                    我们在上面用size记录了数组里面的有效字节
                    我们就可以选择利用数组写的时候元素的范围
                 */
                out.write(b1, 0, size);
            }
        } catch (IOException e) {
            //使用read,write可能会有IO异常
            e.printStackTrace();
        }finally {
            //在程序运行完毕之后需要关闭通道,否则会发生内存泄漏
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.字符流

**每次读取的数据以字符为单位.**一次读一个字符.这个只适合用于文本文件.

字符输入流:FileReader 用来进行读操作

字符输出流:FileWriter 用来进行写操作

这两个类的使用跟字节流类似,这里就不做过多演示.

四.节点流与处理流

我们先看节点流与处理流的常用类有哪些

1.节点流中常用类

字节输入流:FileInputStream

字节输出流:FileOutputStream

字符输入流:FileReader

字符输出流:FileWriter

2.处理流中常用类

缓冲字节输入流:BufferedInputStream

缓冲字节输出流:BufferedOutputStream

缓冲字符输入流:BufferedReader

缓冲字符输出流:BufferedWriter

之前我们介绍的都算是节点流,节点流对象都是直接对数据进行操作的.

那么处理流会对其他流进行包装处理.也可以叫做包装流.它的流对象包含另一个流.提供一些额外的数据处理的方法.

那么这个我们就可以从构造方法判断到底是节点流还是处理流.参数是其他流对象的就可能是处理流.

我们简单看一下这个处理流是怎么使用的./

import java.io.*;

public class BufferedDemo {

    public static void main(String[] args) {

        //创建流对象,对电脑硬盘中的文件进行包装的流就是节点流
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream("F:\\feige.exe");
            out = new FileOutputStream("F:\\fg.exe");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        //这里流对象构造方法里面封装的是流对象,这种就是处理流
        BufferedInputStream bin = new BufferedInputStream(in);
        BufferedOutputStream bout = new BufferedOutputStream(out);

        int b = 0;
        try {
            while (((b = bin.read()) != -1)) {
                bout.write(b);
            }
            //flush这个方法是必要的,要清空缓冲区,把缓冲区里的东西给他写完
            bout.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bout.close();
                bin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

我们要注意这个read方法,使用read方法时,并不是直接把数据直接读到程序中,而是把数据缓冲到底层的数组中去,这个底层的数组默认长度是8192个字节(8KB),只有把这个缓冲区填满才会进行数据的传输.当然我们的write方法也是这个道理.

当然这个时候就会有另一个问题.那么有时候我们传输的文件可能会特别大,比如6GB,那么这个底层缓冲数组8KB明显太"鸡肋"了.因此,java非常人性化,我们可以自定义底层的缓冲数组长度.比如.

//定义底层缓冲区数组的长度
        BufferedOutputStream bout = new BufferedOutputStream(out,2000);

这样底层的缓冲数组长度就变成了2000,当然这个数字是我们想定义什么就来什么.

当然这个使用也差不了多少,我们看代码

public class BufferedDemo1 {

    public static void main(String[] args) {

        //创建流对象,对电脑硬盘中的文件进行包装的流就是节点流
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream("F:\\HJ2204\\2022-7-11_java第八章IO\\练习\\feige.exe");
            out = new FileOutputStream("F:\\fg.exe");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        //这里流对象构造方法里面封装的是流对象,这种就是处理流
        BufferedInputStream bin = new BufferedInputStream(in);
        //定义底层缓冲区数组的长度
        BufferedOutputStream bout = new BufferedOutputStream(out,2000);
        /*
            BufferedOutputStream这个类在底层有一个数组,默认是8192字节(8KB)长度
            我们使用read方法进行读的时候,会把数据读到缓冲区底层的数组中去
            只有这个数组被填满或者说数据已经读完了的时候,才会传输这个数组
            write方法同样也是如此

            在构造方法这里,我们就自己定义了底层缓冲区数组的长度,系统定义的就自己失效
            这个时候程序的执行依然和上面一样,只是缓冲区数组长度发生变化
         */

        int b = 0;
        byte[] bytes = new byte[1000];
        try {
            while (((b = bin.read(bytes)) != -1)) {
                bout.write(bytes,0,b);
            }
            //flush(刷新)这个方法是必要的,要清空缓冲区,把缓冲区里的东西给他写完
            bout.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bout.close();
                bin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

我们再来看看字符流

我们在之前的思维导图就可以看到,字符流只能读取纯文本文件.

他有两个基类:

Reader-----字符输入流基类(实现类:FileReader)

Writer-------字符输出流基类(实现类:FileWriter)

一个汉子在utf-8中需要三个字节容量.底层的存储单位是字节,字符流是如何做到一次读取一个汉字的呢.

中间需要转换流**FileReader.一旦操作文本设计到具体的指定编码,就需要涉及到转换流.**我们可以这么理解,字符流=字节流+编码表.那么FileReader就可以实现将读到的字节按照charset转换为字符.将字节转换为字符输出.

import java.io.*;

public class ReadDemo1 {

    public static void main(String[] args) throws IOException {

        FileReader fileReader = new FileReader("G:\\曹操.txt");
        FileWriter fileWriter = new FileWriter("G:\\曹操1.txt");
/*
        //这里的write依然返回的是int值
        int c = 0;
        while ((c = fileReader.read()) != -1){
            fileWriter.write(c);
        }
*/


        /*
            上面介绍的就是一个字节一个字节的读取
            我们依然可以使用数组进行读取
         */

        char[] chars = new char[5];
        int size = 0;
        //这里的read方法因为是针对字符进行处理,因此是一个字符数组
        while ((size = fileReader.read(chars)) != -1){
            fileWriter.write(chars);
        }

        fileReader.close();
        fileWriter.close();
    }
}

同样的我们的字符流依然有对应的缓冲处理流,基本的使用方法与我们的节点流相同.

import java.io.*;

public class ReadDemo {

    public static void main(String[] args) {

        try {
            FileReader fileReader = new FileReader("G:\\666.txt");
            //后面参数表示,以后写入数据不更改文件原本内容
            FileWriter writer = new FileWriter("G:\\66.txt",true);

            BufferedReader bufferedReader = new BufferedReader(fileReader);
            BufferedWriter bufferedWriter = new BufferedWriter(writer);

            int c = 0;
            while ((c = bufferedReader.read()) != -1){
                bufferedWriter.write(c);
            }
            bufferedWriter.flush();

            if(bufferedReader != null){
                bufferedReader.close();
            }
            if (bufferedWriter != null){
                bufferedWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

五.print流

与之对应的,我们的节点流字符流都有对应的打印流.在之前思维导图我们也可以发现.

这个类相比于之前的其他流来说简单的多.因为没有那么多琐碎的方法.

import java.io.IOException;
import java.io.PrintStream;

public class PrintDemo {

    public static void main(String[] args) throws IOException {

        //可以向文件写入数据
        PrintStream printStream = new PrintStream("G:\\曹操.txt");

        byte[] bytes = "这是打印流".getBytes();
        printStream.println(2);
        printStream.write(bytes);
    }
}
import java.io.FileNotFoundException;
import java.io.PrintWriter;

public class PrintDemo1 {

    public static void main(String[] args) throws FileNotFoundException {

        PrintWriter printWriter = new PrintWriter("G:\\曹操.txt");

        printWriter.write("曹操");

        printWriter.close();
    }
}

六.对象输入输出流

对象的寿命往往随着生成该对象的程序的终止而终止.有时候可能需要将对象的状态保存下来,在需要的时候再将对象恢复就可以.

那对象的输入输出流的主要作用就是**写入对象信息与读取对象信息.**对象信息一旦也到文件上那么对象的信息也就持久化了.

对象的输出流:ObjectOutputStream

对象的输入流:ObjectInputStream

在ObjectInputStream中用readObject()方法可以直接读取一个对象.ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中.

对象的输出流将指定的对象写到文件的过程,就是对象序列化的过程.被序列化的对象的类必须要实现Serializable接口.这个接口中没有任何的方法.当一个类声明实现Serializable接口后,表明该类可被序列化.但是与此同时我们还需要注意一个问题,实现这个接口,系统会对这个类生成一个版本号,这个版本号如果我们对类有任何一个改动都会改变这个版本号,因此我们需要自己定义一个序列化版本号,当我们显示的定义之后就不会有上面我们说的这个问题.

import java.io.*;

public class ObjectDemo {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        User user = new User("123", "123");
        User user1 = new User("1234", "1234");

        FileOutputStream fileOutputStream = new FileOutputStream("G:\\User.txt");
        ObjectOutputStream  objectOutputStream = new ObjectOutputStream(fileOutputStream);

        //写入对象数据
        objectOutputStream.writeObject(user);
        objectOutputStream.writeObject(user1);

        objectOutputStream.close();

        FileInputStream fileInputStream = new FileInputStream("G:\\User.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

        System.out.println(objectInputStream.readObject());
        //读取一个对象,User{account='123', passWord='123'}

        objectInputStream.close();
    }
}

对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程.

transient关键字

默认情况下当执行了对象序列化的时候会将类中的全部属性的内容进行全部的序列化操作.

但是很多情况下有些属性可能并不需要序列化的处理,这个时候就可以在属性的定义上使用transient关键字来完成.

例如:

//表示这个属性不被序列化
    private transient String passWord;

se();

    FileInputStream fileInputStream = new FileInputStream("G:\\User.txt");
    ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

    System.out.println(objectInputStream.readObject());
    //读取一个对象,User{account='123', passWord='123'}

    objectInputStream.close();
}

}




对象的输入流将指定序列化好的文件读出来的过程,就是对象**反序列化**的过程.

**transient关键字**

默认情况下当执行了对象序列化的时候会将类中的全部属性的内容进行全部的序列化操作.

但是很多情况下有些属性可能并不需要序列化的处理,这个时候就可以在**属性的定义上使用transient关键字**来完成.

例如:

```java
//表示这个属性不被序列化
    private transient String passWord;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值