File类及常用方法
文件和文件夹都是用File代表,File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
创建一个文件对象
先了解File类的三个构造方法:
-
File(String pathname)
:通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 -
File(String parent, String child)
:根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 -
File(File parent, String child)
:根据 parent 抽象路径名和 child 路径名字符串创建一个新File
实例。
使用绝对路径或者相对路径创建File对象
package file;
import java.io.File;
public class TestFile {
public static void main(String[] args) {
// 绝对路径
File f1 = new File("d:\\newDirectory");
System.out.println("f1的绝对路径:" + f1.getAbsolutePath()); // d:\newDirectory\
// 相对路径,相对于工作目录,如果在Idea中,就是项目目录
File f2 = new File("a.txt");
System.out.println("f2的绝对路径:" + f2.getAbsolutePath()); // 项目目录\a.txt
File f3 = new File("d:\\newDirectory", "a.txt");
System.out.println("f3的绝对路径:" + f3.getAbsolutePath()); // d:\newDirectory\a.txt
// 把f1作为父目录创建文件对象
File f4 = new File(f1, "a.txt");
System.out.println("f3的绝对路径:" + f3.getAbsolutePath()); // d:\newDirectory\a.txt
}
}
File类中常用方法
获取功能的方法
public String getAbsolutePath()
:返回此 File 的绝对路径名字符串。public string getPath()
: 将此 File 转换为路径名字符串。public String getName()
: 返回由此 File 表示的文件或目录的名称。public long length()
: 返回由此 File 表示的文件的长度。
public class FileGet {
public static void main(String[] args) {
File f = new File("e:\\beifen\\bf.sql");
System.out.println("文件绝对路径:"+ f.getAbsolutePath());
System.out.println("文件构造路径: "+ f.getPath());
System.out.println("文件名称: "+ f.getName()); // 若路径不存在,返回 0
System.out.println("文件长度: "+ f.length()+"字节");
File f2 = new File("e:\\beifen");
System.out.println("目录绝对路径:"+ f2.getAbsolutePath());
System.out.println("目录构造路径:"+ f2.getPath());
System.out.println("目录名称:"+ f2.getName());
System.out.println("目录长度: "+ f2.length()); // 文件夹没有大小概念,所以目录的length为0
}
}
判断功能的方法
public boolean exists()
:判断File表示的文件或文件夹是否存在。public boolean isDirectory()
:判断File表示的是否是一个目录路径。public boolean isFile()
:判断File表示的是否是一个文件。
public static void main(String[] args) {
File f = new File("e:\\beifen\\bf.sql");
System.out.println("该File是否存在:"+ f.exists());
System.out.println("该File是否是个目录路径:"+ f.isDirectory());
System.out.println("该File是否是个文件:"+ f.isFile());
}
创建和删除功能的方法
public boolean createNewFile()
: 当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。public boolean delete()
: 删除由此File表示的文件或目录。public boolean mkdir()
: 创建由此File表示的目录。public boolean mkdirs()
: 创建由此File表示的目录,包括任何必需但不存在的父目录。
目录的遍历
public String[] list()
: 返回-一个String数组,表示该File目录中的所有子文件或目录。public File[] listFiles()
: 返回一个File数组,表示该File目录中的所有的子文件或目录。
public static void main(String[] args) {
File dir = new File("e:\\beifen");
// 获取当前目录下的文件以及文件夹的名称
String[] names = dir.list();
for (String name : names) {
System.out.println(name);
}
// 获取当前目录下的文件及文件夹对象,只要拿到了文件对象,那么就可以获得更多信息
File[] files = dir.listFiles();
for (File file : files) {
System.out.println(file);
}
}
案例 - 文件搜索
需求:搜索 D:\beifen 目录中的 .java 文件
public static void main(String[] args) {
File f = new File("e:\\beifen");
getAllFile(f);
}
private static void getAllFile(File dir) {
System.out.println(dir); // 打印出被遍历的文件目录名称
File[] files = dir.listFiles();
for (File f : files) {
// 对遍历得到的File对象 f 进行判断,是否为文件夹
if(f.isDirectory()){
getAllFile(f);
}else{
String name = f.getName();
if(name.endsWith(".java"))
System.out.println(f);
}
}
}
文件过滤器
File
类中有对listFiles()方法还有两个重载的方法:
-
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。 -
public File[] listFiles(FileFilter filter)
:返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 -
public File[] listFiles(FilenameFilter filter)
:返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
FileFilter
java.io.FileFilter
是一个接口,是File的过滤器。 该接口的对象可以传递给 File 类的listFiles(FileFilter)
方法作为参数,接口中只有一个方法。FileFilter接口只有一个抽象方法:boolean accept(File pathname)
boolean accept(File pathname)
: 测试 pathname 是否应该包含在当前 File 目录中,符合则返回true
分析:
-
接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单。
-
accept()
方法,参数为File ,表示当前File下所有的子文件和子目录。保留住则返回 true ,过滤掉则返回false。
保留规则:
1.要么是 .java 文件。
2.要么是目录,用于继续遍历。
FilenameFilter
java.io.FilenameFilter
接口,实现此接口的类实例可用于过滤器文件名,该接口也有一个抽象方法:
boolean accept(File dir, String name)
:测试指定文件是否应该包含在某一文件列表中。
使用文件过滤器进行文件搜索
注意:FileFilter
和FilenameFilter
这两个过滤器都没有实现类,需要我们自己写实现类。
-
使用FileFilter过滤器
-
1.1 先创建一个FileFilter实现类:FileFilterImpl.java,重写accept方法。
public class FileFilterImpl implements FileFilter { @Override public boolean accept(File pathname) { if(pathname.getName().toLowerCase().endsWith(".java") || pathname.isDirectory()){ return true; } return false; // 简写形式 // return pathname.getName().toLowerCase().endsWith(".java") || pathname.isDirectory() } }
-
1.2 在main主函数中,使用public File[] listFiles(FileFilter filter)方法
public static void main(String[] args) { File f = new File("e:\\beifen"); getAllFile(f); } private static void getAllFile(File dir) { File[] files = dir.listFiles(new FileFilterImpl()); // 注意这里listFiles的参数 for (File f : files) { if(f.isDirectory()){ getAllFile(f); }else{ System.out.println(f); } } }
-
2 其中第6行可以直接写成匿名内部类来简化床架实现类的过程,如:
File[] files = dir.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().toLowerCase().endsWith(".java") || pathname.isDirectory(); } });
-
3 使用Lambda表达式简化
File[] files = dir.listFiles((File pathname) ->{ return pathname.getName().toLowerCase().endsWith(".txt") || pathname.isDirectory(); });
-
-
使用FilenameFilter过滤器
-
1 也可以向上面那样创建FilenameFilter接口实现类,重写accept()方法,这里不多做赘述
-
2 使用匿名内部类的简化形式做参数调用
public File[] listFiles(FilenameFilter filter)
方法,代码示例如下:public static void main(String[] args) { File f = new File("e:\\beifen"); getAllFile(f); } private static void getAllFile(File dir) { // 匿名内部类作为参数调用调用listFiles(FilenameFilter filter)方法 File[] files = dir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return new File(dir, name).isDirectory() || name.toLowerCase().endsWith(".java"); } }); for (File f : files) { if(f.isDirectory()){ getAllFile(f); }else{ System.out.println(f); } } }
-
3 使用Lambda表达式简化
File[] files = dir.listFiles((File d, String name) -> { return new File(d, name).isDirectory() || name.toLowerCase().endsWith(".java"); });
-
什么是流 Stream
概述
当不同的介质之间有数据交互的时候,JAVA就使用流来实现。数据源可以是文件,还可以是数据库,网络甚至是其他的程序
比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流。
- 输入流: InputStream。把数据从其他设备上读取到内存中的流
- 输出流:OutputStream。把数据从内存中写到其他设备上的流。
根据数据的类型分为:字节流 和 字符流。
- 字节流:以字节为单位,读写数据的流
- 字符流:以字符为单位,读写数据的流
(流数据[字符,字节]:1个字符 = 2个字节 1个字节 = 8个二进制位)
顶级父类们:
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输出流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输出流 Reader | 字符输出流 Writer |
文件输入/输出流
如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。
目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。
// 输入流
public static void main(String[] args) {
try {
File f = new File("e:\\beifen\\bf.sql");
// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f); // 如果 f 是个目录路径或者不存在的文件,会抛出异常
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 输出流
public static void main(String[] args) {
try {
File f = new File("e:\\beifen\\bf.sql");
FileOutputStream fis = new FileOutputStream(f);
} catch (IOException e) {
e.printStackTrace();
}
}
字节流
一切皆为字节:
一切文件数据(文本、 图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一 个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节输出流 OutputStream
java.io.OutputStream
:抽象类,是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字
节输出流的基本共性功能方法。
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length 字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流。public abstract void write(int b)
: 将指定的字节输出流。
注意:
close
方法:当完成流的操作时,必须调用此方法,释放系统资源。
FileOutputStream类
FileOutputStream
类是OutputStream
子类,文件字节输出流。
作用:把内存中的数据写入硬盘的文件中
构造方法
-
FileOutputStream(String name)
:创建一个向具有指定名称的文件中写入数据的输出文件流。目的地是一个文件的路径 -
FileOutputStream(File file)
:创建一个向指定 File 对象表示的文件中写入数据的文件输出流。目的地是一个文件 -
FileOutputStream(String name, boolean append)
:创建一个向具有指定 name 的文件中写入数据的输出文件流。 -
FileOutputStream(File file, boolean append)
:创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
写出字节数据
写入数据的原理(内存–>硬盘)
- java程序 -->JVM(java虚拟机) --> OS(操作系统) --> OS调用写数据的方法 --> 把数据写入到文件中
字节输出流的使用步骤(重点):
- 创建一个
FileOutputStream
对象,构造方法中传递写入数据的目的地 - 调用
FileOutputStream
对象中的方法write
,把数据写入到文件中 - 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
代码示例:
public static void main(String[] args) throws IOException {
// 1. 创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
FileOutputStream fos = new FileOutputStream("a.txt");
// 2. 调用FileOutputStream对象中的方法write ,把数据写入到文件中
fos.write(97); // a.txt中写入一个字符a,写数据时,会把十进制97转为二进制,
// 3. 释放资源(流使用会占用一-定的内存,使用完毕要把内存清空,提供程序的效率)
// fos.close();
// 一次写多个字节的方法
byte[] bytes = {66, 67, 68, 69};
fos.write(bytes);
fos.write(bytes, 2, 3);
fos.write("你好".getBytes());
fos.close();
}
声明方法中要抛出异常。
数据追加续写与换行
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt", true); // 第一个参数指目的地,第二个参数指是否追加写
for(int i=0; i<10; i++){
fos.write("你好".getBytes());
fos.write("\r\n".getBytes()); // 换行:windows(\r\n)、Linux(/n)、Mac(/r)
}
fos.close();
}
字节输入流 InputStream
java.io.InputStream
:抽象类,此抽象类是表示字节输入流的所有类的超类。 可以读取字节信息到内存中,它定义了字节输入流的基本共性功能方法:
public void close()
:关闭此输入流并释放与该流关联的所有系统资源。public abstract int read()
:从输入流中读取数据的下一个字节。public int read(byte[] b)
:从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
FileInputStream类
FileInputStream
类是InputStream
的子类。是文件输入流,从文件中读取字节。把硬盘文件中的数据,读取到内存中使用。
构造方法
FileInputStream(String name)
:通过打开一个到实际文件的连接来创建一个FileInputStream
,该文件通过文件系统中的路径名 name 指定。FileInputStream(File file)
: 通过打开一个到实际文件的连接来创建一个FileInputStream
,该文件通过文件系统中的 File 对象 file 指定。
读取字节数据
写入数据的原理(内存 – >硬盘)
java程序 --> JVM(java虚拟机) --> OS (操作系统) --> OS调用写数据的方法 --> 把数据写入到文件中
字节输入流的使用步骤(重点):
- 创建FileInputStream对象,构造方法中绑定要读取的数据源
- 使用FileInputStream对象中的方法read,读取文件
- 释放资源
public static void main(String[] args) throws IOException {
// 1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("a.txt");
// 2. 使用FileInputStream对象中的方法read,读取文件
int read = 0;
while((read = fis.read())!=-1){ // fis.read()=-1时表示文件读取完成
System.out.println(read);
}
// 3. 释放资源
fis.close();
}
字节流案例练习-图片复制
文件复制的步骤:
- 创建一个字节输入流对象,构造方法中绑定要读取的数据源
- 创建一个字节输出流对象,构造方法中绑定要写入的目的地
- 使用字节输入流对象中的方法read读取文件
- 使用字节输出流中的方法write ,把读取到的字节写入到目的地的文件中
- 释放资源(先关写的流,再关读的流)
字符流
当使用字节流读取文本文件时,可能会有一个小问题。 就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类 ,以字符为单位读写数据,专门用于处理文本文件。
使用字节流读取中文文件1个中文
- GBK:1个中文占用两个字节
- UTF-8:1个中文占用3个字节
字符输入流 Reader
java.io.Reader
:抽象类,是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public void close()
: 关闭此流并释放与此流相关联的任何系统资源。public int read()
: 从输入流读取一个字符。public int read(char[] cbuf)
:从输入流中读取一-些字符,并将它们存储到字符数组cbuf中。
FileReader类
java.io.FileReader
:用于读取文件的便捷类。FileReader extends InputStreamReader extends Reader
作用:把硬盘文件中的数据以字符的方式读取到内存中。
构造方法
FileReader(String fileName)
:在给定从中读取数据的文件名的情况下创建一个新 FileReader。FileReader(File file)
:在给定从中读取数据的 File 的情况下创建一个新 FileReader。
构造方法的作用:创建一个FileReader对象,并把FileReader对象指向要读取的文件。
FileReader字符输入流的使用步骤:
- 创建FileReader对象,构造方法中绑定要读取的数据源
- 使用FileReader对象中的方法 read 读取文件
- 释放资源
使用字符流读取文件:
public static void main(String[] args) {
// 准备文件a.txt其中的内容是AB
File f = new File("d:/a.txt");
// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {
// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];
// 以字符流的形式读取文件所有内容
fr.read(all);
for (char b : all) {
System.out.println(b);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
字符输出流 Writer
FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件
public static void main(String[] args) {
// 准备文件a.txt
File f = new File("a.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {
// 以字符流的形式把数据写入到文件中
String data="abcdefg1234567890";
char[] cs = data.toCharArray();
fr.write(cs);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
练习-文件加密
准备一个文本文件(非二进制),其中包含ASCII码的字符和中文字符。
设计一个方法
public static void encodeFile(File encodingFile, File encodedFile);
在这个方法中把encodingFile的内容进行加密,然后保存到encodedFile文件中。
加密算法:
- 数字:
如果不是9的数字,在原来的基础上加1,比如5变成6,3变成4
如果是9的数字,变成0 - 字母字符:
如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z -> a, Z -> A。
字符需要保留大小写 - 非字母字符:
比如 ’ , & ^ 保留不变,中文也保留不变
建议: 使用以前学习的练习题中的某个Java文件,比如循环练习,就有很多的字符和数字
public static void main(String[] args) {
encodeFile(new File("a.txt"),new File("b.txt"),false);
}
/**
* 重载加解密程序 默认是加密
* @param encodingFile 要加密的文件位置
* @param encodedFile 加密后的文件位置
*/
public static void encodeFile(File encodingFile, File encodedFile){
encodeFile(encodingFile,encodedFile,true);
}
/**
* 加解密程序
* @param encodingFile 要加密/解密的文件位置
* @param encodedFile 加密解密后的文件位置
* @param b 加密/解密 true为加密 false为解密
*/
public static void encodeFile(File encodingFile, File encodedFile,boolean b){
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
fileReader = new FileReader(encodingFile);
fileWriter = new FileWriter(encodedFile);
int i;
while((i = fileReader.read()) != -1){
if(b){
System.out.println(i);
int i1 = encodingNumber(i);
System.out.println(i1);
fileWriter.write(i1);
}else{
fileWriter.write(encodingNumberPlus(i));
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fileReader != null){
fileReader.close();
}
if(fileWriter != null){
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 加密逻辑
* @param i ASCII编码
* @return 返回加密后的编码
*/
public static int encodingNumber(int i){
if(i >=65 && i <= 122){
if(i >= 97 && i <= 122){
if(i == 122){
return 97;
}
return ++i;
}
if (i >= 65 && i <= 90){
if(i == 90){
return 65;
}
return ++i;
}
}
if(i >= 48 && i <= 57 ){
if(i == 57){
return 48;
}
return ++i;
}
return i;
}
/**
* 解密逻辑
* @param i ASCII编码
* @return 返回解密后的编码
*/
public static int encodingNumberPlus(int i){
if(i >=65 && i <= 122){
if(i >= 97 && i <= 122){
if(i == 97){
return 122;
}
return --i;
}
if (i >= 65 && i <= 90){
if(i == 65){
return 90;
}
return --i;
}
}
if(i >= 48 && i <= 57 ){
if(i == 48){
return 57;
}
return --i;
}
return i;
}
Properties类
java.util.Propreties
:Properties
类表示了一个持久的属性集。Properties
可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。它使用键值对结构存储数据
Properties集合是一个唯一和IO流相结合的集合。
- 可以使用Properties集合中的store()方法,把集合中临时数据,持久化写入硬盘中存储
- 可以使用Properties集合中的load()方法,把硬盘中保存的文件(键值对),读取到集合中使用
构造方法
public Properties()
:创建一个空的属性列表
基本存储方法
使用Properties集合存储数据,遍历取出Properties集合中国的数据。有一些操作字符串的特有方法:
Object setProperties(String key, String value)
:它会自动调用 Hashtable 的put()方法String getProperties(String key)
:通过key获取value,相当于Map集合中get(key)方法。Set<String> stringPropertyNames()
:返回此属性列表的键值,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet()方法。
public static void main(String[] args){
Properties properties = new Properties();
properties.setProperty("张三", "12"); // 两个参数必须都是字符串,第二个参数当做年龄来看
properties.setProperty("李四", "25");
properties.setProperty("王五", "18");
// 遍历该属性集合
Set<String> key_set = properties.stringPropertyNames(); // 获取properties对象的所有键
for (String key : key_set) {
String value = properties.getProperty(key);
System.out.println(value); // 逐行输出年龄值
}
}
store() 和 load()
把集合中的临时数据,持久化写入到硬盘文件中存储。
void store(OutputStream out, String comments)
:字节输入流,不能写入中文。以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。void store(Writer writer, String comments)
:字符输入流,可以写入中文。以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
其中:comments:注释(不能使用中文),用来解释说明保存的文件是做什么用的,其字段也会被写入至文件中,前面多个井号#
void load(InputStream inStream)
:从输入流中读取属性列表(键和元素对)。void load(Reader reader)
:按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
使用步骤:
- 创建Properties集合对象,添加数据
- 创建字节输入流/字符输入流对象,构造方法中绑定要输出的目的地文件。
- 使用Properties集合中的方法store(),把集合中的临时数据,写入硬盘中存储。查看存储的格式。
- 使用Properties集合中的方法load(),从刚才保存的文件中,把原键值对信息读取出来,放到一个新的Properties集合中。
- 释放资源
public static void main(String[] args) throws IOException {
myMethodStore();
System.out.println("------------------");
myMethodLoad();
}
private static void myMethodStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("张三", "12"); // 两个参数必须都是字符串,第二个参数当做年龄来看
prop.setProperty("李四", "25");
prop.setProperty("王五", "18");
FileWriter fw = new FileWriter("prop.txt");
prop.store(fw, "save data!");
fw.close();
}
private static void myMethodLoad() throws IOException {
Properties prop = new Properties();
prop.load(new FileReader("prop.txt"));
// 遍历。查看读取的对不对
Set<String> key_set = prop.stringPropertyNames();
for (String key : key_set) {
String value = prop.getProperty(key);
System.out.println(key + "=" +value);
}
}
/*
输出结果:
store后,prop.txt文件中的数据是:
#save data!
#Wed May 19 20:55:34 CST 2021
王五=18
张三=12
李四=25
load后,打印输出结果:(井号(#)注释的内容不会被获取)
王五=18
张三=12
李四=25
*/
缓冲流
本小节参考自:https://how2j.cn/k/io/io-bufferedstream/342.html#nowhere
以介质是硬盘为例,字节流和字符流的弊端:
- 在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
为了解决以上弊端,采用缓存流。
-
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲
缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
缓冲流也叫高效流,是对四个基本FileXxx流的增强,所以也有4个流,按照类型分类:
- 字节缓冲流:BufferedInputStream,BufferedOutputStream
- 字符缓冲流:
BufferedReader
、PrintWriter
使用缓存流读取数据
缓存字符输入流 BufferedReader
可以一次读取一行数据
public static void main(String[] args) {
// 准备文件a.txt其中的内容是
// garen kill teemo
// teemo revive after 1 minutes
// teemo try to garen, but killed again
File f = new File("a.txt");
// 创建文件字符流
// 缓存流必须建立在一个存在的流的基础上
try (
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
)
{
while (true) {
// 一次读一行
String line = br.readLine();
if (null == line)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
使用缓存流写出数据
PrintWriter
缓存字符输出流, 可以一次写出一行数据
public static void main(String[] args) {
// 向文件a2.txt中写入三行语句
File f = new File("a2.txt");
try (
// 创建文件字符流
FileWriter fw = new FileWriter(f);
// 缓存流必须建立在一个存在的流的基础上
PrintWriter pw = new PrintWriter(fw);
) {
pw.println("garen kill teemo");
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
flush
有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush
public class TestStream {
public static void main(String[] args) {
//向文件a2.txt中写入三行语句
File f =new File("a2.txt");
//创建文件字符流
//缓存流必须建立在一个存在的流的基础上
try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
pw.println("garen kill teemo");
//强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.flush();
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
练习-移除注释
设计一个方法,用于移除Java文件中的注释
public void removeComments(File javaFile)
比如,移出以//开头的注释行
File f = new File("a.exe");
System.out.println("当前文件是:" + f);
//文件是否存在
System.out.println("判断是否存在:"+ f.exists());
//是否是文件夹
System.out.println("判断是否是文件夹:"+ f.isDirectory());
注: 如果注释在后面,或者是/**/风格的注释,暂不用处理
public class Main{
public static void main(String[] args){
String filename = args[0];
System.out.println(filename);
String newFilename = "new_"+filename;
File fread = new File(filename);
File fwrite = new File(newFilename);
try(
FileReader fr = new FileReader(fread);
FileWriter fw = new FileWriter(fwrite);
BufferedReader bfr = new BufferedReader(fr);
PrintWriter bfw = new PrintWriter(fw);
){
while(true){
String line = bfr.readLine();
if(null==line)
break;
if(!line.startsWith("//")){
bfw.println(line);
}
}
bfw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口
序列化一个对象
ObjectOutputStream
:对象输出流ObjectInputStream
:对象输入流
创建一个Hero对象,设置其名称为garen。
把该对象序列化到一个文件garen.lol。
然后再通过序列化把该文件转换为一个Hero对象
**注:**把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable
接口。
public class TestStream {
public static void main(String[] args) {
//创建一个Hero garen
//要把Hero对象直接保存在文件上,务必让Hero类实现Serializable接口
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
//准备一个文件用于保存该对象
File f =new File("d:/garen.lol");
try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
) {
oos.writeObject(h);
Hero h2 = (Hero) ois.readObject();
System.out.println(h2.name);
System.out.println(h2.hp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// Hero.java文件
public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;
}
练习-序列化数组
准备一个长度是10,类型是Hero的数组,使用10个Hero对象初始化该数组
然后把该数组序列化到一个文件heros.lol
接着使用ObjectInputStream 读取该文件,并转换为Hero数组,验证该数组中的内容,是否和序列化之前一样
public class ObjectIO {
public static void main(String[] args) {
//创建长度为10的Hero数组,并且进行初始化
Hero[] heroes = new Hero[10];
for (int i=0; i<10; i++) {
Hero hero = new Hero(i);
heroes[i] = hero;
}
//创建对象流
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("Heros.txt"));
ois = new ObjectInputStream(new FileInputStream("Heros.txt"));
//将hero数组写入文件
oos.writeObject(heroes);
//进行读文件操作
Hero[] getHeros = (Hero[]) ois.readObject();
for (Hero hero: getHeros) {
System.out.println(hero);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}