1.缓冲流(高效流,比普通流性能更高)
2.转换流(编码相关的流,指定编码)
3.序列化流(操作对象)
4.打印流(System.out.println())
5.设计模式(装饰设计模式,4个步骤)
6.common-io工具包(简化IO的代码)
一。缓冲流
缓冲流也叫高效流,对以前学过的四个基本流的增强(性能,方法上基本一模一样)
优点:
1)适用于读写文件容量大的文件;
2)在标准字节流的基础上,增加缓冲流;
3)通过改变缓冲区大小或字节数组大小来进行优化;
4)优化指标,程序执行次数或执行时间;
缓冲流的分类:
缓冲字节输入流:BufferedInputStream ---> 对普通的字节输入流InputStream的增强
缓冲字节输出流:BufferedOutputStream ---> 对普通的字节输出流OutputStream的增强
缓冲字符输入流: BufferedReader ---> 对普通的字符输入流Reader的增强
缓冲字符输出流: BufferedWriter ---> 对普通的字符输出流Writer的增强
.字节缓冲流的介绍和使用
构造方法:
public BufferedInputStream(InputStream in);//缓冲流的构造需要接收对应普通流
public BufferedOutputStream(OutputStream out);//缓冲流的构造需要接收对应普通流
字符缓冲流的高效测试
public class BufferedDemo01 {
public static void main(String[] args) throws Exception {
copy01();
}
//缓冲流复制 耗时:950毫秒
public static void copy01() throws Exception {
//创建缓冲流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\upload\\666.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy01.png"));
//复制文件
long start = System.currentTimeMillis();
int b = 0;
while ((b = bis.read()) != -1) {
bos.write(b);
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"毫秒");
//释放资源
bos.close();
bis.close();
}
//普通流复制 半天过去了...
public static void copy02() throws IOException {
//创建普通字节流
FileInputStream fis = new FileInputStream("G:\\upload\\666.png");
FileOutputStream fos = new FileOutputStream("copy.png");
long start = System.currentTimeMillis();
//复制,一次一个字节
int b = 0;
while ((b = fis.read()) != -1) {
fos.write(b);
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"毫秒");
//释放资源
fos.close();
fis.close();
}
}
思考: 有没有更快的方式 ??
有! 使用缓冲流的同时,使用一次读取一个字节数组的方式!!!
字符缓冲流的介绍和使用
构造方法“
public BufferedWriter(Writer w);//需要传入普通的字符流
public BufferedReader(Reader r);//需要传入普通的字符流
字符缓冲流也是对普通字符类的性能增强
字符缓冲流的2个特有方法
BufferedWriter 是 普通Writer的增强
多了一个特有方法: 写一个换行符(根据系统而定)
public void newLine();
public class BufferedDemo02 {
public static void main(String[] args) throws IOException {
//缓冲字符流
BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt"));
//写数据
for (int i = 0; i < 10; i++) {
bw.write("java");
//写入一个换行
bw.newLine();
}
//释放资源
bw.close();
//关流的原则:
//a.先开后关
//b.流用完了就立刻关闭
}
}
BufferedReader 是 普通Reader的增强
多个一个特有方法: 一次读取一行内容
public String readLine();
public static void main(String[] args) throws IOException {
//缓冲字符流
BufferedReader br = new BufferedReader(new FileReader("2.txt"));
//读数据
//String line = br.readLine();
//System.out.println(line);
//=============一次读取一行的标准循环代码===============
String line = ""; //用来保存每次读取的一行数据
/**
* (line = br.readLine()) != null
* 以上代码干了三件事!
* a.读取 br.readLine()
* b.赋值 line = 读到的一行数据
* c.判断 line != null
*/
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//释放资源
br.close();
}
注意:
一次读取一行的标准循环,不会因为有一行为"null"字符串内容或者有一行为""字符串内容而停止
只有读取到文件的末尾,没有内容返回值null才能停止!!
综合练习:文本排序
读取文本中的内容,按照序号排序,排序之后从头到尾写入新的文件中
public class TestDemo {
public static void main(String[] args) throws Exception {
//读取文本中的内容,按照序号排序,排序之后从头到尾写入新的文件中
ArrayList<String> arr = new ArrayList<String>();
//1.读取文本中的内容
BufferedReader br = new BufferedReader(new FileReader("csb.txt"));
//2.一次读取一行
String line = "";
while ((line = br.readLine()) != null) {
arr.add(line);
}
//3.释放资源
br.close();
//4.对集合排序
// Collections.sort(arr, new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// //按照字符串的首字母进行升序
// return o1.charAt(0) - o2.charAt(0);
// }
// });
Collections.sort(arr, (o1, o2) -> o1.charAt(0) - o2.charAt(0));
//5.写入到新文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("new.txt"));
for (String s : arr) {
bw.write(s);
bw.newLine();//一句话一行
}
bw.close();
}
}
二。转换流
编码和解码
编码: 把字符 ----> 字节 ,比如 'a' ---> 97(0110 0001)
解码: 把字节 ----> 字符 ,比如 97(0110 0001) ---> 'a'
字符集
字符集: 一个系统支持的所有字符的集合(文字,标点,数字,图形符号等)
字符编码
字符编码: 字符和二进制一一对应的一套规则,比如 字符'a' 对应的码值 97
常见的字符集和字符编码
ASCII字符集 ---> ASCII编码, 规定ASCII字符集中所有的字符都占1个字节
GBK字符集 ---> GBK编码,规定所有中文字符都占2个字节(这两个字节都是负数)
Unicode字符集 ---> UTF-8编码,规定所有中文字符都占3个字节
ISO-8859-1字符集.这是西欧国家的字符集,(我们以后使用Tomcat7以前默认就是使用ISO-8859-1)
编码引出的问题
IDEA默认使用UTF-8编码,Windows默认使用的GBK编码
public class TestLuanMaDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("GBK.txt");//文件用的GBK编码,一个中文两个字节
int ch = fr.read(); //IDEA默认UTF-8编码,一个中文3个字节
System.out.println((char)ch);
fr.close();
}
}
以上问题会导致乱码
出现这个问题了,怎么办??
a.把文件的编码改成UTF-8和IDEA一致
b.把IDEA的编码改成GBK和文件的一致
使用转换流InputStreamReader解决读取中文的问题
转换输入流 InputStreamReader extends Reader
构造方法
public InputStreamReader(InputStream in,String charsetName);//指定使用何种编码读文件
public InputStreamReader(InputStream in);//使用工具默认的编码去读文件(IDEA默认UTF-8)
使用InputStreamReader读取不同编码的文件
public class TestInputStreamReaderDemo {
public static void main(String[] args) throws Exception {
//1.创建InputStreamReader对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8");
// InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
// InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"GBK");
//2.读数据
int ch = isr.read();
System.out.println((char) ch);
ch = isr.read();
System.out.println((char) ch);
ch = isr.read();
System.out.println((char) ch);
//3.释放资源
isr.close();
}
}
使用转换流OutputStreamWriter写不同编码的中文
转换输出流OutputStreamWriter extends Writer
构造方法
public OutputStreamWriter(OutputStream out,String charsetName);//写文件时指定编码
public OutputStreamWriter(OutputStream out);//使用工具默认的编码写文本(IDEA是UTF-8)
输出指定编码的中文
public class TestOutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//1.创建OutputStreamWriter对象
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newgbk.txt"),"GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newutf8.txt"),"UTF-8");
//2.写数据
osw.write("你好");
//3.释放资源
osw.close();
}
}
转换流的理解
转换文件编码:
需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
a.先把GBK文件中内容读取出来(指定以GBK编码读取)
b.再把数据写入到UtF-8文件中(指定以UTF-8编码写入)
public class TestDemo {
public static void main(String[] args) throws IOException {
// 需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
// 分析:
// a.先把GBK文件中内容读取出来(指定以GBK编码读取)
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
// b.再把数据写入到UtF-8文件中(指定以UTF-8编码写入)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF8.txt"),"UTF-8");
//复制
int ch = 0;
while ((ch = isr.read()) != -1) {
osw.write(ch);
}
//释放资源
osw.close();
isr.close();
}
}
三。序列化流
什么是序列化流
序列化流: 写出对象的流
ObjectOutputStream
反序列化流: 读取对象的流
ObjectInputStream
.ObjectOutputStream的介绍和使用
构造方法:
public ObjectOutputStream(OutputStream out); //需要接收一个普通的字节输出流
序列化操作的前提
想要序列化必须实现java.io.Serializable 接口
该接口中没有方法,该接口一般称为标记接口
演示:
public class TestObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//1.创建一个ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dog.txt"));
//2.写对象
Dog dd = new Dog(10,"旺财");
oos.writeObject(dd);//NotSerializableException
//3.释放资源
oos.close();
}
}
注意: 不用查看dog.txt文件,因为这个文件中的字节数据我们是看不懂的
ObjectInputStream的介绍和使用
构造方法:
public ObjectInputStream(InputStream in);
反序列化操作
public class TestObjectInputStreamDemo {
public static void main(String[] args) throws Exception {
//1.创建ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dog.txt"));
//2.读对象
Dog obj = (Dog) ois.readObject();
System.out.println(obj);
//3.释放资源
ois.close();
}
}
反序列化操作的两种异常:
a.ClassNotFoundException 没有找到类异常
出现原因:
序列化之后,反序列化之前,删除了原来序列化的那个类!!
b.InvalidClassException 无效类异常
出现原因:
序列化之后,反序列化之前,修改了原来序列化的那个类!!!
c.实际上序列化流判断类是否有效通过:serialVersionUID来识别的
3333如果需要序列化多个对象怎么操作?
注意: 序列化流一个文件只适合序列化一个对象
分析:
a.把你要序列化的多个对象,保存到一个集合对象
b.将这个集合作为对象,序列化到文件中
c.可以从文件中将整个集合反序列化回来
d.遍历集合把里面的对象一一打印出来
/**
* 如何序列化多个对象?
*/
public class TestDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
read();
}
//序列化多个对象
public static void write() throws IOException {
// 注意: 序列化流一个文件只适合序列化一个对象
// 分析:
// a.把你要序列化的多个对象,保存到一个集合对象
ArrayList<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog(1, "旺财", 4));
dogs.add(new Dog(2, "来福", 5));
dogs.add(new Dog(3, "哮天犬", 6));
// b.将这个集合作为对象,序列化到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dogs.txt"));
oos.writeObject(dogs);
oos.close();
}
public static void read() throws IOException, ClassNotFoundException {
// c.可以从文件中将整个集合反序列化回来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dogs.txt"));
ArrayList<Dog> dogs = (ArrayList<Dog>) ois.readObject();
ois.close();
// d.遍历集合把里面的对象一一打印出来
for (Dog dog : dogs) {
System.out.println(dog);
}
}
}
单一对象,无继承关系:若想实现序列化与反序列化,则必须实现序列化接口,否则报异常:NotSerializableException
对象间有继承关系,但无引用关系,若想实现序列化与反序列化,则父类必须实现序列化接口或提供无参构造函数,否则报invalidClassException
对象间有继承关系,并且有引用关系,若想实现序列化与反序列化,则父类必须实现序列化接口
存在继承关系时,若想序列化子类对象,一并序列化父类对象,则负责类型必须实现序列化接口或者提供无参构造函数
四。打印流
打印流的介绍
a.System.out.println()/print().实际上就是调用了打印流的方法
b.打印流PrintStream
c.打印流中重写各种数据类型的print和println方法,打印数据时非常方便
d.打印流不会抛出IOException(会抛出别的Exception)
2.PrintStream的构造和常用方法
构造方法
public PrintStream(String path);
public PrintStream(File file);
public PrintStream(OutputStream out);
成员方法:
public void print(各种类型); //不带换行的打印
public void println(各种类型); //带换行的打印
public class PrintStreamDemo {
public static void main(String[] args) throws Exception {
//1.创建一个打印流
PrintStream ps1 = new PrintStream("p.txt");
// PrintStream ps2 = new PrintStream(new File("p.txt"));
// PrintStream ps3 = new PrintStream(new FileOutputStream("p.txt"));
//2.打印数据,所见即所得
ps1.println('a');
ps1.println(97);
ps1.println(true);
ps1.println(100);
ps1.println("HelloWorld");
//3.释放资源
ps1.close();
}
}
修改系统打印流的流向
public class PrintStreamDemo02 {
public static void main(String[] args) throws Exception {
//1.创建一个打印流
PrintStream ps1 = new PrintStream("p.txt");
//2.系统的输出
System.out.println("Java");
//3.修改系统打印流的流向
System.setOut(ps1);
System.out.println("aJava");
System.out.println("cJava");
System.out.println("dJava");
System.out.println("eJava");
System.out.println("Java");
}
}
五。装饰设计模式
.装饰模式作用
装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
2.装饰者设计模式的4个基本步骤
-
装饰类(我们定义的新类)和被装饰类(原类)必须实现相同的接口
-
在装饰类中必须传入被装饰类的引用
-
在装饰类中对需要扩展的方法进行扩展
-
在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
.代码实现
/**
* 歌星接口
*/
public interface SingStar {
/**
* 唱歌方法
*/
void sing();
/**
* 跳舞方法
*/
void dance();
}
/**
* 被装饰类
*/
public class LiuDeHua implements SingStar{
//唱歌
public void sing(){
System.out.println("啊哈,给我一杯水..");
}
//跳舞
public void dance(){
System.out.println("蹦恰恰蹦恰...");
}
}
/**
* LiuDeHua类的装饰类
*/
//1.装饰类(我们定义的新类)和被装饰类(原类)必须实现相同的接口
public class LiuDeHuaWrapper implements SingStar{
//2.在装饰类中必须传入被装饰类的引用
private LiuDeHua ldh;
public LiuDeHuaWrapper(LiuDeHua ldh) {
this.ldh = ldh;
}
@Override
public void sing() {
//3.在装饰类中对需要扩展的方法进行扩展
System.out.println("刘德华在北京唱歌..");
ldh.sing();
}
@Override
public void dance() {
//4.在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
ldh.dance();
}
}
public class TestDemo {
public static void main(String[] args) {
//1.创建被装饰类
LiuDeHua ldh = new LiuDeHua();
//2.创建装饰类对象
LiuDeHuaWrapper wrapper = new LiuDeHuaWrapper(ldh);
//3.调用装饰类中方法
wrapper.sing();
wrapper.dance();
}
}
六。commons-io工具包
commons-io的介绍和下载
commons-io是Apache公司提供简化IO操作的工具包
我们需要把commons-io工具下载下来:
a.解压
b.在我们模块下创建lib文件夹,将解压后的 commons-io.jar复制进来
c.选中commons-io.jar右键选中Add as Libary 添加到本模块中官网地址:http://commons.apache.org/proper/commons-io/
下载 :http://commons.apache.org/proper/commons-io/download_io.cgi
工具类包括FileUtils、IOUtils、FilenameUtils和FileSystemUtils,前三者的方法并没有多大的区别,只是操作的对象不同,故名思议:FileUtils主要操作File类,IOUtils主要操作IO流,FilenameUtils则是操作文件名,FileSystemUtils包含了一些JDK没有提供的用于访问文件系统的实用方法。当前,只有一个用于读取硬盘空余空间的方法可用。
常用API介绍
-
复制文件API
-
复制文件夹API
public class TestCommonsDemo {
public static void main(String[] args) throws IOException {
//1.IOUtils 适合复制2G大小以下的文件
IOUtils.copy(new FileInputStream("G:\\up\\1546241961620.png"),new FileOutputStream("copy.png"));
//2.IOUtils适合复制2G大小以上的文件
IOUtils.copyLarge(new FileInputStream("G:\\up\\1546241961620.png"), new FileOutputStream("copy1.png"));
//3.FileUtils 复制文件
FileUtils.copyFileToDirectory(new File("G:\\up\\1546241961620.png"), new File("G:\\uploads"));
//4.FileUtils 复制文件夹
FileUtils.copyDirectoryToDirectory(new File("C:\\Users\\Administrator\\Desktop\\temp\\aaa"),new File("G:\\uploads"));
}
}
总结:
缓冲流
字节缓冲流(BufferedOutputStream和BufferedInputStream),没有特有方法,性能比普通流更高
字符缓冲流(BufferedWriter和BufferedReader),有特有方法,性能比普通流更高
BufferedWriter:
public void newLine();
BufferedReader:
public String readLine();
2.转换流
转换输出流: 可以指定编码写文件
OutputStreamWriter
public OutputStreamWriter(OutputStream out,String 指定的编码);
转换输入流: 可以指定编码读文件
InputStreamReader
public InputStreamReader(InputStream in,String 指定的编码);
3.序列化流
序列化流: 写对象
ObjectOutputStream
public void writeObject(对象);//该对象的类必须实现java.io.Serializable接口
反序列化流: 读对象
ObjectInputStream
public Object readObject();
other----
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
/*
项目根路径下有f.txt文件,内容如下:
我爱中国
123456
请利用IO流的知识读取f.txt文件的内容反转后写入f1.txt文件中,内容如下:
123456
我爱中国
*/
public class Test05 {
public static void main(String[] args) throws IOException {
// 读取f.txt文件中的内容到内存(集合中保存!)
ArrayList<String> list = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader("D:/f.txt"));
String line = null;
while ((line = br.readLine())!=null){
list.add(line);
}
// 使用集合工具类的方法
Collections.reverse(list);
// 写文件
BufferedWriter bw = new BufferedWriter(new FileWriter("D:/f1.txt"));
// 遍历集合
for (String str : list) {
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
import java.io.*;
/*
需求:利用高效字节输入流和高效字节输出流完成文件的复制。要求:
1. 将D盘下的c.png文件复制到D盘下
2. 采用一次读写一个字节数组方式复制
*/
public class Test02 {
public static void main(String[] args) throws IOException {
// 高效输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/c.png"));
// 高效输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:/c.png"));
// 读取硬盘数据到内存
byte[] buffer = new byte[1024];
// 定义变量接收读取的字节数
int len = -1;
// 循环读取数据
while ((len = bis.read(buffer))!=-1){
// 将读到内存的数据给它写到硬盘中!
bos.write(buffer,0,len);
}
// 关闭流
bis.close();
bos.close();
}
}
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/*
需求:用代码实现一个验证码小程序,要求如下:
1. 在项目根目录下新建一个文件:data.txt,键盘录入3个字符串验证码,并存入data.txt中,要求一个验证码占一行;
2. 键盘录入一个需要被校验的验证码,如果输入的验证码在data.txt中存在:在控制台提示验证成功,如果不存在控制台提示验证失败
*/
public class Test02 {
public static void main(String[] args) throws IOException {
// 1. 让用户在控制台输入3个验证码,保存到data.txt文件中
writeCodeToFile();
// 2. 让用户再输入一个验证码(校验),与data.txt文件中的验证码进行比对(校验)
verifyCode();
}
/*
让用户再输入一个验证码(校验),与data.txt文件中的验证码进行比对(校验)
*/
public static void verifyCode() throws IOException {
// 创建一个文件缓冲流对象
BufferedReader br = new BufferedReader(new FileReader("D:/data.txt"));
// 创建一个集合用于存储读取的数据
ArrayList<String> list = new ArrayList<>();
// 读数据
String line = null;
while ((line = br.readLine())!=null){
// 将读到的数据添加到集合中去
list.add(line);
}
// 让用户再输入一个验证码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入被校验的验证码:");
String code = scanner.nextLine();
// 校验
if(list.contains(code)){
System.out.println("校验成功!");
}else{
System.out.println("校验失败!");
}
}
/*
让用户在控制台输入3个验证码,保存到data.txt文件中
*/
public static void writeCodeToFile() throws IOException {
// 提供一个文件缓冲流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("D:/data.txt")));
// 键盘录入
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < 3; i++) {
System.out.println("请输入第"+(i+1) + "个验证码:");
String line = scanner.nextLine();
// 将其写入到data.txt文件中去
bw.write(line);
// 换行
bw.newLine();
}
// 关闭流
bw.close();
}
}