从零开始的java学习Day15----------基础篇(IO流)
IO流
我们电脑中的数据,都是一个个二进制数字(字节)组成,我们传输数据,就可以看做是一种数据的流动,以内存为基准按照流动的方向,就分为输入流(流向内存)和输出流(输出内存)。输入也叫做读取数据,输出也叫做写出数据,这就是IO流
I:input(输入)
O:output(输出)
IO的分类
按照数据的流向分为:
输入流:把数据从其他设备上读取到内存中的流。
输出流:把数据从内存 中写出到其他设备上的流
按照数据类型分为:
字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。
*父类
字节流:字节输入流(InputStream) 字节输出流(OutputStream)
字符流:字符输入流(Reader) 字符输出流(Writer)
字节流
字节输出流OutputStream
OutputStream是所有字节输出流的超类,将指定字节信息写出到目的地,他里面定义了字节输出流的基本共性方法。
常用方法:
void close() :关闭此输出流并释放与此流相关联的任何系统资源。
void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
abstract void write(int b) :将指定的字节输出到输出流(自动转为字符型)
void write(byte[] b):将指定字节数组内元素依次输出到输出流
void write(byte[] b, int off, int len) :将指定数组内元素按照指定索引输出指定个数
FileOutputStream类
但是OutputStream 是一个抽象类,并不能直接创建对象,这里我们介绍他的常用子类:FileOutputStream
该类对象文件是输出流,用于将数据写出到文件
构造方法:
FileOutputStream(File file):创建输出流对象,指向File内的路径,如果没有,会自动创建
FileOutputStream(File file,boolean append):创建输出流对象,指向File内的路径,如果没有,会自动创建,里面的布尔类型如果为true,则执行输出流的时候是添加,如果是false则是覆盖
FileOutputStream(String name): 创建输出流对象,指向填入的路径,如果没有,会自动创建
FileOutputStream(String name,boolean append): 创建输出流对象,指向填入的路径,如果没有,会自动创建,里面的布尔类型如果为true,则执行输出流的时候是添加,如果是false则是覆盖
关于换行:
- Windows系统里,每行结尾是 回车+换行 ,即\r\n;
- Unix系统里,每行结尾只有 换行 ,即\n;
- Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一
字节输入流InputStream
InputStream是所有字节输入流的超类,可以读取字节信息到内存中,他定义了字节输入流的基本共性方法
void close() :关闭此输入流并释放与此流相关联的任何系统资源。
abstract int read():从输入流读取数据的下一个字节,自动转为Int类型返回,如果到了末尾,就返回-1
int read(byte[] b):从输入流中读取一些字节数,并将它们存储到字节数组 b中 ,一次最多读取存储数组的长度个字节,返回赋值的元素数量,用同一个数组会覆盖,到了末尾,就停止赋值,返回-1
read(byte[] b, int off, int len):和上面的区别就是可以从指定索引开始,读取存储指定个数的字节
注意:字节流读取的单位,是一个字节
FileInputStream类
同样的InputStream,是一个抽象类,我们先学习他的常用子类FileInputStream 来使用字节输入流:
FileInputStream类是文件输入流,从文件中读取字节。
构造方法:
FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
注意:这里的文件路径必的文件须是存在的,如果不存在会报错。
字符流
当我们使用字节流读取文本数据的时候,可能会遇到一点问题,那就是遇到中文字符时,没办法显示,那是因为一个中文字符会占多个字节储存。所以Java中也提供了一些字符流,以字符为单位存储数据,专门用于处理文本文件。
注意:
- 字符流,只能操作文本文件,不能操作图片,视频等非文本文件
- 如果是一个中文以gbk编码形式存储,就是占2个字节,以utf-8编码形式存储就会占3个字节
- 而windws系统中默认是gbk编码, idea开发工具则是utf-8编码,意思就是如果用不同编码打开就会乱码。
字符输入流Reader
Reader是字符读取流的所有超类,该类可以读取字节信息到内存中。它定义了字符输入流的基本共性方法
void close() :关闭此流并释放与此流相关联的任何系统资源。
int read(): 从输入流读取一个字符。(基本类型自动转换后返回int格式)如果到末尾则返回-1
int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,返回存入的元素个数(默认存满),如果到末尾了就返回-1
int read(char[] cbuf,int off,int len): 比上面的增加了,存入指定索引,指定个数个元素
FileReader类
同样的,因为Reader是抽象类,我们都是使用它的子类,FileReader就是一个字符输入流的常用类
构造方法:
FileReader(File file): 创建一个新的 FileReader ,使用File对象作为路径指定
FileReader(String fileName): 创建一个新的 FileReader ,使用字符串形式作为路径指定
字符输出流Writer
Writer是字符输出流的所有超类,该类可以将指定的信息写出到目的地,他定义了字节输出流的基本共性功能方法
abstract void close() :关闭此输出流并释放与此流相关联的任何系统资源。
abstract void flush() :刷新此输出流并强制任何缓冲的输出字符被写出。
void write(int c) :写出一个字符,(写入目的地)
void write(char[] cbuf):将整个字符数组写入目的地
abstract void write(char[] b, int off, int len) :比上面的多了指定索引开始和指定个数
void write(String str) :写出一个字符串到目的地
FileWriter类
因为Writer是一个抽象类,我们要实用,也都是用他的子类,FileWriter就是一个他的常用子类
构造方法:
FileWriter(File file): 创建一个新的 FileWriter,用给定的File里的路径指定
FileWriter(String fileName): 创建一个新的 FileWriter,用给定的字符串的路径指定
关闭和刷新
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要Flush方法。
- flush :刷新缓冲区,流对象可以继续使用。
- close :关闭流,释放系统资源。关闭前会刷新缓冲区。(不能再继续读取或写出操作了)
如何拷贝一个文件
- 1.创建一个字节输入流对象,封装数据源文件(路径)
- 2.创建一个字节输出流对象,封装目的地文件
- 3.定义一个int类型的变量,用来存储读取到的字节数据
- 4.循环读取,只要满足条件,就一直读取
- 5.写出数据
- 6.关闭流,释放资源
缓冲流(高效流)
作为IO流的入门,上面都是一些基本的流,现在我们学习一些更加强大的流。缓冲流就是其中之一。
- 缓冲流也叫高效流,它内置了一个长度为8192的byte数组作为缓冲区,我们从读写数据,可以一次性读8192字节到缓冲流的缓冲区,直接在内存中进行我们的操作(比如一次读取一个字节),减少了与硬盘的交互(内存内执行效率比硬盘快),提交了速度。
- 缓冲流也就字节缓冲流和字符缓冲流,都有各自的输入和输出流
字节缓冲流
输入流:BufferedInputStream
输出流:BufferedOutputStream
构造方法:
// 创建字节缓冲输入流,需要传入一个InputStream的实现类,可以使用昨天学的FileInputStream
BufferedInputStream bis = new BufferedInputStream(InputStream a);
// 创建字节缓冲输出流,需要传入一个OutputStream的实现类,可以使用昨天学的FileOutputStream
BufferedOutputStream bos = new BufferedOutputStream(OutputStream a);
字节缓冲流也继承了我们昨天学的两个字节输入输出流的超类IntputStream、OutputStream,所以他的方法和我们昨天学的几个方法一样,也是用read输出,write输出,close关闭。
字符缓冲流
输入流:BufferedReader
输出流:BufferedWriter
构造方法:
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
和字节缓冲流一样,它们各自也都需要传入一个字符(输入/输出)流。方法也都继承了Reader和Writer。
除了继承的方法外,字符缓冲流还有它自己的特有方法:
BufferedReader(输入流):String readLine(): 读一行文字。,返回这一行字符的String,到末尾就返回null
BufferedWriter(输出流):void newLine(): 写一行行分隔符,由系统属性定义符号。相当于写一个"/r/n"
字符编码
- 字符编码,简单来说,就是一套自然语言的字符与二进制数之间对应的规则。
- 计算机中存储的数据都是二进制数表示的,但是我们在屏幕上看到的文字都是一些我们认识的东西(英文,数字,汉字等等),这是因为计算机已经自动帮我们进行转换了。
- 我们要将字符存储到计算机中,称为编码,从计算机中读取二进制数变为字符就叫做解码
- 不同的字符集所使用的编码和解码的规则是不一样的,如果我们用A字符集的规则套到B字符集上用,就会产生乱码现象
常用字符集
- Ascll:美国信息交换标准码,128个(0-127),包含英文字母,数字,常用符号等,我们之前用的int和char自动转换就是用的它
- GBK:常用的中文码表,winds默认使用的字符集,兼容Ascll
- UTF-8:业界标准,统一码,我们以后主要使用的,包含大部分国家的字符,兼容Ascll
转换流
可以将字节流转换为字符流,并且指定使用的字符集
InputStreamReader
InputStreamReader类,继承Reader,他可以将一个字节流输入对象转换为字符流的输入对象,并且可以指定所使用的字符集的解法方式解码
构造方法:
InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流,ider默认是UTF-8
InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流,例如GBK,就填入"gbk"就可以
他继承Reader,有Reader内的方法,例如read和close他都有
OutputStreamWriter
OutputStreamWriter类,继承Writer,他可以将一个字节流输出对象转换为字符流输出对象,并且指定所使用的字符集。
注意:我们直接用字节流输出中文字符,会默认使用UTF-8编码,一个字符占3个字节
构造方法:
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。Ider默认UTF-8
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。
他继承Writer,有Wreter内的方法,例如write和flush、close他都有
打印流
平时我们在控制台打印的数据,都是使用的print和println两个方法完成的,其实这两个方法都来自PrintStream类,只是我们使用System.out会自动返回一个PrintStream类,我们使用的链式编程完成的该方法
注:使用System.setout()方法,可以传入一个PrintStream对象,改变系统默认(默认控制台)的打印位置。
PrintStream
PrintStream类,打印输出流,可以打印输出东西到指定的地方
构造方法:
PrintStream(String n) //里面的String类型就是传入一个路径,如果没有,会自动生成
常用方法:print和pringtln
IO的异常处理
IO类异常有规范的处理形式
-
JDK7以前:
先创建(在try外)所要使用流的对象,赋值null
再使用try…catch,在try后的括号里,使用之前创建的对象,调用构造方法赋值。再执行我们要执行的代码
最后释放资源的时候,要把colse语句写到finally里面,如果有多条colse语句,就嵌套(因为colse也要try)放到finally里 -
JDK7及之后可以使用:
try后面可以加个小括号,把创建流对象的语句写到里面(多条创建以;隔开)。后面的语句体放到try的{}里,不用再写colse语句,系统执行完try里的会自动释放小括号里面定义的流对象的资源。 -
JDK9及之后可以使用:
它在JDK7上面优化了,可以把创建流对象的语句写在try之前,然后在try后的小括号里直接写定义好的对象(多个对象以;隔开)。