JavaEE 文件操作和IO

本文详细介绍了Java中的文件系统操作,包括File类的使用、文件路径管理、文件类型(文本和二进制)、文件流(InputStream和OutputStream)的打开、关闭、读写,以及文件泄露问题的处理。

文件

文件:在硬盘上存储数据的方式
操作系统帮我们把硬盘的细节封装了
我们只需要了解文件相关的接口即可
文件系统:
不同的文件系统 管理的方式都是类似的
通过目录-文件 构成了N叉树 的树型结构(directory)
路径
我们可以通过D盘->tmp->cat.jpg通过这个路径就能找到电脑上的唯一一个文件这个东西就叫路径
**绝对路径:**以盘为开头的路径 例如d:/tmp/cat.jpg
**相对路径:**以 . 或者 … 开头的路径叫做"相对路径" 相对路径需要有一个基准目录,工作目录表示从这个基准目录出发 怎么走才能找到这个目录
例如以D:为基准 ./tmp/cat.jpg
如果以D:/tmp为基准./cat.jpg
如果以D:/tmp/111为基准…/cat.jpg(…表示上一层目录)
如果以D:/tmp/111/aaa为基准
…/…/cat.jpg

文件系统上存储的文件 具体来说分为两个大类

  1. 文本文件
    存储字符(utf8表上数据的组合)
  2. 二进制文件
    二进制的数据
    使用记事本打开文件如果打开后是能看懂的就是文本
    打开后看不懂的就是二进制
    记事本打开文件就是尝试把当前数据在码表中进行查询

Java中的文件系统操作

java.io.File类
IO input和output (输入输出)
硬盘输入到内存为input
内存输入到硬盘位output
(数据靠近cpu输入 数据原理cpu输出)

通过File对象描述一个具体的文件

构造方法

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

注意: windows上的目录之间的分隔符 可以使用/也可以使用\

方法

修饰符及返回值类型方法签名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限方法

例如:

public class Demo1 {
    public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");
        System.out.println(file.getParent());
        System.out.println(file.getName());
        System.out.println(file.getPath());
        System.out.println(file.getAbsoluteFile());//把工作目录拼接上当前目录
        System.out.println(file.getCanonicalFile());//整理过后的绝对目录
    }
}

在这里插入图片描述
示例2:

public class Demo2 {
    public static void main(String[] args) {
        File file = new File("./test.txt");
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }
}

在这里插入图片描述
例3:

public class Demo2 {
    public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");
        file.createNewFile();//创建文件
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }
}

在这里插入图片描述

file.createNewFile();
这个操作可能会抛出异常
比如写入到文件时非法路径
比如创建的文件对于所在目录没有权限操作

删除文件

public class Demo3 {
    public static void main(String[] args) {
        File file = new File("./test.txt");
        file.delete();
    }
}
public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        File file = new File("./test.txt");
        //file.delete();
        file.deleteOnExit();//等到程序退出再删除
        Thread.sleep(2000);
    }
}

临时文件会用到这样的功能 等到程序结束临时文件自动删除

示例3
创建目录

public class Demo4 {
    public static void main(String[] args) {
        File file = new File("./testDir");
        //mk = make dir = directory
        file.mkdir();//只能创建一层目录
        //file.mkdirs();//可以创建多层目录
    }
}
public class Demo4 {
    public static void main(String[] args) {
        File file = new File("./testDir/111/222/333");
        //mk = make dir = directory
        //file.mkdir();//只能创建一层目录
        file.mkdirs();//可以创建多层目录
    }
}

在这里插入图片描述
示例4
重命名文件

public class Demo5 {
    public static void main(String[] args) {
        File file = new File("./test.txt");
        File file1 = new File("./src/test.txt");
        file.renameTo(file1);
    }
}

以上文件系统的操作 都是基于File类来完成的另外还需要文件内容的操作

文件流 stream

文件这里的内容本质时来自于硬盘 硬盘又是操作系统管理的
使用某个编程语言操作文件 本质上都是需要调用系统的api
其核心步骤有四个

  1. 打开文件 fopen
  2. 关闭文件 fclose
  3. 读文件 fread
  4. 写文件 fwrite

一系列类
有两大类
字节流
InputStream
OutputStream
后续的一些操作字节的类衍生自这两个类 是以字节位单位操作(二进制文件)
字符流
Reader
Writer
后续操作字符的类 衍生自这两个类 操作单位是字符为单位(文本文件)

Java IO流是一个庞大的体系 涉及到非常多的类这些不同类 都有各自不同的特性 但总的来说 使用方法都是类似的

  1. 构造方法 打开文件
  2. close方法 关闭文件
  3. 如果衍生自InputSstream 或者 Read 旧可以使用read方法来读去数据
  4. 如果衍生自OutputStream 或者 Writer 就可以使用write 方法来写数据源了

示例:
我们创建一个文档
在这里插入图片描述

public class Demo6 {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("C:\\Users\\86188\\Desktop\\test.txt");//Reader是抽象类需要一个FileReader继承
        reader.close();
    }
}

打开文件 fopen和关闭文件 fclose

使用Reader类读取

Reader reader = new FileReader()

Reader是抽象类需要一个FileReader继承,写文件路径 相对路径和绝对路径都行

reader.close();

文件泄露问题

