Java EE|File类的常用方法和InputStream, OutputStream 的用法
File类常用方法
在Java中通过File对象描述一个文件(结点,结点对应的文件是否存在不一定)
构造方法
public static void main(String[] args) {// 1. 绝对路径的方式创建// 1.1 直接传入字符串的路径即可File file1 = new File("/Users/harley/Desktop/code/FileTest/a/a.txt");// 1.2 传入父路径 + 子路径File file2 = new File("/Users/harley/Desktop/code/FileTest/b", "b.txt");// 1.3 以File传入 parentFile parent = new File("/Users/harley/Desktop/code/FileTest");File file3 = new File(parent, "c.txt");
}
获取文件属性
判断文件是否存在的方法:
boolean exist()
:判断文件是否真实存在boolean isDirectory()
:文件存在 && 文件是文件夹的时候返回trueboolean isFile()
:文件存在 && 文件是普通文件
代码示例:
public static void main(String[] args) throws IOException {File file1 = new File("/Users/harley/Desktop/code/FileTest/hello.txt");System.out.println(file1.exists());// falsefile1.createNewFile();System.out.println(file1.isDirectory());// falseSystem.out.println(file1.isFile());// true
}
获取文件的方法:
- 提取文件名称:
public String getName()
,该方法不区分目录与普通文件- 得到父结点路径:
public String getParent()
- 获得父目录的文件对象:
public File getParentFile()
代码示例2:
public static void main(String[] args) throws IOException {File file = new File("/Users/././harley/Desktop/code/../code/FileTest/test.txt");System.out.println(file.getName());System.out.println(file.getParent());System.out.println(file.getParentFile().getCanonicalPath());
}
// 执行结果
test.txt
/Users/././harley/Desktop/code/../code/FileTest
/Users/harley/Desktop/code/FileTest
得到该文件最后一次被修改的时间:public long lastModified()
,返回值以时间戳timestamp的形式表示
timestamp代表从1970 - 01-01开始到某一时间经过的秒数(毫秒)
通过调用
System.currentTimeMIllis()
方法可以获取到当前的时间戳
public long length()
:获取文件的大小
int compareTo(File pathname)
:按字典序比较两个抽象路径名
获取文件标准路径
public String getAbsolutePath()
:获取绝对路径public File getAbsoluteFile()
:获取绝对路径下的文件public String getCanonicalPath()
:获取标准路径public File getCanonicalFile()
:获取标准路径下得文件
absolute path和canonical path的区别:
public static void main(String[] args) throws IOException {File file = new File("/Users/././harley/Desktop/code/../code/FileTest/test.txt");System.out.println(file.exists());// trueSystem.out.println(file.getAbsolutePath());System.out.println(file.getCanonicalPath());
}
// 执行结果
/Users/././harley/Desktop/code/../code/FileTest/test.txt
/Users/harley/Desktop/code/FileTest/test.txt
对文件树结构的修改
public boolean createNewFile()
:当且仅当此的文件尚不存在时,才以原子方式创建一个以此抽象路径名命名的新的空文件返回true:创建成功
返回false:创建失败(文件已经存在)
抛出IOException:发生了IO异常(最常见的,是文件应该在的目录现在还不存在)
代码示例:
public static void main(String[] args) throws IOException {File file4 = new File("/Users/harley/Desktop/code/FileTest/d.txt");System.out.println(file4.createNewFile());// trueSystem.out.println(file4.createNewFile());// false
}
// 第一次创建文件的时候成功了,后面因为文件已经存在因此不会再创建文件了
执行后的文件目录:
如果在不存在的目录下创建文件,会发生什么情况:
还有两个文件的创建方法:
public static File createTempFile(String prefix, String suffix)
:在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀生成其名称public static File createTempFile(String prefix, String suffix, File directory)
:在指定目录中创建新的空文件,使用给定的前缀和后缀字符串生成其名称
删除方法:
public boolean delete()
:立即删除文件,只有当文件目录是正确的情况下删除成功并返回true,其他情况比如非空目录是无法删除的,回返回falsepublic void deleteOnExit()
:在JVM退出时才真正删除
删除单个文件的代码示例:
public static void main(String[] args) throws IOException {File file1 = new File("/Users/harley/Desktop/code/FileTest/d.txt");// 在d目录下创建一个文本文件 d.txt,然后再删除System.out.println(file1.createNewFile());// trueSystem.out.println(file1.delete());// true
}
如果想要删除非空目录,先采用深度优先遍历,删除这个目录下的所有子孙,让这个目录变成空目录,然后再删除该目录
删除非空目录的代码示例:
public static void main(String[] args) throws IOException {File file = new File("/Users/harley/Desktop/code/FileTest/c/");// 删除c目录下的1目录traversal(file);file.delete();
}private static void traversal(File dir) throws IOException {File[] files = dir.listFiles();// 查看这个目录下得所有孩子for(File file : files) {if(file.isDirectory()) {// 如果这个孩子也是个目录,就继续深度优先遍历System.out.println(file.getCanonicalPath() + "/");traversal(file);// 当深度优先遍历完成时,该目录为空,可删除file.delete();}else {System.out.println(file.getCanonicalPath());// 得到这个文件的一个标准路径(去除一切无意义的.和..)file.delete();}}
}
删除前,我们看看c目录下的所有文件
由于是深度优先遍历所以一旦碰到目录,就先将碰到的目录遍历完再遍历自己目录中剩下的
去掉file.delete()
的注释,执行代码,删除c目录下所有文件
一切针对树的操作,都需要自己写代码搞定,JDK中只提供了对结点的操作,没有提供针对一棵树的操作
调用delete()方法删除的文件,是不进入回收站的,而是真正的删除,OS上的回收站,实际上缴就是把文件剪切到一个固定目录
创建目录文件(make directory):
boolean mkdir()
:创建目录,如果目录已经存在或者路径上的父目录还不存在,则创建失败boolean mkdirs()
:创建目录,一并把路径中目前还不存在的目录都创建出来
代码示例:
public static void main(String[] args) {File file = new File("/Users/harley/Desktop/code/FileTest/c/d/e");// c目录不存在System.out.println(file.mkdir());// falseSystem.out.println(file.mkdirs());// true
}
文件重命名:boolean renameTo()
重命名有两个效果,第一个效果是修改文件名称,第二个效果是更改文件位置,即剪切和粘贴
public static void main(String[] args) {File file = new File("/Users/harley/Desktop/code/FileTest/test.txt");File dest = new File("/Users/harley/Desktop/code/FileTest/test/dest.txt");System.out.println(file.renameTo(dest));// true
}
// 通过renameTo方法,我们即修改了文件名,还把文件的位置移动到了test目录下
获取目录下得所有孩子文件
public File[] listFiles()
,通过该方法可以返回目录下的所有孩子
该方法的几种返回结果:
如果目录为空,则返回空数组
如果该文件不是目录或发生IO异常,则返回null
代码示例:
public static void main(String[] args) throws IOException {File file = new File("/Users/harley/Desktop/code/FileTest/a");File[] files = file.listFiles();System.out.println(Arrays.toString(files));// [/Users/harley/Desktop/code/FileTest/a/.DS_Store, /Users/harley/Desktop/code/FileTest/a/a.txt]File file1 = new File("/Users/harley/Desktop/code/FileTest/test");File[] files1 = file1.listFiles();System.out.println(Arrays.toString(files1));// []File file2 = new File("/Users/harley/Desktop/code/FileTest/test.txt");File[] files2 = file2.listFiles();System.out.println(Arrays.toString(files2));// null
}
我们可以通过该方法对文件系统的进行遍历
// 深度优先遍历(更常见)
private static void traversalDepthFirst(File dir) throws IOException {// 1. 找到这个目录下得所有孩子File[] files = dir.listFiles();if(files == null) {return;}// 2. 针对每个孩子,判断目录还是文件for(File file : files) {if(file.isDirectory()) {// 如果是个目录,则继续地柜取遍历处理System.out.println("[D] " + file.getCanonicalPath());traversalDepthFirst(file);}else {System.out.println("[F] " + file.getCanonicalPath());}}
}// 广度优先遍历
private static void traversalBroadFirst(File dir) throws IOException {Deque<File> queue = new LinkedList<>();queue.offer(dir);while(!queue.isEmpty()) {File file = queue.poll();if(file.isDirectory()) {System.out.println("[D] " + file.getCanonicalPath());File[] files = file.listFiles();if(files == null) {continue;}for(File file1 : files) {queue.offer(file1);}}else {System.out.println("[F] " + file.getCanonicalPath());}}
}
获取所有根目录: public static File[] listRoots()
, 该方法是个静态方法
内容的读写
读取/输入
- 直接读取(以二进制数据的方式读取,表现在代码中byte为单位)
- 文本读取
「InputStream类」
- 该类位于
java.io.InputStream
包下,直译过来就是输入流- 本身是一个抽象类,真正使用过程中要依赖这个抽象的具体实现子类,常用的子类就是FileInputStream这个文件的输入流
- 其抽象模型就类似于一个水龙头(InputStream),打开水龙头,由水塔(Input Device或模拟的Input Device)供水输出数据流,然后我们将这些数据流拿个桶(byte数组)接起来
- 需要关闭资源
FileInputStream类下常用的构造方法:
public FileInputStream(File file) throws FileNotFoundException
:打开file所在的文件public FileInputStream(String name) throws FileNotFoundException
:打开路径所在位置的文件
代码示例:
public static void main(String[] args) throws Exception {// 向上转型InputStream is = new FileInputStream("/Users/harley/Desktop/code/FileTest/hello.txt");// 准备好一个水桶byte[] buf = new byte[1024];// 1024代表最多读取并存储1024滴(字节)的水,即桶的容量// 拿着桶取接水// 这里的n代表这次真正接到了多少滴(字节)水// n一定是小于等于1024 && n >= 0int n = is.read(buf);byte[] bytes = Arrays.copyOf(buf, n);for(byte b : bytes) {System.out.printf("%02x ", b);}is.close();
}
// 执行结果
68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 77 65 20 61 72 65 20 6c 65 61 72 6e 69 6e 67
执行结果对应:
hello world \n we are learning
windows下得换行默认是\r\n
,也写作CRLF,分别对应0x0D
和0x0A
,OSX操作系统下,换行仅为\n
,对应0x0A
EOS:
假设现在输入流的一段不是水塔,而是一个人在往输入流里倒水
那么除了接水(接受数据)之外,我们还需要一个明确的信号来判断以后会不会有新的数据了
因为我们在接水的时候,可能一滴水都没接到,但是我们不知道未来还会不会有水来
因此我们引入一个信号EOS - End Of Stream,在代码中,使用-1表示这个信号,因为正常来说,水的数量应该是大于等于0的,所以检测到输入为-1时,就说明不会有新数据来了
理解EOS之后,来看一下上面用到过的读取文件的方法:
abstract int read()
方法:一次只接一滴水,返回的是下个字节的数据,当返回值是EOS(-1)时,表示以后没有水了int read(byte[] b)
方法:一次接很多水,需要提前准备好桶(即byte[]
数组),返回值是本次接到了多少水,当返回值是EOS(-1)时,表示以后没有水了
代码示例:
// read()方法测试
public static void main(String[] args) throws Exception {InputStream is = new FileInputStream("/Users/harley/Desktop/code/FileTest/hello.txt");while(true) {int data = is.read();if(data == -1) {break;}byte b = (byte) data;System.out.printf("%02x ", b);}is.close();
}// read(byte[] b)方法测试
public static void main(String[] args) throws Exception {InputStream is = new FileInputStream("/Users/harley/Desktop/code/FileTest/hello.txt");byte[] buf = new byte[5];// 一次最多存5个字节的数据while(true) {int n = is.read(buf);// n == 0 只是本次没数据,以后还有// n == -1 本次包括以后都没有数据了if(n == -1) {break;}for (int i = 0; i < n; i++) {byte b = buf[i];System.out.printf("%02x ", b);}}
}
// 两次的执行结果都是
68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 77 65 20 61 72 65 20 6c 65 61 72 6e 69 6e 67
一点小细节:
通过
read(byte[] b)
方法读取数据的时候,只是把byte数组中的数据给覆盖了,上述代码中,byte数组的大小仅为5,对于有27个字节的文件来说需要读取6次,最后一次读取的时候,只读了2个字节的数据,那么后面三个字节是上次读取时的数据因此我们在打印的时候,按照本次读取到的数据数量进行打印,而不是对整个byte数组进行打印
try - with- resources语法糖
// 正常情况下是这么写的
try {// code...
} catch (SomeException e) {// code...
} finally {if(is != null)is.close();
}// try-with-resources写法
try (InputStream is = ...) {// 连接资源// code...
} catch (SomeException e) {// code
}// 无需主动关闭资源,编译的时候,会自动加上is.close()
凡是需要关闭资源的,都可以使用这种写法
处理文本数据(字符数据)
分清字符集(char set)和字符编码规则(char encoding)的概念
计算机的中数据都是数字(有范围的整数),包括图像、声音、文字的本质都是数字
事先规定一个数字范围,给范围内的每个数字都唯一标定一个字符,比如[0,10)
,每个数字对应一个字符,然后拿到一串数字,将每个数字和字符相对应,就能得到一个字符串
这种编码的规则就称为字符集,但是现实中我们不会每个人都定义自己的字符集,因为这对别人来说是没有意义的
所以,总是有标准委员会来规定好一个字符集,大家都遵守这个字符集,所以就有了各种各样的标准字符集
最常用的就是ASCII和Unicode
但是ASCII的表示范围有限,所以就有了Unicode数量在[0,2^32)
之间,可以说ASCII就是Unicode的子集
每个ASCII码占1个字节,每个Unicode码占4个字节,但实际上很多时候我们存储字符只需要ASCII那样一个字节就够了,如果真的按照4个字节存储,就会有很多空间浪费,因此就出现了不同的编码规则,比如UTF-32、UTF-16、UTF-8、GBK
我们常用的是UTF-8(变长的编码规则),一个字符占用的空间1~4不等,ASCII范围内的,仍然是1个字节,中文的一般是3个字节
乱码的问题
看图:
我们可以通过String的构造方法,实现Unicode到给定编码规则的解码
public String(byte bytes[], int offset, int length, String charsetName)
,传入一个byte数组,偏移量和长度,以及编码规则
代码示例:
public static void main(String[] args) throws IOException {try(InputStream is = new FileInputStream("../FileTest/hello.txt")) {byte[] buf = new byte[1024];int n = is.read(buf);String str = new String(buf, 0, n, "UTF-8");System.out.println(str);// 我// 乱码String str1 = new String(buf, 0, n, "GBK");System.out.println(str1);// 鎴�}
}
我们还能通过Scanner类来进行解码,我们常用的Scanner的构造方法是:
public Scanner(InputStream source)
,平时我们传入的参数都是System.in
即通过终端,这说明System.in
的类型就是一个InputStream
类
既然传入的参数是个输入流,我们就可以这么写:
public static void main(String[] args) throws Exception {try (InputStream is = new FileInputStream("../FileTest/hello.txt")) {try (Scanner scanner = new Scanner(is, "UTF-8")) {while(scanner.hasNextLine()){String line = scanner.nextLine();// 默认去掉了换行符System.out.println("|" + line + "|");}}}
}
// 执行结果
|我|
|喜欢|
|写|
|代码|
这种写法的抽象模型还是那个水龙头,只不过在水龙头的出水口加了一个过滤器(字符编码规则),得到的结果就是经过解码之后的值
还有个问题:
public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 直接敲回车,意思是这次没有输入,但是以后还会以,因此不会结束,继续等待输入// 想要结束输入的一种方法(IDEA中:ctrl/cmd + D, 终端中:ctrl/cmd + Z),即传入EOSwhile(scanner.hasNextLine()) {String str = scanner.nextLine();System.out.println(str);}
}
写入/输出
输出使用OutputStream类,它的模型类似于一个下水道,井盖下的管道(OutputStream)接到一个处理数据的地方
OutputStream的一个子类是FileOutputStream,该子类自带一个功能:
如果文件之前不存在,就会进行创建(创建可能失败:1.权限不够;2.路径上的目录不存在)
如果文件之前存在,会清空之前的文件内容,重新写入
由于内存的写速度远远快于硬盘的写速度,所以,为了平衡这个速度之差,一般通过“缓冲区buffer”来处理
就好像你想吃雪糕,比起每次想吃的时候跑过去买,不如一次性买一大堆回来存到冰箱里慢慢吃,这样就减少了我们去超市购买雪糕的频率了
缓冲区也是这么处理数据的,内存不会每次读到数据就往硬盘里存,这样每次都要等前一个数据写完再写下一个,速度很满,因此准备好内存的一块区域即缓冲区,先存一部分数据,然后一次性写到硬盘中,这个把缓冲区的数据写入硬盘的操作称为“刷盘”
三种情况下,会进行刷盘操作:
- 缓冲区满了或者达到一定阈值
- 每过一段时间刷盘一次
- 进程主动刷盘
极端情况下,可能把数据写入缓冲区的时候,其中部分数据正好遇上刷盘,就直接写到硬盘里了,而另一部分数据则遗留在缓冲区,如果这个时候关闭输出资源,就没有把所有数据写入硬盘
因此,为了确保把缓冲区内可能遗留的数据全部写入Output设备中,我们在关闭资源之前,手动进行一次“刷盘”操作
代码示例1:
public static void main(String[] args) throws IOException {try (OutputStream os = new FileOutputStream("../FileTest/test/word.txt")) {// 0xe6 0x88 0x91是'我'的UTF-8编码的字节序列os.write(0xe6);os.write(0x88);os.write(0x91);os.flush();// 关闭之前,手动刷盘}
}
写完之后,在test目录下创建了一个word文件,并写入数据:
代码示例2:
public static void main(String[] args) throws IOException {try (OutputStream os = new FileOutputStream("../FileTest/test/word.txt")) {os.write(0x20);os.write(0x0a);os.write(0x65);os.write(0x65);os.write(0x65);os.flush();}}
再次写入的时候,会清空文件内容,重新写入:
所以我们很少使用createNewFile文件,因为这是个空文件,没什么用,更多还是使用Scanner的构造方法来创建文件
同时,我们可以一次写多个文件
public static void main(String[] args) throws IOException {try (OutputStream os = new FileOutputStream("../FileTest/test/word.txt")) {byte[] buf = {0x65, 0x65, 0x20, 0x65, 0x0a, (byte)0xe6, (byte)0x88, (byte)0x91};// os.write(buf);// 一次将buf全部写入os.write(buf, 2, 3);// 从下标为2的位置开始写,写3个os.flush();}
}
想要直接写入字符我们通过一个抽象类Writer的实现类OutputStreamWriter:
public static void main(String[] args) throws IOException {try (OutputStream os = new FileOutputStream("../FileTest/test/word.txt")) {try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {writer.write("你好世界\n");writer.write("你好中国");}}
}
还以一种更推荐的写法,通过PrintWriter,因为这底下有我们熟悉的println()
、print()
、printf()
方法
public static void main(String[] args) throws IOException {try (OutputStream os = new FileOutputStream("../FileTest/test/word.txt")) {try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {try (PrintWriter printWriter = new PrintWriter(writer)) {printWriter.println(1 + 1);printWriter.print(3);printWriter.printf("%d + %s", 3, "我");printWriter.flush();// 在最内存调用flush,能把所有外层也刷掉}}}
}
Java EE|File类的常用方法和InputStream, OutputStream 的用法相关推荐
- File类的createNewFile()方法和createTempFile()方法
File.createTempFile() ,由此联想到File.createNewFile() 方法,一时间不知道两者到底有什么区别,感觉都是创建新文件嘛,后来查看api文档介绍,并经过自己动手试验 ...
- java技术培训之File类中常用的构造方法
File类用于封装一个路径,这个路径可以是从系统盘符开始的绝对路径,如:"D:\file\a.txt",也可以是相对于当前目录而言的相对路径,如:"src\Hello.j ...
- java io 创建文件夹_Java中Io流操作-File类的常用操作-创建文件,创建文件夹
package com.hxzy.IOSer; import java.io.File; import java.io.IOException; public class Demo03 { publi ...
- 一文读懂Java中File类、字节流、字符流、转换流
一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...
- Java创建数组的三种方法和ArrayList的用法
Java创建数组的三种方法和ArrayList的用法 Java创建数组的三种方法 一.声明并赋值 二.声明数组名开辟空间 三.声明数组并指定元素个数 四.多维数组 ArrayList的用法 一.添加元 ...
- 【高并发】又一个朋友面试栽在了Thread类的stop()方法和interrupt()方法上!
来自:冰河技术 写在前面 新一轮的面试已经过去,可能是疫情的原因吧,很多童鞋纷纷留言说今年的面试题难度又提高了,尤其是对并发编程的知识.我细想了下,也许有那么点疫情的原因吧,但无论面试的套路怎么变,只 ...
- Java之File类
一.初见File类 java.io.File类代表系统中的文件(文件或目录) 常用构造方法 File(String pathname) File(String parent, String child ...
- 据说有人面试栽在了Thread类的stop()方法和interrupt()方法上
摘要:今天就简单的说说Thread类的stop()方法和interrupt()方法到底有啥区别. 本文分享自华为云社区<[高并发]又一个朋友面试栽在了Thread类的stop()方法和inter ...
- 利用java.io.File类实现遍历本地磁盘上指定盘符或文件夹的所有的文件
2016-11-18 这是本人的第一篇随笔博客,纠结了半天还是选择自己学的时候比较用心的一些知识点上.利用java.io.File类指定本地的文件夹进行遍历所有的文件. package org.lxm ...
- Java学习——File类
File类 File类的设计主要目的是为了在Java中实现对文件进行增加.删除和访问文件路径等方法,但File类中不具有对文件内部信息进行增删改查的功能. File类中的方法 研究File类中的方法有 ...
最新文章
- 一个通用的计数限制解决方案
- 分布式系统,本文引用“courage”的博客
- python在中小学教学中的应用-小学信息技术教学中进行Python 编程语言教学的策略...
- Git学习笔记:远程仓库
- 题解-BOI 2004 Sequence
- 高考python必考题目_假如高考考python编程,这些题目你会几个呢?
- q learning简单理解_班级励志标语格言-生活其实很简单,过了今天就是明天
- 计算机自动关闭硬盘,你遇到过电脑硬盘自动关闭的问题吗?
- 计算机信息规划工资,2020年工科类专业薪资最新排名,计算机很吃香,工资水平最高...
- 全国计算机等级考试二级-公共基础知识
- python移植到stm32f205_Micropython学习(二)STM32移植
- 二代身份证号码编码规则
- php编码小坑之调用新浪天气api和阿凡达天气api
- 从码农到艺术家——读《禅与摩托车维修艺术》有感
- weka中的arff格式数据
- 基因组选择中的参考群更新策略
- 【重要】条形码原理39码和EAN-13码
- 信息在计算机中的表示(一)
- 西工大计算机操作系统实验报告,西工大操作系统实验报告os4.doc
- 杨柳青旅游景点_中国民间文化艺术之乡,天津杨柳青古镇最美的六个旅游景点...
热门文章
- Vmware_jjbLite_jb51不是有效的短文件名
- Coding life,云栖社区的个性化首页上线
- o.redisson.client.handler.CommandsQueue : Exception occured.
- EMV TLV 总结
- cannot reach adb server, attempting to reconnect.
- html 5与css 3权威指南 第2版 pdf,html5与css3权威指南
- python浪漫告白源代码_python七夕浪漫表白源码
- 摩拜app显示未能连接到服务器,摩拜单车又现大面积故障?回应称未接到反馈
- python duplicated函数_16、pandas的duplicated和drop_duplicaates函数
- 案例分享 | ASA广告竞争背后的“爱与和平”