一.什么是文件?
1.1文件的概念
文件是存储在计算机储存设备(如硬盘)上的一组相关数据的集合。它可以包含文本,图像,视频,程序代码等各种类型的数据
1.2文件的类型
1.文本文件
文本文件使用字符编码 (如ASCII,UTF-8等)来存储数据。字符编码规定了如何将字符转换为二进制数据,以及如何将二进制数据转换回字符。
2.二进制文件
二进制文件直接存储最原始的二进制数据,它们并不遵循某种字符编码,而是按照特定的文件格式或者协议来组织数据。
当使用记事本试图打开二进制文件的时候,只能看到一堆乱码,因为记事本尝试使用某种字符编码(如UTF-8)将二进制文件中的二进制数据转换为字符 。
就像是二战时期破译电报一样,我方拦截了敌方的某个电报(内容应该是0-9的数字组成,具体我不是很清楚,这里只是类比),如果按照我方的密码本来翻译敌方电报里的内容,翻译出来大概率不是人话。在没有敌方密码本的前提下,很难搞清楚电报里的内容。
1.2文件的存储/管理方式
随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢?
我之前介绍二叉树/树形结构的时候就提到过文件管理。将整个文件管理系统看作一颗大树,那么每一个文件都可以看作是一个叶子节点 ,而叶子节点和根节点之间连接的桥梁(子树)就是本文引出的新概念------目录(文件夹) 。一个目录下面可以有多个目录,也可以有多个文件,还可以同时拥有目录和文件,如下图
文件是保存在硬盘上的但是因为CPU和硬盘之间的读写速度相差太大,于是就引入了介于两者之间,比硬盘读写速度快很多,储存空间远大于寄存器/缓存的内存
硬件 | 读写速度 | 容量大小 | 成本 | 断电能否保存数据 |
---|---|---|---|---|
CPU(寄存器/缓存) | 最快 | 最小 | 最高 | 不能 |
内存 | 其次 | 其次 | 其次 | 不能 |
硬盘 | 最慢 | 最大 | 最低 | 能 |
1.3文件路径
在二叉树中,如果要精确地找到一个子节点/叶子节点,需要通过节点的引用来实现。对于这里的文件系统(N叉树)来说,就需要使用文件路径来精确定位目录/文件,文件路径分为两种
绝对路径:从电脑的根目录一直到达目标目录/文件的路径,进行路径的描述,就叫做绝对路径
例如:D:\gitee\test.-java-ee-primary\File(从根目录D盘开始)
相对路径:除了可以从根目录开始,我们可以从任意结点出发,进行路径的描述,就被称为相对路径
例如:test.-java-ee-primary\File(从除了根目录的任意目录开始)
二.Java 中操作文件
Java 中通过 java.io.File 类来对一个文(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件
2.1初识File
这里的File类只是在目录/问价层面上进行操作,不会影响到文件里面的内容
File构造方法
java
//根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(File parent, String child)
//根据父目录路径 + 孩子文件路径,创建一个新的 File 实例
File(String parent, String child)
//根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String pathname)
File其他成员方法
方法很多,通过代码直接展示
2.2操作文件
java
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操作目录/文件夹
java
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概述和注意事项
java
abstract class InputStream implements Closeable
因为InputStream是抽象类,这意味着无法直接实例化InputStream,所以我们要实例化它的子类FileInputStream。另外,InputStream实现了Closeable接口,这说明可以调用close来释放资源。
problem1:为什么要调用close方法来释放资源?
在操作系统层面,每打开一个文件,都会占用一个文件描述符。文件描述符是一种有限的系统资源,如果总是打开文件而不关闭,当文件描述符耗尽时就无法打开更多的文件。
problem2如何保证close一定被执行到?
对文件的操作出现异常 或者return语句 可能导致系统资源无法释放,为了应对上述非正常情况,有两种保底的办法
(1).使用finally
java
//实例化FileInputStream需要抛异常(FileNotFoundException)
InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text");
try{
//其他代码
}finally {
//close()需要抛异常(IOException)
inputStream.close();
}
(2)使用try with resources(自动执行close)
java
try(InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text")){
//其他代码
}
(注:文件描述符是一个非负整数,用于标识 操作系统打开的文件等。之前我讲初识进程的时候提到过文件描述符表,它作为进程和操作系统之间的桥梁,允许进程对资源进行修改)
read方法
注意事项说了这么多,终于该读文件内容了
java
//无参数read()
public static void main1(String[] args){
try(InputStream inputStream = new FileInputStream("./aaa/bbb/ccc/a.text")) {
while (true) {
//无参数read方法的返回值是无符号整数
//这保证所有的字节值可以被精确表示,避免溢出变成负数
//虽然返回值是整型,但是字节数据的值不可能大于255
//所以ret的范围是0~255
//当读取到最后一个字节后,再次读取会返回-1
int 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的内容相对简单,就先介绍到这里,下篇博文开始讲解网络相关的知识