File类
概述:
1.File:翻译是文件,用于表达java中的路径名。
2.路径:用于表示文件或者文件夹在当前系统中的位置
3.路径的分类:
绝对路径,没有任何的歧义,也没有任何的前提条件,Windows,从盘符开始的路径,例如"D:/a/b/c/d/a.txt"
相对路径,相对于某个文件的路径。
构造方法:
1.File(String path)
将一个字符串描述的路径,封装成一个 File对象。
2.File(String parent,String Child)
将两个字符串(父级路径, 子级路径),拼接之后形成的路径封装成一个File对象。
3.File(File parent, String child)
将File类型的父级路径和String类型的字节路径拼接成一个新路径,封装成File对象
注意:
创建好File对象后,只是封装了一个路径, 和磁盘上是否有这个路径无关。
创建功能
1、说明:
最终创建出来的是一个文件还是文件夹,不取决于路径名称
取决于调用的什么方法去创建
2、创建文件:
createNewFile()
3、创建文件夹:
mkdir()
创建文件夹,如果父级路径不存在,则文件夹创建失败
mkdirs()
创建文件夹,如果父级路径不存在,则自动创建父级路径,再创建子级路径
练习:在D盘下的a/b/c/d文件夹中创建一个HelloWorld.txt。
package File类;
import java.io.File;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
File f = new File("D:/a/b/c/d");
f.mkdirs();
File ff = new File(f,"HelloWorld.txt");
ff.createNewFile();
}
}
删除:
1、方法名称:
delete()
2、既可以删除文件,也可以删除文件夹
3、注意事项:
1、删除的时候不走回收站,直接删除
2、不能删除非空文件夹
重命名的功能
1、方法名称
renameTo(File dest)
2、注意事项:
1、方法的调用者,是一个File对象,方法的参数是另外一个File对象
2、调用者是当前修改之前的路径对象,参数是要修改为的路径对象
3、如果改了父级路径,就是剪切,如果不改父级路径就是重命名
示例代码
import java.io.File;
public class Demo05_File的重命名功能 {
public static void main(String[] args) {
File ori = new File("x/a.txt");
File dest = new File("a/b/c/a.txt");
ori.renameTo(dest);
File f = new File("a/b/c/f.txt");
dest.renameTo(f);
}
}
判断功能
1、exists(),判断调用者路径是否存在
2、isFile(),判断调用者是否是一个文件
3、isDirectory(),判断调用者是否是一个文件夹
示例代码:
import java.io.File;
public class Demo06_File的判断功能 {
public static void main(String[] args) {
File a = new File("a");
System.out.println(a.exists());
System.out.println(a.isFile());
System.out.println(a.isDirectory());
}
}
获取功能
1、获取路径三个方法:
getName()
获取最短的那个文件或者文件夹名称
getPath()
获取相对路径,构造方法中传入的那个字符串
getAbsolutePath()
获取绝对路径
2、获取文件或者文件夹属性的方法
length()
获取文件的字节个数
只能针对文件使用,不能针对文件夹使用
lastModified()
获取文件的最后修改时间
3、获取文件夹中内容的方法
String[] list()
返回文件夹中的所有内容的名称(不包含子文件夹中的内容)
File[] listFiles()
返回文件夹中的所有内容的File对象(不包含子文件夹中的内容)
注意事项:
这两个方法只能针对文件夹使用,不能针对文件使用
练习:定义一个方法:键盘录入一个字符串,表示一个文件夹路径,如果不是文件夹路径则提示重新录入
package File类;
import java.io.File;
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true){
String path = sc.next();
File f = new File(path);
if (f.isDirectory()&&f.exists()){
System.out.println("录入成功");
break;
}else {
System.out.println("请重新录入");
}
}
}
}
IO流
介绍
1、IO:I/O,Input/Output,输入/输出
这是根据流向来进行描述的方式,究竟是输入还是输出,站在内存的角度看待问题。所有从其他设备到内存的过程,称为“输入”;所有从内存到其他设备的过程,称为“输出”。
2、java中操作输入输出使用的都是流对象,都是在io包中的类。
IO分类
1、IO流有非常多,非常复杂庞大的一个体系,需要对这些类型进行分类,分类方式有两种:按照流向分类;按照能处理的数据的类型分类
2、按照流向:
输入流
输出流
3、按照操作数据:
字节流:可以直接操作字节数据(byte数据)的流对象
字符流:可以直接操作字符数据(char数据)的流对象
4、四种顶层抽象父类:
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流:
字符输入流:Reader
字符输出流:Writer
字节流
概述
1、可以直接处理字节信息的流对象
2、计算机中一切数据都是字节数据
无论是文字、音频、图片、视频、网页等内容,底层都是字节数据
3、字节流可以操作计算机中一切数据
4、所有其他流对象,底层都需要依赖字节流
5、顶层抽象父类:
InputStream
OutputStream
6、顶层父类是抽象类
将来会根据不同的交互设备,有不同的实现类型
InputStream
1、是字节输入流的顶层抽象父类,定义了字节输入流应该具有的功能
2、常用方法:
int read()
返回一个字节
int read(byte[] arr)
将读取到的数据,存储在arr数组中
返回的数据表示本次读取到的数据的个数
available()
从该流中,还能读多少字节出来
close()
关闭流对象
FileInputStream
1、InputStream是抽象类,不能直接创建对象;根据不同的设备,有不同的具体子类
2、其中用于和磁盘交互的字节输入流:FileInputStream
3、常用构造方法:
FileInputStream(File f)
FileInputStream(String pathname)
将File对象或者String描述的文件路径,封装成一个输入流对象
4、常用成员方法:
使用父类继承而来的即可
示例代码:
ackage File类;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo1_字节流的输入流 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/b.txt");//定义一个输入字节流。
int len ;
while ((len = fis.read()) != -1){
System.out.print((char) len);
}
fis.close();
}
}
为什么读取的一个字节返回值是int
1、需要有一个读取到末尾的标记,通知我们结束循环
2、如果返回值类型是byte,范围就是-128~127,如果返回值是-1,那么表示的就是一个正常读取到的数字
3、读取到的字节,前面加上24个0,成为32位的int类型,就可以保证这些int全都是正数,如果返回值是负数,那就一定不是从文件中读取到的字节,可以使用负数作为文件结束的标记。
补码形式表示数据
-1 10000001 11111110 11111111(补码) 255 00000000 00000000 00000000 11111111 10000000 00000000 00000000 00000001 肯定不是从文件中读取的数据
InputStream中的另外两个方法
1、int read(byte[] arr)
2、available()
代码示例:
package File类;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
public class Demo2_Fis的另外两个方法 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/b.txt");
System.out.println( fis.available());
byte[] b = new byte[5];
int n = fis.read(b);
System.out.println(n);
System.out.println(Arrays.toString(b));
}
}
在这里插入图片描述在这里插入图片描述
OutputStream
1、是字节输出流的顶层抽象父类,至少应该有一个写出一个字节的方法
2、常用方法:
write(int b)
将一个字节存储到目标文件中
write(byte[] arr)
将参数指定的字节数组中的全部字节,写出到目标文件中
write(byte[] arr, int offset, int len)
将参数指定的字节数组的一部分,写出到目标文件中
从offset索引开始,一共len个字节
close()
关流
FileOutputStream
1、OutputStream是一个抽象的父类,不能创建对象
2、根据不同的输出设备,有不同的具体子类,写出到文件系统的文件的流对象:
FileOutputStream
3、构造方法:
FileOutputStream(String path)
FileOutputStream(File f)
创建一个输出到指定文件中的字节输出流,如果指定文件存在,则清空
FileOutputStream(String path, boolean append)
FileOutputStream(File f, boolean append)
创建一个输出到指定文件中的字节输出流,如果指定文件存在,append值为true, 则在末尾追加
4、对于文件内容的解释
1、字节流中的write方法,将字节数据进行输出,没有编码没有解码,直接将字节写出到磁盘中。
2、使用文本编辑器打开一个文件:先读取字节信息;再将字节信息解码
形成字符
代码示例:
package File类;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_FileOutPutStream {
public static void main(String[] args) throws IOException {
File f = new File("E:/c.txt");
f.createNewFile();
FileOutputStream fos = new FileOutputStream(f,true);
fos.write('a');
fos.write('b');
fos.write('c');
fos.write('d');
fos.close();
}
}
在这里插入图片描述
FileOutputStream的另外两个方法
1、write(byte[] arr) 2、write(byte[] arr, int offset, int len)
将字节数组的一部分写出到目标文件中
注意,第二个方法是为了使用数组写入的时候出现数组为清空导致录入不应当录入的数据。
代码示例:
package File类;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_OutputStream的另外两个方法 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("e:/c.txt",true);
byte[] b = {'a','b','?',2};
fos.write(b);
fos.write(b,1,2);
fos.close();
}
}
文件拷贝
1、本质:
将源文件中的一个字节,使用字节输入流读取到内存中
将内存中的一个字节,使用字节输出流,写出到目标文件中
2、操作:
1、准备一个字节输入流,关联源文件
2、准备一个字节输出流,关联目标文件
3、定义循环,读取源文件,每次读取的字节,写出到目标文件去
代码示例:
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_文件拷贝 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/c.txt");
FileOutputStream fos = new FileOutputStream("E:/copy_c.txt");
int n ;
while ((n = fis.read()) != -1){//如果文件中还存在就写到n,如果不存在就返回-1
fos.write(n);//把n写入
}
fis.close();
fos.close();
}
}
文件拷贝效率提升
1、一个字节一个字节的拷贝,速度太慢
大量的时间浪费在寻找字节上
2、提升效率的思路:
一次读取写出多个字节
一次读取多个:读取到一个较大的数组中
一次写出多个:将数组中的内容一次性写出到目标文件去
3、一次性拷贝:
1、好处:效率高
2、坏处:占用内存太多,磁盘中的文件非常大,无法申请相同大小的数
组
4、使用小数组:
1、准备一个大小为1024正数倍的数组
2、循环读取源文件到数组中,将本次读取到的内容写出到目标文件去
代码示例
先用一个一个读取的字节流试试读取的时间:
目标文件信息如下:
再在刚才的代码中添加个计时器
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_文件拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//计时器
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_a.wmv");
int n ;
while ((n = fis.read()) != -1){//如果文件中还存在就写到n,如果不存在就返回-1
fos.write(n);//把n写入
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println(end - start);//输出结果
}
}
结果如下:
现在我们再试试准备一个很大很大的数组,把里面的信息都存进去然后一次性写出去,也就是说一次读,一次写。
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6_大数组文件拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//计时器
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_a.wmv");
byte[] b = new byte[fis.available()];//准备的数组大小刚好等于文件长度。
fis.read(b);//一次读
fos.write(b);//一次写
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println(end - start);//输出结果
}
}
结果只有38秒,对比之前单个字节的读写快的夸张(也可能是我上次跑的出问题了)。
但是数组的长度和文件的大小相等是效率最快的方法吗?
下面来做个测试,准备一个100MB以上的文件。
先来测试一次读写代码同上,就不再粘出来了。
结果1:
这次来测试第二种方式,数组长度为1024的整数倍。
以上是修改的代码。
结果如下:
比一次存取快了不少,所以最合适的方法还是使用长度为1024的正整数倍的小数组来读写。
高效缓冲流
1、BufferedInputStream和BufferedOutputStream
2、是包装类,本身自己不能作为单独的字节流读写字节,用于加强基础的字节流对象,加强之后,读写的效率就会提升
3、身份:
无论是被加强的对象,还是当前用于加强的高效缓冲流对象,都是顶层抽象父类的具体子类对象
BufferedInputStream和FileInputStream,都是InputStream的子类
BufferedOutputStream和FileOutputStream,都是OutputStream的子类
4、使用:
BufferedInputStream(InputStream is)
将一个普通的字节输入流,加强为一个高效字节输入流 BufferedOutputStream(OutputStream os)
将一个普通的字节输出流,加强为一个高效字节输出流
代码示例:
package File类;
import java.io.*;
public class Demo8_高效缓冲流 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_bos.wmv");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int c ;
while ((c = bis.read()) != -1){
bos.write(c);
}
bis.close();
bos.close();
}
}
高效原因
1、BufferedInputStream
当调用read方法时,不是只读取了一个字节,而是一次读取了8192个字节,存储在流对象自己内部维护的缓冲区数组中,拿出了其中第一个字节返回给调用者;如果第二次再调用read方法,不需要再次去磁盘中读取字节信息了,只需要从内存的数组中取出第二个字节返回即可,减少了和磁盘交互次数8191次,提升了效率
如果数组中的数据全被读完,8193次读取时,就再次到磁盘中读取下一个8192个字节
2、BufferedOutputStream
当调用write方法时,不是直接将一个字节写出到磁盘,将该字节写出到BufferedOutputStream自己对象维护的一个私有字节数组中,数组的大小为8192字节,等到数组被写满,自动将数组中的数据一次性刷新到目标文件去,减少了和磁盘交互的次数,提升了效率。
流的刷新和关闭
1、刷新:
flush()
针对有缓冲区数组的,输出流,缓冲区中的内容,写出到其他设备中
2、关流:
close()
将流对象关闭,释放资源
3、关系:
1、在带缓冲的输出流的close方法中,包含一个flush
2、close方法执行之后,流对象就不能继续使用了;flush方法执行之后,流对象可以继续使用
3、不要在循环中频繁使用刷新,否则就丧失了缓冲区的意义
字符流
1、使用字节流写出字符
可以完成
比较麻烦,因为需要我们自己,转成字节数组
2、使用字节流读取字符
1、每次读取一个字节,无法读取到完整的字符信息
2、每次读取2个字节到一个数组中,可能碰到英文,多读取字节信息,造成后面汉字的混乱
问题总结:不知道每次读取几个字节,才是一个完整的字符信息
3、解决方式:
1、将字节流作为一个私有的成员变量,定义在某个类型中
2、如果需要写出字符,则由当前类型先在内部转成字节数组,再让私有字节流写出到目标文件
3、该类型中,可以定义一个读取完整字符的方法:根据中文、英文的字节特点来使用私有的字节流动态决定每次读取几个字节(在gbk编码表中,英文占一个字节,全都是正数,中文占2个字节,并且其中的第一个字节全是负数),可以读取到一个完整字符的全部字节信息,再该类型的内部进行解码形成字符,将字符返回给调用者
字符流概述
1、用于直接操作(读写)字符的流对象
2、分类:
字符输入流 Reade
r 字符输出流 Writer
Reader
1、字符输出流的顶层抽象父类
2、常用方法:
int read()
读取一个完整的字符,如果返回-1表示到达文件末尾
int read(char[] chs)
读取一系列字符到一个字符数组中
close()
关闭流对象
FileReader
1、Reader是一个顶层抽象父类,根据不同的读取设备,有不同的具体子类,其中读取文件系统的具体子类:FileReader
2、构造方法:
FileReader(String path)
FileReader(File f)
将路径封装成字符文件输入流对象
示例代码:
package File类;
import java.io.FileReader;
import java.io.IOException;
public class Demo9_字符流输入 {
public static void main(String[] args) throws IOException {
FileReader fr =new FileReader("E:\\程序\\8.17笔记总结\\src\\File类\\a.txt");
int c;
while ((c = fr.read()) != -1){
System.out.print((char) c);
}
}
}
Writer
1、字符输出流的顶层抽象父类
2、常用功能:
writer(int c)
将一个字符写出到目标文件
write(char[] chs)
将一个字符数组写出到目标文件
write(char[] chs, int offset, int len)
将一个字符数组中的从offset开始一共len个字符写出到目标文件
write(String str)
写出一个字符串
write(String str, int offset, int len)
将字符串的一部分写出
flush()
刷新缓冲区
close()
关闭流对象
FileWriter
1、Writer是一个顶层的抽象父类,根据输出设备不同,有不同的具体子类,输出到文件系统的,就是用FileWriter
2、构造方法:
FileWriter(String path)
FileWrtier(File f)
将一个路径封装成字符输出流
如果文件存在,则先清空
FileWriter(String path, boolean append)
FileWriter(File f, boolean append)
将一个路径封装成字符输出流
如果第二个参数为true,则在文件存在的情况下,可以追加
代码示例:
import java.io.FileWriter;
import java.io.IOException;
public class Demo03_FileWriter {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("a.txt");
fw.write("你好你好");
fw.write('a');
fw.close();
}
}
字符流的拷贝
1、使用字符输入流读取数据,写出到目标文件中
2、必要性:
不需要使用字符流拷贝 读取数据的过程中,先将字节信息读取到内存,将字节信息转成了字符信息 在写出数据的过程中,先把字符信息转成字节信息,再写出到目标文件中 在内存中,先把字节信息转成了字符信息,再将字符信息转成了字节信息,这个转换浪费资源,并且容易出错
4、字符流的使用场景
1、如果单纯读取一些纯文本文件时,需要使用字符流
2、如果单纯将一些字符信息写出到目标文件时,也需要使用字符流
3、既需要读取,也需要写出,在读取和写出之间,还要对字符信息做编辑或者处理,就需要使用字符 流读取和写出(如果需要对字符操作,就使用字符流)
字符流是否可以拷贝非纯文本文件
1、答案:
不能拷贝
2、原因:
读取非纯文本文件时,读取到字节信息,字符流要对字节信息进行解码,由于是非纯文本文件,所以读取的字节可能无法找到任何对应的字符,只能使用英文的?来代替这个字节信息(这一步就对源文件的字节信息篡改),输出过程中,把?转成一个字节,写出到目标文件中,写出的字节已经和当初读取的字节不是同一个字节了。字符流拷贝纯文本文件,读取的每个字节,都会有对应的字符信息,所以不需要使用其他字符代替,不会对信息做篡改
字符流拷贝效率提升
1、使用字符类型的小数组,分批拷贝源文件的内容
2、使用到的方法:
Reader中的
int read(char[] arr)
Writer中的
write(char[] arr, int offset, int len)
代码示例:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo06_字符流小数组拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("a_copy2.txt");
char[] chs = new char[1024 * 8];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fr.close();
fw.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
使用带缓冲的字符流
1、BufferedReader和BufferedWriter
2、介绍:
也是包装类,将没有缓冲功能的普通字符流,包装加强之后形成高效的缓冲字符流包装类也是顶层抽象父类的子类
3、构造方法:
BufferedReader(Reader r)
将一个普通的Reader加强为BufferedReader
BufferedWriter(Writer w)
将一个普通的Writer加强为BufferedWriter
4、使用:
仍然使用的是顶层抽象父类Reader和Writer中的方法
代码示例:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo07_带缓冲的字符流 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a_copy3.txt"));
int c;
while ((c = br.read()) != -1) {
bw.write(c);
}
br.close();
bw.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
带缓冲的字符流特有方法
1、BufferedReader中:
readLine()
可以从源文件中一次读取一行
2、BufferedWriter中:
newLine() 跨平台的换行符