今日内容
上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili
同步笔记沐沐霸的博客_CSDN博客-Java2301
零、 复习昨日
一、作业
二、缓冲流
三、字符流
四、缓冲字符流
五、匿名内部类
零、 复习昨日
File: 通过路径代表一个文件或目录
方法: 创建型,查找类,判断类,其他IO
- 输入& 输出
- 字节&字符
try-catch代码
一、作业
给定路径删除该文件夹
public static void main(String[] args) {
deleteDir(new File("E:\\A"));
}
// 删除文件夹
public static void deleteDir(File file) {
File[] files = file.listFiles( );
for (File file1 : files) {
if(file1.isFile()) { // 如果是文件,直接删除
file1.delete();
} else {
deleteDir(file1);
}
}
file.delete(); // 删除空文件夹
}
二、缓冲字节流
演示: 拷贝一首歌
public static void main(String[] args) throws Exception {
// 拷贝歌曲,耗时 183372毫秒
long begin = System.currentTimeMillis( );
FileInputStream fis = new FileInputStream("E:\\Angel.mp3");
FileOutputStream fos = new FileOutputStream("E:\\Angel2.mp3");
int b = -1;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
long end = System.currentTimeMillis( );
System.out.println("拷贝歌曲,耗时 " + (end - begin) + "毫秒" );
}
很慢很慢~~~
原因: 一次读写一个字节,但是歌曲10M有1100万多个字节…
那么,如果可以一次读多个,写多个不就快了吗?! 是!! 那就是我们的缓冲区字节流
缓冲区字节输入流 BufferedInputStream,缓冲区字节输出流 BufferedOutputStream
之所以快,是因为它们内部有一个缓冲区数组
(长度8192),在一次读取或者写出的时候通过数组完成,即一次读取或者写出多个使用缓存区输入/输出流,需要给构造方法传入对应输入/输出流
public static void main(String[] args) throws Exception {
// 拷贝歌曲,耗时 183372毫秒
long begin = System.currentTimeMillis( );
FileInputStream fis = new FileInputStream("E:\\Angel.mp3");
FileOutputStream fos = new FileOutputStream("E:\\Angel2.mp3");
// 创建缓冲区输入.输出流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int b = -1;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
// 内部是数组传输数据,最后一次输出数据时,数组不一定装满
// 如果执行close,会关流的同时会强制刷新剩余数据输出
// 也可以执行flush手动刷新
// bos.flush();
long end = System.currentTimeMillis( );
System.out.println("拷贝歌曲,耗时 " + (end - begin) + "毫秒" );
}
三、字符流
字节流适合读取二进制文件,读取字符数据可能会乱码!
建议读取字符,采用字符流!
字符流有两个抽象父类
- Reader (字符输入流 )
- Writer (字符输出流)
一般使用其子类
- FileReader
- FileWriter
3.1 FileReader
构造方法
- FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。
- FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
方法
- int read() 读取单个字符。
读完末尾返回-1
- int read(char[] cbuf) 将字符读入数组。
- void close() 关闭该流并释放与之关联的所有资源。
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("a.txt");
int ch = -1;
while((ch = fr.read()) != -1) {
System.out.println((char)ch );
}
fr.close();
}
FileReader fr = new FileReader("a.txt");
char[] chars = new char[4]; // 创建空字符数组
fr.read(chars); // 一次读取数组长度个字符,存储到数组中
System.out.println(Arrays.toString(chars ) );
fr.close();
3.2 FileWriter
FileWriter在创建时,内部默认构造一个缓冲数组,用于一次写出多个,大小是1024字节
构造方法
- FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。
- FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
- FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
- FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
append指定成true,在原文件后面追加,指定成false,覆盖原文件
如果不知道,默认就是false
方法
- void close() 关闭此流,但要先刷新它。
- void flush() 刷新该流的缓冲。
- void write(char[] cbuf) 写入字符数组。
- void write(int c) 写入单个字符。
- void write(String str) 写入字符串。
- void write(String str, int off, int len) 写入字符串的某一部分。
public static void main(String[] args) throws Exception {
FileWriter fw = new FileWriter("a.txt");
// 写字符
fw.write('j');
// 写字符串
fw.write("java");
// 写字符数组
char[] chars = {'a','b','c'};
fw.write(chars);
// 写字符串中一部分内容
String str = "javabigdata";
fw.write(str,4,3);
// 因为有缓冲区,不关流的话有些数据无法输出
// 因为没有达到缓冲区大小
//fw.close();
// 也可以强制刷新出来
fw.flush();
}
3.3 练习 复制小说
使用 踹凯吃 来完成
public static void main(String[] args) {
long begin = System.currentTimeMillis( );
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("E:\\《雪中悍刀行》.txt");
fw = new FileWriter("E:\\血刀.txt");
int ch = -1;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
try {
fr.close();
fw.close();
} catch (IOException e) {
e.printStackTrace( );
}
}
long end = System.currentTimeMillis( );
System.out.println("拷贝小说,耗时 " + (end - begin) + "毫秒" );
}
3.4 练习
使用字符流把a文件中的数据转换后写到b文件中
要求:1) 大写转换为小写 2)小写转换为大写 3)删除数字
四、缓冲字符流
BufferedReader BufferedWriter
缓冲区字符输入输出流,内部在创建对象时会构建一个长度为8192的缓冲数组.ps: 查看构造方法源码…
BufferedReader
构造方法
- BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。
方法
- int read() 读取单个字符。
- String readLine() 读取一个文本行。
- void close() 关闭该流并释放与之关联的所有资源。
BufferedWriter
构造方法
- BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
方法
- void close() 关闭此流,但要先刷新它。
- void flush() 刷新该流的缓冲。
- void newLine() 写入一个行分隔符。
- void write(int c) 写入单个字符。
- void write(String s) 写入字符串
public static void main(String[] args) {
long begin = System.currentTimeMillis( );
FileReader fr = null;
FileWriter fw = null;
BufferedReader br = null;
BufferedWriter bw = null;
try {
fr = new FileReader("E:\\《雪中悍刀行》.txt");
fw = new FileWriter("E:\\血刀.txt");
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
int ch = -1;
// 一次读一个放入缓冲区
// while((ch = br.read()) != -1) {
// bw.write(ch);// 写一个字符
// }
String line = null;
// 一次读取一行,读取到换行终止符结束并返回,但是不包含终止符
while((line = br.readLine()) != null) {
bw.write(line); // 写一行字符串
// 写出一个换行符
// bw.write("\r\n");
bw.newLine();
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
try {
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace( );
}
}
long end = System.currentTimeMillis( );
System.out.println("拷贝小说,耗时 " + (end - begin) + "毫秒" );
}
练习
读取一个文本,按行倒着输出,即读取的第一行输出在最后一行,读取的第二行,输出在倒数第二行.
思路: 不能读完直接输出了,而是读一行,向集合中存一行.读取完毕后,倒着遍历集合即可
public static void main(String[] args) {
long begin = System.currentTimeMillis( );
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader("E:\\a.txt"));
bw = new BufferedWriter(new FileWriter("E:\\a2.txt"));
ArrayList<String> list = new ArrayList<>( );
// 读取每一行,转入集合
String line = null;
while((line = br.readLine()) != null) {
list.add(line);
}
// 倒着遍历集合
for (int i = list.size() - 1;i >= 0;i--) {
String s = list.get(i);
bw.write(s);
bw.newLine();
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
try {
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace( );
}
}
long end = System.currentTimeMillis( );
System.out.println("倒着拷贝,耗时 " + (end - begin) + "毫秒" );
}
练习
1. 将上面歌词内容存放到本地磁盘D根目录,文件命名为 `word.txt`
2. 选择合适的IO流读取word.txt文件的内容
3. 统计每个单词出现的次数(单词忽略大小写)
4. 如果出现组合单词如 `you're`按一个单词处理
5. 将统计的结果存储到本地磁盘D根目录下的`wordcount.txt`文件
【该题使用缓冲字符流更好】
wordcout.txt每行数据个数如下
you --> 9次
my --> 9次
I --> 9次
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("E:\\word.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\wordcount.txt"));
// TreeMap会有排序效果
TreeMap<String, Integer> map = new TreeMap<>( );
String line = null;
while((line = br.readLine()) != null) {
// 将每行字符串拆分成单个单词
String[] strArr = line.split(" ");
for (String s : strArr) {
// 将每个单词全部转成小写
String lowerStr = s.toLowerCase( );
// 判断是否存在
if (map.containsKey(lowerStr)) {
Integer count = map.get(lowerStr); // 如果存在,则取出
count++; // 次数+1
map.put(lowerStr,count); // 再存入
} else {
map.put(lowerStr,1); // 如果不包含,即第一次存,次数1
}
}
}
// 输出
Set<Map.Entry<String, Integer>> entrySet = map.entrySet( );
for (Map.Entry<String, Integer> entry : entrySet) {
String word = entry.getKey( );
Integer count = entry.getValue( );
bw.write(word + " ---> "+count+"次");
bw.newLine();
}
br.close();
bw.close();
}
五、IO流总结
画思维导图 https://www.processon.com/view/link/6360e893f346fb33540a61c1 访问密码:2301
六、匿名内部类
ps: 为了明天讲多线程做准备,会用到匿名内部类这个知识
思考一个问题?
假如有一个接口,现在让你创建一个接口的实现类对象,该怎么做?
换句话,有个方法,参数列表是接口,应该如何调用?
解决:
先创建一个类,实现接口,重写方法
创建对象
现在有个更简单的写法,可以不用创建类就可以实现出一个接口的实现类
// 接口
public interface USB {
void run();
}
public static void main(String[] args) {
// 有一个接口,现在让你创建一个接口的实现类对象
// new USBImpl();
// test(new USBImpl());
// 就相当于是创建了USB接口的实现类,并且重写了方法
// 这就是匿名内部类
test(new USB(){
@Override
public void run() {
System.out.println("匿名实现.." );
}
});
// 这样是将匿名内部类,取了名字叫usb
USB usb = new USB(){
@Override
public void run() {
System.out.println("匿名实现.." );
}
};
test(usb);
}
public static void test(USB usb) {
usb.run();
}
总结: 匿名内部类就是简化了创建子类对象的过程.
实战
使用匿名内部类完成. 创建TreeSet时指定比较器.