一. 文件类
java中提供了一个File类来表示一个文件或目录(文件夹),并提供了一些方法可以操作该文件
1. 文件类的常用方法
File(String pathname) | 构造方法,里面传一个路径名,用来表示一个文件 |
boolean canRead() | 判断文件是否是可读文件 |
boolean canWrite() | 判断文件是否是可写文件 |
boolean exists() | 判断文件或目录是否存在 |
String getAbsolutePath() | 获取文件的绝对路径 |
String getName() | 获取文件名 |
String getParent() | 获取文件的上级目录 |
boolean isDirectory() | 判断是不是文件夹 |
boolean isFile() | 判断是不是文件 |
Long lastModified() | 返回文件最后一次的修改时间,由于返回值是Long类型,配合Date的构造方法,可观察时间 |
Long length() | 返回文件有多少个字节 |
boolean isHidden() | 判断文件是不是隐藏文件 |
boolean delete() | 删除文件或文件夹,注意删除文件夹时必须保证文件夹是空的,否则删除失败 |
boolean mkdir() | 创建单级文件夹 |
boolean mkdirs() | 创建多级文件夹 |
2. 删除整个文件夹
public class FileDemo2 {
public static void main(String[] args) throws IOException {
//递归删除文件夹中的文件和文件夹
File f = new File("E:/download");
DeleteFile(f);
}
public static void DeleteFile(File files) {
File[] file = files.listFiles();
for (File f : file) {
if (f.isDirectory()) {
DeleteFile(f);
} else {
f.delete();//删除文件
}
}
files.delete();
}
当文件夹不为空时调用delete方法删除是不成功的,要想删除文件夹,必须保证文件夹为空,即要先删除我文件夹中的文件和文件夹,这就要使用递归来删除
二. 输入输出概念
输入输出是一个相对概念,要有一定的参照物才能说清楚到底是输入还是输出,比如:我们将文件中的内容读到java程序中,对于java程序来讲是输入,但对于硬盘上的文件来讲则是输出,所以我们一般规定,把硬盘上的文件读到java程序中是输入,从java程序中写回硬盘上是输出
三. 流的分类
1.输入流和输出流
按照数据传输的方向,流可以分为输入流和输出流
输入流:往程序中读叫输入流
输出流:从程序中往外写叫输出流
2. 字节流和字符流
从读取文件的单位不同分为字节流和字符流
字节流:是以一个个字节为单位读取文件,可以读取任意的文件,这是因为,在计算机中任何类型的数据都是以字节存储的
字符流:是以一个个字符为单位读取文件,只能读文本文件
注意:在java中有InputStream和OutputStream,Reader和Writer四个抽象类,只要是以InputStream和OutPutStream结尾的都是字节流,以Reader和Writer结尾的都是字符流
3. 节点流和处理流
根据封装类型不同流分为节点流和处理流
节点流:就是直接对数据进行读写操作的流,FileInputStream,FileOutputStream,FileReader等
处理流(包装流):对节点流进行封装,可以提高节点流对数据操作的效率,BufferReader等带Buffer的流
四. 读写文件
1. 用文件输入字节流和文件输出字节流读写文件
1.1 一个一个字节读文件
public class StreamDemo1 {
public static void main(String[] args) throws IOException {
//输入,从硬盘上把文件读入到程序
/*File file = new File("D:/demo.txt");
FileInputStream inputStream1 = new FileInputStream(file);*/
//文件输入字节流
FileInputStream inputStream = new FileInputStream("E:/demo.txt");//输入管道
//文件输出字节流
FileOutputStream outputStream = new FileOutputStream("D:/demo.txt");//输出管道
int b = 0;
while((b = inputStream.read())!=-1)
{
outputStream.write(b);
}
inputStream.close();//关闭通道
outputStream.close();
}
}
由于上述方法每次是一个一个字节去读文件,效率非常低,所以java还提供每次读一个byte数组个字节大小,数组的长度可以自己定,但不建议太大内存装不下,也不建议太小效率低
2.1 一次读一个byte数组(高效文件读写)
public class StrteamDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream("E:/demo.txt");
FileOutputStream outputStream = new FileOutputStream("D:/demo.txt");
byte[] bytes = new byte[10];
int size = 0;
while((size = inputStream.read(bytes))!=-1)
{
outputStream.write(bytes,0,size);
}
inputStream.close();
outputStream.close();
}
}
注意:当写文件时调用的是write(byte b[], int off, int len),而不是write(byte b[]),调用第二个有可能会导致最后一次数组中还留有上次的元素,导致读完后的文件和原文件不同
2. 用包装流封装节点流读取文件
public class StreamDemo3 {
public static void main(String[] args) throws IOException {
//FileInputStream 直接封装数据,称为节点流(最基础去读数据的流)
FileInputStream inputStream = new FileInputStream("E:/demo.txt");
//BufferedInputSteam封装的是一个节点流对象,可以提供缓冲功能,称为处理流/包装流
//缓冲字节输入流 默认缓冲区大小是8192个字节,可以自定义缓冲区大小
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
FileOutputStream outputStream = new FileOutputStream("D:/demo.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
byte[] bytes = new byte[10];
int size = 0;
while((size = bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,size);
}
//包装流一定要关闭,否则有可能导致数据停留在底层缓冲区
//没有真正刷新缓冲区写入到文件中
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
2.1 BufferedInputStream和BufferedOutputStream读写文件底层
在包装流的底层也提供了一个缓冲数组默认长度是8192个字节,当我们去读文件时,如果一次读的字节个数比底层的缓冲数组少,那么他不会直接将读到的内容写入到文件中,而是先放到缓冲数组中,等到底层缓冲数组满时,才会写入到文件中,这样可以大大提高效率,这也是包装流的作用,但如果我们自己定义的byte数组比底层缓冲数组大,那么不会用到底层的缓冲数组,而是直接将读到的内容写入到文件中,下面是源码
注意:
1. 包装流使用完毕后一定要关闭,否则有可能导致数据停留在底层缓冲区,没有真正刷新缓冲区写入到文件中,因为真正写入文件中是flushBuffer()方法中的write,而close()方法中调用了这个方法,如果仅仅只是上述源码,当我们最后一次读取数据时,如果缓冲数组没装满是不会调用flushBuffer(0方法,所以为了确保一定调用了flushBuffer(0方法,使用完必须调用close()方法来关闭包装流
2.底层缓冲数组可以自定义大小,在构造方法的第二个参数中
五. 对文件进行分割与合并
/*
写一个方法,将feige.exe文件分割为每份1MB大小的若干份(最后一份可以不满1MB),
存储在一个temp的文件夹中(每份文件名自己定义,例如1.temp 2.temp),
然后再写一个方法,将temp文件夹中的若干份合并为一个文件fg.exe
*/
public class HomeWork4 {
public static void main(String[] args) throws IOException {
File file1 = new File("E:/feige.exe");
File file2 = new File("E:/temp");
SplitFile(file1);
MergeFile(file2);
}
public static void SplitFile(File file) throws IOException {
File file1 = new File("E:/temp");
if(!file1.exists())
{
file1.mkdir();
}
FileInputStream inputStream = new FileInputStream(file);
int length = (int)Math.ceil(file.length()/(1024*1024*1.0));
int size = 0;
byte[] bytes = new byte[1024];
for (int i = 0; i < length; i++) {
FileOutputStream outputStream = new FileOutputStream("E:/temp/"+(i+1)+".temp");
for (int j = 0; j <1024; j++) {
if((size = inputStream.read(bytes))!=-1)
{
outputStream.write(bytes,0,size);
}
}
outputStream.close();
}
inputStream.close();
}
public static void MergeFile(File file) throws IOException {
File file1 = new File("E:/Merge");
if(!file1.exists()){
file1.mkdir();
}
File[] files = file.listFiles();
int size = 0;
byte[] bytes = new byte[1024];
FileOutputStream outputStream = new FileOutputStream("E:/Merge/fg.exe");
for(File file2 : files){
FileInputStream inputStream = new FileInputStream(file2);
while((size = inputStream.read(bytes))!=-1)
{
outputStream.write(bytes,0,size);
}
inputStream.close();
}
outputStream.close();
}
}
六. 数据输入输出字节流
数据输入输出字节流即:DataInputStream和DataOutputStream,他们除了是字节流,同时也是包装流(处理流),用于对节点流进行便捷处理的流
DataInputStream | readUTF() | 直接将读到的数据转为字符串形式,不用自己将字节数组转换成字符串 |
DataOutputStream | writeUFT(String s) | 直接将数据以字符串形式写出,不用自己将字节数组转换成字符串 |