这个操作非常重要 释放必要资源 让一个进程打开一个文件 是要从系统这里申请一定的资源
(占用进程的pcb里的文件描述符表中的一个表项) 是一个顺序表长度有限不会自动扩容
如果不释放就会出现 文件资源泄露 而且一旦一直打开文件而不去关闭文件 文件描述符表就会被占满 后续就无法继续打开新的文件了

如果上述代码中间抛出异常或者 return close旧都执行不到了 可能会发生文件资源泄露
所以我们需要用finally执行close()

public class Demo6 {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("C:\\Users\\86188\\Desktop\\test.txt");//Reader是抽象类需要一个FileReader继承
        try{
            
        }finally {
            reader.close();
        }
    }
}

或者使用

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

        try(Reader reader = new FileReader("C:\\Users\\86188\\Desktop\\test.txt")){
        }
    }
}

只要try代码块使用完毕了 就会自动调用close方法

读文件read

修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完了
intread(byte[] b,int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

我们在编译器可以看到
在这里插入图片描述
read()一次是读一个字节 那为什么返回值是int呢?
这是因为
我们可以查看源码
在这里插入图片描述
在这里插入图片描述
返回:字符读取,作为0到65535(0x00-0xffff)范围内的整数,或者-1(如果结束)。

读取的字符是按照一个整型去表示的 而整数的范围是0到65535(正是两个字节的范围) -1(eof)则是表示到达文件末尾读取完毕(windows 上可以通过ctrl+z输入eof linux可以输入ctrl+d输入eof)

我们的文件是utf8格式 一个字符应该是3个字节 为什么这里是两个字节呢?
java的char类型是使用unicode编码的(一个字符两个字节)时用这个方法读取读取一个字符 java标准库会帮助我们自动转换

在这里插入图片描述
一次读多个字符 会把读到的内容填充到参数这个cbuf数组中 此处的参数 相当于是一个"输出型参数" 通过read 会把一个空的数组填充上内容
例如:
在这里插入图片描述
在这里插入图片描述
n表示实际读取的字符个数 如果文件>1024就会把数组填满 如果文件<1024就会返回实际读到的字符个数

在这里插入图片描述
有的时候 可能会涉及到有许多个小文件都需要读取并且拼接到一起
就可以用到这个方法
例如:
3个文件每个文件大小时100字节

read(cbuf,100,200);

就会把文件读取到这个位置
在这里插入图片描述
往往读一个比较大的文件 需要循环读取

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

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

        try(Reader reader = new FileReader("C:\\Users\\86188\\Desktop\\test.txt")){
                while (true){
                    char buf [] = new char[1024];
                    int n = reader.read(buf);
                    if(n ==-1){
                        //读到文件末尾了
                        break;
                    }
                    //输出文件内容
                    for (int i = 0; i < n; i++) {
                        System.out.print(buf[i]+" ");;
                    }
                }
        }
    }
}

在这里插入图片描述

InputStream

InputStream就是字节流 用法和Reader非常相似
文本文件,也可以已使用字节流打开 只不过此时读到的每个字节 就不是完整的字符
在这里插入图片描述
字节流的方法和字符流的相似
只不过返回的整数是一个0~225的整数
在这里插入图片描述
同样是返回-1结束
字节流以十六进制的方式打印文件

public class Demo7 {
    public static void main(String[] args) throws IOException {
        try (InputStream inputStream = new FileInputStream("C:\\Users\\86188\\Desktop\\test.txt")) {
           while (true){
               byte[] buf = new byte[1024];
               int n = inputStream.read(buf);
               if (n == -1) {
                   break;
               }
               for (int i = 0; i < n; i++) {
                   System.out.printf("%x ",buf[i]);
               }
           }
        }
    }
}

在这里插入图片描述
每个字节按照十六进制打印
正好是"你好世界"的编码对应的十六进制
可以使用一些工具类 完成字节/字符 =>字符串的转换
我们可以使用

  Scanner scanner = new Scanner(System.in);

打开.in的源码可以看到
在这里插入图片描述
也是一个InputStream类的对象
在操作系统中"文件"是一个广义的概念Sysyem.in是一个特殊的文件对应到"标准输入"普通硬盘上的文件也是文件 后面的网络编程 网卡(socket)也是文件
Scanner都是一视同仁只是把当前读到的字节数据进行转换 不关心这个数据究竟是来自于标准输入 还是文件 还是网卡

public class Demo8 {
    public static void main(String[] args) throws IOException {
        try(InputStream inputStream = new FileInputStream("C:\\Users\\86188\\Desktop\\test.txt")){
            Scanner scanner = new Scanner(inputStream);
            String s = scanner.next();
            System.out.println(s);
        }
    }
}

在这里插入图片描述
Scanner的操作在这都能使用
但是要注意 Scanner只能用来读文本文件 不适合读取二进制文件

输出write

输出使用方法和输入相似

public class Demo9 {
    public static void main(String[] args) throws IOException {
        try(Writer writer = new FileWriter("C:\\Users\\86188\\Desktop\\test.txt")){
            writer.write("hello java");
        }
    }
}

在这里插入图片描述
输出流对象(无论是字节流还是字符流打开文件后会清空文件内容)
还可以按照追加写方式打开
在这里插入图片描述

在这里插入图片描述
OutputStream使用方式完全一样
只不过不能支持字符串参数 只能按照字节 或者字节数组来写入

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值