欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > Java EE(11)——文件I(input)/O(output)

Java EE(11)——文件I(input)/O(output)

2025/3/17 12:05:03 来源:https://blog.csdn.net/2401_89167985/article/details/146285092  浏览:    关键词:Java EE(11)——文件I(input)/O(output)

一.什么是文件?

1.1文件的概念

文件是存储在计算机储存设备(如硬盘)上的一组相关数据的集合。它可以包含文本,图像,视频,程序代码等各种类型的数据

1.2文件的类型

1.文本文件
文本文件使用字符编码(如ASCII,UTF-8等)来存储数据。字符编码规定了如何将字符转换为二进制数据,以及如何将二进制数据转换回字符。
2.二进制文件
二进制文件直接存储最原始的二进制数据,它们并不遵循某种字符编码,而是按照特定的文件格式或者协议来组织数据。
当使用记事本试图打开二进制文件的时候,只能看到一堆乱码,因为记事本尝试使用某种字符编码(如UTF-8)将二进制文件中的二进制数据转换为字符
就像是二战时期破译电报一样,我方拦截了敌方的某个电报(内容应该是0-9的数字组成,具体我不是很清楚,这里只是类比),如果按照我方的密码本来翻译敌方电报里的内容,翻译出来大概率不是人话。在没有敌方密码本的前提下,很难搞清楚电报里的内容。

1.2文件的存储/管理方式

随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢?
我之前介绍二叉树/树形结构的时候就提到过文件管理。将整个文件管理系统看作一颗大树,那么每一个文件都可以看作是一个叶子节点,而叶子节点和根节点之间连接的桥梁(子树)就是本文引出的新概念——目录(文件夹)。一个目录下面可以有多个目录,也可以有多个文件,还可以同时拥有目录和文件,如下图
在这里插入图片描述
文件是保存在硬盘上的但是因为CPU和硬盘之间的读写速度相差太大,于是就引入了介于两者之间,比硬盘读写速度快很多,储存空间远大于寄存器/缓存的内存

硬件读写速度容量大小成本断电能否保存数据
CPU(寄存器/缓存)最快最小最高不能
内存其次其次其次不能
硬盘最慢最大最低

1.3文件路径

在二叉树中,如果要精确地找到一个子节点/叶子节点,需要通过节点的引用来实现。对于这里的文件系统(N叉树)来说,就需要使用文件路径来精确定位目录/文件,文件路径分为两种

  1.  绝对路径:从电脑的根目录一直到达目标目录/文件的路径,进行路径的描述,就叫做绝对路径
    

例如:D:\gitee\test.-java-ee-primary\File(从根目录D盘开始)

  1.  相对路径:除了可以从根目录开始,我们可以从任意结点出发,进行路径的描述,就被称为相对路径
    

例如:test.-java-ee-primary\File(从除了根目录的任意目录开始)

二.Java 中操作文件

