文件:File这个概念,在计算机中是一词多用的
狭义的“文件”:指的是硬盘上的文件和目录(文件夹)
广义的“文件”:泛指计算机中的很多软硬件资源,操作系统中,把很多的硬件设备和软件资源抽象成了文件,按照文件的方式来统一管理。后面的网络编程,操作系统也会把网卡当成了一个文件
目录
每个文件,在硬盘上都有一个具体的“路径”。在路径这里,有两种表示路径的风格:
1.绝对路径,以C、D盘符开头的路径
2.相对路径,以当前所在的目录为基准,以.或者..开头(又是可以省略),找到指定的路径
当前所在的目录称为工作目录,每个程序运行的时候都有一个工作目录。
例如当前的工作目录是D:/tmp
定位到111这个目录,就可以表示成./111(./就表示当前的目录)
word,exe,图片,视频,音频,源代码,动态库,这一些不同的文件,整体可以归纳到两类中:
1.文本文件(存的是文本,字符串):每一个字符都是通过一个数字来表示的,这个文本文件中存的数组,一定是合法的字符,都是在你指定的字符编码的码表之内的数据
2.二进制文件(存的是二进制数据,不一定是字符串):没有任何限制,可以储存任何想要的数据
1.针对文件系统操作(文件的创建、删除、重命名)
2.针对文件内容操作(文件的读和写)
Java的标准库,提供了一个File这个类,通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有File 对象,并不代表真实存在该文件。
File file = new File("d:/test.txt");
不要求在d:/这里真的有个test.txt,如果没有的话可以手动用createNewFile创建,不能自动创建。
对于这样一个绝对路径:
但是对于相对路径来说,当前的相对路径就是当前IDEA文件所在的目录
在调用getCanonicalPath这个方法时需要用到的异常,这个异常也是我们之后需要频繁用到的~
对于这样一个绝对路径:
对于这样一个相对路径:
是因为当前相对路径下(这个项目所在路径)没有一个test.txt文件,我们可以用代码创建,用createNewFile后项目里面也就有了一个test.txt文件
和这个类似的还有mkdir,mk->make,dir->directory,也就是创建一个目录。
我们通过数据流来对文件内容进行读写
因此就把读写文件的相关对象称为“流对象”,这个比喻并非是Java独有的,其实是操作系统api就是这样设定的。
Java标准库的流对象,从类型上分为两个大类:
1.字节流,操作二进制数据的,如InputStream,FileInputStream,OutStream,FileOutputStream
2.字符流,操作文本数据的,Reader,FileReader,Writer,FileWriter
这些类的使用方式是非常固定的,核心就是四个操作:
1.打开文件(构造对象)
2.关闭文件(close)
3.读文件(read)=>针对InputStream/Reader
4.写文件(write)=>针对OutPutStream/Writer
通过InputStream来完成对文件的读取,创建inputStream对象的时候,使用绝对路径和相对路径都是可以的,也可以使用FIle对象。打开文件就是通过这样的一个构造方法来打开文件。
public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("D:/test.txt"); while(true){ int b = inputStream.read(); if(b == -1){ return; } System.out.printf("%x\n",(byte)b); } }通过while循环来读取文件,read方法一次性读取的就是一个字节,虽然是用int类型接收的,但是最后我们打印的时候可以转换成byte。
read的第二个版本,需要调用者提前准备好一个数组
public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("D:/test.txt"); while(true){ byte[] buffer = new byte[1024]; int len = inputStream.read(buffer); if(len == -1){ return; } for(int i = 0 ; i < len ; i++){ System.out.printf("%x\n",buffer[i]); } } }通过read来读取文件,然后存取到buffer这个数组中去。这里的传参操作,相当于是把刚才准备好的数组,交给read方法,让read方法内部针对这个数组进行填写。
read会尽可能的把参数传进来的数组给填满。
上面这里给的数组长度是1024,read就会尽可能的读取1024个字节,填到数组里。
实际上,文件剩余长度超过1024时,此时1024个字节都会填满,返回值就是1024了。如果当前剩余的长度不足1024,此时有多少就填多少,read方法就会返回当前实际读取的长度。
对于OutputStream来说,默认情况下,打开一个文件就会清空文件原有的内容(如果不想清空,流对象还提供了一个“追加写”对象,通过这个就可以不清空文件,把新内容追加写到后面)
- public static void main(String[] args) throws IOException {
- OutputStream outputStream = new FileOutputStream("D:/test.txt");
- outputStream.write(101);
- outputStream.write(102);
- outputStream.write(103);
- outputStream.write(104);
- outputStream.close();
- }
和InputStream类似,也是通过构造方法来写入文件。
最后的close需要额外注意一下:
这里的close操作,含义是关闭文件。
每次打开文件操作,都会在文件描述符表中,申请一个位置,把这个信息放进去,每次关闭文件也会把这个文件描述符表对应的表项给释放掉。
如果这个close操作没写会写会怎么样呢?
那如何确保这个close被执行到呢?
- public static void main(String[] args) throws IOException {
- try(OutputStream outputStream = new FileOutputStream("D:/test.txt")) {
- outputStream.write(101);
- outputStream.write(102);
- outputStream.write(103);
- outputStream.write(104);
- }
- }
我们用try with resources这样的写法,这个写法虽然没有显示的写close,但是实际上会执行的,只要try语句块执行完毕,就可以自动执行close。
以上方法是字节流的用法,我们接下来介绍字符流。
- public static void main(String[] args) throws IOException {
- Reader reader = new FileReader("D:/test.txt");
- while(true){
- int ch = reader.read();
- if(ch == -1){
- return;
- }
- System.out.println("" + (char)ch);
- }
- }
用reader的构造方法,通过read来读取文件
- public static void main(String[] args) throws IOException {
- Writer writer = new FileWriter("D:/test.txt");
- writer.write("hello world");
- }
但是这样写后,会发现test.txt没有出现hello world
所以我们需要用write.close来触发缓冲区的更新(刷新操作就是把缓冲区里面的内容写到硬盘中),除了close以外,还可以用flush方法也能刷新缓冲区。
writer.flush();
- public static void main(String[] args) throws IOException {
- InputStream inputStream = new FileInputStream("D:/test.txt");
- Scanner scanner = new Scanner(inputStream);
- scanner.next();
- }
此时scanner的内容就是从test.txt这个文件来读取。
虽然这一部分的相关知识并不会在面试中出现,但是以后工作中会有非常高频的使用场景~