文件与IO
start time:2020年10月26日
01 文件File类
- 程序代码和文件目录的关系:主要就是对文件和目录进行增删改查,俗称CRUD;简单了解IO,即Input/Output:
- 把内存的中数据存储到持久化设备到上的动作称为输出,Output操作;
- 把持久化设备的数据读取到内存中的动作称为输入,Input操作;
- 一般把输入和输出的动作称为IO操作,IO也分网络IO、文件IO;
- java文件类
File:主要是计算机文件和目录的操作,即对文件的和目录的增删改查; File类表示磁盘中存在的文件和目录;File类的包名是java.io,也实现了Serializable,Comparable两大接口以便于其对象可序列化和比较;
package java.io;
public class File implements Serializable, Comparable<File> {}
-
File.separator目录分隔符,在不同的系统下不一样,windows和mac/Linux; -
常见的构造函数:
// Creates a new File instance by converting the given pathname string into an abstract pathname.
public File(String pathname) {}
// Creates a new File instance from a parent pathname string and a child pathname string.
public File(String parent, String child) {}
- 常见的方法:
public class FileTest {
public static void main(String[] args) {
String dir = "F:\\00-harvest";
String fileName = "test.txt";
// File file = new File(dir, fileName);
File file = new File(dir);
System.out.println("文件分隔符: " + File.separator); // 文件分隔符: \
System.out.println("基本路径: " + file.getPath()); // 基本路径: F:\00-harvest
System.out.println("文件名: " + file.getName()); // 文件名: 00-harvest
System.out.println("绝对路径: " + file.getAbsolutePath()); // 绝对路径: F:\00-harvest
System.out.println("父路径: " + file.getParent()); // 父路径: F:\
System.out.println("是否绝对路径: " + file.isAbsolute()); // 是否绝对路径: true
System.out.println("是否一个目录: " + file.isDirectory()); // 是否一个目录: true
System.out.println("文件是否存在: " + file.exists()); // 文件是否存在: true
System.out.println("目录中的文件和目录的名称所组成字符串数组list");
/*
* 输出:
* 00 学习书籍
* 01 小滴class
* 02 运维&容器
* 2020年8月17日.md
* ......
*/
String[] list = file.list();
for (String str : list) {
System.out.println(str);
}
// 创建指定目录
File mkdirFile = new File(dir + "\\testdir");
mkdirFile.mkdir();
// 创建多个层级目录
File mkdirsFile = new File(dir + "\\testdir\\test");
mkdirsFile.mkdirs();
// 创建一个新的文件
File newFile = new File(dir + "\\testdir\\test.txt");
try {
newFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
// // 删除文件
// newFile.delete();
}
}
note:删除文件;当前文件必须是最终的文件才可以删除,即如果是文件夹,需要把文件夹里面的内容删除完后才能删除文件夹。
note:File的构造函数只是创建一个File实例,即使目录错误也不出报错,因为没对文件进行操作;
02 Input/OutputStream流
IO:Input/Ouput即输入输出;
- 输出流:程序(内存) -> 外界设备;
- 输入流:外界设备 -> 程序(内存);
处理数据类型分类
- 字符流:处理字符相关,如处理文本数据(如txt文件),
Reader/Writer; - 字节流: 处理字节相关,如声音或者图片等二进制,
InputStream/OutputStream;
两者区别:
- 字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节;
- 字节流可以处理几乎所有文件,字符流只能处理字符类型的数据;
- 功能不同,但是具有共性内容,通过不断抽象形成4个抽象类,抽象类下面有很多子类是具体的实现;
- 字符流
Reader/Writer - 字节流
InputStream/OutputStream
- 字符流
2.1 InputStream
InputStream是输入字节流的父类,它是一个抽象类(一般用它的子类);
int read()
// 讲解:从输入流中读取单个字节,返回0到255范围内的int字节值,字节数据可直接转换为int类型,如果已经到达流末尾而没有可用的字节,则返回-1;
int read(byte[] buf)
// 讲解:从输入流中读取一定数量的字节,并将其存储在缓冲区数组buf中,返回实际读取的字节数,如果已经到达流末尾而没有可用的字节,则返回-1;
long skip(long n)
// 讲解:从输入流中跳过并丢弃n个字节的数据;
int available()
// 讲解:返回这个流中有多少个字节数,可以把buf数组长度定为这个;
void close() throws IOException
// 讲解:关闭输入流并释放与该流关联的系统资源
抽象类InputStream用来具体实现类的创建对象,文件字节输入流,对文件数据以字节的形式进行读取操作;
- 常见子类
FileInputStream,及构造函数:
public class FileInputStream extends InputStream {}
// 传入文件地址
public FileInputStream(String name) throws FileNotFoundException {
......
}
// 传入文件对象
public FileInputStream(File file) throws FileNotFoundException {
......
}
- 使用范例
public class InputStreamTest {
public static void main(String[] args) throws IOException {
String dir = "F:\\00-harvest";
String fileName = "test.txt";
File file = new File(dir, fileName);
InputStream inputStream = new FileInputStream(file);
// testRead(inputStream);
// testSkip(inputStream);
testReadByteArr(inputStream);
}
/**
* “F:\00-harvest\test.txt”中的内容为:
* hello world!
* 2020年10月26日16:32:06
*/
private static void testRead(InputStream inputStream) throws IOException {
// 对于汉字等unicode中的字符则不能正常读取,只能以乱码的形式显示;
int read = inputStream.read();
System.out.println(read);
System.out.println((char) read);
}
private static void testSkip(InputStream inputStream) throws IOException {
long skipSize = inputStream.skip(2);
System.out.println(skipSize);
int read = inputStream.read();
System.out.println(read);
System.out.println((char) read);
}
private static void testReadByteArr(InputStream inputStream) throws IOException {
// 如果buf的长度为0,则不读取任何字节并返回0;每次读取的字节数最多等于buf的长度;
byte[] buf = new byte[1024];
int length;
/*
循环读取文件内容,输入流中将最多buf.length个字节的数据读入到一个buf数组中,返回类型是读取到的字节数;
如果这个缓冲区没有满的话,则返回真实的字节个数;
当文件读取到结尾时返回-1,循环结束;
*/
while ((length = inputStream.read(buf)) != -1) {
// System.out.println(new String(buf, 0, length));
// 遇到乱码问题,解决方案是保证编码一致;
System.out.println(new String(buf, 0, length, "UTF-8"));
}
inputStream.close();
}
}
编码小知识(节省空间):
- 操作的中文内容多则推荐GBK:GBK中英文也是两个字节,用GBK节省了空间;
- UTF-8编码的中文使用了三个字节,如果是英文内容多则推荐UFT-8:因为UFT-8里面英文只占一个字节,UTF-8编码的中文使用了三个字节;
2.2 OutputStream
OutputStream是输出字节流的父类,它是一个抽象类;
void write(int b)
// 讲解:将指定的字节写入输出流;
void write(byte[] b)throws IOException
// 讲解:将b.length个字节的byte数组写入当前输出流;
void flush() throws IOException
// 讲解:write是写到缓冲区中,可以认为是内存中,当缓冲区满时系统会自动将缓冲区的内容写入文件,
// 但是一般还有一部分有可能会留在内存这个缓冲区中,所以需要调用flush清空缓冲区数据;
void close() throws IOException
// 讲解:关闭输入流并释放与该流关联的系统资源;
抽象类OutputStream用来具体实现类的创建对象,文件字节输出流,对文件数据以字节的形式进行输出的操作;
- 常见子类
FileOutputStream,及构造函数:
public class FileOutputStream extends OutputStream {
// 传入输出文件地址
public FileOutputStream(String name)
// 传入目标输出的文件对象
public FileOutputStream(File file)
// 传入目标输出的文件对象,是否可以追加内容
public FileOutputStream(File file, boolean append)
}
- 使用范例
public class OutputStreamTest {
public static void main(String[] args) throws IOException {
String dir = "F:\\00-harvest";
String fileName = "test.txt";
String target = "abc.txt";
File file = new File(dir, fileName);
InputStream inputStream = new FileInputStream(file);
// 会自动创建文件,但是不会创建多级目录下的文件;
OutputStream outputStream = new FileOutputStream(dir + "\\" + target);
// 不覆盖文件,追加数据;
// OutputStream outputStream = new FileOutputStream(dir + target, true);
// testOut(inputStream, outputStream);
testOutBuf(inputStream, outputStream);
}
// 单个字节读取,中文会有乱码问题
private static void testOut(InputStream inputStream, OutputStream outputStream) throws IOException {
int value = 0;
while (value != -1) {
value = inputStream.read();
outputStream.write(value);
}
// 最后记得关闭流
inputStream.close();
outputStream.close();
}
private static void testOutBuf(InputStream inputStream, OutputStream outputStream) throws IOException {
byte[] buf = new byte[1024];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
inputStream.close();
outputStream.close();
}
}
2.3 BufferInputStream/BufferOutputStream
什么是缓冲Buffer?
- 它是内存空间的一部分,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分空间就叫做缓冲区,缓冲区是具有一定大小的;
为啥要用缓冲?
- 缓冲,缓和冲击,例如操作磁盘比内存慢的很多,所以不用缓冲区效率很低;
- 数据传输速度和数据处理的速度存在不平衡,比如你每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两个事件,用buffer暂存起来,变成每10秒写一次硬盘,数据可以直接送往缓冲区,高速设备不用再等待低速设备,对系统的冲击就很小,写入效率高了;(通过减少和磁盘的IO来提升效率-ddh 2020年10月26日17:56:14)
Java IO包里面的两个缓冲类(高级流)
BufferInputStream和BufferOutputStream- 采用包装设计模式(锦上添花的意思)(即在外包装一层,使得功能增强-ddh 2020年10月26日17:57:31)
BufferInputStream缓冲字节输入流:
-
BufferedInputStream通过预先读入一整段原始输入流数据至缓冲区中,而外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取; -
好处:避免了大量的磁盘IO,原始的
InputStream类实现的read是即时读取的,每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),如果数据量巨大,这样的磁盘消耗非常可怕; -
缓冲区的实现: 读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,下一次读取一般情况就直接可以从缓冲区读取,减少了磁盘IO;
-
默认缓冲区大小是8k;
int DEFAULT_BUFFER_SIZE = 8192; -
BufferedInputStream,及构造函数:
public class BufferedInputStream extends FilterInputStream {
// 默认缓冲区大小
private static int DEFAULT_BUFFER_SIZE = 8192;
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
- 常用的方法
// 从输入流中读取一个字节
public int read();
// 从字节输入流中给定偏移量处开始将各字节读取到指定的byte数组中
public int read(byte[] buf,int off,int len);
// 关闭释放资源,关闭的这个流即可,InputStream会在里面被关闭
void close();
- BufferedOutputStream,及构造函数:
public class BufferedOutputStream extends FilterOutputStream {
protected byte buf[];
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
- 常用的方法
// 向输出流中输出一个字节
public void write(int b);
// 将指定byte数组中从偏移量off开始的len个字节写入缓冲的输出流
public void write(byte[] buf,int off,int len);
// 刷新此缓冲的输出流,强制使所有缓冲的输出字节被写出到底层输出流中
public void flush();
// 关闭释放资源,关闭这个流即可,OutputStream会在里面被关闭,JDK7新特性try(在这里声明的会自动关闭){}
void close();
- 缓冲输入输出流-Java文件拷贝范例
public class BufferedStreamTest {
public static void main(String[] args) throws IOException {
String dir = "F:\\00-harvest";
String fileName = "test.txt";
String target = "b.txt";
File file = new File(dir, fileName);
InputStream inputStream = new FileInputStream(file);
// 会自动创建文件,但是不会创建多级目录下的文件;
OutputStream outputStream = new FileOutputStream(dir + "\\" + target);
// 包装了一层FileInput/OutputStream
BufferedInputStream bis = new BufferedInputStream(inputStream);
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
int size;
byte[] buf = new byte[1024];
while ((size = bis.read(buf)) != -1) {
bos.write(buf, 0, size);
}
// 刷新此缓冲的输出流,才可以保证数据全部输出完成,close会自动关闭;
// 如果缓存区是8k,缓存区满了就会写入到磁盘,如果只剩6k数据,则需要用flush把剩余的数据写入磁盘
// bos.flush();
bis.close();
bos.close();
}
}
- 注意:BufferedOutputStream在close()时会自动flush
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
- BufferedOutputStream在不调用close()的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush;
- 流的关闭顺序:后开先关,如果A依赖B,先关闭B;
2.4 ObjectInputStream/ObjectOutputStream(保存对象)
ObjectOutputStreamTest
public class ObjectOutputStreamTest {
public static void main(String[] args) {
String dir = "F:\\00-harvest";
String target = "object.txt";
File file = new File(dir, target);
User user = new User();
user.setAge(18);
user.setName("dawson");
Order order = new Order();
order.setId(1);
order.setNumber("abc");
order.setPrice(10.1);
List<Object> objectList = new ArrayList<>();
objectList.add(user);
objectList.add(order);
FileOutputStream out;
try {
out = new FileOutputStream(file);
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(objectList);
objOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStreamTest
public class ObjectInputStreamTest {
public static void main(String[] args) {
String dir = "F:\\00-harvest";
String target = "object.txt";
File file = new File(dir, target);
List<Object> objectList;
FileInputStream in;
try {
in = new FileInputStream(file);
ObjectInputStream objIn = new ObjectInputStream(in);
objectList = (List<Object>) objIn.readObject();
System.out.println(objectList.get(0).toString());
System.out.println(objectList.get(1).toString());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
User{age=18, name='dawson'}
Order{id=1, number='abc', price=10.1}
2.5 获取目录下的所有文件
GetFileByTimeTest
/**
* @author ddhhdd
* @date 2020/10/27 17:16
*/
public class GetFileByTimeTest {
public static void main(String[] args) {
String path="F:\\00-harvest";
List<File> fileList = getFileSort(path);
for (File file : fileList) {
System.out.println(file.getName());
}
}
/**
* 获取目录下的所有文件(按时间排序)
* @param path
* @return
*/
private static List<File> getFileSort(String path) {
List<File> fileList = getFiles(path, new ArrayList<>());
if (fileList != null && fileList.size() > 0) {
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File file, File newFile) {
// 大于,升序;小于,降序;
if (file.lastModified() > newFile.lastModified()) {
return 1;
} else if (file.lastModified() == newFile.lastModified()) {
return 0;
} else {
return -1;
}
}
});
}
return fileList;
}
/**
* 获取目录下的所有文件
* @param path
* @param fileList
* @return
*/
private static List<File> getFiles(String path, List<File> fileList) {
File file = new File(path);
if (file.isDirectory()) {
File[] subFiles = file.listFiles();
for (File f : subFiles) {
if (f.isDirectory()) {
getFiles(f.getAbsolutePath(), fileList);
} else {
fileList.add(f);
}
}
}
return fileList;
}
}
Comparator
public interface Comparator<T> {
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
/
}

浙公网安备 33010602011771号