Java 中通过 java.io.File 类来对一个文(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件

2.1初识File

这里的File类只是在目录/问价层面上进行操作,不会影响到文件里面的内容
File构造方法

//根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(File parent, String child)
//根据父目录路径 + 孩子文件路径,创建一个新的 File 实例
File(String parent, String child)
//根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String pathname)

File其他成员方法
方法很多,通过代码直接展示

2.2操作文件

public class FileDemo1 {public static void main(String[] args) throws IOException {//相对路径File file1 = new File(".\\aaa\\bbb\\ccc");//绝对路径File file2 = new File("D:\\a.text");////判断文件是否存在System.out.println("file1.exists = " + file1.exists());System.out.println("file2.exists = " + file2.exists());System.out.println("=====================================================================");//获取文件名System.out.println("file1.getName = " + file1.getName());System.out.println("file2.getName = " + file2.getName());System.out.println("=====================================================================");//获取当前路径下所有目录/文件名System.out.println("file1.list = " + Arrays.toString(file1.list()));System.out.println("file2.list = " + Arrays.toString(file2.list()));System.out.println("=====================================================================");//获取当前路径下所有目录/文件的对象System.out.println("file1.listFiles = " + Arrays.toString(file1.listFiles()));System.out.println("file2.listFiles = " + Arrays.toString(file2.listFiles()));System.out.println("=====================================================================");//获取相对路径//返回的结果和构造方法中传的参数一模一样System.out.println("file1.getPath = " + file1.getPath());System.out.println("file2.getPath = " + file2.getPath());System.out.println("=====================================================================");//获取父路径System.out.println("file1.getParent = " + file1.getParent());System.out.println("file2.getParent = " + file2.getParent());System.out.println("=====================================================================");//获取完全路径System.out.println("file1.getAbsolutePath = " + file1.getAbsolutePath());System.out.println("file2.getAbsolutePath = " + file2.getAbsolutePath());System.out.println("=====================================================================");//获取优化后的完全路径System.out.println("file1.getCanonicalPath = " + file1.getCanonicalPath());System.out.println("file2.getCanonicalPath = " + file2.getCanonicalPath());System.out.println("=====================================================================");}
}
//运行结果
file1.exists = true
file2.exists = false
=====================================================================
file1.getName = ccc
file2.getName = a.text
=====================================================================
//返回的是目录/文件名,本质上是字符串,没有实际作用
file1.list = [a.text]
file2.list = null
=====================================================================
//返回File对象
//假设创建一个file3引用接收
//File file3 = file1.listFiles
//通过file3就可以找到a.text文件
file1.listFiles = [.\aaa\bbb\ccc\a.text]
file2.listFiles = null
=====================================================================
file1.getPath = .\aaa\bbb\ccc
file2.getPath = D:\a.text
=====================================================================
//获取\\ccc的父路径
file1.getParent = .\aaa\bbb
//获取\\a.text的父路径
file2.getParent = D:\
=====================================================================
//\File\.\aaa这中间的“.”意思是当前目录,和File等效
file1.getAbsolutePath = D:\gitee\test.-java-ee-primary\File\.\aaa\bbb\ccc
file2.getAbsolutePath = D:\a.text
=====================================================================
//这里把“.”给优化了
file1.getCanonicalPath = D:\gitee\test.-java-ee-primary\File\aaa\bbb\ccc
file2.getCanonicalPath = D:\a.text
=====================================================================
//当该文件已经存在时会创建失败并返回false
file1.createNewFile = false
file2.createNewFile = true
=====================================================================
//这里删除\\ccc目录失败的原因是ccc目录下面还有a.text文件
//也就是说,不为空的目录删不了
file1.delete = false
file2.delete = true
=====================================================================

2.3操作目录/文件夹

public class mkdirDemo1 {public static void main(String[] args) {File file1 = new File("./xxx");File file2 = new File("./aaa/bbb/ccc");//创建单级目录System.out.println(file1.mkdir());//创建多级目录System.out.println(file2.mkdirs());//判断该引用指向的是不是目录System.out.println(file1.isDirectory());System.out.println(file2.isDirectory());//reNameTo//用法一:重命名//将file3指向的ddd目录重命名为file4指向的fff目录//要求xxx目录必须存在,fff目录不存在/*File file4 = new File("./aaa");*/  //重命名失败,因为该目录下已经存在aaa目录File file3 = new File("./ddd");File file4 = new File("./fff");System.out.println(file3.renameTo(file4));//用法二:转移//将file5指向的文件转移到file6指向的zzz目录下File file5 = new File("./z.text");File file6 = new File("./zzz/z.text");System.out.println(file5.renameTo(file6));}
}

运行结果如下:
在这里插入图片描述

三.文件内容的读写

在操作系统中,文件的读写操作是基于"流"实现的。所谓"流",就是有序的数据序列。
站在CPU的角度,数据从外部向CPU传输叫做"输入"(input),从CPU向外传输叫做"输出"(output)
Java的I/O系统对操作系统的"流"进行了一系列的封装,从读写的数据类型来说分为两类,一类以字节为单位进行读写,一类以字符为单位进行读写

3.1字节流

使用InputStream(读)和OutputStream(写)两个类

3.1.1读字节(InputStream)

InputStream概述和注意事项

abstract class InputStream implements Closeable

因为InputStream是抽象类,这意味着无法直接实例化InputStream,所以我们要实例化它的子类FileInputStream。另外,InputStream实现了Closeable接口,这说明可以调用close来释放资源。
problem1:为什么要调用close方法来释放资源?
在操作系统层面,每打开一个文件,都会占用一个文件描述符。文件描述符是一种有限的系统资源,如果总是打开文件而不关闭,当文件描述符耗尽时就无法打开更多的文件。

problem2如何保证close一定被执行到?
对文件的操作出现异常或者return语句可能导致系统资源无法释放,为了应对上述非正常情况,有两种保底的办法
(1).使用finally

//实例化FileInputStream需要抛异常(FileNotFoundException)InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text");try{//其他代码}finally {//close()需要抛异常(IOException)inputStream.close();}

(2)使用try with resources(自动执行close)

try(InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text")){//其他代码}

(注:文件描述符是一个非负整数,用于标识操作系统打开的文件等。之前我讲初识进程的时候提到过文件描述符表,它作为进程和操作系统之间的桥梁,允许进程对资源进行修改)
read方法
注意事项说了这么多,终于该读文件内容了

//无参数read()public static void main1(String[] args){try(InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text")) {while (true) {//无参数read方法的返回值是无符号整数//这保证所有的字节值可以被精确表示,避免溢出变成负数//虽然返回值是整型,但是字节数据的值不可能大于255//所以ret的范围是0~255//当读取到最后一个字节后,再次读取会返回-1int ret = inputStream.read();if (ret == -1) {break;}System.out.printf("%x ", ret);//e4 b9 9d e8 bd ac e8 8b 8d e7 bf 8e}} catch (IOException e) {throw new RuntimeException(e);}}//参数传入字节数组read((byte b[])public static void main(String[] args){try(InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text")) {byte[] array = new byte[40];//无参数read方法的返回值是有符号整数//所以如果直接用十进制打印byte数组,很有可能出现负数//这里的ret表示读取到的字节数量,返回值是真正的整型int ret = inputStream.read(array);/*System.out.println(Arrays.toString(array));*/for (int i = 0; i < ret; i++) {System.out.printf("%x ", array[i]);//e4 b9 9d e8 bd ac e8 8b 8d e7 bf 8e}} catch (IOException e) {throw new RuntimeException(e);}}

为什么无参数版本返回值是无符号整数,而一个参数的版本返回值是有符号整数?
前者用于逐字节读取数据,返回的是字节的值;后者用于批量读取数据,返回的是读取的字节数量
read还有三个参数的版本
在这里插入图片描述

3.1.1写字节(OutputStream)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2字符流

使用Reader和Writer两个类
以字符为单位进行读写操作,一次最少读一个字符,使用方法和字节流的两个类差不多,以演示为主,就不赘述了

3.2.1写字符

在这里插入图片描述

3.2.1读字符

在这里插入图片描述

四.小结

文件IO的内容相对简单,就先介绍到这里,下篇博文开始讲解网络相关的知识

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词