IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> IO流[刷级] -> 正文阅读

[Java知识库]IO流[刷级]

Author:老九
计算机专业
可控之事 沉重冷静 不可控之事 乐观面对
85180586@qq.com
😄 😆 😵 😭 😰 😅 😢 😤 😍 ?? 😎 😩

👍 👎 💯 👏 🔔 🎁 ? 💣 ?? ?? 🌀 🙇 💋 🙏 💦 💩 ?? 💢
————————————————
版权声明:本文为CSDN博主「浦上青天」的原创文章


知识点

IO

一切皆文件的思想
例如:通过网卡来传输数据
从网卡接受数据的操作就类似于读文件
往网卡中发送数据的操作就类似于写文件

File类

java中使用File类来描述一个文件
构造File对象的时候需要有一个pathname这样的参数
路径的两种表示方式
绝对路径:以C:/D:盘符开头的路径,就叫绝对路径~
相对路径:先指定一个工作目录,然后用.或者…开头的路径,表示一个具体的文件位置,相对路径的参照点就是当前的工作目录
。表示当前目录。。表示当前目录的上级目录

IOException

学习IO部分和网络编程部分,非常常见的一个异常种类
受查异常

listFiles()

罗列当前目录中包含哪些文件和目录
直接调用只能看到当前目录下的内容
怎样才能看到目录中嵌套的内容(递归查看所有内容呢?)
面试题

 //递归罗列出一个目录的所有文件
    private static void listAllFiles(File f)
    {
        if(f.isDirectory())
        {
            //如果是目录,就把目录中包含的文件都罗列出来
            File[] files = f.listFiles();
            for(File file:files)
            {
                listAllFiles(file);
            }
        }
        else{
            //把这个文件的路径直接打印出来
            System.out.println(f);
        }
    }

File file = new File("c:/" );
        File[] files = file.listFiles();
        for(File f : files)
        {
            System.out.println(f);
        }

IO知识

统一正斜杠就可以
length(获取文件长度)的单位是long
如果是int,意味着最多只能支持到大小为2G的文件
21亿=》2G

File中,虽然能进行一些常规的文件操作,但是这里少了两个非常核心的操作!
读文件
写文件
流是java中针对文件操作,又进行了进一步的抽象
流是一组类/一组API,描述了如何来进行文件读写操作

字符(char)流

Reader

Writer

特例

InputStreamReader
OutputStreamWriter

字节(byte)流

InputStream

输入,从输入设备读取数据到内存中

OutputStream

输出,把内存中的数据写入到输出设备中

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

/**
 * Description:
 * User: 李明浦
 * Date: 2022-02-04
 * Time: 20:04
 */
public class IODemo2 {
    public static void main(String[] args) throws IOException {
        copyFile("c:/a.txt","c:/b.txt");
    }
    private static void copyFile(String srcPath,String destPath) throws IOException {
        //0.先打开文件,才能够读写(创建InputStream/OutputStream对象的过程)
        FileInputStream fileInputStream = new FileInputStream(srcPath);
        FileOutputStream fileOutputStream = new FileOutputStream(destPath);
        //1.需要读取srcPath对应文件的内容
        byte[] buffer = new byte[1024];
        //返回值表示这次read操作实际读取到几个字节
        //单词读取的内容是存在上限(缓冲区的长度),要想把整个文件读完,需要搭配循环来使用
        int len = -1;
        while((len = fileInputStream.read(buffer))!=-1)
        {
            //读取成功,就把独到的内容写入到另外一个文件中即可
            //因为len的值不一定就是和缓冲区一样长
            fileOutputStream.write(buffer,0,len);
        }
        //2.把读取到的内容写入到destPath对应的内容
		//关闭文件,如果不关闭文件,就会造成“文件资源泄露”
		fileInputStream.close();
        fileOutputStream.close();
    }
}

资源泄露(没有close()):
首先内核中,当一个进程中打开一个文件的时候,就会在PCB中进行一定的记录,PCB中有一个文件描述符表(是一个数组),每个数组里分为多个file_struct结构体,代码每次打开一个文件,本质上就是在文件描述符表中,创建了一个新的项,代码中每次关闭一个文件,本质上就是把文件描述符表上的对应项给删除了
文件资源泄露的关键在于,文件描述符表,是有上限的,如果代码中一直在反复打开新的文件,而没有关闭的话,文件描述符表就会被打满~,一旦满了之后,后面再想打开新的文件,就会打开失败
一个进程的文件描述符表的上限数目(打开的最多文件数目),是可以配置的,一个进程最多打开65535这么多的文件,就无法继续打开了,除非把前面不用的文件关闭掉
此处资源泄露最直接导致的就是打开新的文件会失败,新的客户端也无法连接到服务器

总结

