java的IO目录

本文转载:https://blog.csdn.net/weixin_45676630/article/details/105691569

1.什么是IO流以及IO的作用

1.1 什么是IO

I/O实际上是input和output,也就是输入和输出。而流其实是一种抽象的概念,它表示的是数据的无结构化传递。

1.2 IO的作用

在这里插入图片描述

1.3 应用场景

比如,我们想要把本地磁盘的文件上传到一个FTP服务器或者远程的云服务器上,那么我们会涉及到以下操作,首先,使用imputstream把本地的磁盘的文件读取到内存中,然后我们会拿到这个文件的所有二进制流数据,之后再使用outputsteam把文件输出到FTP服务器上。这就是IO流的一个基本的概念。
在这里插入图片描述

2.Java中的IO流以及IO流的分类

2.1Java中的IO体系

在Java中I/O流操作的类很多,但是核心体系实际上就只有File、InputStream、OutputStream、Reader、Writer。

字节流:操作的数据单元是8位的字节。InputStream、OutputStream作为抽象基类
字符流:操作的数据单元是字符。以Writer、Reader作为抽象基类

在这里插入图片描述

在这里插入图片描述

2.2 应用示例

案例:使用IO流读取本地磁盘指定文件进行输出。
首先,在G盘根目录下创建了test.txt文件,并输入了“Hello world !”

public class FileInputStreamDemo {
    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream("G:/test.txt");
            int i = 0;
            while ((i = fileInputStream.read()) != -1){
                System.out.print((char)i);
            }
            System.out.println((char)i);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出:

在这里插入图片描述

3. 深入分析Java中的IO流

3.1 IO流的数据来源与API

IO流的数据来源有硬盘、内存、键盘、网络。

代码示例:

		// 磁盘IO
        try {
            FileInputStream inputStream = new FileInputStream("G:/test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
		// 内存IO
        String str = "Hello World !";
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(str.getBytes());
		// 键盘
        // Scanner
        InputStream inputStream = System.in;
		// 网络IO
		// 后面详细讲解
        Socket socket ;
        socket.getInputStream();
        socket.getOutputStream();

3.2 File类简介

File类是Java中为文件进行创建、删除、重命名、移动等操作而设计的一个类,它属于Java.io包下。提供了四种构造方法:

  • File(File parent,String child): 根据parent抽象路径名和child路径名字符串创建一个新的File实例
  • File(String pathname): 将指定路径名转化为抽象路径名创建一个新的File实例
  • File(String parent,String child):根据parent路径名和child路径名创建一个File实例
  • File(URI uri): 指定URI转化为抽象路径名

通过结合前面的部分内容,来整合一个案例,根据用户端输入的路径进行目录的遍历

代码示例:

public class FileDemo {
    public static void main(String[] args) {
        InputStreamReader reader = new InputStreamReader(System.in);
        BufferedReader bufferedReader = new BufferedReader(reader);
        try {
            String path = bufferedReader.readLine();// 读取用户输入的路径
            File file = new File(path);
            if (file.isDirectory() && file.exists()) {
                // 遍历这个目录下的所有子目录
                fileList(file);
            } else {
                System.out.println("文件路径输入错误");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufferedReader.close();
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public static void fileList(File filePath) {
        File[] files = filePath.listFiles();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                if (files[i].isFile()) {
                    System.out.println(files[i].getName()); // 输出具体的文件名
                } else {
                    fileList(files[i]);
                }
            }
        }
    }
}

3.3.基于文件的字节输入输出流实战

3.3.1文件的拷贝案例

public class InputStreamDemo {
    public static void main(String[] args) throws IOException {
        File file = new File("G:/io.jpg");
        FileInputStream fileInputStream = new FileInputStream(file);
        FileOutputStream fileOutputStream = new FileOutputStream("G:/io-1.jpg");
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = fileInputStream.read(buffer)) != -1) {
            // 读取的数据可以保存在内存中
            //也可以写出到磁盘
            fileOutputStream.write(buffer,0,len); // 把InputStream的输入字节写出到指定的目录下
        }
        fileInputStream.close();
        fileOutputStream.close();
    }
}

执行结果:

在这里插入图片描述

3.3.2 深入浅出read方法

在这里插入图片描述
我们首先看下上面这个图,会发现最后一次输出中并不是“ld”,而是“ldr”,这是为什么呢???
原因是这样的,我们定义了byte数组的长度为3,可以理解为先创建了一个长度为3的数组,每次读取是将读取的内存替换掉原来数组的元素。最后一次读取时只读取到了“ld”,上一次的“r”还没被替换掉,所以这里输出了“ldr”。 所以,为了防止出现这种情况,我们经常是这样写的System.out.println(new String(buffer,0,i));

并且从上面例子中,可以看到,如果不设置byte数组,那么需要和磁盘进行11次IO操作,增加之后只需要4次IO,那如果把数组的长度设置成读取的字节长度,就只会进行一次IO操作。那么是不是越大越好呢?显然,肯定不是的。我们来以下两点来分析:

  • 设置数组的长度,这里是会 占用内存空间的,如果设置的值太大,全部都写入到内存中的话是可能会导致内存溢出的。
  • 数据读取越大,虽然IO操作次数少了,但是写入的速度也慢了

3.4 基于内存的字节输入输出流实战

3.4.1 字符串转大写案例

public class MemoryDemo {
    static String str = "Hello world";
    public static void main(String[] args) {
        // 从内存中读取数据
        ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes());
        // 写出到内存中
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int i=0;
        while((i=inputStream.read()) != -1){
            char c = (char)i;
            outputStream.write(Character.toUpperCase(c));
        }
        System.out.println(outputStream.toString());
    }

}

输出结果:

在这里插入图片描述

3.5 基于缓冲流的输入输出实战

3.5.1 缓冲流与普通流复制文件性能对比

这里为了更好的体验出效果,我准备了一个400M左右的文件。

在这里插入图片描述

public class BufferedCopyDemo {
    private static File fileSource = new File("G:/mysql-installer-community-5.7.24.0.msi");
    private static File fileTarget = new File("G:/mysql-installer-community-5.7.24.0_copy.msi");

