File文件类基本操作
首先我们在了解Java流之前,我们先了解 File 文件类
文件类用于写入和写出,也就是我们的输入和输出流。我们首先要知道关于文件的基本操作。例如创建,删除,添加,修改等等。
public static void main(String[] args) {
//创建File对象
//创建File对象时,要给定URL,可以是相对路径也可以是绝对路径
//绝对路径:
File file1 = new File("D:\\TulunJava\\test\\a.txt");
//相对路径:
File file2 = new File(".\\test\\b.txt");
//如果在这个路径下不存在此文件,那么我们就创建文件
try {
if(!file1.exists()){
//创建了a.txt
file1.createNewFile();
}
if(!file2.exists()) {
//创建了b.txt
file2.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
//创建单级文件夹
File file3 = new File("D:\\TulunJava\\test\\folder");
file3.mkdir();
//如果要创建多级文件夹,
// 也就是在folder中创建一个文件夹叫folder1,在foldder1中创建了folder2
File file4 = new File("D:\\TulunJava\\test\\folder\\folder1\\folder2");
file4.mkdirs();
//获取文件的大小,名字,绝对路径
System.out.println(file1.length());
System.out.println(file1.getName());
System.out.println(file1.getAbsolutePath());
//判断一个文件对象是 文件夹 还是 文件
if(file1.isFile()){
System.out.println("file1文件对象是一个文件");
}
if(file4.isDirectory()){
System.out.println("file4文件对象是一个文件夹");
}
//获取一个目录下的所有文件名
File file = new File(".\\test\\");
//把file文件目录下所有的文件的名字返回到String数组中
String[] strs = file.list();
for(String s1: strs){
System.out.println(s1);
}
//把file文件目录下所有的文件返回到File数组中
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
//文件的删除
//文件可以直接删除
file1.delete();
}
//但是文件夹必须为空才可以直接删除它,
// 所以在这里我们要先进入文件夹将文件夹的文件全部删除,我们在将文件夹删除
//这里我们运用递归
public static boolean delete(File file1){
if(file1 == null || !file1.exists()){
return false;
}
//如果这个文件对象是文件直接删除即可
if(file1.isFile()){
file1.delete();
return true;
}
//如果这个对象是文件夹
if(file1.isDirectory()){
File[] files = file1.listFiles();
//如果文件夹不为空遍历删除
if(files != null){
for(File f1 : files){
//遍历到文件夹调用删除文件方法递归
if(f1.isDirectory()){
delete(f1);
//删除完文件夹内容后,再删除
f1.delect();
}
//遍历到文件直接删除
if(f1.isFile())
f1.delete();
}
}else {
//如果文件夹为空直接删除即可
file1.delete();
return true;
}
}
//如果是文件夹并且出了循环,代表文件夹内容删除干净了,删除自己即可
file.delete();
return true;
}
以上就是File文件类的常用操作方法
Java 中有那些流?
Java 中按流向分为 输入流 和 输出流
输入流:InputStream 和 Reader 作为基类
输出流:OutputStream 和 Writer 作为基类
(tips:输入和输出是相对j计算机内存而言,而不是源和目标。)
按处理数据单元功能划分为 字节流 和 字符流
字节流:字节输入流基类:InputStream、字节输出流基类: OutputStream
字符流:字符输入流基类:Reader、字符输出流基类:Writer
(tips:字节流是通用8位字节流,字符流是16位 Unicode 字符流,亦或者其他字符流。)
按照功能划分,分为 节点流 和 处理流
节点流:可以直接从数据源或目的地读写数据
处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的是简化操作,提升性能。
(tips:节点流和处理流的关系:节点流处于I/O操作的第一线,所有操作b必须通过它执行;处理流可以对其他流进行处理(提高效率或保证操作灵活性))
输入流 和 输出流
输入流: 我们可以理解为读取文件,把文件的内容输入给内存;
输出流:我们可以理解为写入文件,把内存中的数据输出给文件。
我们实现把 a.txt 文件的内容复制到 b.txt。
(这里我们已经存在 a.txt 和 b.txt 文件,如果不存在,那么就创建。)
public static void main(String[] args) {
//创建源文件对象,目的文件对象,以及输入输出流资源,
File src = new File("./a.txt");
File dest = new File("./b.txt");
InputStream inputStream = null;
OutputStream outputStram = null;
try {
inputStream = new FileInputStream(src);
outputStram = new FileOutputStream(dest);
//方法1:边读取,边写入
//将读取的内容(字节)先存储到 temp 中
// int temp = 0;
// //inputStream的read()方法是按字节读取,当字节读取为-1时,证明读取结束
// while( (temp = inputStream.read()) != -1){
// outputStram.write(temp);
// }
//方法2:将数据读取到缓存再去写入,优点:减少了磁盘
//读取到的内容长度
int length = 0;
//创建了一个字节数组,先将
byte[] buffer = new byte[1024];
//把内容读取到缓存数组buffer中
if((length = inputStream.read(buffer)) != -1){
//把缓存中的内容进行写入
outputStram.write(buffer);
}
//flush方法强制将缓冲区中的数据刷新到目标文件中
outputStram.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//无论如何,关闭资源
inputStream.close();
outputStram.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(tips: 读取 和 写入都是在文件的最开始的位置读取和写入的,所以当程序重复执行时,会发现目的文件并没有任何变化,这就是原因)
额外知识:为什么read()在读取的时候是按照字节读取的,但是方法返回的却是int类型??
因为我们在处理输入流时,我们最终会在read方法返回 -1 的时候中止读取,如果read方法返回的是byte类型,-1也就对应的是8个字节全部为1。这个时候有很大的概率读取到中间读取到了8个字节全部为1的情况,但是此时并没有将源文件读取完,所以就会产生错误。但是当我们用 int 类型去返回时,读取到-1时,也只是8位为1,避免了中途中断读取的错误。但是读取到32位全部为1这个概率就非常小了,所以这避免了读取到中间就中止读取的错误。
额外知识:什么时候需要用 flush 方法?
1.再输出流关闭之前,每一次都 flush 一下(最保险的方法,推荐使用)
2.当某一个输出流对象中有缓冲区,就需要flush。
字符流和字节流
字符流,我们对源文件和目的文件按照指定字符去读取和写入。
字节流:我们对源文件和目的文件按照字节去读取和写入。
从这里我们就可以发现,字符流的效率一定是比字节低的!因为计算机的磁盘的存储本质上就是字节存储,如果按照字符去读取和写入,那么我们必须先按照字节读取,然后再进行指定编码读取,再写入到目的文件。这个效率比字节读取和写入要低下。
并且字符流实际上只能处理文本文件,当时字节流可以处理一切文件。
但是!既然如此为什么还会存在字符流?大家可以去试试读取中文文本,每当按照字节读取时,每读取一次就输出一次,我们会发现输出的是乱码,所以这就是问题(中文字符占1-5个字节),但是当我们使用字节流时就不需要担心了,我们按照了指定的字符编码去读取和写入,所以每一次读取的都是一个字符并且经过Unicode编码,所以就不会出现乱码了。
字节流
public static void main(String[] args) {
//创建源文件和目标文件对象,以及输入输出资源
File src = new File("./a.txt");
File dest = new File("./b.txt");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(src);
outputStream = new FileOutputStream(dest);
//利用缓冲区提高效率
int length = 0;
byte[] buffer = new byte[1024];
while( (length= inputStream.read(buffer)) != -1){
outputStream.write(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流
public static void main(String[] args) {
//创建源文件、目标文件对象,以及输入输出资源
File src = new File("./a.txt");
File dest = new File("./b.txt");
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader(src);
writer = new FileWriter(dest);
//创建字符缓冲区提高效率
int length = 0;
char[] buffer = new char[1024];
while((length = reader.read(buffer)) != -1 ){
writer.write(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
reader.close();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
节点流和处理流
节点流:源文件和目的文件直接连接,并没有中间流进行包装。
处理流:源文件和目的文件不直接连接,中间有字节流或者字符流对源文件的流进行包装,然后再输出给目的文件,这样做能提高我们的效率,并且简化我们的操作。
节点流:
public static void main(String[] args) {
//创建源文件和目标文件对象,以及输入输出资源
File src = new File("./a.txt");
File dest = new File("./b.txt");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(src);
outputStream = new FileOutputStream(dest);
//利用缓冲区提高效率
int length = 0;
byte[] buffer = new byte[1024];
//源文件和目的文件全部直接连接,并没有用包装类或者其他类进行操作
while( (length= inputStream.read(buffer)) != -1){
outputStream.write(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
处理流:
再演示处理流之前,我们要对流的Java类的结构进行梳理和查看。
再此之前,我们对基类都进行了基本的实现。基类也就是InputStream、OutputStream、Reader、Writer。还有FileInputStream、
FileOutputStream进行实现。
这里我们对常用的子类进行梳理。
常用处理流
BufferedReader / BufferedWriter 和 BufferedInputStream / BufferedOutputStream
BufferedReader / BufferedWriter 和 BufferedInputStream / BufferedOutputStream 这些处理流都是内置一个缓冲区(大小为8kb)。
前者处理字符流,后者处理字节流。
我们来做个演示。
public static void main(String[] args) {
File src = new File("./a.txt");
File dest = new File("./b.txt");
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
// BufferdeInputStream 和 BufferedReader使用几乎一致
// BufferedInputStream bufferedInputStream = null;
// BufferedOutputStream bufferedOutputStream = null;
try {
//我们也可以指定缓冲区的大小。
// bufferedReader = new BufferedReader(new FileReader(src),8192);
bufferedReader = new BufferedReader(new FileReader(src));
bufferedWriter = new BufferedWriter(new FileWriter(dest));
//buffered特殊方法skip
// bufferedReader.skip(4);
//如果使用这种方法,那么就会进行跳跃式的读取
//例如 abcdefghijklmnopq
//skip读取:a f k p 间隔4个读取一次
int length = 0 ;
while((length = bufferedReader.read()) != -1){
bufferedWriter.write(length);
}
//BufferReader特有方法按行读取的方式
// String line = null;
// //注意:readLine读取到末尾时返回的是null
// while((line = bufferedReader.readLine()) != null){
// bufferedWriter.write(line);
//换行
// bufferedWriter.newLine();
// }
//强制刷新到目标数组中
bufferedWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭资源
bufferedWriter.close();
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们讲解一下处理流 Buffered输入流 的 read() 方法,read()方法,直接从源文件读取8192字节大小(或者字符大小)的数据,每当我们进行读取时,就直接从 buf 也就是缓存中去读写,如果读取的内容的大小已经大于了buf的长度,那么 buf 直接返回内存中指定需要的数据(内存大小8192,如果4096的内容已经都去了,这一次要读取8192,那么直接将剩余的4096字节的内容读取,再对磁盘进行交互),然后再读取源文件8192大小,返回给buf缓存,再返回给read(),直到满足需求。
write()方法,同样,先把数据写入缓存,当调用write()方法时,直接从缓存中去写入,如果写入的大小大于了 buf 的大小,那么直接flush(),再去将内存更新,再写入。
额外知识:这些类自带的缓冲区,与我们自己创建的缓冲区有什么不同??
read 方法和 write 方法一样。本质上提升了效率,减少了磁盘的交互。
InputStreamReader / OutputStreamWriter
InputStreamReader / OutputStreamWriter都是字符流的子类,对文本文件的处理是按照1个字符一个字符去输入和读写的。
当我们处理文本文件时,我们按照字符去读写,效率会比字节流相对要高很多
实现:
public static void main(String[] args) {
//源文件、目的文件、输入流、输出流资源创建
File src = new File("./a.txt");
File dest = new File("./b.txt");
// FileInputStream fileInputStream = null;
// FileOutputStream fileOutputStream = null;
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
try {
//我们可以把FileInputStream定义成临时变量节省资源
// fileInputStream = new FileInputStream(src);
// fileOutputStream = new FileOutputStream(dest);
inputStreamReader = new InputStreamReader( new FileInputStream(src));
outputStreamWriter = new OutputStreamWriter(new FileOutputStream(dest));
//InputStreamReader 和 OutputStreamWriter在构造器中可以指定字符集
// inputStreamReader = new InputStreamReader(new FileInputStream(src),"gbk");
// outputStreamWriter = new OutputStreamWriter(new FileOutputStream(dest),"gbk");
int length = 0;
char[] chars = new char[1024];
// //介绍一些Reader和Writer的重载方法
// //重载方法Reader
// //按字符读取
// inputStreamReader.read();
// //按字符缓存进行读取(父类Reader方法)
// inputStreamReader.read(chars);
// //按字符缓冲数组的大小进行读取(这个大小是参数,可以自定义)
// //成员变量:缓冲字符数组,从那个位置开始读取,读取多长。
// inputStreamReader.read(chars,0,chars.length);
// //重载方法Writer
// //写入整数
// outputStreamWriter.write(1);
// //写入字符串
// outputStreamWriter.write("asdfg");
// //写入字符串,从那个位置开始写入,写入多长
// outputStreamWriter.write("abcdefg",0,4);
// //写入缓冲字符数组中的数据
// outputStreamWriter.write(chars);
// //写入缓冲字符数组,从那里开始写入,写入多长
// outputStreamWriter.write(chars,0,chars.length);
while((length = inputStreamReader.read(chars,0,chars.length ))!= -1){
outputStreamWriter.write(chars);
}
//强制刷新
outputStreamWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//注意,如果我们还创建了FileInputSream,按照创建顺序的相反顺序关闭
inputStreamReader.close();
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
为什么已经有了字符流Reader / Writer,要这个处理流 InputStreamReader / OutputStreamReader 干嘛?就为了给定一个字符编码?
现在处理的都是文件处理,但是在开发过程中,更多的是接收网络端源的数据,不是字节就是字符,可能是纯文本。使用传输更多的就是字节流。那么如果我们知道传输的数据是纯文本,我们是不是就可以使用到这个处理流来提高效率了!