java进行IO操作的基本体系,一组类,附带了一组API
流:像水流一样,一次读一个字节,一次读两个字节,一次读N个字节。。都是可以的
字节流:读写数据的单位都是字节:读写二进制文件时
字符流:读写数据的单位都是字符:读写文本文件时
FileInputStream:输入(从文件中数据读到内存中)
FileOutputStream:输出(从内存中把数据写入文件中)
Reader/Writer:字符流的输入输出
InputSreamReader和OutputStreamWriter是两个特殊的,功能是可以把字节流转成字符流
流对象的核心操作:
1.打开文件
2.读文件
3.写文件
4.关闭文件(非常重要,不要遗漏)
文件描述符表,文件资源泄露问题,如果只打开,而不关闭,时间一直积累,文件描述符表就满了,后续再尝试打开,就会出现打开失败的情况(具体的上限是可配置的,linux一般默认是65535)

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

/**
 * Description:
 * User: 李明浦
 * Date: 2022-02-04
 * Time: 20:04
 */
public class IODemo2 {
    public static void main(String[] args) throws IOException {
        copyFile("c:/a.txt", "c:/b.txt");
    }

    private static void copyFile(String srcPath, String destPath) throws IOException {
        //0.先打开文件,才能够读写(创建InputStream/OutputStream对象的过程)
        FileInputStream fileInputStream = new FileInputStream(srcPath);
        FileOutputStream fileOutputStream = new FileOutputStream(destPath);
        //1.需要读取srcPath对应文件的内容
        byte[] buffer = new byte[1024];
        //返回值表示这次read操作实际读取到几个字节
        //单词读取的内容是存在上限(缓冲区的长度),要想把整个文件读完,需要搭配循环来使用
        int len = -1;
        while ((len = fileInputStream.read(buffer)) != -1) {
            //读取成功,就把独到的内容写入到另外一个文件中即可
            //因为len的值不一定就是和缓冲区一样长
            fileOutputStream.write(buffer, 0, len);
        }
        //2.把读取到的内容写入到destPath对应的内容
        fileInputStream.close();
        fileOutputStream.close();
    }


    private static void copyFile2(String srcPath, String destPath) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            //0.先打开文件,才能够读写(创建InputStream/OutputStream对象的过程)
            fileInputStream = new FileInputStream(srcPath);
            fileOutputStream = new FileOutputStream(destPath);
            //1.需要读取srcPath对应文件的内容
            byte[] buffer = new byte[1024];
            //返回值表示这次read操作实际读取到几个字节
            //单词读取的内容是存在上限(缓冲区的长度),要想把整个文件读完,需要搭配循环来使用
            int len = -1;
            while ((len = fileInputStream.read(buffer)) != -1) {
                //读取成功,就把独到的内容写入到另外一个文件中即可
                //因为len的值不一定就是和缓冲区一样长
                fileOutputStream.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            //2.把读取到的内容写入到destPath对应的内容
            try {
                if (fileInputStream!=null) {
                    fileInputStream.close();
                }
                if (fileOutputStream!=null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
}

BufferedInputStream/BufferedOutputStream

这样的流对象
首先内置了缓冲区,缓冲区就是一块内存空间,缓冲区存在的意义,就是为了提高程序的效率。程序访问内存比程序访问磁盘要快很多(3-4个数量级),IO涉及到磁盘访问,IO次数越多,整体的程序效率就越低

在这里插入图片描述

这里在try语句代码块会自动调用close方法,因为实现了closeable接口,触发的异常也可以在catch里进行捕获,这样写阅读性也很好

读写文件的时候,系统内部会维护一个光标,当读的时候,光标也相应的往后挪

带缓冲区的流对象比不带缓冲区的流对象效率快很多很多

flush操作

手动刷新缓冲区,把数据从缓冲区写入磁盘/IO设备
缓冲区何时会被刷新
1.缓冲区满了的时候会自动刷新
2.调用close方法的时候也会触发刷新
3.主动调用flush也会刷新

FileReader/FileWriter

  • 为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度。BufferedReader和BufferedWriter类各拥有8192个字符的缓冲区。当BufferedReader在读取文本文件的时候,会先尽量从文件中读入字符数据并放慢缓冲区,而之后若使用read()方法,会先从缓冲区进行读取,如果缓冲区数据不足,才会再文件中读取,使用BufferedWriter时,写入的数据并不会先输入到目的地,而是先到缓冲区,如果缓冲区中的数据满了,才会一次对目的地进行写出
private static void copyFile()
    {
        //处理文本文件需要使用字符流
        try(FileReader fileReader = new FileReader("c:/a.txt")
            ; FileWriter fileWriter = new FileWriter("c:/b.txt"))
        {
            char[] buffer = new char[1024];
            int len = -1;
            while((len = fileReader.read(buffer))!=-1)
            {
                fileWriter.write(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

BufferedReader/BufferedWriter

 private static void copyFile2()
    {
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("c:/a.txt"));
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("c:/b.txt")))
        {
            char[] buffer = new char[1024];
            int len = -1;
            while((len = bufferedReader.read(buffer))!=-1)
            {
                bufferedWriter.write(buffer);
            }
        }
        catch (IOException e)
        {
				e.printStackTrace();
        }
    }

带缓冲区的字符流用法有一个特殊的点
可以按行读取,readline表示读一行,读到换行符为止,如果读取文件完毕,就会返回null
readline读到的一行数据,会自动把最末尾的换行符去掉,把内容写入文件时,如果想换行,就需要手动添加一个换行

 private static void copyFil3()
    {
        //带缓冲区的字符流有一种特殊的用法,按行读取
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("c:/a.txt"));
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("c:/b.txt")))
        {
            String line = "";
            while((line = bufferedReader.readLine())!=null)
            {
                bufferedWriter.write(line+"\n");
                //bufferedWriter.newLine();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

序列化和反序列化

序列化:把一个结构化数据(对象)变成一个二进制的bit流(可以把这个bit流保存到文件,或者通过网络传输)
反序列化:把二进制的bit流,还原回原来的对象
序列化和反序列化的目的:就是为了对象能够通过网络传输/能够在文件中保存
序列化和反序列化的过程中,要保证”信息不丢失“,如果丢失了,就无法还原出一模一样的对象了

为什么需要序列化

  1. 当我们需要把内存中的对象状态保存到一个文件中或者数据库中的时候
  2. 用套接字再网络上传送对象的时候
  3. 序列化最大的目的就是为了让对象通过网络传输,能够保存在文件中,防止丢失找不到

Java自带的序列化方式

要注意的几点

  1. 如果对象要进行序列化,需要实现Serializable接口
  2. ObjectOutputStream用于序列化
  3. ObjectInputStream用于反序列化
  4. transient修饰的变量不可以序列化
  5. static 修饰的变量不能被序列化
    但注意static是静态变量,在代码编译期间就已经存储在方法区的,所以他不需要序列化也可以获得值

借助流对象来完成的
ObjectInputStream:负责反序列化
ObjectOutputStream:负责序列化

import javax.rmi.CORBA.StubDelegate;
import java.io.*;

/**
 * Description:
 * User: 李明浦
 * Date: 2022-02-05
 * Time: 21:59
 */
class student implements Serializable {
    public String name;
    public int age;
    public int score;
}

public class IODemo5 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        student s = new student();
//        s.name = "张三";
//        s.age = 20;
//        s.score = 100;
//        serializeStudent(s);

        student s = deserializeStudent();
        System.out.println(s.age);
        System.out.println(s.name);
        System.out.println(s.score);


    }

    private static void serializeStudent(student s) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("c:/a.txt"));
        //这个wirteObject集序列化+写文件,两者同时搞定~
        objectOutputStream.writeObject(s);
        objectOutputStream.close();


    }

