给普通的IO流, 套上一个缓冲区。 所有的使用缓冲流进行的读写操作, 都是和缓冲区进行交互的, 避免了频繁的IO操作。 这样一来, 带来的好处就是可以提高读写的效率。 这个缓冲区, 其实是一个数组。
为了提高读写的能力,本身没有读写的能力,要想进行读写就必须借助于字符流/字节流实现.
BufferedInputStream :
缓冲字节输入流BufferedOutputStream :
缓冲字节输出流BufferedReader :
缓冲字符输入流BufferedWriter :
缓冲字符输出流使用缓冲流实现读写的步骤与字符流一样,只是需要我们先通过构造方法传入一个字符流对象.同时缓冲流可以提高读写效率
大家在使用流读写数据时,尽量使用缓冲流,缓冲流中尽量使用缓冲字符流,在字符缓冲流中比缓冲字节流多了readLine()
和newLine()
方法.
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException
public class BufferedInputStreamTest {
public static void main(String[] args) {
// 过程和InputStream一模一样的
// 缓冲字节输入流是需要基于一个字节输入流来进行实例化的
// 在这里,BufferedInputStream构造方法中的InputStream对象,只是用来做当前的对象的实例化,在使用结束的时候,理论上来讲,是需要关闭的
// 实际在使用中,使用结束后,只需要关闭BufferedInputStream即可。
try (BufferedInputStream bufferedInputStream = new
BufferedInputStream(new FileInputStream("file\\day26\\source")))
{
// 1. 实例化一个字节数组
byte[] array = new byte[1024];
// 2. 声明一个整型变量,用来记录每次读取了多少个字节数据
int length = 0;
// 3. 循环读取
while ((length = bufferedInputStream.read(array)) != -1) {
// 4. 将读取到的数据转成字符串输出到控制台
String msg = new String(array, 0, length);
System.out.println(msg);
catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamTest {
public static void main(String[] args) {
// 1. 实例化一个缓冲字节输出流对象
try (BufferedOutputStream bufferedOutputStream = new
BufferedOutputStream(new
FileOutputStream("file\\day26\\target"))) {
// 2. 将数据写入到输出流中
bufferedOutputStream.write("hello
world".getBytes());
bufferedOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
public static void main(String[] args) {
// 借助一个字符流,实例化一个缓冲字符输入流
try (BufferedReader bufferedReader = new
BufferedReader(new FileReader("file\\day26\\src"))) {
// 从流中读取数据
char[] array = new char[100];
int length = 0;
while ((length = bufferedReader.read(array)) != -1)
{
System.out.print(new String(array, 0, length));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterTest {
public static void main(String[] args) {
// 借助一个字符输出流,实例化一个缓冲字符输出流对象
try (BufferedWriter bufferedWriter = new
BufferedWriter(new FileWriter("file\\day26\\dst"))) {
bufferedWriter.write("hello world");
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader
类中多了一个方法 readLine()
readLine()
是逐行读取, 但是, 只能读取到一行中的内容, 并不能读取走换行符。import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderSpecial {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new
FileReader("file\\day26\\src"))) {
// 1. 定义一个字符串,用来接收每一行读取到的数据
String line = "";
// 2. 循环读取数据
while ((line = reader.readLine()) != null) {
// 3. 将读取到的数据输出
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedWriter
类中多了一个方法 newLine()
linux
\nimport java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterSpecial {
public static void main(String[] args) {
try (BufferedWriter bufferedWriter = new
BufferedWriter(new FileWriter("file\\day26\\dst"))) {
bufferedWriter.write("hello world");
bufferedWriter.newLine();
bufferedWriter.write("你好,世界");
bufferedWriter.newLine();
bufferedWriter.write("end");
} catch (IOException e) {
e.printStackTrace();
}
}
}
LineNumberReader
是BufferedReader
的子类,不能读.但是可以提高效率,特有功能:设置行号,获取行号
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class Demo10{
public static void main(String[] args) throws IOException {
LineNumberReader lineNumberReader = new
LineNumberReader(new
FileReader("BigData2005N18\\src\\com\\qf\\test\\Demo1.java"));
//设置行号,默认从0开始,从1开始打印
lineNumberReader.setLineNumber(10);
String data = null;
while ((data = lineNumberReader.readLine()) != null) {
System.out.print(lineNumberReader.getLineNumber());//获取行号
System.out.print(data);
System.out.println();
}
lineNumberReader.close();
}
}
设计模式, 前人总结出来的对一些常⻅问题的解决方案,后人直接拿来使用.常用的设计模式:单例,工厂,代理,适配器,装饰,模板,观察者等,一共有23种
基于已经实现的功能,提供增强的功能
装饰设计模式的由来就来自于对缓冲流的实现
从缓冲流的⻆度讲解
1.使流原来的继承体更加的简单
2.提高了效率
3.由于是在原有的基础上提高增强的功能,所以他还要属于原来的体系
这个类, 并不是一个IO流。 是一个扫描器, 这个类最主要的作用, 是从一个文件中或者从一个流中浏览数据。 在这个类中封装了若干个方法, 方便了数据的读取。
ps
:这里nextLine
和BufferedReader
中的readLine
都可以读取一行数据。 但是区别在于: 结束条件不同。
BufferedReader:
如果读取到的数据是null, 说明没有下一行数据了。hasNextLine()
为falseimport java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ScannerTest {
public static void main(String[] args) {
// 其实,Scanner在使用结束之后,也是需要进行关闭的。 调用close方
法。
try (Scanner scanner = new Scanner(new
File("file\\day26\\src"))) {
// 读取文件中的内容
while (scanner.hasNextLine()) {
System.out.println(scanner.hasNextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
System.in
: “标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。输入源:可以发送数据到内存的设备
输出源:可以接收内存的数据的设备
1.当前的流已经打开并关联了输入源--键盘
2.如果不想让键盘充当输入源,可以通过setIn进行更换
3.是一个字节流
System.out
: 标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Scanner;
public class SystemInTest {
public static void main(String[] args) {
//创建了标准输入流并关联了键盘(默认的)
//InputStream inputStream = System.in;
//阻塞式方法
//int num = inputStream.read();
//System.out.println(num);
//实例演示
try (BufferedInputStream bis = new
BufferedInputStream(System.in)) {
byte[] array = new byte[128];
int length = 0;
while ((length = bis.read(array)) != -1) {
String str = new String(array, 0, length);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class SystemOutTest {
public static void main(String[] args) {
PrintStream original = System.out;
// PrintStream: 是一个打印流,可以将数据输出到指定位置。
try (PrintStream ps = new PrintStream(new
FileOutputStream("file\\day26\\logs", true))) {
// ps.println("hello world!");
// 重定向标准输出流
System.setOut(ps);
System.out.println("123");
}
catch (IOException e) {
e.printStackTrace();
} finally {
System.setOut(original);
}
System.out.println("你好");
// System.out; 标准输出流地址
// System.out -> ps
}
}
使用场景:在进行文件读取的时候, 如果项目采用的字符集和文件的字符集不同,会出现乱码的情况
import java.io.*;
public class TransforeTest {
public static void main(String[] args) {
read();
}
private static void read() {
// 当前的项目是 utf-8, 读取的文件是 GBK
// 如果需要以指定的字符集进行文件的读取,需要使用
//InputStreamReader(InputStream inputStream, String charsetName)
try (InputStreamReader reader = new
InputStreamReader(new FileInputStream("file\\day26\\src"),
"GBK")) {
char[] array = new char[128];
int length = 0;
while ((length = reader.read(array)) != -1) {
System.out.println(new String(array, 0,
length));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
public class TransforeTest {
public static void main(String[] args) {
write();
}
private static void write() {
// 以指定的字符集写数据
try (OutputStreamWriter writer = new
OutputStreamWriter(new FileOutputStream("file\\day26\\dst",
true), "GBK")) {
writer.write("hello world");
writer.write("你好,世界");
} catch (IOException e) {
e.printStackTrace();
}
}
}
PrintStream
PrintWriter
File
类型的文件public class Demo14 {
public static void main(String[] args) throws IOException {
//设备:File类型的文件
// PrintStream p1 = new PrintStream(new
File("BigData2005N18\\test7.txt"));
//设备:字节输出流
// PrintStream p2 = new PrintStream(new
FileOutputStream("BigData2005N18\\test7.txt"));
//设备:字符串类型的文件
// PrintStream p3 = new
PrintStream("BigData2005N18\\test7.txt");
//实例
PrintStream p3 = new
PrintStream("BigData2005N18\\test7.txt");
//直接使用write方法打印,默认只支持一个字节,所以当数据超出了一个字节的范围,会出现下面的错误.
p3.write(97);//00000000 01100001 默认会将高字节删掉 01100001-----a
p3.write(353);//00000001 01100001 ----01100001---a
//一般我们不直接使用write方法,而是使用print方法
p3.println(353);//直接输出353
//这是print方法的内部实现原理
//先将353转成字符串再转成字节数组
p3.write(String.valueOf(353).getBytes());
p3.close();
}
}
字符打印流支持的设备:
File
类型的文件注意点:
方法:public PrintWriter(Writer out, boolean autoFlush)
autoFlush - boolean
变量;如果为 true,则 println、printf
或 format 方法将自动刷新输出缓冲区
但是执行print方式时需要手动刷新
public class Demo11 {
public static void main(String[] args) throws IOException {
PrintWriter pWriter = new PrintWriter(new
FileWriter("test5.txt"));
pWriter.write("bingbing");
pWriter.close();
}
}
常用字符集
GBK/GB2312
ISO8859-1
UTF-8
对中文的处理
一个汉字:GBK:2个字节 ISO8859-1:1个字节 utf-8:3个字节 unicode:2个字节
(内部编码)
说明:GBK,UTF-8是支持中文的,ISO8859-1不支持中文
编码:将字符串转化成byte序列的过程
解码:是将byte序列转成字符串的过程
编码错误:乱码:在执行读与写的时候,由于使用的字符集不同,造成了编码的错误.
注意事项:
ObjectInputStream、 ObjectOutputStream
, 主要是用来做对象的序列化和反序列化的。java.io.Serilizable
接口。实现了Serializable接口的类可以达到的目的:
1.可以进行序列化
2.进行序列化的类的元素都必须支持序列化
3.可序列化类的所有子类型本身都是可序列化的。
4.接口本身没有方法或字段,只是用来表示可序列化的语义
注意点:
1. ClassNotFoundException:当前的类没有找到
分析:将Person对象进行序列化之后,将Person类删除,再进行反序列化的时候出现了
异常
原因:反序列化在执行的时候依赖字节码文件,当类没有了,字节码文件无法创建,反序列
化失败
2.java.io.InvalidClassException 无效的类
出现的原因:没有声明自己的serialVersionUID,而使用系统的.在进行反序列化的时
候,类被改动了,系统认为现在的类
已经不是原来的类了(在使用系统的id进行识别的时候,重写给Person设置了id),认为
此类无效
3.使用系统的serialVersionUID与自定义的ID的区别?
使用系统的,序列化和反序列化,id不能手动设置,使用的是编译器默认生成的,一旦类
发生了改动,id会重新赋值
使用自定义的,序列化和反序列化,id不会发生改变,所以当反序列化的时候,即使对
Person类进行了一些改动,也能继续反序列化
/*
创建Person对象,并进行序列化处理
要想让Person类的对象可以实现序列化,必须让Person类实现Serializable接口
类通过实现 java.io.Serializable 接口以启用其序列化功能。
未实现此接口的类将无法使其任何状态序列化或反序列化。
注意:不仅要求当前类可序列化,而且要求当前类的所有元素本身都是可序列化的
(比如:ArrayList)
*/
class Person implements Serializable{
// /**
// * generated:由编译器自动生成的,后面加L表示long型
// */
private static final long serialVersionUID = -7224641225172644265L;
// /**
// * default:UID是由用户自己指定的,默认值是1L]
// private static final long serialVersionUID = 1L;
String name;
int age;
double height;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void show(){
System.out.println("show-oo");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void run(){
System.out.println("run");
}
}
//可序列化类的所有子类型本身都是可序列化的。
class GoodPerson extends Person{
double weight;
public GoodPerson(String name, int age, double weight) {
super(name, age);
this.weight = weight;
}
}
public class Demo13 {
static Person p1;
public static void main(String[] args) throws IOException,
ClassNotFoundException {
//序列化
//fun1();
//逆序列化
fun2();
}
//序列化
public static void fun1() throws IOException {
//创建序列化流
ObjectOutputStream objectOutputStream = new
ObjectOutputStream(new
FileOutputStream("D:\\ideaProgram\\BigDataBK2001N06\\BigDataBK20
01N20\\copyDemo5.java"));
//objectOutputStream.writeInt(2);
//对对象进行序列化
p1 = new Person("zhangsan",20);
objectOutputStream.writeObject(p1);
//可序列化类的所有子类型本身都是可序列化的。
//objectOutputStream.writeObject(new
GoodPerson("hah",20,20));
//序列化后要及时关闭流
objectOutputStream.close();
}
//逆序列化
public static void fun2() throws IOException,
ClassNotFoundException {
ObjectInputStream objectInputStream = new
ObjectInputStream(new
FileInputStream("D:\\ideaProgram\\BigDataBK2001N06\\BigDataBK200
1N20\\copyDemo5.java"));
// int num = objectInputStream.readInt();
// System.out.println(num);
//反序列化
Object o = objectInputStream.readObject();
System.out.println(o == p1);//false 说明反序列化后的对象与原来的对象是两块儿空间.
System.out.println(o);
//objectInputStream.close();
}
Serializable
接口,只要实现了接口就可以序列化.包括集合,包装类等Serializable
接口Properties也不是一个IO流, 是一个集合。 是Hashtable
的子类。使用Properties主要是为了描述程序中的属性列表文件。 有时候, 我们会将一些比较简单的项目的配置信息, 以 .properties 格式的文件进行存储。 可以使用Properties对象读写 .properties 文件。
因为存储的是属性,属性本来就是以键值对的方式存储.这里的键和值都必须是字符串.所以不需要考虑泛型
HashTable
的子类,所以也是键值对形式,保存,操作更容易