    public void copyWithNormal(){
        try(FileInputStream inputStream = new FileInputStream(fileSource);
            FileOutputStream outputStream = new FileOutputStream(fileTarget)){
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len=inputStream.read(buf)) != -1){
                outputStream.write(buf,0,len);
            }
        }catch (Exception e){

        }
    }

    public void copyWithBuffered(){
        try(FileInputStream inputStream = new FileInputStream(fileSource);
            FileOutputStream outputStream = new FileOutputStream(fileTarget);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)){
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len=bufferedInputStream.read(buf)) != -1){
                bufferedOutputStream.write(buf,0,len);
            }
        }catch (Exception e){

        }
    }

    public static void main(String[] args) {
        BufferedCopyDemo copyDemo = new BufferedCopyDemo();
        long star = System.currentTimeMillis();
        copyDemo.copyWithNormal();
        System.out.println("普通复制耗时:"+(System.currentTimeMillis()-star));

        star = System.currentTimeMillis();
        copyDemo.copyWithBuffered();
        System.out.println("缓冲流赋值耗时:"+(System.currentTimeMillis()-star));

    }
}

运行结果:

在这里插入图片描述

可以看出,缓冲流的性能明显更好。当然,我们通过调整普通流的byte数组的大小,也有可能比缓冲流效率更高,使用缓冲流的好处在于我们不需要去考虑这个过程了。

3.6 详解Flush方法

在使用缓冲流BufferedOutputStream时,我们常用到flush这个方法,这个方法是用来做什么的呢?
这里通过几个案例来了解这个方法。

3.6.1 demo1

	public static void main(String[] args) throws IOException {
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("G:/test-2.txt"));
            outputStream.write("Hello World".getBytes());
    }

3.6.2 demo2

这里加入了flush

	public static void main(String[] args) throws IOException {
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("G:/test-3.txt"));
        outputStream.write("Hello World".getBytes());
        outputStream.flush();
    }

3.6.3 demo3

	public static void main(String[] args) throws IOException {
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("G:/test-4.txt"));
        outputStream.write("Hello World".getBytes());
        outputStream.close();
    }

3.6.4 最终结果

在这里插入图片描述
可以看到,最终生成了三个文件,但是第一个文件是0kb,里面的内容为空。其实close方法里面也调用flush方法,源码如下:

在这里插入图片描述
那么,为什么为什么没调用flush方法的时候生成的文件为空呢?其实,我们通过write方法去写入的时候,会首先写到缓冲区里面,缓冲区的默认大小为8kb,当8kb满了之后才会触发写入到磁盘的动作,显然,我们的测试案例里面远远没有达到8kb。通过flush方法就可以触发写入到磁盘的动作。

3.7 基于文件的字符输入输出流实战

3.7.1

我在G盘 的test.txt中写入了“Hello World 晚上好”这句话,通过FileInputStream去读取的时候,发现后面的中文变成了乱码,这是为什么呢? 我们知道,一个中文是占了三个字节的,这里每次读取一个字节,那么肯定不能正常显示中文了,可以通过设置byte[] 来解决这个问题,但是难免会遇到临界的时候还是会有个别字符无法正确读取,所以引入了字符流。
在这里插入图片描述

3.7.2 FileReader&FileWriter Demo

通过FileReader&FileWriter实现txt的拷贝案例

 	public static void main(String[] args) {
        try (FileReader reader = new FileReader("G:/test.txt");
             FileWriter writer = new FileWriter("G:/test-8.txt")) {
            int len = 0;
            char[] by = new char[1024];
            while ((len = reader.read(by)) != -1) {
                writer.write(by,0,len);
            }
        } catch (Exception e) {

        }
    }

虽然这样可以保证每个中文都被读取,但是这里存在编码格式的问题,txt的默认编码格式是GBK,但是一般我们的编程环境都是UTF-8,这样就导致新生成的文件产生乱码的问题。如我们刚生成的文件。

在这里插入图片描述

3.7.3 BufferedReader&BufferedWriter

通过BufferedReader&BufferedWriter可以解决上面的乱码问题,对于读写都可以指定字符集编码

	public static void main(String[] args) {
        try(BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("G:/test.txt"));
            InputStreamReader reader = new InputStreamReader(inputStream,"GBK");
            BufferedReader bf = new BufferedReader(reader);
            OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("G:/test-9.txt"),"GBK");
            BufferedWriter bw = new BufferedWriter(writer)){
            bw.write(bf.readLine());
            bw.flush();

        }catch (Exception e){

        }
    }

在这里插入图片描述

4.序列化和反序列化

4.1什么是序列化和反序列化

  • 序列化是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化。
  • 反序列化是序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化。

user类

public class User implements Serializable {
    private  String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
	public static void main(String[] args) {
        User user = new User("DBL",18);
        try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("G:/user"));){
            objectOutputStream.writeObject(user);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在这里插入图片描述

	public static void main(String[] args) {
        try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("G:/user"))){
            User user = (User) objectInputStream.readObject();
            System.out.println(user.toString());
        }catch (Exception e){

        }
    }

在这里插入图片描述

接下来一次阅读

IO底层

NIO详解

RPC原理加手写源码

附加图(网上找的图)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值