    private  static student deserializeStudent() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c:/a.txt"));
        student s = (student) objectInputStream.readObject();
        objectInputStream.close();
        return s;

    }

}

关于serialVersionUID的问题

  • 如果序列化程序和反序列化程序不是用同一个程序,然后两个程序中都有一个Person类他们之间只有名字相同,个别属性不相同的时候,就会出现问题
  • 这时需要serialVersionUID变量的UID,这个UID无需手动设置,编译器在编译的时候就会自动生成这个值,生成的这个UID差距很大,所以在反序列化的时候,就可以检查这个UID的值,看是否符合预期,就能判断当前类的代码是否一致

编译型语言的定义

  • 编译型语言:把做好的源程序全部编译成二进制代码的可执行程序,然后直接运行这个程序
  • 编译型语言,执行速度快,效率高;依靠编译器,跨平台性差些

解释性语言的定义

  • 解释性语言:把做好的源程序翻译一句,然后执行一句,直至结束
  • 解释型语言,执行速度慢,效率低;依靠解释器,跨平台性好

Java到底是编译型语言还是解释型语言

  • c,c++这种,他们经过一次编译之后,直接可以编译成操作系统了解的类型,没有经过第二次处理,可以直接执行的,他们是编译型语言
  • 但是java的话,首先由编译器编译成.class(字节码)文件,然后再通过JVM从.class文件中读一行解释执行一行,虽然java也需要编译,编译成.class文件,但是并不是机器可以识别的二进制文件,而是字节码文件,不可以直接运行这个程序,需要借助JVM的解释才可以运行,所以是解释型语言

如何利用这一特性实现跨平台

  • 刚刚我们说java是解释型语言,其源程序虽然百年一国,但是编译成字节码文件,最终还是需要jvm的解释,才是再各个平台执行,也正是由于java对于多种不同操作系统有不同的jvm,所以实现了真正意义上的跨平台

先赞后看,养成习惯!!!^ _ ^???
每天都更新知识点哦!!!
码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘记关注我哦!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-06 13:41:29  更:2022-02-06 13:43:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:19:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码