day35
学习注意事项
- 按照流的发展历史去学习
- 注意流与流之间的继承关系
- 举一反三
IO流
继day36
字节流继承图
字节流
应用场景:操作二进制数据(音频、视频、图片)
abstract class InputStream – 字节输入流的基类(抽象类)
abstract class OutputStream – 字节输出流的基类(抽象类)
class FileInputStream extends InputStream – 文件字节输入流
class FileOutputStream extends OutputStream – 文件字节输出流
class FilterInputStream extends InputStream – 过滤器字节输入流
class FilterOutputStream extends OutputStream – 过滤器字节输出流
class BufferedInputStream extends FilterInputStream – 带缓冲区的字节输入流
class BufferedOutputStream extends FilterOutputStream – 带缓冲区的字节输出流
默认缓冲区大小:8192字节
3.带缓冲区的字节输出流
利用 带缓冲区的字节输出流 向文件写入数据
1)不处理异常的方式
2)文件存在的情况
3)文件不存在的情况
经验:所有的输出流,文件不存在的情况都会创建文件
public class Test01 {
public static void main(String[] args) throws IOException {
//1.创建流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io.txt"));
//2.写入数据
bos.write("123abc".getBytes());
//3.关闭资源
bos.close();
}
}
4)在文件末尾追加内容
经验:在文件末尾追加考虑基础流的构造方法
public class Test02 {
public static void main(String[] args) throws IOException {
//1.创建流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io.txt",true));
//2.写入数据
bos.write("123abc".getBytes());
//3.关闭资源
bos.close();
}
}
5)处理异常的方式
写入数据也要抛异常
public static void main(String[] args){
BufferedOutputStream bos = null;
try {
//1.创建流对象
bos = new BufferedOutputStream(new FileOutputStream("io.txt",true));
//2.写入数据
bos.write("123abc".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
深入 带缓冲区的字节输出流
字节输出流对象调用write()【父类的write方法,但子类重写了】
理解: 将数据写入到字节缓冲数组中,提高效率
满了将数据写入到文件中
关闭时才将数据写入到文件中,即不管满没满都写入文件
public class Test04 {
public static void main(String[] args) throws IOException{
// FileOutputStream fos = new FileOutputStream("io.txt");
// //写几次,就和硬盘交互几次 -- 6次(内存与硬盘交互的次数)
// fos.write("1".getBytes());
// fos.write("2".getBytes());
// fos.write("3".getBytes());
// fos.write("a".getBytes());
// fos.write("b".getBytes());
// fos.write("c".getBytes());
// fos.close();
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io.txt"));
// //将数据写入到字节缓冲数组中 -- 1次(内存与硬盘交互的次数)
// bos.write("1".getBytes());
// bos.write("2".getBytes());
// bos.write("3".getBytes());
// bos.write("a".getBytes());
// bos.write("b".getBytes());
// bos.write("c".getBytes());
// //关闭时才将数据写入到文件中
// bos.close();
//默认缓冲区 -- 8192个字节(8*1024)
//BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io.txt"));
//自定义缓冲区大小 -- 2048个字节
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io.txt"), 2048);
}
}
手撕BufferedOutputStream底层源码
public class FilterOutputStream extends OutputStream {
protected OutputStream out;//0x001
//out - 0x001
public FilterOutputStream(OutputStream out) {
this.out = out;
}
//b - [65]
public void write(byte[] b) throws IOException {
this.write(b, 0, b.length);
}
@SuppressWarnings("try")
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}
}
public class BufferedOutputStream extends FilterOutputStream {
//缓冲区数组
protected byte[] buf;//new byte[8192]
//缓冲区存放数据的个数
protected int count;//1
//out - 0x001
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
//out - 0x001
//size - 8192
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
//b - [65]
//off - 0
//len - 1
public synchronized void write(byte b[], int off, int len) throws IOException {
//判断现在需要写出的数据长度是否大于缓冲区数组
if (len >= buf.length) {//1 >= 8192
//将缓冲区数组里的数据写入到文件,将缓冲区清空
flushBuffer();
//将线程需要写出的数据写入到文件
out.write(b, off, len);
return;
}
//判断现在需要写出的数据长度 超过了缓冲区剩余的存储长度
if (len > buf.length - count) {//1 > 8192-0
//将缓冲区数组里的数据写入到文件,将缓冲区清空
flushBuffer();
}
//将数据写入到缓冲区数组里
System.arraycopy(b, off, buf, count, len);
//更新缓冲区数组数据个数
count += len;
}
private void flushBuffer() throws IOException {
//count > 0说明缓冲区数组里有数据
if (count > 0) {
super.out.write(buf, 0, count);//调用的是FileOutputSteam的write()
count = 0;//缓冲区清空
}
}
public synchronized void flush() throws IOException {
//将缓冲区数组里的数据写入到文件,将缓冲区清空
flushBuffer();
super.out.flush();//调用的是FileOutputSteam的close() -- 关流
}
}
FileOutputStream fos = new FileOutputStream("io.txt");//0x001
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write("1".getBytes());
bos.write("2".getBytes());
bos.write("3".getBytes());
bos.write("a".getBytes());
bos.write("b".getBytes());
bos.write("c".getBytes());
bos.close();
4.带缓冲区的字节输入流
利用 带有缓冲区的字节输入流 读取文件里的数据
不处理异常
文件存在
文件不存在
经验:所有输入流,当文件不存在都会报错
public class Test05 {
public static void main(String[] args) throws IOException {
//1.创建流对象 (默认缓冲区大小:8192字节)
//BufferedInputStream bis = new BufferedInputStream(new FileInputStream("io.txt"));
//1.创建流对象 (自定义缓冲区大小:2048字节)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("io.txt"),2048);
//2.读取数据
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
System.out.println(new String(bs, 0, len));
}
//3.关闭资源
bis.close();
}
}
- 处理异常
public static void main(String[] args) {
BufferedInputStream bis = null;
try {
//1.创建流对象 (默认缓冲区大小:8192字节)
bis = new BufferedInputStream(new FileInputStream("io.txt"));
//2.读取数据
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
System.out.println(new String(bs, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
拷贝文件
利用 带有缓冲区的字节输入、输出流
思路:读取源文件,写入目标文件
public class Copy {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("奇男子.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp4"));
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bis.close();
bos.close();
}
}
字符流
应用场景:操作纯文本数据
注意:字符流 = 字节流+编译器
编译器:可以识别中文字符和非中文字符,非中文字符获取1个字节(一个字节=一个字符),编译器会根据编码格式获取中文字符对应的字节数(GBK获取两个字节,UTF-8获取三个字节)
abstract class Reader – 字符输入流的基类(抽象类)
abstract class Writer – 字符输出流的基类(抽象类)
class InputStreamReader extends Reader – 字符输入转换流
class OutputStreamWriter extends Writer – 字符输出转换流
特点:将字节流转换为字符流,字符转换流是字节流和字符流的桥梁
class FileReader extends InputStreamReader – 文件字符输入流
class FileWriter extends OutputStreamWriter – 文件字符输出流
利用 字符输出转换流 向文件写入数据
不处理异常
文件存在
文件不存在
经验:所有的输出流,当文件不存在时都会创建文件
public class Test01 {
public static void main(String[] args) throws IOException {
//1.创建流对象(将文件字节输出流 转换为 字符输出转换流)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("io.txt"));
//2.写入数据
//osw.write(97);//写入码值
//char[] cs = {'1','2','3','a','b','c','我','爱','你'};
//osw.write(cs);//写入字符数组
//osw.write(cs, 3, 4);//写入字符数组,偏移量,写入长度
//osw.write("123abc我爱你");//字符串
osw.write("123abc我爱你", 3, 4);//写入字符串,偏移量,写入长度
//3.关闭资源
osw.close();
}
}
- 文件末尾追加
经验:考虑基础流的构造方法
public class Test02 {
public static void main(String[] args) throws IOException {
//1.创建流对象(将文件字节输出流 转换为 字符输出转换流)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("io.txt",true));
//2.写入数据
osw.write("123abc我爱你");
//3.关闭资源
osw.close();
}
}
- 处理异常
public static void main(String[] args) {
OutputStreamWriter osw = null;
try {
//1.创建流对象(将文件字节输出流 转换为 字符输出转换流)
osw = new OutputStreamWriter(new FileOutputStream("io.txt",true),"UTF-8");
//2.写入数据
osw.write("123abc我爱你");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if(osw != null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
利用 字符输入转换流 读取文件里的数据
不处理异常
文件存在
文件不存在
经验:所有的输入流,在文件不存在时都会报错
public class Test04 {
public static void main(String[] args) throws IOException {
//1.创建流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("io.txt"));
//2.读取数据
//read() -- 读取字符,如果读取到文件末尾则返回-1
int read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println((char)read);
read = isr.read();
System.out.println(read);
//3.关闭资源
isr.close();
}
}
减少冗余代码,但还是一个字节一个字节的读取数据
//while循环读取
//2.读取数据
int read;
while((read = isr.read()) != -1){
System.out.println((char)read);
}
加快读取
//指定长度读取
//2.读取数据
//isr.read(cs):读入cs数组长度的字符,将字符数据存入到数组中,并返回读取到的有效字节数,如果读取到文件末尾,则返回-1
char[] cs = new char[1024];
int len;
while((len = isr.read(cs)) != -1){
System.out.println(new String(cs, 0, len));
}
- 处理异常
注意:使用字符转换流最好加上编码格式!
public static void main(String[] args){
InputStreamReader isr = null;
try {
//1.创建流对象
isr = new InputStreamReader(new FileInputStream("io.txt"),"UTF-8");
//2.读取数据
//isr.read(cs):读入cs数组长度的字符,将字符数据存入到数组中,并返回读取到的有效字节数,如果读取到文件末尾,则返回-1
char[] cs = new char[1024];
int len;
while((len = isr.read(cs)) != -1){
System.out.println(new String(cs, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
拷贝文件
利用 字符输入、输出流 转换流
思路:读取源文件,写入目标文件
public class Copy {
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("小说.txt"), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copy.txt"), "GBK");
char[] cs = new char[1024];
int len;
while((len = isr.read(cs)) != -1){
osw.write(cs, 0, len);
}
isr.close();
osw.close();
}
}
总结:
1.BufferedInputStream 和 BufferedOutputStream
理解底层
理解缓冲区是如何提高效率2.InputStreamReader 和 OutputStreamWriter
理解转换流(字节流 -> 字符流)