一.认识文件
1.含义
文件本身是广义的概念,在操作系统中把软件资源/硬件资源都抽象为了“文件”。
我们平时所说的文件,指的是硬盘中的文件
2.硬盘与存内比较
- 硬盘存储空间大,内容存储空间小
- 硬盘读写速度较慢,内存读写速度较快
- 硬盘成本较低,内存成本较高
- 硬盘能够持久化存储,内存断电后数据丢失
3.文件路径
文件路径用于表示文件所在的硬盘的具体位置,根据路径可以找到文件。
文件路径也可以认为是文件的一种身份标识,通过标识,区分唯一的一个文件。
文件路径分为绝对路径和相对路径
绝对路径
绝对路径是从盘符开始,一层一层的深入,一直到文件名结束
例:C:\Program Files\Git\bin
相对路径
相对路径是指定一个目录作为基准目录,在此目录的基础上一层一层找到目标文件的路径
相对路径中./表示当前路径,../表示上一次路径。
4.文件类型
文件类型主要分为两大类:文本文件与二进制文件
文本文件
文本文件中存储的所有内容都是字符串(合法的字符)
但本质上还是二进制文件(计算机存储的数据都是二机制的,可通过字符编码将二机制转换成字符)
二进制文件
二进制文件中保存的都是二进制数据,由“0”和“1”表示
区分一个文件是文本文件和二进制文件,可以使用记事本打开文件,如果是乱码,就是二进制文件,如果不是,则是文本文件。
如图
此文件就是二进制文件。
二.文件系统操作
1.file类
1)构造方法
构造的过程中,需使用绝对路径和相对路径进行初始化,这个路径所指向的文件可以是存在的,也可以是不存在的。
以第二种为例,代码如下:
File file=new File("../test.txt");
2)方法
修饰符及返回值 | 方法签名 | 说明 |
String | getParent() | 返回File对象的父目录文件路径 |
String | getName() | 返回File对象的纯文件名称 |
String | getPath() | 返回File对象的文件路径 |
String | getAbsolutePath() | 返回File对象的绝对路径 |
String | getCanonicalPath() | 返回File对象修饰过的绝对路径 |
boolean | exists() | 判断File对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断File对象代表的文件是否是一个目录 |
boolean | isFile() | 判断File对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据File对象自动创建一个空文件,成功返回true |
boolean | delete() | 根据File对象删除该文件,成功返回true |
void | deleteOnExit() | 根据File对象,标注文件将被删除,JVM结束后 进行 |
String[ ] | list() | 返回File对象代表的目录下所有的文件名 |
File[ ] | listFlies() | 返回File对象代码的目录下所有的文件, 以File对象表示 |
boolean | mkdir() | 创建File对象所代表的目录 |
boolean | mkdirs() | 创建File对象所代表的目录,如有必要 会创建中间目录 |
代码示例:
public class Demo1 {
public static void main(String[] args) throws IOException {
File file=new File("./text.txt");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
public class Demo2 {
public static void main(String[] args) throws IOException {
File file=new File("/text.txt");
System.out.println("是否存在"+file.exists());
System.out.println("是否为文件"+ file.isFile());
System.out.println("是否为目录"+file.isDirectory());
//因为文件没创建 当然全是false
file.createNewFile();
System.out.println("是否存在"+file.exists());
System.out.println("是否为文件"+ file.isFile());
System.out.println("是否为目录"+file.isDirectory());
}
}
面试题:遍历目录,打印每一个文件的路径
import java.io.File;
public class Demo3 {
public static void scan(File file){
if(!file.isDirectory()){
return;
}
File[] f=file.listFiles();
if(f==null||f.length==0){
return;
}
System.out.println(file.getAbsolutePath());
for(File f1:f){
if(f1.isFile()){
System.out.println(f1.getAbsolutePath());
}
else {
scan(f1);
}
}
}
public static void main(String[] args) {
File f=new File("./");
scan(f);
}
}
三.文件内容操作
文件内容操作,主要是读文件和写文件,都是由操作系统提供了API。(stream,流对象)
1.字节流(二进制)
字节流读写数据的基本单位是一个字节
JAVA针对读写两种操作,分别为字节流提供了 InputStream(输入) 和 OutputStream(输出) 类,为字符流提供了 Reader(输入) 和 Writer(输出) 类。
怎样去区分输入和输出?
如图:
靠近CPU的是输入,远离cpu的是输出
1)InputStream
实现
InputStream stream=new FileInputStream("./text.txt");
此处隐含着“打开文件”的操作。
针对文件进行读写,务必需先打开,结束后,也需要关闭。
stream.close();
打开文件,其实是在该进程的文件描述符表中,创建了一个新的表项
进程描述符表描述了该进程要执行哪些操作,可以认为是一个数组,数组的每一个元素就是一个struct file对象。数组的下标就称为“文件描述符”
每打开一个文件,就相当于在数组上占用了一个位置。
而在系统内核中,文件描述符表数组,是固定长度的。
因而,如果不主动释放文件,会导致这里的资源越来越少,数组满了,后续再打开文件就会失败,这个问题称为“文件资源泄漏”
为了避免该问题,我们可以用try/catch来确保文件的释放
try (InputStream stream = new FileInputStream("./text.txt")) {
}catch (IOException e){
e.printStackTrace();
}
try(){}的代码执行完毕,最终都会执行close~
读文件
代码如下:
public class Demo5 {
public static void main(String[] args) throws IOException {
try (InputStream stream = new FileInputStream("./text.txt")) {
while (true){
int b=stream.read();
if(b==-1){
break;
}
System.out.printf("0x%x\n",b);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
由于read()是一个字节一个字节读取,所以需要循环读取,当读取完毕时,返回-1。
频繁的读取多次硬盘,IO耗时比较大。
可以使用第二种构造方法,一次读取所有数据
代码:
public class Demo6 {
public static void main(String[] args) throws IOException {
try(InputStream stream=new FileInputStream("./text.txt")) {
byte[] buffer=new byte[1024];
//b表示实际读到的字节数
int b= stream.read(buffer);
for (int i = 0; i < b; i++) {
System.out.printf("0x%x\n",buffer[i]);
}
}
}
}
2)OutputStream
写文件
写文件与读文件用法相似
代码:
public class Demo7 {
public static void main(String[] args) throws IOException {
try (OutputStream stream=new FileOutputStream("./text.txt")){
stream.write(0xe4);
stream.write(0xbd);
stream.write(0xa0);
stream.write(0xe5);
stream.write(0xa5);
stream.write(0xbd);
}
}
}
当前的写操作,会把之前的内容清空,再写入内容。
若是想在保持原有内容不变,新增信写内容,可以开启“追加写”模式
write也可以一次性写多个字节
public class Demo8 {
public static void main(String[] args) throws IOException {
try (OutputStream stream = new FileOutputStream("./text.txt", true)){
byte[] buffer = new byte[]{(byte) 0xe4, (byte) 0xbd, (byte) 0xa0, (byte) 0xe5, (byte) 0xa5, (byte) 0xbd};
stream.write(buffer);
}
}
}
2.字符流(文本)
字符流读写数据的基本单位是一个字符
字符流内部做的工作更多,它会自动查询码表,把二进制数据转换成对应字符。
1)Reader
一次读一个字符,代码:
public class Demo9 {
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("./text.txt")) {
while (true) {
int c = reader.read();
if (c == -1) {
return;
}
char ch = (char) c;
System.out.println(ch);
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
一次性读所有字符
代码:
public class Demo10 {
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("./text.txt")) {
char[] buffer=new char[1024];
int n=reader.read(buffer);
for (int i = 0; i < n; i++) {
System.out.println(buffer[i]);
}
}
}
}
2)Writer
使用:
第一个构造方法可以之间添加字符串
代码
public class Demo11 {
public static void main(String[] args) throws IOException {
try (Writer writer=new FileWriter("./text.txt")){
writer.write("你好,世界");
}
}
}
以上便是全部内容,如有不对,欢迎指正