IO流
1 流的作用:
流是对各类文件的操作,而且是从头到尾进行读和写的 (按顺序) 不想RandomAccessFile 一样可以随意
2 流的分类
按照方向:输入输出流
* a: 参照物
*到底是输入还是输出,都是以Java程序为参照
*b: Output
*把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作
*程序到文件称为输出
*c: Input
*把持久设备上的数据读取到内存中的这个动作称为输入(读)Input操作
*文件到程序称为输入
*d: IO操作
*把上面的这种输入和输出动作称为IO操作
按照数据单位分:字符流字节流
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
功能不同:节点流和处理流
节点流(不管是字符节点流还是字节节点流)可以从特定的数据源上读取数据 处理流不能单独使用在数据源上可以套用在节点流或者处理流上面(能用的流说明源代码已经实现了)
3所有的流类型位于IO包下的四个抽象类
| 字节流 | 字符流 |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
5 四大抽象类中的方法
1)inputStream
基本方法
intread(); 读取一个字节并且放在int 的低八位以整数的形式返回 0-255 -1 读到头
intread(byte[] buffer); 返回实际读取的字节数如果读前已经到末尾就返回 -1 读到字符数组中
intread(byte[] buffer,int offset ,int length)throws IOException; 读数组中的开始位置和长度
voidclose() 关闭流
longskip();跳过n个字节不读返回实际跳过的字节数
2)outputStream
void write(int b) 写一个字节并且放在int 的低8位
void write(byte [] b)
void write(byte [] b int offset ,int length) 读数组中的开始位置和长度
void close() 关闭流
void flush();
3)Reader
int read() 读取一个字符并且以整数的形式返回作为整数读取的字符,范围在0 到 65535 之间)返回-1 说明到末尾了放在低16位
int read(char[] buf) 读到字符数组中返回实际读到的字符数
intread(char[] buffer,int offset ,int length)throws IOException; 读数组中的开始位置和长度
void close() 关闭流
longskip();跳过n个字节不读返回实际跳过的字节数
4)Writer
void writer (int c) 向输出流中写入一个字符数据该字节数据位参数的低16 位
void write(char [] b) 从字符类型的数组中的数据写入输出流
void write(char [] b int offset ,int length) 写数组中的开始位置和长度到流中
void close() 关闭流
void flush();将输出流中缓冲的数据全部写出到目的地
5)flush方法和close方法区别
*a: flush()方法
* 用来刷新缓冲区的,刷新后可以再次写出,只有字符流才需要刷新
*b: close()方法
* 用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出 关闭流只用一个最大的那个流进行关闭
5)流的分别讲解
5.1) * A: 字节输出流FileOutputStream写字节
*a: FileOutputStream
*写入数据文件,学习父类方法,使用子类对象
*b: FileOutputStream构造方法
*作用:绑定输出的输出目的
*FileOutputStream(File file)
*创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
*FileOutputStream(File file, boolean append)
*创建一个向指定 File 对象表示的文件中写入数据的文件输出流,以追加的方式写入。
*FileOutputStream(String name)
*创建一个向具有指定名称的文件中写入数据的输出文件流。
*FileOutputStream(String name, boolean append)
* 创建一个向具有指定 name 的文件中写入数据的输出文件流,以追加的方式写入。
*c: 流对象使用步骤
* 1. 创建流子类的对象,绑定数据目的
* 2. 调用流对象的方法write写
* 3. close释放资源
* d: 注意事项
* 流对象的构造方法,可以创建文件,如果文件存在,直接覆盖文件的内容也进行覆盖
5.2)字节输出流FileOutputStream写字节数组
*A: 字节输出流FileOutputStream写字节数组
*a: 方法介绍
* void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流
* void write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
*b: 案例代码
/*
* FileOutputStream
* 写入数据文件,学习父类方法,使用子类对象
*
* 子类中的构造方法: 作用:绑定输出的输出目的
* 参数:
* File 封装文件
* String 字符串的文件名
*
* 流对象使用步骤
* 1.创建流子类的对象,绑定数据目的
* 2.调用流对象的方法write写
* 3.close释放资源
*
* 流对象的构造方法,可以创建文件,如果文件存在,直接覆盖
5.3): 文件的续写和换行符号
*a: 文件的续写
* FileOutputStream构造方法, 的第二个参数中,加入true
*b: 换行符号
*在文件中,写入换行,符号换行 \r\n
*\r\n 可以写在上一行的末尾, 也可以写在下一行的开头
*c: 案例代码
/*
* FileOutputStream 文件的续写和换行问题
* 续写: FileOutputStream构造方法, 的第二个参数中,加入true
* 在文件中,写入换行,符号换行 \r\n
* \r\n 可以写在上一行的末尾, 也可以写在下一行的开头
*/
publicclass FileOutputStreamDemo1 {
publicstatic void main(String[] args)throws IOException {
Filefile = new File("c:\\b.txt");
FileOutputStreamfos = new FileOutputStream(file,true);
fos.write("hello\r\n".getBytes());
fos.write("world".getBytes());
fos.close();
}
}
5.4)字节输入流FileInputStream读取字节
*A: 字节输入流FileInputStream读取字节
*a: 方法介绍 没有追加的说法
*abstract int read() :
*从输入流中读取数据的下一个字节,返回-1表示文件结束
*int read(byte[] b)
*从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
*读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
*int read(byte[] b, int off, int len)
*将输入流中最多 len 个数据字节读入 byte 数组。
*void close()
*关闭此输入流并释放与该流关联的所有系统资源。
*b: 案例代码
/*
* FileInputStream读取文件
*
* 构造方法: 为这个流对象绑定数据源
*
* 参数:
* File 类型对象
* String 对象
* 输入流读取文件的步骤
* 1.创建字节输入流的子类对象
* 2.调用读取方法read读取
* 3.关闭资源
*
* read()方法,
* read()执行一次,就会自动读取下一个字节
* 返回值,返回的是读取到的字节, 读取到结尾返回-1
读出来的byte[] b 数组可以通过 new String(b) 来进行解码
String(byte[] bytes, int offset, int length)
通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的String。
5.4)字符输出流写文本FileWriter类
*A: 字符输出流写文本FileWriter类
*a: 方法介绍
* void write(int c)
* 写入单个字符
*void write(String str)
*写入字符串
*void write(String str, int off, int len)
*写入字符串的某一部分
*void write(char[] cbuf)
*写入字符数组
*abstract void write(char[] cbuf, intoff, int len)
* 写入字符数组的某一部分
*b: 案例代码
/*
* 字符输出流
* java.io.Writer 所有字符输出流的超类
* 写文件,写文本文件
*
* 写的方法 write
* write(int c) 写1个字符
* write(char[] c)写字符数组
* write(char[] c,int,int)字符数组一部分,开始索引,写几个
* write(String s) 写入字符串
*
* Writer类的子类对象 FileWriter
*
* 构造方法: 写入的数据目的
* File 类型对象
* String 文件名
*
* 字符输出流写数据的时候,必须要运行一个功能,刷新功能
* flush()
*/
5.5)字符输入流读取文本FileReader类
*A: 字符输入流读取文本FileReader类
*a: 方法介绍
* int read()
*读取单个字符
*int read(char[] cbuf)
*将字符读入数组
*abstract int read(char[] cbuf, int off,int len)
*将字符读入数组的某一部分。
*b: 案例代码
/*
* 字符输入流读取文本文件,所有字符输入流的超类
* java.io.Reader
* 专门读取文本文件
*
* 读取的方法 : read()
* intread() 读取1个字符
* intread(char[] c) 读取字符数组
*
* Reader类是抽象类,找到子类对象 FileReader
*
* 构造方法: 绑定数据源
* 参数:
* File 类型对象
* String文件名
可以强制转换进行解码(char)直接转 构造函数中可以直接写文件的名字 因为底层已经实现了字节流和转换流来交节看源码
*/
5.6)* a: 转换流概述
*OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的字符编码表,将要写入流中的字符编码成字节
* 将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去
OutputStreamWriter写文本文件
*A: OutputStreamWriter写文本文件
*a: OutputStreamWriter
*java.io.OutputStreamWriter 继承Writer类
*就是一个字符输出流,写文本文件
* write()字符,字符数组,字符串
*字符通向字节的桥梁,将字符流转字节流
*OutputStreamWriter 使用方式
*构造方法:
*OutputStreamWriter(OuputStream out)接收所有的字节输出流
*字节输出流: FileOutputStream
*OutputStreamWriter(OutputStream out, String charsetName)
*String charsetName 传递编码表名字 GBK UTF-8
* OutputStreamWriter 有个子类, FileWriter
*b: 案例代码
publicclass OutputStreamWriterDemo {
publicstatic void main(String[] args)throws IOException {
// writeGBK();
writeUTF();
}
/*
* 转换流对象OutputStreamWriter写文本
* 采用UTF-8编码表写入
*/
转换流_字节转字符流过程
*A: 转换流_字节转字符流过程
*a: InputStreamReader
*java.io.InputStreamReader 继承 Reader
*字符输入流,读取文本文件
*字节流向字符的敲了,将字节流转字符流
*读取的方法:
* read() 读取1个字符,读取字符数组
*技巧
*OuputStreamWriter写了文件
*InputStreamReader读取文件
*OutputStreamWriter(OutputStream out)所有字节输出流
*InputStreamReader(InputStream in) 接收所有的字节输入流
*可以传递的字节输入流: FileInputStream
* InputStreamReader(InputStream in,String charsetName) 传递编码表的名字
*b: 图解
* 详见day24_source/转换流.JPG图片
* OutputStreamWriter 有个子类, FileWriter
InputStreamReader 有个子类 FileReader
区别
*OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
*FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。
当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
* 以下三句话功能相同
* InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"));//默认字符集。
* InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
* FileReader fr = newFileReader("a.txt");
InputSteamReader读取文本文件
*A: InputSteamReader读取文本文件
*a: 案例代码
publicclass InputStreamReaderDemo {
publicstatic void main(String[] args) throws IOException {
// readGBK();
readUTF();
}
/*
* 转换流,InputSteamReader读取文本
* 采用UTF-8编码表,读取文件utf
*/
publicstatic void readUTF()throws IOException{
//创建自己输入流,传递文本文件
FileInputStreamfis = new FileInputStream("c:\\utf.txt");
//创建转换流对象,构造方法中,包装字节输入流,同时写编码表名
InputStreamReaderisr = new InputStreamReader(fis,"UTF-8");
char[]ch = new char[1024];
intlen = isr.read(ch);
System.out.println(newString(ch,0,len));
isr.close();
}
/*
* 转换流,InputSteamReader读取文本
* 采用系统默认编码表,读取GBK文件
*/
public static voidreadGBK()throws IOException{
//创建自己输入流,传递文本文件
FileInputStreamfis = new FileInputStream("c:\\gbk.txt");
//创建转换流对象,构造方法,包装字节输入流
InputStreamReaderisr = new InputStreamReader(fis);
char[]ch = new char[1024];
int len =isr.read(ch);
System.out.println(newString(ch,0,len));
isr.close();
}
}
5.6)字节输出流缓冲流BufferedOutputStream
*A: 字节输出流缓冲流BufferedOutputStream
*a: BufferedOutputStream
*字节输出流的缓冲流
*java.io.BufferedOuputStream 作用: 提高原有输出流的写入效率
*BufferedOuputStream 继承 OutputStream
*方法,写入 write 字节,字节数组
*构造方法:
*BufferedOuputStream(OuputStream out)
*可以传递任意的字节输出流, 传递的是哪个字节流,就对哪个字节流提高效率
5.7)字节输入流缓冲流BufferedInputStream
*A: 字节输入流缓冲流BufferedInputStream
*a: BufferedInputStream
*字节输入流的缓冲流
*继承InputStream,标准的字节输入流
*读取方法 read() 单个字节,字节数组
*构造方法:
*BufferedInputStream(InputStream in)
*可以传递任意的字节输入流,传递是谁,就提高谁的效率
* 可以传递的字节输入流 FileInputStream)
5.8)字符输出流缓冲流BufferedWriter
*A: 字符输出流缓冲流BufferedWriter
*a: BufferedWriter
*字符输出流缓冲区流
*java.io.BufferedWriter 继承 Writer
*写入方法 write () 单个字符,字符数组,字符串
*构造方法:
*BufferedWriter(Writer w)传递任意字符输出流
*传递谁,就高效谁
*能传递的字符输出流 FileWriter, OutputStreamWriter
12字符输出流缓冲流BufferedWriter特有方法newLine
*A: 字符输出流缓冲流BufferedWriter特有方法newLine
*a: 方法介绍
*void newLine() 写换行
*newLine()文本中换行, \r\n也是文本换行
*方法具有平台无关性
*Windows \r\n
*Linux \n
*newLine()运行结果,和操作系统是相互关系
*JVM: 安装的是Windows版本,newLine()写的就是\r\n
*安装的是Linux版本,newLine()写的就是\n
/*
5.9)字符输入流缓冲流BufferedReader
*A: 字符输入流缓冲流BufferedReader
*a: 概述
*从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取
*public String readLine() 读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
###14字符输入流缓冲流BufferedReader读取文本行
*A: 字符输入流缓冲流BufferedReader读取文本行
*a: BufferedReader
*字符输入流缓冲流
*java.io.BufferedReader 继承 Reader
*读取功能 read() 单个字符,字符数组
*构造方法:
*BufferedReader(Reader r)
*可以任意的字符输入流
FileReader InputStreamReader
*BufferedReader自己的功能
*String readLine() 读取文本行 \r\n
*方法读取到流末尾,返回null
* b: 小特点
* 获取内容的方法一般都有返回值
* int 没有返回的都是负数
* 引用类型找不到返回null
* boolean 找不到返回false
5.10)对象的序列化与反序列化
*A: 对象的序列化与反序列化
*a: 基本概念
*对象的序列化
*对象中的数据,以流的形式,写入到文件中保存过程称为写出对象,对象的序列化
*ObjectOutputStream将对象写道文件中,实现序列化
*对象的反序列化
*在文件中,以流的形式,将对象读出来,读取对象,对象的反序列化
*ObjectInputStream 将文件对象读取出来
ObjectOutputStream流写对象
*A: ObjectOutputStream流写对象
*a: 简单介绍
* IO流对象,实现对象Person序列化,和反序列化
* ObjectOutputStream 写对象,实现序列化
* ObjectInputStream 读取对象,实现反序列化
ObjectInputStream流读取对象
*A: ObjectInputStream流读取对象
*a: 简单介绍
*ObjectInputStream
*构造方法:ObjectInputStream(InputStream in)
*传递任意的字节输入流,输入流封装文件,必须是序列化的文件
*Object readObject() 读取对象
*b: 案例代码
/*
* IO流对象,实现对象Person序列化,和反序列化
* ObjectOutputStream 写对象,实现序列化
* ObjectInputStream 读取对象,实现反序列化
*/
需要掌握流的总结
FileInputStream | FileOutputStream |
InputStreamReader | OutputStreamWriter |
FileReader | FileWriter |
ObjectInputStream | * ObjectOutputStream |
| PrintStream PrintWriter |
DataInputStream | DataOutputStream |
BufferedReader | BufferedWriter |
ByteArrayInputStream | ByteArrayOutputStream |
BufferedInputStream | BufferedOutputStream |
5.11)静态不能序列化
*A: 静态不能序列化
*a: 原因
*序列化是把对象数据进行持久化存储
*静态的东西不属于对象,而属于类
transient关键字
*A: transient关键字
*a: 作用
*被transient修饰的属性不会被序列化
*transient关键字只能修饰成员变量
Serializable接口的含义
*A:Serializable接口的含义
*a: 作用
*给需要序列化的类上加标记。该标记中没有任何抽象方法
*只有实现了 Serializable接口的类的对象才能被序列化
序列化中的序列号冲突问题
*A: 序列化中的序列号冲突问题
*a: 问题产生原因
*当一个类实现Serializable接口后,创建对象并将对象写入文件,之后更改了源代码(比如:将成员变量的修饰符有private改成public),
再次从文件中读取对象时会报异常
*见day25_source文件夹下的"序列号的冲突.JPG"文件
序列化中自定义的序列号
*A: 序列化中自定义的序列号
*a: 定义方式
*private static final long serialVersionUID = 1478652478456L;
* 这样每次编译类时生成的serialVersionUID值都是固定的
### 对象流
Java 提供了对象流,是处理流,可以简化对象的IO。对象流要求参与对象序列化的类必须实现序列化接口
> 注意:实现序列化接口的类最好添加序列化版本号 serialVersionUID
> 瞬态关键字修饰的属性在序列化时候被忽略掉。
5.12)打印流和特性
*A: 打印流和特性
*a: 概述
*打印流添加输出数据的功能,使它们能够方便地打印各种数据值表示形式.
*打印流根据流的分类:
*字节打印流 PrintStream
*字符打印流 PrintWriter
*方法:
*void print(String str): 输出任意类型的数据,
*void println(String str): 输出任意类型的数据,自动写入换行操作
*b: 特点
*此流不负责数据源,只负责数据目的
*为其他输出流,添加功能
*永远不会抛出IOException,但是可能抛出别的异常
*两个打印流的方法,完全一致
*构造方法,就是打印流的输出目的端
* PrintStream构造方法
*接收File类型,接收字符串文件名,接收字节输出流OutputStream
*PrintWriter构造方法
* 接收File类型,接收字符串文件名,接收字节输出流OutputStream, 接收字符输出流Writer
* b: 结果分析
*println数组,只有打印字符数组时只有容,其余均打印数组的地址
*因为api中定义了打印字符数组的方法,其底层是在遍历数组中的元素
* 而其他打印数组的方法,都是将数组对象编程Object,其底层再将对象编程String,调用了String s = String.valueOf(x);方法
PrintWriter(File file) |
PrintWriter(File file, String csn) |
PrintWriter(OutputStream out) |
PrintWriter(OutputStream out, boolean autoFlush) |
PrintWriter(String fileName) |
PrintWriter(String fileName, String csn) |
PrintWriter(Writer out) |
PrintWriter(Writer out, boolean autoFlush) |
* 打印流,可以开启自动刷新功能
* 满足2个条件:
* 1. 输出的数据目的必须是流对象
* OutputStream Writer
* 2. 必须调用println,printf,format三个方法中的一个,启用自动刷新
6 字符节点流可以直接连数据源的(不能和字节节点流连用) 字符处理流只能套结在字符节点流或者字符处理流上 (而转换流除外)它可以把字符节点流转化成字符处理流转化流也可以套结在字节处理流上把()
printWriter() 里面封装了可以直接放文件名具体的看构造函数
publicPrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(newFileOutputStream(fileName))),
false);
}
7 RamdomAccessFile 与流
7.1. RamdomAccessFile 可以随机访问(可以在文件的任何位置开始读写),而流只能单向读写,读写过去就不能返回头重写读写了。流读写是分开的两个类型。
7.2. 流是一套可以扩展的体系,提供了无限的扩展功能流。而RAF只能出来byte数据和基本类型数据。RAF功能没有提供丰富扩展功能。
### 节点流与处理流
- **节点流**:是连接到数据源进行最基本的IO功能的流,是其他处理流的基础。
- **处理流**:是流的功能扩展,有很多处理流,体现了流的丰富扩展功能。处理流因为是基于其他流进行的扩展功能,所以处理流不能单独工作,必须依赖其他的流。
按照功能不同分为节点流和处理流
节点流连接数据源的
处理流是节点流之上的处理流套结在节点流或者处理流之上的
IO流得操作都是从头到尾得不能通过指针来定位 写的时候
1 PrintWriter(String fileName, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。
2 PrintWriter(File file)
使用指定文件创建不具有自动行刷新的新 PrintWriter。
3
|
PrintWriter(String fileName) |
8 编码表
* A: 编码表
* a: 定义:
* 生活中字符和计算机二进制的对应关系表,就是编码表
* b: 分类
* 1、ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx
* 2、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
* 3、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
* GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
* GB18030:最新的中文码表,目前还没有正式使用。
* 4、unicode:国际标准码表:无论是什么文字,都用两个字节存储。
* Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
* Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
* 5、UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。
* 6、能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
* 对于我们开发而言;常见的编码 GBK UTF-8 ISO-8859-1
* 文字--->(数字) :编码。“abc”.getBytes() byte[]
* (数字)--->文字 : 解码。 byte[]b={97,98,99} new String(b)
9流对象的操作规律
* A: IO流对象的操作规律
* a: 明确一:要操作的数据是数据源还是数据目的。
* 源:InputStream Reader
* 目的:OutputStreamWriter
* 先根据需求明确要读,还是要写。
* b: 明确二:要操作的数据是字节还是文本呢?
* 源:
* 字节:InputStream
* 文本:Reader
* 目的:
* 字节:OutputStream
* 文本:Writer
* c: 明确三:明确数据所在的具体设备。
* 源设备:
* 硬盘:文件 File开头。
* 内存:数组,字符串。
* 键盘:System.in;
* 网络:Socket
* 目的设备:
* 硬盘:文件 File开头。
* 内存:数组,字符串。
* 屏幕:System.out
* 网络:Socket
* 完全可以明确具体要使用哪个流对象。
* d: 明确四:是否需要额外功能呢?
* 额外功能:
* 转换吗?转换流。InputStreamReader OutputStreamWriter
* 高效吗?缓冲区对象。BufferedXXX
* 已经明确到了具体的体系上。